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
;
38 void CLogCache::CloseDataHandles()
42 UnmapViewOfFile(m_pCacheData
);
47 CloseHandle(m_DataFileMap
);
48 m_DataFileMap
=INVALID_HANDLE_VALUE
;
52 CloseHandle(m_DataFile
);
53 m_DataFile
= INVALID_HANDLE_VALUE
;
57 void CLogCache::CloseIndexHandles()
61 UnmapViewOfFile(m_pCacheIndex
);
67 CloseHandle(m_IndexFileMap
);
68 m_IndexFileMap
= INVALID_HANDLE_VALUE
;
71 //this->m_IndexFile.Close();
72 //this->m_DataFile.Close();
75 CloseHandle(m_IndexFile
);
76 m_IndexFile
=INVALID_HANDLE_VALUE
;
80 CLogCache::~CLogCache()
86 int CLogCache::AddCacheEntry(GitRev
&Rev
)
88 this->m_HashMap
[Rev
.m_CommitHash
] = Rev
;
92 GitRev
* CLogCache::GetCacheData(CGitHash
&hash
)
94 m_HashMap
[hash
].m_CommitHash
=hash
;
95 return &m_HashMap
[hash
];
98 ULONGLONG
CLogCache::GetOffset(CGitHash
&hash
,SLogCacheIndexFile
*pData
)
102 pData
= m_pCacheIndex
;
107 SLogCacheIndexItem
*p
=(SLogCacheIndexItem
*)bsearch(hash
.m_hash
,pData
->m_Item
,
108 pData
->m_Header
.m_ItemCount
,
109 sizeof(SLogCacheIndexItem
),
118 int CLogCache::FetchCacheIndex(CString GitDir
)
121 if (!g_GitAdminDir
.GetAdminDirPath(GitDir
, m_GitDir
))
126 if( m_IndexFile
== INVALID_HANDLE_VALUE
)
128 CString file
= m_GitDir
+ INDEX_FILE_NAME
;
129 m_IndexFile
= CreateFile(file
,
131 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
134 FILE_ATTRIBUTE_NORMAL
,
137 if( m_IndexFile
== INVALID_HANDLE_VALUE
)
144 if( m_IndexFileMap
== INVALID_HANDLE_VALUE
)
146 m_IndexFileMap
= CreateFileMapping(m_IndexFile
, NULL
, PAGE_READONLY
,0,0,NULL
);
147 if( m_IndexFileMap
== INVALID_HANDLE_VALUE
)
154 if( m_pCacheIndex
== NULL
)
156 m_pCacheIndex
= (SLogCacheIndexFile
*)MapViewOfFile(m_IndexFileMap
,FILE_MAP_READ
,0,0,0);
157 if( m_pCacheIndex
==NULL
)
164 if( !CheckHeader(&m_pCacheIndex
->m_Header
))
170 if( m_DataFile
== INVALID_HANDLE_VALUE
)
172 CString file
= m_GitDir
+ DATA_FILE_NAME
;
173 m_DataFile
= CreateFile(file
,
175 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
178 FILE_ATTRIBUTE_NORMAL
,
181 if(m_DataFile
== INVALID_HANDLE_VALUE
)
188 if( m_DataFileMap
== INVALID_HANDLE_VALUE
)
190 m_DataFileMap
= CreateFileMapping(m_DataFile
, NULL
, PAGE_READONLY
,0,0,NULL
);
191 if( m_DataFileMap
== INVALID_HANDLE_VALUE
)
198 if( m_pCacheData
== NULL
)
200 m_pCacheData
= (BYTE
*)MapViewOfFile(m_DataFileMap
,FILE_MAP_READ
,0,0,0);
201 if( m_pCacheData
==NULL
)
208 if(!CheckHeader( (SLogCacheDataFileHeader
*)m_pCacheData
))
219 ::DeleteFile(m_GitDir
+ INDEX_FILE_NAME
);
220 ::DeleteFile(m_GitDir
+ DATA_FILE_NAME
);
226 int CLogCache::SaveOneItem(GitRev
&Rev
, LONG offset
)
228 if(!Rev
.m_IsDiffFiles
)
231 SetFilePointer(this->m_DataFile
, offset
,0, 0);
233 SLogCacheRevItemHeader header
;
235 header
.m_Magic
=LOG_DATA_ITEM_MAGIC
;
236 header
.m_Version
=LOG_INDEX_VERSION
;
237 header
.m_FileCount
=Rev
.m_Files
.GetCount();
241 if(!WriteFile(this->m_DataFile
,&header
, sizeof(header
),&num
,0))
245 CString name
,oldname
;
246 for(int i
=0;i
<Rev
.m_Files
.GetCount();i
++)
248 SLogCacheRevFileHeader header
;
249 header
.m_Magic
=LOG_DATA_FILE_MAGIC
;
250 header
.m_Version
=LOG_INDEX_VERSION
;
251 header
.m_Action
= Rev
.m_Files
[i
].m_Action
;
252 header
.m_Stage
= Rev
.m_Files
[i
].m_Stage
;
253 header
.m_ParentNo
= Rev
.m_Files
[i
].m_ParentNo
;
254 name
= Rev
.m_Files
[i
].GetGitPathString();
255 header
.m_FileNameSize
= name
.GetLength();
256 oldname
= Rev
.m_Files
[i
].GetGitOldPathString();
257 header
.m_OldFileNameSize
= oldname
.GetLength();
259 stat
= Rev
.m_Files
[i
].m_StatAdd
;
260 header
.m_Add
= (stat
== _T("-"))? 0xFFFFFFFF:_tstol(stat
);
261 stat
= Rev
.m_Files
[i
].m_StatDel
;
262 header
.m_Del
= (stat
== _T("-"))? 0xFFFFFFFF:_tstol(stat
);
264 if(!WriteFile(this->m_DataFile
,&header
, sizeof(header
)-sizeof(TCHAR
),&num
,0))
269 if(!WriteFile(this->m_DataFile
,name
.GetBuffer(), name
.GetLength()*sizeof(TCHAR
),&num
,0))
272 if(!oldname
.IsEmpty())
274 if(!WriteFile(this->m_DataFile
,oldname
.GetBuffer(), oldname
.GetLength()*sizeof(TCHAR
),&num
,0))
282 int CLogCache::LoadOneItem(GitRev
&Rev
,ULONGLONG offset
)
284 if(m_pCacheData
== NULL
)
287 SLogCacheRevItemHeader
*header
;
288 header
= (SLogCacheRevItemHeader
*)(this->m_pCacheData
+ offset
);
290 if( !CheckHeader(header
))
294 SLogCacheRevFileHeader
*fileheader
;
296 offset
+= sizeof(SLogCacheRevItemHeader
);
297 fileheader
=(SLogCacheRevFileHeader
*)(this->m_pCacheData
+ offset
);
299 for (DWORD i
= 0; i
< header
->m_FileCount
; i
++)
305 if(!CheckHeader(fileheader
))
308 CString
file(fileheader
->m_FileName
, fileheader
->m_FileNameSize
);
309 if(fileheader
->m_OldFileNameSize
)
311 oldfile
= CString(fileheader
->m_FileName
+ fileheader
->m_FileNameSize
, fileheader
->m_OldFileNameSize
);
313 path
.SetFromGit(file
,&oldfile
);
315 path
.m_ParentNo
= fileheader
->m_ParentNo
;
316 path
.m_Stage
= fileheader
->m_Stage
;
317 path
.m_Action
= fileheader
->m_Action
;
318 Rev
.m_Action
|= path
.m_Action
;
320 if(fileheader
->m_Add
== 0xFFFFFFFF)
321 path
.m_StatAdd
=_T("-");
323 path
.m_StatAdd
.Format(_T("%d"),fileheader
->m_Add
);
325 if(fileheader
->m_Del
== 0xFFFFFFFF)
326 path
.m_StatDel
=_T("-");
328 path
.m_StatDel
.Format(_T("%d"), fileheader
->m_Del
);
330 Rev
.m_Files
.AddPath(path
);
332 offset
+= sizeof(*fileheader
) + fileheader
->m_OldFileNameSize
*sizeof(TCHAR
) + fileheader
->m_FileNameSize
*sizeof(TCHAR
) - sizeof(TCHAR
);
333 fileheader
= (SLogCacheRevFileHeader
*) (this->m_pCacheData
+ offset
);
337 int CLogCache::RebuildCacheFile()
339 SLogCacheIndexHeader Indexheader
;
341 Indexheader
.m_Magic
= LOG_INDEX_MAGIC
;
342 Indexheader
.m_Version
= LOG_INDEX_VERSION
;
343 Indexheader
.m_ItemCount
=0;
345 SLogCacheDataFileHeader dataheader
;
347 dataheader
.m_Magic
= LOG_DATA_MAGIC
;
348 dataheader
.m_Version
= LOG_INDEX_VERSION
;
350 ::SetFilePointer(m_IndexFile
,0,0,0);
351 ::SetFilePointer(m_DataFile
,0,0,0);
352 SetEndOfFile(this->m_IndexFile
);
353 SetEndOfFile(this->m_DataFile
);
356 WriteFile(this->m_IndexFile
,&Indexheader
,sizeof(SLogCacheIndexHeader
),&num
,0);
357 SetEndOfFile(this->m_IndexFile
);
358 WriteFile(this->m_DataFile
,&dataheader
,sizeof(SLogCacheDataFileHeader
),&num
,0);
359 SetEndOfFile(this->m_DataFile
);
362 int CLogCache::SaveCache()
365 BOOL bIsRebuild
=false;
367 if (this->m_HashMap
.empty()) // is not sufficient, because "working copy changes" are always included
370 if( this->m_GitDir
.IsEmpty())
373 if (this->m_pCacheIndex
&& m_pCacheIndex
->m_Header
.m_ItemCount
== 0) // check for empty log list (issue #915)
376 SLogCacheIndexFile
*pIndex
= NULL
;
377 if(this->m_pCacheIndex
)
379 pIndex
= (SLogCacheIndexFile
*)malloc(sizeof(SLogCacheIndexFile
)
380 +sizeof(SLogCacheIndexItem
) * (m_pCacheIndex
->m_Header
.m_ItemCount
) );
384 memcpy(pIndex
,this->m_pCacheIndex
,
385 sizeof(SLogCacheIndexFile
) + sizeof(SLogCacheIndexItem
) *( m_pCacheIndex
->m_Header
.m_ItemCount
-1)
389 this->CloseDataHandles();
390 this->CloseIndexHandles();
392 SLogCacheIndexHeader header
;
393 CString file
= this->m_GitDir
+ INDEX_FILE_NAME
;
396 m_IndexFile
= CreateFile(file
,
397 GENERIC_READ
|GENERIC_WRITE
,
401 FILE_ATTRIBUTE_NORMAL
,
404 if(m_IndexFile
== INVALID_HANDLE_VALUE
)
410 file
= m_GitDir
+ DATA_FILE_NAME
;
412 m_DataFile
= CreateFile(file
,
413 GENERIC_READ
|GENERIC_WRITE
,
417 FILE_ATTRIBUTE_NORMAL
,
420 if(m_DataFile
== INVALID_HANDLE_VALUE
)
429 memset(&header
,0,sizeof(SLogCacheIndexHeader
));
431 if((!ReadFile(m_IndexFile
,&header
, sizeof(SLogCacheIndexHeader
),&num
,0)) ||
432 !CheckHeader(&header
)
441 SLogCacheDataFileHeader header
;
443 if((!ReadFile(m_DataFile
,&header
,sizeof(SLogCacheDataFileHeader
),&num
,0)||
444 !CheckHeader(&header
)))
452 header
.m_ItemCount
=0;
454 SetFilePointer(m_DataFile
,0,0,2);
455 SetFilePointer(m_IndexFile
,0,0,2);
457 CGitHashMap::iterator i
;
458 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
460 if(this->GetOffset((*i
).second
.m_CommitHash
,pIndex
) ==0 || bIsRebuild
)
462 if((*i
).second
.m_IsDiffFiles
&& !(*i
).second
.m_CommitHash
.IsEmpty())
464 LARGE_INTEGER offset
;
469 SetFilePointerEx(this->m_DataFile
,start
,&offset
,1);
470 if (this->SaveOneItem((*i
).second
, (LONG
)offset
.QuadPart
))
472 TRACE(_T("Save one item error"));
473 SetFilePointerEx(this->m_DataFile
,offset
, &offset
,0);
477 SLogCacheIndexItem item
;
478 item
.m_Hash
= (*i
).second
.m_CommitHash
;
479 item
.m_Offset
=offset
.QuadPart
;
482 WriteFile(m_IndexFile
,&item
,sizeof(SLogCacheIndexItem
),&num
,0);
483 header
.m_ItemCount
++;
487 FlushFileBuffers(m_IndexFile
);
489 m_IndexFileMap
= CreateFileMapping(m_IndexFile
, NULL
, PAGE_READWRITE
,0,0,NULL
);
490 if(m_IndexFileMap
== INVALID_HANDLE_VALUE
)
496 m_pCacheIndex
= (SLogCacheIndexFile
*)MapViewOfFile(m_IndexFileMap
,FILE_MAP_WRITE
,0,0,0);
497 if(m_pCacheIndex
== NULL
)
503 m_pCacheIndex
->m_Header
.m_ItemCount
= header
.m_ItemCount
;
505 FlushViewOfFile(m_pCacheIndex
,0);
509 this->CloseDataHandles();
510 this->CloseIndexHandles();
519 void CLogCache::Sort()
521 if(this->m_pCacheIndex
)
523 qsort(m_pCacheIndex
->m_Item
, m_pCacheIndex
->m_Header
.m_ItemCount
,sizeof(SLogCacheIndexItem
), Compare
);
527 int CLogCache::ClearAllParent()
529 CGitHashMap::iterator i
;
530 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
532 (*i
).second
.m_ParentHash
.clear();
533 (*i
).second
.m_Lanes
.clear();