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"
31 IMPLEMENT_DYNAMIC(CGitRefCompareList
, CHintListCtrl
)
33 BEGIN_MESSAGE_MAP(CGitRefCompareList
, CHintListCtrl
)
37 BOOL
CGitRefCompareList::m_bSortLogical
= FALSE
;
50 IDGITRCLH_HIDEUNCHANGED
,
53 CGitRefCompareList::CGitRefCompareList()
55 , m_Repository(nullptr)
63 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
65 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
66 m_bHideUnchanged
= CRegDWORD(_T("Software\\TortoiseGit\\RefCompareHideUnchanged"), FALSE
);
69 void CGitRefCompareList::Init()
72 colRef
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_REF
)));
73 colChange
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_CHANGETYPE
)));
74 colOldHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDHASH
)));
75 colOldMessage
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_OLDMESSAGE
)));
76 colNewHash
= InsertColumn(index
++, CString(MAKEINTRESOURCE(IDS_NEWHASH
)));
77 colNewMessage
= InsertColumn(index
++,CString(MAKEINTRESOURCE(IDS_NEWMESSAGE
)));
78 for (int i
= 0; i
< index
; ++i
)
79 SetColumnWidth(i
, LVSCW_AUTOSIZE_USEHEADER
);
80 SetColumnWidth(colRef
, 130);
82 CImageList
*imagelist
= new CImageList();
83 imagelist
->Create(IDB_BITMAP_REFTYPE
, 16, 3, RGB(255, 255, 255));
84 SetImageList(imagelist
, LVSIL_SMALL
);
86 SetWindowTheme(m_hWnd
, L
"Explorer", NULL
);
89 int CGitRefCompareList::OpenRepository()
91 m_Repository
= nullptr;
92 CStringA gitdir
= CUnicodeUtils::GetMulti(CTGitPath(g_Git
.m_CurrentDir
).GetGitPathString(), CP_UTF8
);
93 if (git_repository_open(&m_Repository
, gitdir
))
95 CMessageBox::Show(m_hWnd
, CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
102 void CGitRefCompareList::CloseRepository()
105 git_repository_free(m_Repository
);
106 m_Repository
= nullptr;
109 int CGitRefCompareList::AddEntry(CString ref
, CGitHash
*oldHash
, CGitHash
*newHash
)
112 entry
.fullName
= ref
;
113 entry
.shortName
= CGit::GetShortName(ref
, &entry
.refType
);
115 entry
.oldHash
= oldHash
->ToString().Left(g_Git
.GetShortHASHLength());
117 entry
.newHash
= newHash
->ToString().Left(g_Git
.GetShortHASHLength());
119 git_commit
*oldCommit
= nullptr;
122 if (!git_commit_lookup(&oldCommit
, m_Repository
, (const git_oid
*)&oldHash
->m_hash
))
123 entry
.oldMessage
= GetCommitMessage(oldCommit
);
126 git_commit
*newCommit
= nullptr;
129 if (!git_commit_lookup(&newCommit
, m_Repository
, (const git_oid
*)&newHash
->m_hash
))
130 entry
.newMessage
= GetCommitMessage(newCommit
);
133 if (oldHash
&& newHash
)
135 if (*oldHash
== *newHash
)
137 entry
.change
= CString(MAKEINTRESOURCE(IDS_SAME
));
138 entry
.changeType
= ChangeType::Same
;
142 size_t ahead
= 0, behind
= 0;
143 if (!git_graph_ahead_behind(&ahead
, &behind
, m_Repository
, (const git_oid
*)&oldHash
->m_hash
, (const git_oid
*)&newHash
->m_hash
))
146 if (ahead
> 0 && behind
== 0)
148 entry
.change
.Format(IDS_FORWARDN
, ahead
);
149 entry
.changeType
= ChangeType::FastForward
;
151 else if (ahead
== 0 && behind
> 0)
153 entry
.change
.Format(IDS_REWINDN
, behind
);
154 entry
.changeType
= ChangeType::Rewind
;
158 git_time_t oldTime
= git_commit_committer(oldCommit
)->when
.time
;
159 git_time_t newTime
= git_commit_committer(newCommit
)->when
.time
;
160 if (oldTime
< newTime
)
162 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_NEWERTIME
));
163 entry
.changeType
= ChangeType::NewerTime
;
165 else if (oldTime
> newTime
)
167 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_OLDERTIME
));
168 entry
.changeType
= ChangeType::OlderTime
;
172 entry
.change
= CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_SAMETIME
));
173 entry
.changeType
= ChangeType::SameTime
;
181 entry
.change
= CString(MAKEINTRESOURCE(IDS_DELETED
));
182 entry
.changeType
= ChangeType::Deleted
;
186 entry
.change
= CString(MAKEINTRESOURCE(IDS_NEW
));
187 entry
.changeType
= ChangeType::New
;
191 git_commit_free(oldCommit
);
193 git_commit_free(newCommit
);
195 m_RefList
.push_back(entry
);
196 return (int)m_RefList
.size() - 1;
199 void CGitRefCompareList::Show()
202 std::sort(m_RefList
.begin(), m_RefList
.end(), SortPredicate
);
204 for (auto entry
= m_RefList
.begin(); entry
!= m_RefList
.end(); ++entry
)
206 if (entry
->changeType
== ChangeType::Same
&& m_bHideUnchanged
)
210 if (entry
->refType
== CGit::REF_TYPE::LOCAL_BRANCH
)
212 else if (entry
->refType
== CGit::REF_TYPE::REMOTE_BRANCH
)
214 else if (entry
->refType
== CGit::REF_TYPE::TAG
)
216 InsertItem(index
, entry
->shortName
, nImage
);
217 SetItemText(index
, colChange
, entry
->change
);
218 SetItemText(index
, colOldHash
, entry
->oldHash
);
219 SetItemText(index
, colOldMessage
, entry
->oldMessage
);
220 SetItemText(index
, colNewHash
, entry
->newHash
);
221 SetItemText(index
, colNewMessage
, entry
->newMessage
);
226 void CGitRefCompareList::Clear()
232 void CGitRefCompareList::OnContextMenu(CWnd
*pWnd
, CPoint point
)
236 OnContextMenuList(pWnd
, point
);
238 else if (pWnd
== GetHeaderCtrl())
240 OnContextMenuHeader(pWnd
, point
);
244 void CGitRefCompareList::OnContextMenuList(CWnd
* /*pWnd*/, CPoint point
)
246 int selIndex
= GetSelectionMark();
247 if (selIndex
< 0 || selIndex
>= m_RefList
.size())
250 CString refName
= m_RefList
[selIndex
].fullName
;
251 CString oldHash
= m_RefList
[selIndex
].oldHash
;
252 CString newHash
= m_RefList
[selIndex
].newHash
;
254 popup
.CreatePopupMenu();
256 if (!oldHash
.IsEmpty())
258 logStr
.Format(IDS_SHOWLOG_OF
, oldHash
);
259 popup
.AppendMenuIcon(IDGITRCL_OLDLOG
, logStr
, IDI_LOG
);
261 if (!newHash
.IsEmpty() && oldHash
!= newHash
)
263 logStr
.Format(IDS_SHOWLOG_OF
, newHash
);
264 popup
.AppendMenuIcon(IDGITRCL_NEWLOG
, logStr
, IDI_LOG
);
266 popup
.AppendMenuIcon(IDGITRCL_REFLOG
, IDS_MENUREFLOG
, IDI_LOG
);
268 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
269 AfxGetApp()->DoWaitCursor(1);
272 case IDGITRCL_OLDLOG
:
273 case IDGITRCL_NEWLOG
:
276 sCmd
.Format(_T("/command:log /path:\"%s\" /endrev:\"%s\""), g_Git
.m_CurrentDir
, cmd
== IDGITRCL_OLDLOG
? oldHash
: newHash
);
277 CAppUtils::RunTortoiseGitProc(sCmd
);
280 case IDGITRCL_REFLOG
:
283 sCmd
.Format(_T("/command:reflog /path:\"%s\" /ref:\"%s\""), g_Git
.m_CurrentDir
, refName
);
284 CAppUtils::RunTortoiseGitProc(sCmd
);
288 AfxGetApp()->DoWaitCursor(-1);
291 static void AppendMenuChecked(CMenu
&menu
, UINT nTextID
, UINT_PTR nItemID
, BOOL checked
= FALSE
, BOOL enabled
= TRUE
)
294 text
.LoadString(nTextID
);
295 menu
.AppendMenu(MF_STRING
| (enabled
? MF_ENABLED
: MF_DISABLED
) | (checked
? MF_CHECKED
: MF_UNCHECKED
), nItemID
, text
);
298 void CGitRefCompareList::OnContextMenuHeader(CWnd
* /*pWnd*/, CPoint point
)
301 if (popup
.CreatePopupMenu())
303 AppendMenuChecked(popup
, IDS_HIDEUNCHANGED
, IDGITRCLH_HIDEUNCHANGED
, m_bHideUnchanged
);
305 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
308 case IDGITRCLH_HIDEUNCHANGED
:
309 m_bHideUnchanged
= !m_bHideUnchanged
;
316 CString
CGitRefCompareList::GetCommitMessage(git_commit
*commit
)
318 int encode
= CP_UTF8
;
319 const char *encodingString
= git_commit_message_encoding(commit
);
320 if (encodingString
!= nullptr)
323 g_Git
.StringAppend(&str
, (BYTE
*)encodingString
, CP_UTF8
);
324 encode
= CUnicodeUtils::GetCPCode(str
);
328 g_Git
.StringAppend(&message
, (BYTE
*)git_commit_message(commit
), encode
);
330 message
= message
.Tokenize(_T("\n"), start
);
334 bool CGitRefCompareList::SortPredicate(const RefEntry
&e1
, const RefEntry
&e2
)
336 if (e1
.changeType
< e2
.changeType
)
338 if (e1
.changeType
> e2
.changeType
)
341 return StrCmpLogicalW(e1
.fullName
, e2
.fullName
) < 0;
342 return e1
.fullName
.Compare(e2
.fullName
) < 0;