Use CAutoGeneralHandle
[TortoiseGit.git] / src / TortoiseProc / GitLogCache.cpp
blob3f253bbe9b184b1fe0813a22c75f1c932b4a9b60
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.
19 #include "stdafx.h"
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;
31 m_pCacheIndex = NULL;
33 m_DataFile = INVALID_HANDLE_VALUE;
34 m_DataFileMap = INVALID_HANDLE_VALUE;
35 m_pCacheData = NULL;
38 void CLogCache::CloseDataHandles()
40 if(m_pCacheData)
42 UnmapViewOfFile(m_pCacheData);
43 m_pCacheData=NULL;
45 if(m_DataFileMap)
47 CloseHandle(m_DataFileMap);
48 m_DataFileMap=INVALID_HANDLE_VALUE;
50 if(m_DataFile)
52 CloseHandle(m_DataFile);
53 m_DataFile = INVALID_HANDLE_VALUE;
57 void CLogCache::CloseIndexHandles()
59 if(m_pCacheIndex)
61 UnmapViewOfFile(m_pCacheIndex);
62 m_pCacheIndex = NULL;
65 if(m_IndexFileMap)
67 CloseHandle(m_IndexFileMap);
68 m_IndexFileMap = INVALID_HANDLE_VALUE;
71 //this->m_IndexFile.Close();
72 //this->m_DataFile.Close();
73 if(m_IndexFile)
75 CloseHandle(m_IndexFile);
76 m_IndexFile=INVALID_HANDLE_VALUE;
80 CLogCache::~CLogCache()
82 CloseIndexHandles();
83 CloseDataHandles();
86 int CLogCache::AddCacheEntry(GitRev &Rev)
88 this->m_HashMap[Rev.m_CommitHash] = Rev;
89 return 0;
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)
101 if(pData==NULL)
102 pData = m_pCacheIndex;
104 if(pData == 0)
105 return 0;
107 SLogCacheIndexItem *p=(SLogCacheIndexItem *)bsearch(hash.m_hash,pData->m_Item,
108 pData->m_Header.m_ItemCount,
109 sizeof(SLogCacheIndexItem),
110 Compare);
112 if(p)
113 return p->m_Offset;
114 else
115 return 0;
118 int CLogCache::FetchCacheIndex(CString GitDir)
120 int ret=0;
121 if (!g_GitAdminDir.GetAdminDirPath(GitDir, m_GitDir))
122 return -1;
126 if( m_IndexFile == INVALID_HANDLE_VALUE)
128 CString file = m_GitDir + INDEX_FILE_NAME;
129 m_IndexFile = CreateFile(file,
130 GENERIC_READ,
131 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
132 NULL,
133 OPEN_EXISTING,
134 FILE_ATTRIBUTE_NORMAL,
135 NULL);
137 if( m_IndexFile == INVALID_HANDLE_VALUE)
139 ret = -1;
140 break;;
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)
149 ret = -1;
150 break;
154 if( m_pCacheIndex == NULL )
156 m_pCacheIndex = (SLogCacheIndexFile*)MapViewOfFile(m_IndexFileMap,FILE_MAP_READ,0,0,0);
157 if( m_pCacheIndex ==NULL )
159 ret = -1;
160 break;
164 if( !CheckHeader(&m_pCacheIndex->m_Header))
166 ret =-1;
167 break;
170 if( m_DataFile == INVALID_HANDLE_VALUE )
172 CString file = m_GitDir + DATA_FILE_NAME;
173 m_DataFile = CreateFile(file,
174 GENERIC_READ,
175 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
176 NULL,
177 OPEN_EXISTING,
178 FILE_ATTRIBUTE_NORMAL,
179 NULL);
181 if(m_DataFile == INVALID_HANDLE_VALUE)
183 ret =-1;
184 break;
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)
193 ret = -1;
194 break;
198 if( m_pCacheData == NULL)
200 m_pCacheData = (BYTE*)MapViewOfFile(m_DataFileMap,FILE_MAP_READ,0,0,0);
201 if( m_pCacheData ==NULL )
203 ret = -1;
204 break;
208 if(!CheckHeader( (SLogCacheDataFileHeader*)m_pCacheData))
210 ret = -1;
211 break;
214 }while(0);
216 if(ret)
218 CloseIndexHandles();
219 ::DeleteFile(m_GitDir + INDEX_FILE_NAME);
220 ::DeleteFile(m_GitDir + DATA_FILE_NAME);
222 return ret;
226 int CLogCache::SaveOneItem(GitRev &Rev,ULONGLONG offset)
228 if(!Rev.m_IsDiffFiles)
229 return -1;
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();
239 DWORD num;
241 if(!WriteFile(this->m_DataFile,&header, sizeof(header),&num,0))
242 return -1;
244 CString stat;
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))
265 return -1;
267 if(!name.IsEmpty())
269 if(!WriteFile(this->m_DataFile,name.GetBuffer(), name.GetLength()*sizeof(TCHAR),&num,0))
270 return -1;
272 if(!oldname.IsEmpty())
274 if(!WriteFile(this->m_DataFile,oldname.GetBuffer(), oldname.GetLength()*sizeof(TCHAR),&num,0))
275 return -1;
279 return 0;
282 int CLogCache::LoadOneItem(GitRev &Rev,ULONGLONG offset)
284 if(m_pCacheData == NULL)
285 return -1;
287 SLogCacheRevItemHeader *header;
288 header = (SLogCacheRevItemHeader *)(this->m_pCacheData + offset);
290 if( !CheckHeader(header))
291 return -2;
293 Rev.m_Action = 0;
294 SLogCacheRevFileHeader *fileheader;
296 offset += sizeof(SLogCacheRevItemHeader);
297 fileheader =(SLogCacheRevFileHeader *)(this->m_pCacheData + offset);
299 for(int i=0;i<header->m_FileCount;i++)
301 CTGitPath path;
302 CString file,oldfile;
303 path.Reset();
305 if(!CheckHeader(fileheader))
306 return -1;
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("-");
324 else
325 path.m_StatAdd.Format(_T("%d"),fileheader->m_Add);
327 if(fileheader->m_Del == 0xFFFFFFFF)
328 path.m_StatDel=_T("-");
329 else
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);
337 return 0;
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);
357 DWORD num;
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);
362 return 0;
364 int CLogCache::SaveCache()
366 int ret =0;
367 BOOL bIsRebuild=false;
369 if( this->m_HashMap.size() == 0 ) // is not sufficient, because "working copy changes" are always included
370 return 0;
372 if( this->m_GitDir.IsEmpty())
373 return 0;
375 if (this->m_pCacheIndex && m_pCacheIndex->m_Header.m_ItemCount == 0) // check for empty log list (issue #915)
376 return 0;
378 SLogCacheIndexFile *pIndex = NULL;
379 if(this->m_pCacheIndex)
381 pIndex = (SLogCacheIndexFile *)malloc(sizeof(SLogCacheIndexFile)
382 +sizeof(SLogCacheIndexItem) * (m_pCacheIndex->m_Header.m_ItemCount) );
383 if(pIndex ==NULL)
384 return -1;
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,
401 NULL,
402 OPEN_ALWAYS,
403 FILE_ATTRIBUTE_NORMAL,
404 NULL);
406 if(m_IndexFile == INVALID_HANDLE_VALUE)
408 ret = -1;
409 break;
412 file = m_GitDir + DATA_FILE_NAME;
414 m_DataFile = CreateFile(file,
415 GENERIC_READ|GENERIC_WRITE,
417 NULL,
418 OPEN_ALWAYS,
419 FILE_ATTRIBUTE_NORMAL,
420 NULL);
422 if(m_DataFile == INVALID_HANDLE_VALUE)
424 ret = -1;
425 break;
431 memset(&header,0,sizeof(SLogCacheIndexHeader));
432 DWORD num=0;
433 if((!ReadFile(m_IndexFile,&header, sizeof(SLogCacheIndexHeader),&num,0)) ||
434 !CheckHeader(&header)
437 RebuildCacheFile();
438 bIsRebuild =true;
441 if(!bIsRebuild)
443 SLogCacheDataFileHeader header;
444 DWORD num=0;
445 if((!ReadFile(m_DataFile,&header,sizeof(SLogCacheDataFileHeader),&num,0)||
446 !CheckHeader(&header)))
448 RebuildCacheFile();
449 bIsRebuild=true;
453 if(bIsRebuild)
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;
467 offset.LowPart=0;
468 offset.HighPart=0;
469 LARGE_INTEGER start;
470 start.QuadPart = 0;
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);
476 continue;
479 SLogCacheIndexItem item;
480 item.m_Hash = (*i).second.m_CommitHash;
481 item.m_Offset=offset.QuadPart;
483 DWORD num;
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)
494 ret =-1;
495 break;
498 m_pCacheIndex = (SLogCacheIndexFile*)MapViewOfFile(m_IndexFileMap,FILE_MAP_WRITE,0,0,0);
499 if(m_pCacheIndex == NULL)
501 ret = -1;
502 break;
505 m_pCacheIndex->m_Header.m_ItemCount = header.m_ItemCount;
506 Sort();
507 FlushViewOfFile(m_pCacheIndex,0);
509 }while(0);
511 this->CloseDataHandles();
512 this->CloseIndexHandles();
514 if(pIndex)
515 free(pIndex);
516 return ret;
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();
537 return 0;