Make use of unordered_set and unordered_map for CGitHash
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blob31f3b7c4ae794c09c0f1d4f497750fd1701ac70c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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 // GitLogList.cpp : implementation file
21 #pragma once
23 #include "HintCtrl.h"
24 #include "ResizableColumnsListCtrl.h"
25 #include "Git.h"
26 #include "ProjectProperties.h"
27 #include "TGitPath.h"
28 #include "registry.h"
29 #include "Colors.h"
30 #include "LogDlgHelper.h"
31 #include "GitRevLoglist.h"
32 #include "lanes.h"
33 #include "GitLogCache.h"
34 #include <regex>
35 #include "GitStatusListCtrl.h"
36 #include "FindDlg.h"
37 #include <unordered_set>
39 // CGitLogList
40 #define ICONITEMBORDER 5
42 #define GITLOG_START 0
43 #define GITLOG_START_ALL 1
44 #define GITLOG_END 100
46 #define LOGFILTER_ALL 0xFFFF
47 #define LOGFILTER_TOGGLE 0x8000
48 #define LOGFILTER_MESSAGES 0x0001
49 #define LOGFILTER_PATHS 0x0002
50 #define LOGFILTER_AUTHORS 0x0004
51 #define LOGFILTER_REVS 0x0008
52 #define LOGFILTER_REGEX 0x0010
53 #define LOGFILTER_BUGID 0x0020
54 #define LOGFILTER_SUBJECT 0x0040
55 #define LOGFILTER_REFNAME 0x0080
56 #define LOGFILTER_EMAILS 0x0100
57 #define LOGFILTER_NOTES 0x0200
58 #define LOGFILTER_ANNOTATEDTAG 0x0400
59 #define LOGFILTER_CASE 0x0800
61 #define LOGLIST_SHOWNOTHING 0x0000
62 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
63 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
64 #define LOGLIST_SHOWTAGS 0x0004
65 #define LOGLIST_SHOWSTASH 0x0008
66 #define LOGLIST_SHOWBISECT 0x0010
67 #define LOGLIST_SHOWALLREFS 0xFFFF
69 //typedef void CALLBACK_PROCESS(void * data, int progress);
70 #define MSG_LOADED (WM_USER+110)
71 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
72 #define MSG_REFLOG_CHANGED (WM_USER+112)
73 #define MSG_FETCHED_DIFF (WM_USER+113)
75 class SelectionHistory
77 #define HISTORYLENGTH 50
78 public:
79 SelectionHistory(void)
80 : location(0)
82 lastselected.reserve(HISTORYLENGTH);
84 void Add(CGitHash &hash)
86 if (hash.IsEmpty())
87 return;
89 size_t size = lastselected.size();
91 // re-select last selected commit
92 if (size > 0 && hash == lastselected[size - 1])
94 // reset location
95 if (location != size - 1)
96 location = size - 1;
97 return;
100 // go back and some commit was highlight
101 if (size > 0 && location != size - 1)
103 // Re-select current one, it may be a forked point.
104 if (hash == lastselected[location])
105 // Discard it later.
106 // That is that discarding forward history when a forked entry is really coming.
107 // And user has the chance to Go Forward again in this situation.
108 // IOW, (hash != lastselected[location]) means user wants a forked history,
109 // and this change saves one step from old behavior.
110 return;
112 // Discard forward history if any
113 while (lastselected.size() - 1 > location)
114 lastselected.pop_back();
117 if (lastselected.size() >= HISTORYLENGTH)
118 lastselected.erase(lastselected.cbegin());
120 lastselected.push_back(hash);
121 location = lastselected.size() - 1;
123 BOOL GoBack(CGitHash& historyEntry)
125 if (location < 1)
126 return FALSE;
128 historyEntry = lastselected[--location];
130 return TRUE;
132 BOOL GoForward(CGitHash& historyEntry)
134 if (location >= lastselected.size() - 1)
135 return FALSE;
137 historyEntry = lastselected[++location];
139 return TRUE;
141 private:
142 std::vector<CGitHash> lastselected;
143 size_t location;
146 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
148 CComCriticalSection *m_critSec;
149 public:
150 CThreadSafePtrArray(CComCriticalSection *section){ m_critSec = section ;}
151 GitRevLoglist* SafeGetAt(size_t i)
153 if(m_critSec)
154 m_critSec->Lock();
156 SCOPE_EXIT
158 if (m_critSec)
159 m_critSec->Unlock();
162 if (i >= size())
163 return nullptr;
165 return (*this)[i];
168 void SafeAdd(GitRevLoglist* newElement)
170 if(m_critSec)
171 m_critSec->Lock();
172 push_back(newElement);
173 if(m_critSec)
174 m_critSec->Unlock();
177 void SafeRemoveAt(size_t i)
179 if (m_critSec)
180 m_critSec->Lock();
182 SCOPE_EXIT
184 if (m_critSec)
185 m_critSec->Unlock();
188 if (i >= size())
189 return;
191 erase(begin() + i);
194 void SafeAddFront(GitRevLoglist* newElement)
196 if (m_critSec)
197 m_critSec->Lock();
198 insert(cbegin(), newElement);
199 if (m_critSec)
200 m_critSec->Unlock();
203 void SafeRemoveAll()
205 if(m_critSec)
206 m_critSec->Lock();
207 clear();
208 if(m_critSec)
209 m_critSec->Unlock();
213 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>
215 DECLARE_DYNAMIC(CGitLogListBase)
217 public:
218 CGitLogListBase();
219 virtual ~CGitLogListBase();
220 ProjectProperties m_ProjectProperties;
222 void UpdateProjectProperties()
224 m_ProjectProperties.ReadProps();
226 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
227 m_bShowBugtraqColumn = true;
228 else
229 m_bShowBugtraqColumn = false;
232 void ResetWcRev(bool refresh = false)
234 m_wcRev.Clear();
235 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
236 m_wcRev.m_Mark = L'-';
237 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
238 m_wcRev.GetBody() = L'\n' + m_wcRev.GetBody();
239 m_wcRev.m_CallDiffAsync = DiffAsync;
240 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
241 if (refresh && m_bShowWC)
242 m_arShownList[0] = &m_wcRev;
245 volatile LONG m_bNoDispUpdates;
246 BOOL m_IsIDReplaceAction;
247 BOOL m_IsOldFirst;
248 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
249 BOOL m_IsRebaseReplaceGraph;
250 BOOL m_bNoHightlightHead;
252 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
254 BOOL m_bStrictStopped;
255 BOOL m_bShowBugtraqColumn;
256 BOOL m_bSearchIndex;
257 BOOL m_bCancelled;
258 bool m_bIsCherryPick;
259 unsigned __int64 m_ContextMenuMask;
261 bool m_hasWC;
262 bool m_bShowWC;
263 GitRevLoglist m_wcRev;
264 volatile LONG m_bThreadRunning;
265 CLogCache m_LogCache;
267 CString m_sRange;
268 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
269 enum
271 LOGLIST_GRAPH,
272 LOGLIST_REBASE,
273 LOGLIST_ID,
274 LOGLIST_HASH,
275 LOGLIST_ACTION,
276 LOGLIST_MESSAGE,
277 LOGLIST_AUTHOR,
278 LOGLIST_DATE,
279 LOGLIST_EMAIL,
280 LOGLIST_COMMIT_NAME,
281 LOGLIST_COMMIT_EMAIL,
282 LOGLIST_COMMIT_DATE,
283 LOGLIST_BUG,
284 LOGLIST_SVNREV,
285 LOGLIST_MESSAGE_MAX=300,
286 LOGLIST_MESSAGE_MIN=200,
288 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
289 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
290 GIT_LOG_ID = 1<< LOGLIST_ID,
291 GIT_LOG_HASH = 1<< LOGLIST_HASH,
292 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
293 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
294 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
295 GIT_LOG_DATE = 1<< LOGLIST_DATE,
296 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
297 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
298 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
299 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
300 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
301 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
304 enum
306 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
307 ID_COMPARE = 1, // compare revision with WC
308 ID_SAVEAS,
309 ID_COMPARETWO, // compare two revisions
310 ID_COPY,
311 ID_REVERTREV,
312 ID_MERGEREV,
313 ID_GNUDIFF1, // compare with WC, unified
314 ID_GNUDIFF2, // compare two revisions, unified
315 ID_FINDENTRY,
316 ID_OPEN,
317 ID_BLAME,
318 ID_REPOBROWSE,
319 ID_LOG,
320 ID_EDITNOTE,
321 ID_DIFF,
322 ID_OPENWITH,
323 ID_COPYCLIPBOARD,
324 ID_COPYHASH,
325 ID_REVERTTOREV,
326 ID_BLAMECOMPARE,
327 ID_BLAMEDIFF,
328 ID_VIEWREV,
329 ID_VIEWPATHREV,
330 ID_EXPORT,
331 ID_COMPAREWITHPREVIOUS,
332 ID_BLAMEPREVIOUS,
333 ID_CHERRY_PICK,
334 ID_CREATE_BRANCH,
335 ID_CREATE_TAG,
336 ID_SWITCHTOREV,
337 ID_SWITCHBRANCH,
338 ID_RESET,
339 ID_REBASE_PICK,
340 ID_REBASE_EDIT,
341 ID_REBASE_SQUASH,
342 ID_REBASE_SKIP,
343 ID_COMBINE_COMMIT,
344 ID_STASH_SAVE,
345 ID_STASH_LIST,
346 ID_STASH_POP,
347 ID_REFLOG_STASH_APPLY,
348 ID_REFLOG_DEL,
349 ID_REBASE_TO_VERSION,
350 ID_CREATE_PATCH,
351 ID_DELETE,
352 ID_COMMIT,
353 ID_PUSH,
354 ID_PULL,
355 ID_FETCH,
356 ID_SHOWBRANCHES,
357 ID_COPYCLIPBOARDMESSAGES,
358 ID_BISECTSTART,
359 ID_LOG_VIEWRANGE,
360 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
361 ID_MERGE_ABORT,
362 ID_CLEANUP,
363 ID_SUBMODULE_UPDATE,
364 ID_BISECTGOOD,
365 ID_BISECTBAD,
366 ID_BISECTRESET,
367 ID_BISECTSKIP,
368 ID_SVNDCOMMIT,
369 ID_COMPARETWOCOMMITCHANGES,
371 enum
373 ID_COPY_ALL,
374 ID_COPY_MESSAGE,
375 ID_COPY_SUBJECT,
376 ID_COPY_HASH,
378 enum FilterShow
380 FILTERSHOW_REFS = 1,
381 FILTERSHOW_MERGEPOINTS = 2,
382 FILTERSHOW_ANYCOMMIT = 4,
383 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
385 enum : unsigned int
387 // For Rebase only
388 LOGACTIONS_REBASE_CURRENT = 0x08000000,
389 LOGACTIONS_REBASE_PICK = 0x04000000,
390 LOGACTIONS_REBASE_SQUASH = 0x02000000,
391 LOGACTIONS_REBASE_EDIT = 0x01000000,
392 LOGACTIONS_REBASE_DONE = 0x00800000,
393 LOGACTIONS_REBASE_SKIP = 0x00400000,
394 LOGACTIONS_REBASE_MASK = 0x0FC00000,
395 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
397 inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
398 static CString GetRebaseActionName(int action);
399 void InsertGitColumn();
400 void CopySelectionToClipBoard(int toCopy = ID_COPY_ALL);
401 void DiffSelectedRevWithPrevious();
402 bool IsSelectionContinuous();
403 int BeginFetchLog();
404 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
405 int FillGitLog(std::unordered_set<CGitHash>& hashes);
406 CString MessageDisplayStr(GitRev* pLogEntry);
407 BOOL IsMatchFilter(bool bRegex, GitRevLoglist* pRev, std::tr1::wregex& pat);
408 bool ShouldShowFilter(GitRevLoglist* pRev, const std::unordered_map<CGitHash, std::unordered_set<CGitHash>>& commitChildren);
409 void ShowGraphColumn(bool bShow);
410 CString GetTagInfo(GitRev* pLogEntry);
412 CFindDlg *m_pFindDialog;
413 static const UINT m_FindDialogMessage;
414 void OnFind();
416 static const UINT m_ScrollToMessage;
417 static const UINT m_ScrollToRef;
418 static const UINT m_RebaseActionMessage;
420 inline int ShownCountWithStopped() const { return (int)m_arShownList.size() + (m_bStrictStopped ? 1 : 0); }
421 void FetchLogAsync(void* data = nullptr);
422 CThreadSafePtrArray m_arShownList;
423 void Refresh(BOOL IsCleanFilter=TRUE);
424 void RecalculateShownList(CThreadSafePtrArray * pShownlist);
425 void Clear();
427 DWORD m_SelectedFilters;
428 FilterShow m_ShowFilter;
429 bool m_bFilterWithRegex;
430 bool m_bFilterCaseSensitively;
431 CLogDataVector m_logEntries;
432 void RemoveFilter();
433 void StartFilter();
434 bool ValidateRegexp(LPCTSTR regexp_str, std::tr1::wregex& pat, bool bMatchCase = false );
435 CString m_sFilterText;
437 CFilterData m_Filter;
439 CTGitPath m_Path;
440 int m_ShowMask;
441 CGitHash m_lastSelectedHash;
442 SelectionHistory m_selectionHistory;
443 CGitHash m_highlight;
444 int m_ShowRefMask;
446 CGitHash m_superProjectHash;
448 void GetTimeRange(CTime &oldest,CTime &latest);
449 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
450 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
451 void UpdateSubmodulePointer()
453 m_superProjectHash.Empty();
454 if (CRegDWORD(L"Software\\TortoiseGit\\LogShowSuperProjectSubmodulePointer", TRUE) != TRUE)
455 return;
456 if (GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
457 return;
458 CString superprojectRoot;
459 GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, false, &superprojectRoot);
460 if (superprojectRoot.IsEmpty())
461 return;
463 CAutoRepository repo(superprojectRoot);
464 if (!repo)
465 return;
466 CAutoIndex index;
467 if (git_repository_index(index.GetPointer(), repo))
468 return;
470 CString submodulePath;
471 if (superprojectRoot[superprojectRoot.GetLength() - 1] == L'\\')
472 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength());
473 else
474 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength() - 1);
475 submodulePath.Replace(L'\\', L'/');
476 const git_index_entry* entry = git_index_get_bypath(index, CUnicodeUtils::GetUTF8(submodulePath), 0);
477 if (!entry)
478 return;
480 m_superProjectHash = entry->id.id;
482 void ReloadHashMap()
484 m_RefLabelPosMap.clear();
485 m_HashMap.clear();
487 if (g_Git.GetMapHashToFriendName(m_HashMap))
488 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
490 m_CurrentBranch=g_Git.GetCurrentBranch();
492 if (g_Git.GetHash(m_HeadHash, L"HEAD"))
494 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash. Quitting..."), L"TortoiseGit", MB_ICONERROR);
495 ExitProcess(1);
498 m_wcRev.m_ParentHash.clear();
499 m_wcRev.m_ParentHash.push_back(m_HeadHash);
501 FetchRemoteList();
502 FetchTrackingBranchList();
504 UpdateSubmodulePointer();
506 void StartAsyncDiffThread();
507 void StartLoadingThread();
508 void SafeTerminateThread()
510 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
512 DWORD ret = WAIT_TIMEOUT;
513 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
514 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
515 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
516 ::TerminateThread(m_LoadingThread, 0);
517 m_LoadingThread = nullptr;
521 bool IsInWorkingThread()
523 return (AfxGetThread() == m_LoadingThread);
526 void SetRange(const CString& range)
528 m_sRange = range;
531 CString GetRange() const { return m_sRange; }
533 bool HasFilterText() const { return !m_sFilterText.IsEmpty() && m_sFilterText != L"!"; }
535 int m_nSearchIndex;
537 volatile LONG m_bExitThread;
538 CWinThread* m_LoadingThread;
539 MAP_HASH_NAME m_HashMap;
540 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
542 public:
543 CString m_ColumnRegKey;
545 protected:
546 typedef struct {
547 CString name;
548 COLORREF color;
549 CString simplifiedName;
550 CString fullName;
551 bool singleRemote;
552 bool hasTracking;
553 bool sameName;
554 CGit::REF_TYPE refType;
555 } REFLABEL;
557 DECLARE_MESSAGE_MAP()
558 afx_msg void OnDestroy();
559 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult);
560 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult);
561 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
562 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
563 afx_msg LRESULT OnScrollToRef(WPARAM wParam, LPARAM lParam);
564 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
565 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
566 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
567 void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
568 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
569 void PreSubclassWindow();
570 virtual BOOL PreTranslateMessage(MSG* pMsg);
571 static UINT LogThreadEntry(LPVOID pVoid);
572 UINT LogThread();
573 bool IsOnStash(int index);
574 bool IsStash(const GitRev * pSelLogEntry);
575 bool IsBisect(const GitRev * pSelLogEntry);
576 void FetchRemoteList();
577 void FetchTrackingBranchList();
578 void FetchLastLogInfo();
579 void FetchFullLogInfo(CString &from, CString &to);
581 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
582 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
583 CString GetToolTipText(int nItem, int nSubItem);
585 /** Checks whether a referenfe label is under pt and returns the index/type
586 * pLogEntry IN: the entry of commit
587 * pt IN: the mouse position in client coordinate
588 * type IN: give the specific reference type, then check if it is the same reference type.
589 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
590 * pShortname OUT: the short name of that reference label
591 * pIndex OUT: the index value of label of that entry
593 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
594 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
596 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
597 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
598 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
599 void DrawGraph(HDC,CRect &rect,INT_PTR index);
601 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
602 const COLORREF& col,const COLORREF& activeColor, int top) ;
603 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
605 * Save column widths to the registry
607 void SaveColumnWidths(); // save col widths to the registry
609 BOOL IsEntryInDateRange(int i);
611 int GetHeadIndex();
613 std::vector<GitRevLoglist*> m_AsynDiffList;
614 CComCriticalSection m_AsynDiffListLock;
615 HANDLE m_AsyncDiffEvent;
616 volatile LONG m_AsyncThreadExit;
617 CWinThread* m_DiffingThread;
618 volatile LONG m_AsyncThreadRunning;
620 static int DiffAsync(GitRevLoglist* rev, void* pdata)
622 auto data = reinterpret_cast<CGitLogListBase*>(pdata);
623 ULONGLONG offset = data->m_LogCache.GetOffset(rev->m_CommitHash);
624 if (!offset || data->m_LogCache.LoadOneItem(*rev, offset))
626 data->m_AsynDiffListLock.Lock();
627 data->m_AsynDiffList.push_back(rev);
628 data->m_AsynDiffListLock.Unlock();
629 ::SetEvent(data->m_AsyncDiffEvent);
630 return 0;
633 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
634 if (!rev->m_IsCommitParsed)
635 return 0;
636 InterlockedExchange(&rev->m_IsFull, TRUE);
637 // we might need to signal that the changed files are now available
638 if (data->GetSelectedCount() == 1)
640 POSITION pos = data->GetFirstSelectedItemPosition();
641 int nItem = data->GetNextSelectedItem(pos);
642 if (nItem >= 0)
644 GitRevLoglist* data2 = data->m_arShownList.SafeGetAt(nItem);
645 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
646 data->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
649 return 0;
652 static UINT AsyncThread(LPVOID data)
654 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
657 int AsyncDiffThread();
659 public:
660 void SafeTerminateAsyncDiffThread()
662 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
664 ::SetEvent(m_AsyncDiffEvent);
665 DWORD ret = WAIT_TIMEOUT;
666 // do not block here, but process messages and ask until the thread ends
667 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
669 MSG msg;
670 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
671 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
672 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
674 m_DiffingThread = nullptr;
675 InterlockedExchange(&m_AsyncThreadExit, FALSE);
679 protected:
680 CComCriticalSection m_critSec;
682 HICON m_hModifiedIcon;
683 HICON m_hReplacedIcon;
684 HICON m_hConflictedIcon;
685 HICON m_hAddedIcon;
686 HICON m_hDeletedIcon;
687 HICON m_hFetchIcon;
689 CFont m_boldFont;
690 CFont m_FontItalics;
691 CFont m_boldItalicsFont;
693 CRegDWORD m_regMaxBugIDColWidth;
695 void *m_ProcData;
697 CColors m_Colors;
699 CString m_CurrentBranch;
700 CGitHash m_HeadHash;
702 COLORREF m_LineColors[Lanes::COLORS_NUM];
703 DWORD m_LineWidth;
704 DWORD m_NodeSize;
705 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
706 bool m_bRelativeTimes; // Show relative times
707 GIT_LOG m_DllGitLog;
708 CString m_SingleRemote;
709 bool m_bTagsBranchesOnRightSide;
710 bool m_bFullCommitMessageOnLogLine;
711 bool m_bSymbolizeRefNames;
712 bool m_bIncludeBoundaryCommits;
714 DWORD m_dwDefaultColumns;
715 TCHAR m_wszTip[8192];
716 char m_szTip[8192];
717 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
718 int m_OldTopIndex;
720 GIT_MAILMAP m_pMailmap;