1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2014 - 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
, CHintListCtrl
)
34 BEGIN_MESSAGE_MAP(CGitRefCompareList
, CHintListCtrl
)
38 BOOL
CGitRefCompareList::m_bSortLogical
= FALSE
;
52 IDGITRCLH_HIDEUNCHANGED
,
55 CGitRefCompareList::CGitRefCompareList()
64 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
66 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
67 m_bHideUnchanged
= CRegDWORD(_T("Software\\TortoiseGit\\RefCompareHideUnchanged"), FALSE
);
70 void CGitRefCompareList::Init()
73 colRef
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_REF
)));
74 colChange
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_CHANGETYPE
)));
75 colOldHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDHASH
)));
76 colOldMessage
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDMESSAGE
)));
77 colNewHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_NEWHASH
)));
78 colNewMessage
= InsertColumn(index
++,CString(MAKEINTRESOURCE(IDS_NEWMESSAGE
)));
79 for (int i
= 0; i
< index
; ++i
)
80 SetColumnWidth(i
, LVSCW_AUTOSIZE_USEHEADER
);
81 SetColumnWidth(colRef
, 130);
83 CImageList
*imagelist
= new CImageList();
84 imagelist
->Create(IDB_BITMAP_REFTYPE
, 16, 3, RGB(255, 255, 255));
85 SetImageList(imagelist
, LVSIL_SMALL
);
87 SetWindowTheme(m_hWnd
, L
"Explorer", NULL
);
90 int CGitRefCompareList::AddEntry(git_repository
*repo
, CString ref
, CGitHash
*oldHash
, CGitHash
*newHash
)
94 entry
.shortName
= CGit::GetShortName(ref
, &entry
.refType
);
96 entry
.oldHash
= oldHash
->ToString().Left(g_Git
.GetShortHASHLength());
98 entry
.newHash
= newHash
->ToString().Left(g_Git
.GetShortHASHLength());
100 CAutoCommit oldCommit
;
103 if (!git_commit_lookup(oldCommit
.GetPointer(), repo
, (const git_oid
*)&oldHash
->m_hash
))
104 entry
.oldMessage
= GetCommitMessage(oldCommit
);
107 CAutoCommit newCommit
;
110 if (!git_commit_lookup(newCommit
.GetPointer(), repo
, (const git_oid
*)&newHash
->m_hash
))
111 entry
.newMessage
= GetCommitMessage(newCommit
);
114 if (oldHash
&& newHash
)
116 if (*oldHash
== *newHash
)
118 entry
.change
= CString(MAKEINTRESOURCE(IDS_SAME
));
119 entry
.changeType
= ChangeType::Same
;
123 size_t ahead
= 0, behind
= 0;
124 if (!git_graph_ahead_behind(&ahead
, &behind
, repo
, (const git_oid
*)&newHash
->m_hash
, (const git_oid
*)&oldHash
->m_hash
))
127 if (ahead
> 0 && behind
== 0)
129 entry
.change
.Format(IDS_FORWARDN
, ahead
);
130 entry
.changeType
= ChangeType::FastForward
;
132 else if (ahead
== 0 && behind
> 0)
134 entry
.change
.Format(IDS_REWINDN
, behind
);
135 entry
.changeType
= ChangeType::Rewind
;
139 git_time_t oldTime
= git_commit_committer(oldCommit
)->when
.time
;
140 git_time_t newTime
= git_commit_committer(newCommit
)->when
.time
;
141 if (oldTime
< newTime
)
143 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_NEWERTIME
));
144 entry
.changeType
= ChangeType::NewerTime
;
146 else if (oldTime
> newTime
)
148 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_OLDERTIME
));
149 entry
.changeType
= ChangeType::OlderTime
;
153 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_SAMETIME
));
154 entry
.changeType
= ChangeType::SameTime
;
162 entry
.change
= CString(MAKEINTRESOURCE(IDS_DELETED
));
163 entry
.changeType
= ChangeType::Deleted
;
167 entry
.change
= CString(MAKEINTRESOURCE(IDS_NEW
));
168 entry
.changeType
= ChangeType::New
;
171 m_RefList
.push_back(entry
);
172 return (int)m_RefList
.size() - 1;
175 void CGitRefCompareList::Show()
178 std::sort(m_RefList
.begin(), m_RefList
.end(), SortPredicate
);
180 for (auto entry
= m_RefList
.begin(); entry
!= m_RefList
.end(); ++entry
)
182 if (entry
->changeType
== ChangeType::Same
&& m_bHideUnchanged
)
186 if (entry
->refType
== CGit::REF_TYPE::LOCAL_BRANCH
)
188 else if (entry
->refType
== CGit::REF_TYPE::REMOTE_BRANCH
)
190 else if (entry
->refType
== CGit::REF_TYPE::TAG
)
192 InsertItem(index
, entry
->shortName
, nImage
);
193 SetItemText(index
, colChange
, entry
->change
);
194 SetItemText(index
, colOldHash
, entry
->oldHash
);
195 SetItemText(index
, colOldMessage
, entry
->oldMessage
);
196 SetItemText(index
, colNewHash
, entry
->newHash
);
197 SetItemText(index
, colNewMessage
, entry
->newMessage
);
202 void CGitRefCompareList::Clear()
208 void CGitRefCompareList::OnContextMenu(CWnd
*pWnd
, CPoint point
)
212 OnContextMenuList(pWnd
, point
);
214 else if (pWnd
== GetHeaderCtrl())
216 OnContextMenuHeader(pWnd
, point
);
220 void CGitRefCompareList::OnContextMenuList(CWnd
* /*pWnd*/, CPoint point
)
222 int selIndex
= GetSelectionMark();
223 if (selIndex
< 0 || selIndex
>= m_RefList
.size())
226 CString refName
= m_RefList
[selIndex
].fullName
;
227 CString oldHash
= m_RefList
[selIndex
].oldHash
;
228 CString newHash
= m_RefList
[selIndex
].newHash
;
230 popup
.CreatePopupMenu();
232 if (!oldHash
.IsEmpty())
234 logStr
.Format(IDS_SHOWLOG_OF
, oldHash
);
235 popup
.AppendMenuIcon(IDGITRCL_OLDLOG
, logStr
, IDI_LOG
);
237 if (!newHash
.IsEmpty() && oldHash
!= newHash
)
239 logStr
.Format(IDS_SHOWLOG_OF
, newHash
);
240 popup
.AppendMenuIcon(IDGITRCL_NEWLOG
, logStr
, IDI_LOG
);
242 if (!oldHash
.IsEmpty() && !newHash
.IsEmpty() && oldHash
!= newHash
)
243 popup
.AppendMenuIcon(IDGITRCL_COMPARE
, IDS_LOG_POPUP_COMPAREWITHPREVIOUS
, IDI_DIFF
);
244 popup
.AppendMenuIcon(IDGITRCL_REFLOG
, IDS_MENUREFLOG
, IDI_LOG
);
246 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
247 AfxGetApp()->DoWaitCursor(1);
250 case IDGITRCL_OLDLOG
:
251 case IDGITRCL_NEWLOG
:
254 sCmd
.Format(_T("/command:log /path:\"%s\" /endrev:\"%s\""), g_Git
.m_CurrentDir
, cmd
== IDGITRCL_OLDLOG
? oldHash
: newHash
);
255 CAppUtils::RunTortoiseGitProc(sCmd
);
258 case IDGITRCL_COMPARE
:
261 sCmd
.Format(_T("/command:showcompare /path:\"%s\" /revision1:\"%s\" /revision2:\"%s\""), g_Git
.m_CurrentDir
, oldHash
, newHash
);
262 CAppUtils::RunTortoiseGitProc(sCmd
);
265 case IDGITRCL_REFLOG
:
268 sCmd
.Format(_T("/command:reflog /path:\"%s\" /ref:\"%s\""), g_Git
.m_CurrentDir
, 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)
308 g_Git
.StringAppend(&str
, (BYTE
*)encodingString
, CP_UTF8
);
309 encode
= CUnicodeUtils::GetCPCode(str
);
313 g_Git
.StringAppend(&message
, (BYTE
*)git_commit_message(commit
), encode
);
315 message
= message
.Tokenize(_T("\n"), start
);
319 bool CGitRefCompareList::SortPredicate(const RefEntry
&e1
, const RefEntry
&e2
)
321 if (e1
.changeType
< e2
.changeType
)
323 if (e1
.changeType
> e2
.changeType
)
326 return StrCmpLogicalW(e1
.fullName
, e2
.fullName
) < 0;
327 return e1
.fullName
.Compare(e2
.fullName
) < 0;