SyncDlg: Make check if local branch fast-fowards to remote branch work again
[TortoiseGit.git] / src / TortoiseProc / GitLogCache.cpp
bloba98451badb0141593e29a0470f0db3dcf8bb1e1a
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, LONG 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 (DWORD i = 0; i < header->m_FileCount; i++)
301 CTGitPath path;
302 CString oldfile;
303 path.Reset();
305 if(!CheckHeader(fileheader))
306 return -1;
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("-");
322 else
323 path.m_StatAdd.Format(_T("%d"),fileheader->m_Add);
325 if(fileheader->m_Del == 0xFFFFFFFF)
326 path.m_StatDel=_T("-");
327 else
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);
335 return 0;
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);
355 DWORD num;
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);
360 return 0;
362 int CLogCache::SaveCache()
364 int ret =0;
365 BOOL bIsRebuild=false;
367 if (this->m_HashMap.empty()) // is not sufficient, because "working copy changes" are always included
368 return 0;
370 if( this->m_GitDir.IsEmpty())
371 return 0;
373 if (this->m_pCacheIndex && m_pCacheIndex->m_Header.m_ItemCount == 0) // check for empty log list (issue #915)
374 return 0;
376 SLogCacheIndexFile *pIndex = NULL;
377 if(this->m_pCacheIndex)
379 pIndex = (SLogCacheIndexFile *)malloc(sizeof(SLogCacheIndexFile)
380 +sizeof(SLogCacheIndexItem) * (m_pCacheIndex->m_Header.m_ItemCount) );
381 if(pIndex ==NULL)
382 return -1;
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,
399 NULL,
400 OPEN_ALWAYS,
401 FILE_ATTRIBUTE_NORMAL,
402 NULL);
404 if(m_IndexFile == INVALID_HANDLE_VALUE)
406 ret = -1;
407 break;
410 file = m_GitDir + DATA_FILE_NAME;
412 m_DataFile = CreateFile(file,
413 GENERIC_READ|GENERIC_WRITE,
415 NULL,
416 OPEN_ALWAYS,
417 FILE_ATTRIBUTE_NORMAL,
418 NULL);
420 if(m_DataFile == INVALID_HANDLE_VALUE)
422 ret = -1;
423 break;
429 memset(&header,0,sizeof(SLogCacheIndexHeader));
430 DWORD num=0;
431 if((!ReadFile(m_IndexFile,&header, sizeof(SLogCacheIndexHeader),&num,0)) ||
432 !CheckHeader(&header)
435 RebuildCacheFile();
436 bIsRebuild =true;
439 if(!bIsRebuild)
441 SLogCacheDataFileHeader header;
442 DWORD num=0;
443 if((!ReadFile(m_DataFile,&header,sizeof(SLogCacheDataFileHeader),&num,0)||
444 !CheckHeader(&header)))
446 RebuildCacheFile();
447 bIsRebuild=true;
451 if(bIsRebuild)
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;
465 offset.LowPart=0;
466 offset.HighPart=0;
467 LARGE_INTEGER start;
468 start.QuadPart = 0;
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);
474 continue;
477 SLogCacheIndexItem item;
478 item.m_Hash = (*i).second.m_CommitHash;
479 item.m_Offset=offset.QuadPart;
481 DWORD num;
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)
492 ret =-1;
493 break;
496 m_pCacheIndex = (SLogCacheIndexFile*)MapViewOfFile(m_IndexFileMap,FILE_MAP_WRITE,0,0,0);
497 if(m_pCacheIndex == NULL)
499 ret = -1;
500 break;
503 m_pCacheIndex->m_Header.m_ItemCount = header.m_ItemCount;
504 Sort();
505 FlushViewOfFile(m_pCacheIndex,0);
507 }while(0);
509 this->CloseDataHandles();
510 this->CloseIndexHandles();
512 if(pIndex)
513 free(pIndex);
514 return ret;
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();
535 return 0;