1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "GitLogCache.h"
22 int static Compare(const void *p1
, const void*p2
)
24 return memcmp(p1
,p2
,20);
27 CLogCache::CLogCache()
29 m_IndexFile
= INVALID_HANDLE_VALUE
;
30 m_IndexFileMap
= INVALID_HANDLE_VALUE
;
33 m_DataFile
= INVALID_HANDLE_VALUE
;
34 m_DataFileMap
= INVALID_HANDLE_VALUE
;
39 void CLogCache::CloseDataHandles()
43 UnmapViewOfFile(m_pCacheData
);
48 CloseHandle(m_DataFileMap
);
49 m_DataFileMap
=INVALID_HANDLE_VALUE
;
53 CloseHandle(m_DataFile
);
54 m_DataFile
= INVALID_HANDLE_VALUE
;
58 void CLogCache::CloseIndexHandles()
62 UnmapViewOfFile(m_pCacheIndex
);
68 CloseHandle(m_IndexFileMap
);
69 m_IndexFileMap
= INVALID_HANDLE_VALUE
;
72 //this->m_IndexFile.Close();
73 //this->m_DataFile.Close();
76 CloseHandle(m_IndexFile
);
77 m_IndexFile
=INVALID_HANDLE_VALUE
;
81 CLogCache::~CLogCache()
87 int CLogCache::AddCacheEntry(GitRev
&Rev
)
89 this->m_HashMap
[Rev
.m_CommitHash
] = Rev
;
93 GitRev
* CLogCache::GetCacheData(CGitHash
&hash
)
95 m_HashMap
[hash
].m_CommitHash
=hash
;
96 return &m_HashMap
[hash
];
99 ULONGLONG
CLogCache::GetOffset(CGitHash
&hash
,SLogCacheIndexFile
*pData
)
103 pData
= m_pCacheIndex
;
108 SLogCacheIndexItem
*p
=(SLogCacheIndexItem
*)bsearch(hash
.m_hash
,pData
->m_Item
,
109 pData
->m_Header
.m_ItemCount
,
110 sizeof(SLogCacheIndexItem
),
119 int CLogCache::FetchCacheIndex(CString GitDir
)
122 if (!g_GitAdminDir
.GetAdminDirPath(GitDir
, m_GitDir
))
127 if( m_IndexFile
== INVALID_HANDLE_VALUE
)
129 CString file
= m_GitDir
+ INDEX_FILE_NAME
;
130 m_IndexFile
= CreateFile(file
,
132 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
135 FILE_ATTRIBUTE_NORMAL
,
138 if( m_IndexFile
== INVALID_HANDLE_VALUE
)
145 if( m_IndexFileMap
== INVALID_HANDLE_VALUE
)
147 m_IndexFileMap
= CreateFileMapping(m_IndexFile
, NULL
, PAGE_READONLY
,0,0,NULL
);
148 if( m_IndexFileMap
== INVALID_HANDLE_VALUE
)
155 if( m_pCacheIndex
== NULL
)
157 m_pCacheIndex
= (SLogCacheIndexFile
*)MapViewOfFile(m_IndexFileMap
,FILE_MAP_READ
,0,0,0);
158 if( m_pCacheIndex
==NULL
)
165 if( !CheckHeader(&m_pCacheIndex
->m_Header
))
171 if( m_DataFile
== INVALID_HANDLE_VALUE
)
173 CString file
= m_GitDir
+ DATA_FILE_NAME
;
174 m_DataFile
= CreateFile(file
,
176 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
179 FILE_ATTRIBUTE_NORMAL
,
182 if(m_DataFile
== INVALID_HANDLE_VALUE
)
189 if( m_DataFileMap
== INVALID_HANDLE_VALUE
)
191 m_DataFileMap
= CreateFileMapping(m_DataFile
, NULL
, PAGE_READONLY
,0,0,NULL
);
192 if( m_DataFileMap
== INVALID_HANDLE_VALUE
)
198 m_DataFileLength
= GetFileSize(m_DataFile
, NULL
);
199 if( m_pCacheData
== NULL
)
201 m_pCacheData
= (BYTE
*)MapViewOfFile(m_DataFileMap
,FILE_MAP_READ
,0,0,0);
202 if( m_pCacheData
==NULL
)
209 if(!CheckHeader( (SLogCacheDataFileHeader
*)m_pCacheData
))
220 ::DeleteFile(m_GitDir
+ INDEX_FILE_NAME
);
221 ::DeleteFile(m_GitDir
+ DATA_FILE_NAME
);
227 int CLogCache::SaveOneItem(GitRev
&Rev
, LONG offset
)
229 if(!Rev
.m_IsDiffFiles
)
232 SetFilePointer(this->m_DataFile
, offset
,0, 0);
234 SLogCacheRevItemHeader header
;
236 header
.m_Magic
=LOG_DATA_ITEM_MAGIC
;
237 header
.m_Version
=LOG_INDEX_VERSION
;
238 header
.m_FileCount
=Rev
.m_Files
.GetCount();
242 if(!WriteFile(this->m_DataFile
,&header
, sizeof(header
),&num
,0))
246 CString name
,oldname
;
247 for(int i
=0;i
<Rev
.m_Files
.GetCount();i
++)
249 SLogCacheRevFileHeader header
;
250 header
.m_Magic
=LOG_DATA_FILE_MAGIC
;
251 header
.m_Version
=LOG_INDEX_VERSION
;
252 header
.m_Action
= Rev
.m_Files
[i
].m_Action
;
253 header
.m_Stage
= Rev
.m_Files
[i
].m_Stage
;
254 header
.m_ParentNo
= Rev
.m_Files
[i
].m_ParentNo
;
255 name
= Rev
.m_Files
[i
].GetGitPathString();
256 header
.m_FileNameSize
= name
.GetLength();
257 oldname
= Rev
.m_Files
[i
].GetGitOldPathString();
258 header
.m_OldFileNameSize
= oldname
.GetLength();
260 stat
= Rev
.m_Files
[i
].m_StatAdd
;
261 header
.m_Add
= (stat
== _T("-"))? 0xFFFFFFFF:_tstol(stat
);
262 stat
= Rev
.m_Files
[i
].m_StatDel
;
263 header
.m_Del
= (stat
== _T("-"))? 0xFFFFFFFF:_tstol(stat
);
265 if(!WriteFile(this->m_DataFile
,&header
, sizeof(header
)-sizeof(TCHAR
),&num
,0))
270 if(!WriteFile(this->m_DataFile
,name
.GetBuffer(), name
.GetLength()*sizeof(TCHAR
),&num
,0))
273 if(!oldname
.IsEmpty())
275 if(!WriteFile(this->m_DataFile
,oldname
.GetBuffer(), oldname
.GetLength()*sizeof(TCHAR
),&num
,0))
283 int CLogCache::LoadOneItem(GitRev
&Rev
,ULONGLONG offset
)
285 if(m_pCacheData
== NULL
)
288 if (offset
+ sizeof(SLogCacheRevItemHeader
) > m_DataFileLength
)
291 SLogCacheRevItemHeader
*header
;
292 header
= (SLogCacheRevItemHeader
*)(this->m_pCacheData
+ offset
);
294 if( !CheckHeader(header
))
298 SLogCacheRevFileHeader
*fileheader
;
300 offset
+= sizeof(SLogCacheRevItemHeader
);
301 fileheader
=(SLogCacheRevFileHeader
*)(this->m_pCacheData
+ offset
);
303 for (DWORD i
= 0; i
< header
->m_FileCount
; i
++)
309 if (offset
+ sizeof(SLogCacheRevFileHeader
) > m_DataFileLength
)
312 if(!CheckHeader(fileheader
))
315 CString
file(fileheader
->m_FileName
, fileheader
->m_FileNameSize
);
316 if(fileheader
->m_OldFileNameSize
)
318 oldfile
= CString(fileheader
->m_FileName
+ fileheader
->m_FileNameSize
, fileheader
->m_OldFileNameSize
);
320 path
.SetFromGit(file
,&oldfile
);
322 path
.m_ParentNo
= fileheader
->m_ParentNo
;
323 path
.m_Stage
= fileheader
->m_Stage
;
324 path
.m_Action
= fileheader
->m_Action
;
325 Rev
.m_Action
|= path
.m_Action
;
327 if(fileheader
->m_Add
== 0xFFFFFFFF)
328 path
.m_StatAdd
=_T("-");
330 path
.m_StatAdd
.Format(_T("%d"),fileheader
->m_Add
);
332 if(fileheader
->m_Del
== 0xFFFFFFFF)
333 path
.m_StatDel
=_T("-");
335 path
.m_StatDel
.Format(_T("%d"), fileheader
->m_Del
);
337 Rev
.m_Files
.AddPath(path
);
339 offset
+= sizeof(*fileheader
) + fileheader
->m_OldFileNameSize
*sizeof(TCHAR
) + fileheader
->m_FileNameSize
*sizeof(TCHAR
) - sizeof(TCHAR
);
340 fileheader
= (SLogCacheRevFileHeader
*) (this->m_pCacheData
+ offset
);
344 int CLogCache::RebuildCacheFile()
346 SLogCacheIndexHeader Indexheader
;
348 Indexheader
.m_Magic
= LOG_INDEX_MAGIC
;
349 Indexheader
.m_Version
= LOG_INDEX_VERSION
;
350 Indexheader
.m_ItemCount
=0;
352 SLogCacheDataFileHeader dataheader
;
354 dataheader
.m_Magic
= LOG_DATA_MAGIC
;
355 dataheader
.m_Version
= LOG_INDEX_VERSION
;
357 ::SetFilePointer(m_IndexFile
,0,0,0);
358 ::SetFilePointer(m_DataFile
,0,0,0);
359 SetEndOfFile(this->m_IndexFile
);
360 SetEndOfFile(this->m_DataFile
);
363 WriteFile(this->m_IndexFile
,&Indexheader
,sizeof(SLogCacheIndexHeader
),&num
,0);
364 SetEndOfFile(this->m_IndexFile
);
365 WriteFile(this->m_DataFile
,&dataheader
,sizeof(SLogCacheDataFileHeader
),&num
,0);
366 SetEndOfFile(this->m_DataFile
);
369 int CLogCache::SaveCache()
372 BOOL bIsRebuild
=false;
374 if (this->m_HashMap
.empty()) // is not sufficient, because "working copy changes" are always included
377 if( this->m_GitDir
.IsEmpty())
380 if (this->m_pCacheIndex
&& m_pCacheIndex
->m_Header
.m_ItemCount
== 0) // check for empty log list (issue #915)
383 SLogCacheIndexFile
*pIndex
= NULL
;
384 if(this->m_pCacheIndex
)
386 pIndex
= (SLogCacheIndexFile
*)malloc(sizeof(SLogCacheIndexFile
)
387 +sizeof(SLogCacheIndexItem
) * (m_pCacheIndex
->m_Header
.m_ItemCount
) );
391 memcpy(pIndex
,this->m_pCacheIndex
,
392 sizeof(SLogCacheIndexFile
) + sizeof(SLogCacheIndexItem
) *( m_pCacheIndex
->m_Header
.m_ItemCount
-1)
396 this->CloseDataHandles();
397 this->CloseIndexHandles();
399 SLogCacheIndexHeader header
;
400 CString file
= this->m_GitDir
+ INDEX_FILE_NAME
;
403 m_IndexFile
= CreateFile(file
,
404 GENERIC_READ
|GENERIC_WRITE
,
408 FILE_ATTRIBUTE_NORMAL
,
411 if(m_IndexFile
== INVALID_HANDLE_VALUE
)
417 file
= m_GitDir
+ DATA_FILE_NAME
;
419 m_DataFile
= CreateFile(file
,
420 GENERIC_READ
|GENERIC_WRITE
,
424 FILE_ATTRIBUTE_NORMAL
,
427 if(m_DataFile
== INVALID_HANDLE_VALUE
)
436 memset(&header
,0,sizeof(SLogCacheIndexHeader
));
438 if((!ReadFile(m_IndexFile
,&header
, sizeof(SLogCacheIndexHeader
),&num
,0)) ||
439 !CheckHeader(&header
)
448 SLogCacheDataFileHeader header
;
450 if((!ReadFile(m_DataFile
,&header
,sizeof(SLogCacheDataFileHeader
),&num
,0)||
451 !CheckHeader(&header
)))
459 header
.m_ItemCount
=0;
461 SetFilePointer(m_DataFile
,0,0,2);
462 SetFilePointer(m_IndexFile
,0,0,2);
464 CGitHashMap::iterator i
;
465 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
467 if(this->GetOffset((*i
).second
.m_CommitHash
,pIndex
) ==0 || bIsRebuild
)
469 if((*i
).second
.m_IsDiffFiles
&& !(*i
).second
.m_CommitHash
.IsEmpty())
471 LARGE_INTEGER offset
;
476 SetFilePointerEx(this->m_DataFile
,start
,&offset
,1);
477 if (this->SaveOneItem((*i
).second
, (LONG
)offset
.QuadPart
))
479 TRACE(_T("Save one item error"));
480 SetFilePointerEx(this->m_DataFile
,offset
, &offset
,0);
484 SLogCacheIndexItem item
;
485 item
.m_Hash
= (*i
).second
.m_CommitHash
;
486 item
.m_Offset
=offset
.QuadPart
;
489 WriteFile(m_IndexFile
,&item
,sizeof(SLogCacheIndexItem
),&num
,0);
490 header
.m_ItemCount
++;
494 FlushFileBuffers(m_IndexFile
);
496 m_IndexFileMap
= CreateFileMapping(m_IndexFile
, NULL
, PAGE_READWRITE
,0,0,NULL
);
497 if(m_IndexFileMap
== INVALID_HANDLE_VALUE
)
503 m_pCacheIndex
= (SLogCacheIndexFile
*)MapViewOfFile(m_IndexFileMap
,FILE_MAP_WRITE
,0,0,0);
504 if(m_pCacheIndex
== NULL
)
510 m_pCacheIndex
->m_Header
.m_ItemCount
= header
.m_ItemCount
;
512 FlushViewOfFile(m_pCacheIndex
,0);
516 this->CloseDataHandles();
517 this->CloseIndexHandles();
526 void CLogCache::Sort()
528 if(this->m_pCacheIndex
)
530 qsort(m_pCacheIndex
->m_Item
, m_pCacheIndex
->m_Header
.m_ItemCount
,sizeof(SLogCacheIndexItem
), Compare
);
534 int CLogCache::ClearAllParent()
536 CGitHashMap::iterator i
;
537 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
539 (*i
).second
.m_ParentHash
.clear();
540 (*i
).second
.m_Lanes
.clear();