1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-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 // GitRefCompareList.cpp : implementation file
23 #include "GitRefCompareList.h"
25 #include "UnicodeUtils.h"
26 #include "MessageBox.h"
29 #include "..\TortoiseShell\resource.h"
30 #include "LoglistCommonResource.h"
32 IMPLEMENT_DYNAMIC(CGitRefCompareList
, CHintCtrl
<CListCtrl
>)
34 BEGIN_MESSAGE_MAP(CGitRefCompareList
, CHintCtrl
<CListCtrl
>)
38 BOOL
CGitRefCompareList::m_bSortLogical
= FALSE
;
50 IDGITRCLH_HIDEUNCHANGED
= 1,
53 CGitRefCompareList::CGitRefCompareList()
54 : CHintCtrl
<CListCtrl
>()
62 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
64 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
65 m_bHideUnchanged
= CRegDWORD(_T("Software\\TortoiseGit\\RefCompareHideUnchanged"), FALSE
);
68 void CGitRefCompareList::Init()
71 colRef
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_REF
)));
72 colChange
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_CHANGETYPE
)));
73 colOldHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDHASH
)));
74 colOldMessage
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDMESSAGE
)));
75 colNewHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_NEWHASH
)));
76 colNewMessage
= InsertColumn(index
++,CString(MAKEINTRESOURCE(IDS_NEWMESSAGE
)));
77 for (int i
= 0; i
< index
; ++i
)
78 SetColumnWidth(i
, LVSCW_AUTOSIZE_USEHEADER
);
79 SetColumnWidth(colRef
, 130);
81 CImageList
*imagelist
= new CImageList();
82 imagelist
->Create(IDB_BITMAP_REFTYPE
, 16, 3, RGB(255, 255, 255));
83 SetImageList(imagelist
, LVSIL_SMALL
);
85 SetWindowTheme(m_hWnd
, L
"Explorer", nullptr);
88 int CGitRefCompareList::AddEntry(git_repository
* repo
, const CString
& ref
, const CGitHash
* oldHash
, const CGitHash
* newHash
)
92 entry
.shortName
= CGit::GetShortName(ref
, &entry
.refType
);
94 entry
.oldHash
= oldHash
->ToString().Left(g_Git
.GetShortHASHLength());
96 entry
.newHash
= newHash
->ToString().Left(g_Git
.GetShortHASHLength());
98 CAutoCommit oldCommit
;
101 if (!git_commit_lookup(oldCommit
.GetPointer(), repo
, (const git_oid
*)&oldHash
->m_hash
))
102 entry
.oldMessage
= GetCommitMessage(oldCommit
);
105 CAutoCommit newCommit
;
108 if (!git_commit_lookup(newCommit
.GetPointer(), repo
, (const git_oid
*)&newHash
->m_hash
))
109 entry
.newMessage
= GetCommitMessage(newCommit
);
112 if (oldHash
&& newHash
)
114 if (*oldHash
== *newHash
)
116 entry
.change
.LoadString(IDS_SAME
);
117 entry
.changeType
= ChangeType::Same
;
121 size_t ahead
= 0, behind
= 0;
122 if (!git_graph_ahead_behind(&ahead
, &behind
, repo
, (const git_oid
*)&newHash
->m_hash
, (const git_oid
*)&oldHash
->m_hash
))
125 if (ahead
> 0 && behind
== 0)
127 entry
.change
.Format(IDS_FORWARDN
, ahead
);
128 entry
.changeType
= ChangeType::FastForward
;
130 else if (ahead
== 0 && behind
> 0)
132 entry
.change
.Format(IDS_REWINDN
, behind
);
133 entry
.changeType
= ChangeType::Rewind
;
137 git_time_t oldTime
= git_commit_committer(oldCommit
)->when
.time
;
138 git_time_t newTime
= git_commit_committer(newCommit
)->when
.time
;
139 if (oldTime
< newTime
)
141 entry
.change
.LoadString(IDS_SUBMODULEDIFF_NEWERTIME
);
142 entry
.changeType
= ChangeType::NewerTime
;
144 else if (oldTime
> newTime
)
146 entry
.change
.LoadString(IDS_SUBMODULEDIFF_OLDERTIME
);
147 entry
.changeType
= ChangeType::OlderTime
;
151 entry
.change
.LoadString(IDS_SUBMODULEDIFF_SAMETIME
);
152 entry
.changeType
= ChangeType::SameTime
;
160 entry
.change
.LoadString(IDS_DELETED
);
161 entry
.changeType
= ChangeType::Deleted
;
165 entry
.change
.LoadString(IDS_NEW
);
166 entry
.changeType
= ChangeType::New
;
169 m_RefList
.push_back(entry
);
170 return (int)m_RefList
.size() - 1;
173 void CGitRefCompareList::Show()
176 std::sort(m_RefList
.begin(), m_RefList
.end(), SortPredicate
);
178 for (const auto& entry
: m_RefList
)
180 if (entry
.changeType
== ChangeType::Same
&& m_bHideUnchanged
)
184 if (entry
.refType
== CGit::REF_TYPE::LOCAL_BRANCH
)
186 else if (entry
.refType
== CGit::REF_TYPE::REMOTE_BRANCH
)
188 else if (entry
.refType
== CGit::REF_TYPE::ANNOTATED_TAG
|| entry
.refType
== CGit::REF_TYPE::TAG
)
190 InsertItem(index
, entry
.shortName
, nImage
);
191 SetItemText(index
, colChange
, entry
.change
);
192 SetItemText(index
, colOldHash
, entry
.oldHash
);
193 SetItemText(index
, colOldMessage
, entry
.oldMessage
);
194 SetItemText(index
, colNewHash
, entry
.newHash
);
195 SetItemText(index
, colNewMessage
, entry
.newMessage
);
200 void CGitRefCompareList::Clear()
206 void CGitRefCompareList::OnContextMenu(CWnd
*pWnd
, CPoint point
)
210 OnContextMenuList(pWnd
, point
);
212 else if (pWnd
== GetHeaderCtrl())
214 OnContextMenuHeader(pWnd
, point
);
218 void CGitRefCompareList::OnContextMenuList(CWnd
* /*pWnd*/, CPoint point
)
220 int selIndex
= GetSelectionMark();
221 if (selIndex
< 0 || (size_t)selIndex
>= m_RefList
.size())
224 CString refName
= m_RefList
[selIndex
].fullName
;
225 CString oldHash
= m_RefList
[selIndex
].oldHash
;
226 CString newHash
= m_RefList
[selIndex
].newHash
;
228 popup
.CreatePopupMenu();
230 if (!oldHash
.IsEmpty())
232 logStr
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)oldHash
);
233 popup
.AppendMenuIcon(IDGITRCL_OLDLOG
, logStr
, IDI_LOG
);
235 if (!newHash
.IsEmpty() && oldHash
!= newHash
)
237 logStr
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)newHash
);
238 popup
.AppendMenuIcon(IDGITRCL_NEWLOG
, logStr
, IDI_LOG
);
240 if (!oldHash
.IsEmpty() && !newHash
.IsEmpty() && oldHash
!= newHash
)
241 popup
.AppendMenuIcon(IDGITRCL_COMPARE
, IDS_LOG_POPUP_COMPAREWITHPREVIOUS
, IDI_DIFF
);
242 popup
.AppendMenuIcon(IDGITRCL_REFLOG
, IDS_MENUREFLOG
, IDI_LOG
);
244 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
245 AfxGetApp()->DoWaitCursor(1);
248 case IDGITRCL_OLDLOG
:
249 case IDGITRCL_NEWLOG
:
252 sCmd
.Format(_T("/command:log /path:\"%s\" /endrev:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, cmd
== IDGITRCL_OLDLOG
? (LPCTSTR
)oldHash
: (LPCTSTR
)newHash
);
253 CAppUtils::RunTortoiseGitProc(sCmd
);
256 case IDGITRCL_COMPARE
:
259 sCmd
.Format(_T("/command:showcompare /path:\"%s\" /revision1:\"%s\" /revision2:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)oldHash
, (LPCTSTR
)newHash
);
260 if (!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000))
261 sCmd
+= L
" /alternative";
262 CAppUtils::RunTortoiseGitProc(sCmd
);
265 case IDGITRCL_REFLOG
:
268 sCmd
.Format(_T("/command:reflog /path:\"%s\" /ref:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)refName
);
269 CAppUtils::RunTortoiseGitProc(sCmd
);
273 AfxGetApp()->DoWaitCursor(-1);
276 static void AppendMenuChecked(CMenu
&menu
, UINT nTextID
, UINT_PTR nItemID
, BOOL checked
= FALSE
, BOOL enabled
= TRUE
)
279 text
.LoadString(nTextID
);
280 menu
.AppendMenu(MF_STRING
| (enabled
? MF_ENABLED
: MF_DISABLED
) | (checked
? MF_CHECKED
: MF_UNCHECKED
), nItemID
, text
);
283 void CGitRefCompareList::OnContextMenuHeader(CWnd
* /*pWnd*/, CPoint point
)
286 if (popup
.CreatePopupMenu())
288 AppendMenuChecked(popup
, IDS_HIDEUNCHANGED
, IDGITRCLH_HIDEUNCHANGED
, m_bHideUnchanged
);
290 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
293 case IDGITRCLH_HIDEUNCHANGED
:
294 m_bHideUnchanged
= !m_bHideUnchanged
;
301 CString
CGitRefCompareList::GetCommitMessage(git_commit
*commit
)
303 int encode
= CP_UTF8
;
304 const char *encodingString
= git_commit_message_encoding(commit
);
305 if (encodingString
!= nullptr)
306 encode
= CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString
));
308 CString message
= CUnicodeUtils::GetUnicode(git_commit_message(commit
), encode
);
310 message
= message
.Tokenize(_T("\n"), start
);
314 bool CGitRefCompareList::SortPredicate(const RefEntry
&e1
, const RefEntry
&e2
)
316 if (e1
.changeType
< e2
.changeType
)
318 if (e1
.changeType
> e2
.changeType
)
321 return StrCmpLogicalW(e1
.fullName
, e2
.fullName
) < 0;
322 return e1
.fullName
.Compare(e2
.fullName
) < 0;