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
,ULONGLONG 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(int i
=0;i
<header
->m_FileCount
;i
++)
302 CString file
,oldfile
;
305 if(!CheckHeader(fileheader
))
308 _tcsncpy(file
.GetBufferSetLength(fileheader
->m_FileNameSize
), fileheader
->m_FileName
, fileheader
->m_FileNameSize
);
309 if(fileheader
->m_OldFileNameSize
)
311 _tcsncpy(oldfile
.GetBufferSetLength(fileheader
->m_OldFileNameSize
),
312 fileheader
->m_FileName
+fileheader
->m_FileNameSize
,
313 fileheader
->m_OldFileNameSize
);
315 path
.SetFromGit(file
,&oldfile
);
317 path
.m_ParentNo
= fileheader
->m_ParentNo
;
318 path
.m_Stage
= fileheader
->m_Stage
;
319 path
.m_Action
= fileheader
->m_Action
;
320 Rev
.m_Action
|= path
.m_Action
;
322 if(fileheader
->m_Add
== 0xFFFFFFFF)
323 path
.m_StatAdd
=_T("-");
325 path
.m_StatAdd
.Format(_T("%d"),fileheader
->m_Add
);
327 if(fileheader
->m_Del
== 0xFFFFFFFF)
328 path
.m_StatDel
=_T("-");
330 path
.m_StatDel
.Format(_T("%d"), fileheader
->m_Del
);
332 Rev
.m_Files
.AddPath(path
);
334 offset
+= sizeof(*fileheader
) + fileheader
->m_OldFileNameSize
*sizeof(TCHAR
) + fileheader
->m_FileNameSize
*sizeof(TCHAR
) - sizeof(TCHAR
);
335 fileheader
= (SLogCacheRevFileHeader
*) (this->m_pCacheData
+ offset
);
339 int CLogCache::RebuildCacheFile()
341 SLogCacheIndexHeader Indexheader
;
343 Indexheader
.m_Magic
= LOG_INDEX_MAGIC
;
344 Indexheader
.m_Version
= LOG_INDEX_VERSION
;
345 Indexheader
.m_ItemCount
=0;
347 SLogCacheDataFileHeader dataheader
;
349 dataheader
.m_Magic
= LOG_DATA_MAGIC
;
350 dataheader
.m_Version
= LOG_INDEX_VERSION
;
352 ::SetFilePointer(m_IndexFile
,0,0,0);
353 ::SetFilePointer(m_DataFile
,0,0,0);
354 SetEndOfFile(this->m_IndexFile
);
355 SetEndOfFile(this->m_DataFile
);
358 WriteFile(this->m_IndexFile
,&Indexheader
,sizeof(SLogCacheIndexHeader
),&num
,0);
359 SetEndOfFile(this->m_IndexFile
);
360 WriteFile(this->m_DataFile
,&dataheader
,sizeof(SLogCacheDataFileHeader
),&num
,0);
361 SetEndOfFile(this->m_DataFile
);
364 int CLogCache::SaveCache()
367 BOOL bIsRebuild
=false;
369 if( this->m_HashMap
.size() == 0 ) // is not sufficient, because "working copy changes" are always included
372 if( this->m_GitDir
.IsEmpty())
375 if (this->m_pCacheIndex
&& m_pCacheIndex
->m_Header
.m_ItemCount
== 0) // check for empty log list (issue #915)
378 SLogCacheIndexFile
*pIndex
= NULL
;
379 if(this->m_pCacheIndex
)
381 pIndex
= (SLogCacheIndexFile
*)malloc(sizeof(SLogCacheIndexFile
)
382 +sizeof(SLogCacheIndexItem
) * (m_pCacheIndex
->m_Header
.m_ItemCount
) );
386 memcpy(pIndex
,this->m_pCacheIndex
,
387 sizeof(SLogCacheIndexFile
) + sizeof(SLogCacheIndexItem
) *( m_pCacheIndex
->m_Header
.m_ItemCount
-1)
391 this->CloseDataHandles();
392 this->CloseIndexHandles();
394 SLogCacheIndexHeader header
;
395 CString file
= this->m_GitDir
+ INDEX_FILE_NAME
;
398 m_IndexFile
= CreateFile(file
,
399 GENERIC_READ
|GENERIC_WRITE
,
403 FILE_ATTRIBUTE_NORMAL
,
406 if(m_IndexFile
== INVALID_HANDLE_VALUE
)
412 file
= m_GitDir
+ DATA_FILE_NAME
;
414 m_DataFile
= CreateFile(file
,
415 GENERIC_READ
|GENERIC_WRITE
,
419 FILE_ATTRIBUTE_NORMAL
,
422 if(m_DataFile
== INVALID_HANDLE_VALUE
)
431 memset(&header
,0,sizeof(SLogCacheIndexHeader
));
433 if((!ReadFile(m_IndexFile
,&header
, sizeof(SLogCacheIndexHeader
),&num
,0)) ||
434 !CheckHeader(&header
)
443 SLogCacheDataFileHeader header
;
445 if((!ReadFile(m_DataFile
,&header
,sizeof(SLogCacheDataFileHeader
),&num
,0)||
446 !CheckHeader(&header
)))
454 header
.m_ItemCount
=0;
456 SetFilePointer(m_DataFile
,0,0,2);
457 SetFilePointer(m_IndexFile
,0,0,2);
459 CGitHashMap::iterator i
;
460 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
462 if(this->GetOffset((*i
).second
.m_CommitHash
,pIndex
) ==0 || bIsRebuild
)
464 if((*i
).second
.m_IsDiffFiles
&& !(*i
).second
.m_CommitHash
.IsEmpty())
466 LARGE_INTEGER offset
;
471 SetFilePointerEx(this->m_DataFile
,start
,&offset
,1);
472 if(this->SaveOneItem((*i
).second
,offset
.QuadPart
))
474 TRACE(_T("Save one item error"));
475 SetFilePointerEx(this->m_DataFile
,offset
, &offset
,0);
479 SLogCacheIndexItem item
;
480 item
.m_Hash
= (*i
).second
.m_CommitHash
;
481 item
.m_Offset
=offset
.QuadPart
;
484 WriteFile(m_IndexFile
,&item
,sizeof(SLogCacheIndexItem
),&num
,0);
485 header
.m_ItemCount
++;
489 FlushFileBuffers(m_IndexFile
);
491 m_IndexFileMap
= CreateFileMapping(m_IndexFile
, NULL
, PAGE_READWRITE
,0,0,NULL
);
492 if(m_IndexFileMap
== INVALID_HANDLE_VALUE
)
498 m_pCacheIndex
= (SLogCacheIndexFile
*)MapViewOfFile(m_IndexFileMap
,FILE_MAP_WRITE
,0,0,0);
499 if(m_pCacheIndex
== NULL
)
505 m_pCacheIndex
->m_Header
.m_ItemCount
= header
.m_ItemCount
;
507 FlushViewOfFile(m_pCacheIndex
,0);
511 this->CloseDataHandles();
512 this->CloseIndexHandles();
521 void CLogCache::Sort()
523 if(this->m_pCacheIndex
)
525 qsort(m_pCacheIndex
->m_Item
, m_pCacheIndex
->m_Header
.m_ItemCount
,sizeof(SLogCacheIndexItem
), Compare
);
529 int CLogCache::ClearAllParent()
531 CGitHashMap::iterator i
;
532 for(i
=m_HashMap
.begin();i
!=m_HashMap
.end();i
++)
534 (*i
).second
.m_ParentHash
.clear();
535 (*i
).second
.m_Lanes
.clear();