Can also refresh Rebase dialog by double clicking to delete conflict file
[TortoiseGit.git] / src / TortoiseProc / GitLogCache.cpp
blob70f816c516322034d353681e22c2e811c9ab0df1
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;
36 m_DataFileLength = 0;
39 void CLogCache::CloseDataHandles()
41 if(m_pCacheData)
43 UnmapViewOfFile(m_pCacheData);
44 m_pCacheData=NULL;
46 if(m_DataFileMap)
48 CloseHandle(m_DataFileMap);
49 m_DataFileMap=INVALID_HANDLE_VALUE;
51 if(m_DataFile)
53 CloseHandle(m_DataFile);
54 m_DataFile = INVALID_HANDLE_VALUE;
58 void CLogCache::CloseIndexHandles()
60 if(m_pCacheIndex)
62 UnmapViewOfFile(m_pCacheIndex);
63 m_pCacheIndex = NULL;
66 if(m_IndexFileMap)
68 CloseHandle(m_IndexFileMap);
69 m_IndexFileMap = INVALID_HANDLE_VALUE;
72 //this->m_IndexFile.Close();
73 //this->m_DataFile.Close();
74 if(m_IndexFile)
76 CloseHandle(m_IndexFile);
77 m_IndexFile=INVALID_HANDLE_VALUE;
81 CLogCache::~CLogCache()
83 CloseIndexHandles();
84 CloseDataHandles();
87 int CLogCache::AddCacheEntry(GitRev &Rev)
89 this->m_HashMap[Rev.m_CommitHash] = Rev;
90 return 0;
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)
102 if(pData==NULL)
103 pData = m_pCacheIndex;
105 if(pData == 0)
106 return 0;
108 SLogCacheIndexItem *p=(SLogCacheIndexItem *)bsearch(hash.m_hash,pData->m_Item,
109 pData->m_Header.m_ItemCount,
110 sizeof(SLogCacheIndexItem),
111 Compare);
113 if(p)
114 return p->m_Offset;
115 else
116 return 0;
119 int CLogCache::FetchCacheIndex(CString GitDir)
121 int ret=0;
122 if (!g_GitAdminDir.GetAdminDirPath(GitDir, m_GitDir))
123 return -1;
127 if( m_IndexFile == INVALID_HANDLE_VALUE)
129 CString file = m_GitDir + INDEX_FILE_NAME;
130 m_IndexFile = CreateFile(file,
131 GENERIC_READ,
132 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
133 NULL,
134 OPEN_EXISTING,
135 FILE_ATTRIBUTE_NORMAL,
136 NULL);
138 if( m_IndexFile == INVALID_HANDLE_VALUE)
140 ret = -1;
141 break;;
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)
150 ret = -1;
151 break;
155 if( m_pCacheIndex == NULL )
157 m_pCacheIndex = (SLogCacheIndexFile*)MapViewOfFile(m_IndexFileMap,FILE_MAP_READ,0,0,0);
158 if( m_pCacheIndex ==NULL )
160 ret = -1;
161 break;
165 if( !CheckHeader(&m_pCacheIndex->m_Header))
167 ret =-1;
168 break;
171 if( m_DataFile == INVALID_HANDLE_VALUE )
173 CString file = m_GitDir + DATA_FILE_NAME;
174 m_DataFile = CreateFile(file,
175 GENERIC_READ,
176 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
177 NULL,
178 OPEN_EXISTING,
179 FILE_ATTRIBUTE_NORMAL,
180 NULL);
182 if(m_DataFile == INVALID_HANDLE_VALUE)
184 ret =-1;
185 break;
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)
194 ret = -1;
195 break;
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 )
204 ret = -1;
205 break;
209 if(!CheckHeader( (SLogCacheDataFileHeader*)m_pCacheData))
211 ret = -1;
212 break;
215 }while(0);
217 if(ret)
219 CloseIndexHandles();
220 ::DeleteFile(m_GitDir + INDEX_FILE_NAME);
221 ::DeleteFile(m_GitDir + DATA_FILE_NAME);
223 return ret;
227 int CLogCache::SaveOneItem(GitRev &Rev, LONG offset)
229 if(!Rev.m_IsDiffFiles)
230 return -1;
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();
240 DWORD num;
242 if(!WriteFile(this->m_DataFile,&header, sizeof(header),&num,0))
243 return -1;
245 CString stat;
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))
266 return -1;
268 if(!name.IsEmpty())
270 if(!WriteFile(this->m_DataFile,name.GetBuffer(), name.GetLength()*sizeof(TCHAR),&num,0))
271 return -1;
273 if(!oldname.IsEmpty())
275 if(!WriteFile(this->m_DataFile,oldname.GetBuffer(), oldname.GetLength()*sizeof(TCHAR),&num,0))
276 return -1;
280 return 0;
283 int CLogCache::LoadOneItem(GitRev &Rev,ULONGLONG offset)
285 if(m_pCacheData == NULL)
286 return -1;
288 if (offset + sizeof(SLogCacheRevItemHeader) > m_DataFileLength)
289 return -2;
291 SLogCacheRevItemHeader *header;
292 header = (SLogCacheRevItemHeader *)(this->m_pCacheData + offset);
294 if( !CheckHeader(header))
295 return -2;
297 Rev.m_Action = 0;
298 SLogCacheRevFileHeader *fileheader;
300 offset += sizeof(SLogCacheRevItemHeader);
301 fileheader =(SLogCacheRevFileHeader *)(this->m_pCacheData + offset);
303 for (DWORD i = 0; i < header->m_FileCount; i++)
305 CTGitPath path;
306 CString oldfile;
307 path.Reset();
309 if (offset + sizeof(SLogCacheRevFileHeader) > m_DataFileLength)
310 return -2;
312 if(!CheckHeader(fileheader))
313 return -2;
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("-");
329 else
330 path.m_StatAdd.Format(_T("%d"),fileheader->m_Add);
332 if(fileheader->m_Del == 0xFFFFFFFF)
333 path.m_StatDel=_T("-");
334 else
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);
342 return 0;
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);
362 DWORD num;
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);
367 return 0;
369 int CLogCache::SaveCache()
371 int ret =0;
372 BOOL bIsRebuild=false;
374 if (this->m_HashMap.empty()) // is not sufficient, because "working copy changes" are always included
375 return 0;
377 if( this->m_GitDir.IsEmpty())
378 return 0;
380 if (this->m_pCacheIndex && m_pCacheIndex->m_Header.m_ItemCount == 0) // check for empty log list (issue #915)
381 return 0;
383 SLogCacheIndexFile *pIndex = NULL;
384 if(this->m_pCacheIndex)
386 pIndex = (SLogCacheIndexFile *)malloc(sizeof(SLogCacheIndexFile)
387 +sizeof(SLogCacheIndexItem) * (m_pCacheIndex->m_Header.m_ItemCount) );
388 if(pIndex ==NULL)
389 return -1;
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,
406 NULL,
407 OPEN_ALWAYS,
408 FILE_ATTRIBUTE_NORMAL,
409 NULL);
411 if(m_IndexFile == INVALID_HANDLE_VALUE)
413 ret = -1;
414 break;
417 file = m_GitDir + DATA_FILE_NAME;
419 m_DataFile = CreateFile(file,
420 GENERIC_READ|GENERIC_WRITE,
422 NULL,
423 OPEN_ALWAYS,
424 FILE_ATTRIBUTE_NORMAL,
425 NULL);
427 if(m_DataFile == INVALID_HANDLE_VALUE)
429 ret = -1;
430 break;
436 memset(&header,0,sizeof(SLogCacheIndexHeader));
437 DWORD num=0;
438 if((!ReadFile(m_IndexFile,&header, sizeof(SLogCacheIndexHeader),&num,0)) ||
439 !CheckHeader(&header)
442 RebuildCacheFile();
443 bIsRebuild =true;
446 if(!bIsRebuild)
448 SLogCacheDataFileHeader header;
449 DWORD num=0;
450 if((!ReadFile(m_DataFile,&header,sizeof(SLogCacheDataFileHeader),&num,0)||
451 !CheckHeader(&header)))
453 RebuildCacheFile();
454 bIsRebuild=true;
458 if(bIsRebuild)
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;
472 offset.LowPart=0;
473 offset.HighPart=0;
474 LARGE_INTEGER start;
475 start.QuadPart = 0;
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);
481 continue;
484 SLogCacheIndexItem item;
485 item.m_Hash = (*i).second.m_CommitHash;
486 item.m_Offset=offset.QuadPart;
488 DWORD num;
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)
499 ret =-1;
500 break;
503 m_pCacheIndex = (SLogCacheIndexFile*)MapViewOfFile(m_IndexFileMap,FILE_MAP_WRITE,0,0,0);
504 if(m_pCacheIndex == NULL)
506 ret = -1;
507 break;
510 m_pCacheIndex->m_Header.m_ItemCount = header.m_ItemCount;
511 Sort();
512 FlushViewOfFile(m_pCacheIndex,0);
514 }while(0);
516 this->CloseDataHandles();
517 this->CloseIndexHandles();
519 if(pIndex)
520 free(pIndex);
521 return ret;
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();
542 return 0;