Fixed issue #4132: Error "Could not get next commit. libgit returns: -4" in Log Messa...
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob81aa7c8eeb27a43eac2cf2bdd5d5206c6f255efb
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2021, 2023-2024 - 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 // BrowseRefsDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
25 #include "LogDlg.h"
26 #include "AddRemoteDlg.h"
27 #include "AppUtils.h"
28 #include "Settings/SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
32 #include "IconMenu.h"
33 #include "FileDiffDlg.h"
34 #include "DeleteRemoteTagDlg.h"
35 #include "UnicodeUtils.h"
36 #include "InputDlg.h"
37 #include "SysProgressDlg.h"
38 #include "LoglistUtils.h"
39 #include "GitRevRefBrowser.h"
40 #include "StringUtils.h"
41 #include "BrowseRefsDlgFilter.h"
42 #include "DPIAware.h"
44 static int SplitRemoteBranchName(CString ref, CString &remote, CString &branch)
46 if (CStringUtils::StartsWith(ref, L"refs/remotes/"))
47 ref = ref.Mid(static_cast<int>(wcslen(L"refs/remotes/")));
48 else if (CStringUtils::StartsWith(ref, L"remotes/"))
49 ref = ref.Mid(static_cast<int>(wcslen(L"remotes/")));
51 STRING_VECTOR list;
52 const int result = g_Git.GetRemoteList(list);
53 if (result != 0)
54 return result;
56 for (size_t i = 0; i < list.size(); ++i)
58 if (CStringUtils::StartsWith(ref, list[i] + L"/"))
60 remote = list[i];
61 branch = ref.Mid(list[i].GetLength() + 1);
62 return 0;
64 if (ref == list[i])
66 remote = list[i];
67 branch.Empty();
68 return 0;
72 return -1;
75 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
77 if (!control)
78 return;
79 // set the sort arrow
80 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
81 HDITEM HeaderItem = {0};
82 HeaderItem.mask = HDI_FORMAT;
83 for (int i=0; i<pHeader->GetItemCount(); ++i)
85 pHeader->GetItem(i, &HeaderItem);
86 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
87 pHeader->SetItem(i, &HeaderItem);
89 if (nColumn >= 0)
91 pHeader->GetItem(nColumn, &HeaderItem);
92 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
93 pHeader->SetItem(nColumn, &HeaderItem);
97 class CRefLeafListCompareFunc
99 public:
100 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
101 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
102 if (m_bSortLogical)
103 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
106 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
108 return reinterpret_cast<CRefLeafListCompareFunc*>(lParamSort)->Compare(lParam1, lParam2);
111 int Compare(LPARAM lParam1, LPARAM lParam2)
113 return Compare(
114 reinterpret_cast<CShadowTree*>(m_pList->GetItemData(static_cast<int>(lParam1))),
115 reinterpret_cast<CShadowTree*>(m_pList->GetItemData(static_cast<int>(lParam2))));
118 int Compare(const CShadowTree* pLeft, const CShadowTree* pRight)
120 const int result = CompareNoDesc(pLeft, pRight);
121 if(m_desc)
122 return -result;
123 return result;
126 int CompareNoDesc(const CShadowTree* pLeft, const CShadowTree* pRight)
128 switch(m_col)
130 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
131 case CBrowseRefsDlg::eCol_Upstream: return SortStrCmp(pLeft->m_csUpstream, pRight->m_csUpstream);
132 case CBrowseRefsDlg::eCol_Date: return ((pLeft->m_csDate == pRight->m_csDate) ? 0 : ((pLeft->m_csDate > pRight->m_csDate) ? 1 : -1));
133 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
134 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
135 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
136 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
138 return 0;
140 int SortStrCmp(const CString& left, const CString& right)
142 if (m_bSortLogical)
143 return StrCmpLogicalW(left, right);
144 return StrCmpI(left, right);
147 int m_col;
148 bool m_desc;
149 CListCtrl* m_pList;
150 bool m_bSortLogical;
153 // CBrowseRefsDlg dialog
155 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
157 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=nullptr*/)
158 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
159 m_cmdPath(cmdPath),
160 m_regCurrSortCol(L"Software\\TortoiseGit\\RefBrowserSortCol", 0),
161 m_regCurrSortDesc(L"Software\\TortoiseGit\\RefBrowserSortDesc", FALSE),
162 m_initialRef(L"HEAD"),
163 m_SelectedFilters(LOGFILTER_ALL),
164 m_bIncludeNestedRefs(TRUE)
166 // get short/long datetime setting from registry
167 DWORD RegUseShortDateFormat = CRegDWORD(L"Software\\TortoiseGit\\LogDateFormat", TRUE);
168 if (RegUseShortDateFormat)
169 m_DateFormat = DATE_SHORTDATE;
170 else
171 m_DateFormat = DATE_LONGDATE;
172 // get relative time display setting from registry
173 DWORD regRelativeTimes = CRegDWORD(L"Software\\TortoiseGit\\RelativeTimes", FALSE);
174 m_bRelativeTimes = (regRelativeTimes != 0);
176 m_regIncludeNestedRefs = CRegDWORD(L"Software\\TortoiseGit\\RefBrowserIncludeNestedRefs", TRUE);
178 m_currSortCol = m_regCurrSortCol;
179 m_currSortDesc = m_regCurrSortDesc == TRUE;
182 CBrowseRefsDlg::~CBrowseRefsDlg()
184 m_regCurrSortCol = m_currSortCol;
185 m_regCurrSortDesc = m_currSortDesc;
188 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
190 CDialog::DoDataExchange(pDX);
191 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
192 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
193 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
194 DDX_Check(pDX, IDC_INCLUDENESTEDREFS, m_bIncludeNestedRefs);
195 DDX_Control(pDX, IDC_BROWSE_REFS_BRANCHFILTER, m_cBranchFilter);
199 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
200 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
201 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
202 ON_WM_CONTEXTMENU()
203 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
204 ON_WM_DESTROY()
205 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
206 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
207 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
208 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
209 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
210 ON_REGISTERED_MESSAGE(CFilterEdit::WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
211 ON_REGISTERED_MESSAGE(CFilterEdit::WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
212 ON_WM_TIMER()
213 ON_BN_CLICKED(IDC_CURRENTBRANCH, OnBnClickedCurrentbranch)
214 ON_BN_CLICKED(IDC_INCLUDENESTEDREFS, &CBrowseRefsDlg::OnBnClickedIncludeNestedRefs)
215 ON_CBN_SELCHANGE(IDC_BROWSE_REFS_BRANCHFILTER, &CBrowseRefsDlg::OnCbnSelchangeBrowseRefsBranchfilter)
216 END_MESSAGE_MAP()
219 // CBrowseRefsDlg message handlers
221 void CBrowseRefsDlg::OnBnClickedOk()
223 if (m_bPickOne || !m_bShowRangeOptionWithTwoRefs || m_ListRefLeafs.GetSelectedCount() != 2)
225 OnOK();
226 return;
229 CIconMenu popupMenu;
230 popupMenu.CreatePopupMenu();
232 std::vector<CShadowTree*> selectedLeafs;
233 GetSelectedLeaves(selectedLeafs);
235 popupMenu.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG);
236 popupMenu.SetDefaultItem(1);
237 popupMenu.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs, selectedLeafs[0]->GetRefName(), L".."), IDI_LOG);
238 popupMenu.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs, selectedLeafs[1]->GetRefName(), L".."), IDI_LOG);
239 popupMenu.AppendMenuIcon(4, GetTwoSelectedRefs(selectedLeafs, selectedLeafs[1]->GetRefName(), L"..."), IDI_LOG);
241 RECT rect;
242 GetDlgItem(IDOK)->GetWindowRect(&rect);
243 TPMPARAMS params;
244 params.cbSize = sizeof(TPMPARAMS);
245 params.rcExclude = rect;
246 const int selection = popupMenu.TrackPopupMenuEx(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_VERTICAL, rect.left, rect.top, this, &params);
247 switch (selection)
249 case 1:
250 OnOK();
251 break;
252 case 2:
254 m_bPickedRefSet = true;
255 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, selectedLeafs[0]->GetRefName(), L"..");
256 OnOK();
258 break;
259 case 3:
261 m_bPickedRefSet = true;
262 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, selectedLeafs[1]->GetRefName(), L"..");
263 OnOK();
265 break;
266 case 4:
268 m_bPickedRefSet = true;
269 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, selectedLeafs[1]->GetRefName(), L"...");
270 OnOK();
272 break;
273 default:
274 break;
278 BOOL CBrowseRefsDlg::OnInitDialog()
280 CResizableStandAloneDialog::OnInitDialog();
281 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
283 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
284 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED, 14, 14);
285 m_ctrlFilter.SetInfoIcon(IDI_LOGFILTER, 19, 19);
286 SetFilterCueText();
288 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
289 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
290 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
291 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_CENTER);
292 AddAnchor(IDC_BROWSE_REFS_BRANCHFILTER, TOP_CENTER, TOP_RIGHT);
293 AddAnchor(IDC_INFOLABEL, BOTTOM_LEFT, BOTTOM_RIGHT);
294 AddAnchor(IDC_INCLUDENESTEDREFS, BOTTOM_LEFT);
295 AddAnchor(IDHELP, BOTTOM_RIGHT);
297 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle() | LVS_EX_INFOTIP | LVS_EX_DOUBLEBUFFER);
298 static UINT columnNames[] = { IDS_BRANCHNAME, IDS_TRACKEDBRANCH, IDS_DATELASTCOMMIT, IDS_LASTCOMMIT, IDS_LASTAUTHOR, IDS_HASH, IDS_DESCRIPTION };
299 static int columnWidths[] = { 0, 0, 0, CDPIAware::Instance().ScaleX(GetSafeHwnd(), 300), 0, 0, CDPIAware::Instance().ScaleX(GetSafeHwnd(), 80) };
300 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Upstream ) | (1 << eCol_Date) | (1 << eCol_Msg) |
301 (1 << eCol_LastAuthor) | (1 << eCol_Hash) | (1 << eCol_Description);
302 m_ListRefLeafs.m_bAllowHiding = false;
303 m_ListRefLeafs.Init();
304 m_ListRefLeafs.SetListContextMenuHandler([&](CPoint point) {OnContextMenu_ListRefLeafs(point); });
305 m_ListRefLeafs.m_ColumnManager.SetNames(columnNames, _countof(columnNames));
306 constexpr int columnVersion = 6; // adjust when changing number/names/etc. of columns
307 m_ListRefLeafs.m_ColumnManager.ReadSettings(dwDefaultColumns, 0, L"BrowseRefs", columnVersion, _countof(columnNames), columnWidths);
308 m_bPickedRefSet = false;
310 AddAnchor(IDOK,BOTTOM_RIGHT);
311 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
312 AddAnchor(IDC_CURRENTBRANCH, BOTTOM_RIGHT);
314 m_bIncludeNestedRefs = !!m_regIncludeNestedRefs;
315 UpdateData(FALSE);
317 Refresh(m_initialRef);
319 EnableSaveRestore(L"BrowseRefs");
321 CAppUtils::SetWindowTitle(*this, g_Git.m_CurrentDir);
323 m_bHasWC = !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
325 if (m_bPickOne)
326 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
328 m_cBranchFilter.AddString(CString(MAKEINTRESOURCE(IDS_ALL)));
329 m_cBranchFilter.AddString(CString(MAKEINTRESOURCE(IDS_BROWSE_REFS_ONLYMERGED)));
330 m_cBranchFilter.AddString(CString(MAKEINTRESOURCE(IDS_BROWSE_REFS_ONLYUNMERGED)));
331 m_cBranchFilter.SetCurSel(0);
333 m_ListRefLeafs.SetFocus();
334 return FALSE;
337 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
339 const int posSlash = nameLeft.Find('/');
340 CString nameSub;
341 if(posSlash<0)
343 nameSub=nameLeft;
344 nameLeft.Empty();//Nothing left
346 else
348 nameSub=nameLeft.Left(posSlash);
349 nameLeft=nameLeft.Mid(posSlash+1);
351 if(nameSub.IsEmpty())
352 return nullptr;
354 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
355 return nullptr;
357 CShadowTree& nextNode=m_ShadowTree[nameSub];
358 nextNode.m_csRefName=nameSub;
359 nextNode.m_pParent=this;
360 return &nextNode;
363 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
365 if(IsLeaf())
367 if (CStringUtils::EndsWith(partialRefName, m_csRefName))
369 //Match of leaf name. Try match on total name.
370 CString totalRefName = GetRefName();
371 if (CStringUtils::EndsWith(totalRefName, partialRefName))
372 return this; //Also match. Found.
375 else
377 //Not a leaf. Search all nodes.
378 for (auto itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
380 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
381 if (pSubtree)
382 return pSubtree; //Found
385 return nullptr; //Not found
388 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
390 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
391 //List ctrl selection?
392 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
394 //A leaf is selected
395 return GetListEntry(m_ListRefLeafs.GetNextSelectedItem(pos))->GetRefName();
397 else if (pos && !pickFirstSelIfMultiSel)
399 // at least one leaf is selected
400 CString refs;
401 int index;
402 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
404 CString ref = GetListEntry(index)->GetRefName();
405 if (CStringUtils::StartsWith(ref, L"refs/"))
406 ref = ref.Mid(static_cast<int>(wcslen(L"refs/")));
407 if (CStringUtils::StartsWith(ref, L"heads/"))
408 ref = ref.Mid(static_cast<int>(wcslen(L"heads/")));
409 refs += ref + L' ';
411 return refs.Trim();
413 else if(!onlyIfLeaf)
415 //Tree ctrl selection?
416 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
417 if (hTree)
418 return GetTreeEntry(hTree)->GetRefName();
420 return CString();//None
423 void CBrowseRefsDlg::Refresh(CString selectRef)
425 remotes.clear();
426 if (g_Git.GetRemoteList(remotes))
427 MessageBox(CGit::GetLibGit2LastErr(L"Could not get a list of remotes."), L"TortoiseGit", MB_ICONERROR);
429 if(!selectRef.IsEmpty())
431 if (selectRef == L"HEAD")
433 if (g_Git.GetCurrentBranchFromFile(g_Git.m_CurrentDir, selectRef))
434 selectRef = L"refs/heads";
435 else
436 selectRef = L"refs/heads/" + selectRef;
439 else
440 selectRef = GetSelectedRef(false, true);
442 m_RefTreeCtrl.DeleteAllItems();
443 m_ListRefLeafs.DeleteAllItems();
444 m_TreeRoot.m_ShadowTree.clear();
445 m_TreeRoot.m_csRefName = L"refs";
446 m_TreeRoot.m_hTree = m_RefTreeCtrl.InsertItem(L"refs");
447 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree, reinterpret_cast<DWORD_PTR>(&m_TreeRoot));
449 MAP_REF_GITREVREFBROWSER refMap;
450 if (CString err; GitRevRefBrowser::GetGitRevRefMap(refMap, m_cBranchFilter.GetCurSel(), err, [&](const CString& refName)
452 //Use ref based on m_pickRef_Kind
453 if (CStringUtils::StartsWith(refName, L"refs/heads/") && !(m_pickRef_Kind & gPickRef_Head))
454 return false; //Skip
455 if (CStringUtils::StartsWith(refName, L"refs/tags/") && !(m_pickRef_Kind & gPickRef_Tag))
456 return false; //Skip
457 if (CStringUtils::StartsWith(refName, L"refs/remotes/") && !(m_pickRef_Kind & gPickRef_Remote))
458 return false; //Skip
459 if (m_pickRef_Kind == gPickRef_Remote && !CStringUtils::StartsWith(refName, L"refs/remotes/")) // do not show refs/stash if only remote branches are requested
460 return false;
461 return true;
464 MessageBox(L"Get refs failed:" + err, L"TortoiseGit", MB_OK | MB_ICONERROR);
468 STRING_VECTOR remoteBranches;
469 if (g_Git.GetBranchList(remoteBranches, nullptr, CGit::BRANCH_REMOTE))
470 MessageBox(L"Loading remote tracking branches failed.", L"TortoiseGit", MB_OK | MB_ICONERROR);
471 std::sort(remoteBranches.begin(), remoteBranches.end());
473 //Populate ref tree
474 for (auto iterRefMap = refMap.cbegin(); iterRefMap != refMap.cend(); ++iterRefMap)
476 CShadowTree& treeLeaf = GetTreeNode(iterRefMap->first, nullptr, true);
477 GitRevRefBrowser ref = iterRefMap->second;
479 treeLeaf.m_csRefHash = ref.m_CommitHash.ToString();
480 CGit::GetShortName(ref.m_UpstreamRef, ref.m_UpstreamRef, L"refs/");
481 treeLeaf.m_csUpstream = ref.m_UpstreamRef;
482 CGit::GetShortName(treeLeaf.m_csUpstream, treeLeaf.m_csUpstream, L"remotes/");
483 if (!ref.m_UpstreamRef.IsEmpty() && !std::binary_search(remoteBranches.cbegin(), remoteBranches.cend(), ref.m_UpstreamRef))
484 treeLeaf.m_csUpstream = L"(gone: " + treeLeaf.m_csUpstream + L")";
485 treeLeaf.m_csSubject = ref.GetSubject();
486 treeLeaf.m_csAuthor = ref.GetAuthorName();
487 treeLeaf.m_csDate = ref.GetAuthorDate();
488 treeLeaf.m_csDescription = ref.m_Description;
491 // always expand the tree first
492 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree, TVE_EXPAND);
494 // try exact match first
495 if (!selectRef.IsEmpty() && !SelectRef(selectRef, true))
496 SelectRef(selectRef, false);
497 else if (refMap.empty())
498 SelectRef(L"refs", false);
501 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
503 if(!bExactMatch)
505 CString newRefName = GetFullRefName(refName);
506 if(!newRefName.IsEmpty())
507 refName = newRefName;
508 //else refName is not a valid ref. Try to select as good as possible.
510 if (!CStringUtils::StartsWith(refName, L"refs"))
511 return false; // Not a ref name
513 CShadowTree& treeLeafHead = GetTreeNode(refName, nullptr, false);
514 if (treeLeafHead.m_hTree)
516 //Not a leaf. Select tree node and return
517 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
518 return true;
521 if (!treeLeafHead.m_pParent)
522 return false; //Weird... should not occur.
524 //This is the current head.
525 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
527 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
529 auto pCurrShadowTree = GetListEntry(indexPos);
530 if(pCurrShadowTree == &treeLeafHead)
532 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
533 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
537 return true;
540 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
542 if (!pTreePos)
544 if (CStringUtils::StartsWith(refName, L"refs/"))
545 refName = refName.Mid(static_cast<int>(wcslen(L"refs/")));
546 pTreePos=&m_TreeRoot;
548 if(refName.IsEmpty())
549 return *pTreePos;//Found leaf
551 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
552 if (!pNextTree)
554 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
555 ASSERT(!bCreateIfNotExist);
556 return *pTreePos;
559 if(!refName.IsEmpty())
561 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
562 //Leafs are for the list control.
563 if (!pNextTree->m_hTree)
565 //New tree. Create node in control.
566 pNextTree->m_hTree = m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName, pTreePos->m_hTree);
567 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree, reinterpret_cast<DWORD_PTR>(pNextTree));
571 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
575 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
577 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
578 *pResult = 0;
580 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
583 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
585 m_ListRefLeafs.DeleteAllItems();
587 auto pTree = GetTreeEntry(treeNode);
588 if (!pTree)
589 return;
591 CString filterText;
592 m_ctrlFilter.GetWindowText(filterText);
594 CBrowseRefsDlgFilter filter(filterText, false, m_SelectedFilters, false);
596 FillListCtrlForShadowTree(pTree, L"", true, filter);
597 m_ListRefLeafs.m_ColumnManager.SetVisible(eCol_Upstream, pTree->IsFrom(L"refs/heads"));
598 m_ListRefLeafs.m_ColumnManager.SetVisible(eCol_Description, pTree->IsFrom(L"refs/heads"));
599 m_ListRefLeafs.AdjustColumnWidths();
600 UpdateInfoLabel();
603 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel, const CBrowseRefsDlgFilter& filter)
605 if(pTree->IsLeaf())
607 CString ref = refNamePrefix + pTree->m_csRefName;
608 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == L"refs" && !pTree->m_pParent) && filter(pTree, ref))
610 const int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
612 m_ListRefLeafs.SetItemData(indexItem, reinterpret_cast<DWORD_PTR>(pTree));
613 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
614 m_ListRefLeafs.SetItemText(indexItem, eCol_Upstream, pTree->m_csUpstream);
615 m_ListRefLeafs.SetItemText(indexItem, eCol_Date, pTree->m_csDate != 0 ? CLoglistUtils::FormatDateAndTime(pTree->m_csDate, m_DateFormat, true, m_bRelativeTimes) : CString());
616 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
617 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
618 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
619 CString descrition = pTree->m_csDescription;
620 descrition.Replace(L'\n', L' ');
621 m_ListRefLeafs.SetItemText(indexItem, eCol_Description, descrition);
624 else
626 CString csThisName;
627 if (!isFirstLevel && !m_bIncludeNestedRefs)
628 return;
629 else if (!isFirstLevel)
630 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
631 else
632 m_pListCtrlRoot = pTree;
633 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
635 FillListCtrlForShadowTree(&itSubTree->second, csThisName, false, filter);
638 if (isFirstLevel)
640 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
641 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, reinterpret_cast<DWORD_PTR>(&compareFunc));
643 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
647 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
649 ASSERT(!leafs.empty());
651 CString csMessage;
652 UINT mbIcon=MB_ICONQUESTION;
654 bool bIsRemoteBranch = false;
655 bool bIsBranch = false;
656 if (leafs[0]->IsFrom(L"refs/remotes/")) {bIsBranch = true; bIsRemoteBranch = true;}
657 else if (leafs[0]->IsFrom(L"refs/heads/")) {bIsBranch = true;}
659 if(bIsBranch)
661 if(leafs.size() == 1)
663 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
664 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, static_cast<LPCWSTR>(branchToDelete));
666 //Check if branch is fully merged in HEAD
667 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), L"HEAD"))
669 csMessage += L"\r\n\r\n";
670 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
671 mbIcon = MB_ICONWARNING;
674 if(bIsRemoteBranch)
676 csMessage += L"\r\n\r\n";
677 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
678 mbIcon = MB_ICONWARNING;
681 else
683 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
685 csMessage += L"\r\n\r\n";
686 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
687 mbIcon = MB_ICONWARNING;
689 if(bIsRemoteBranch)
691 csMessage += L"\r\n\r\n";
692 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
693 mbIcon = MB_ICONWARNING;
698 else if(leafs[0]->IsFrom(L"refs/tags/"))
700 if(leafs.size() == 1)
702 CString tagToDelete = leafs[0]->GetRefName().Mid(static_cast<int>(wcslen(L"refs/tags/")));
703 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, static_cast<LPCWSTR>(tagToDelete));
705 else
706 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
709 return MessageBox(csMessage, L"TortoiseGit", MB_YESNO | mbIcon) == IDYES;
712 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs)
714 bool allRemoteBranch = true;
715 std::map<CString, STRING_VECTOR> remoteBranches;
716 for (auto i = leafs.cbegin(); i != leafs.cend(); ++i)
718 CString completeRefName = (*i)->GetRefName();
719 if (CStringUtils::StartsWith(completeRefName, L"refs/remotes/"))
721 CString branchToDelete = completeRefName.Mid(static_cast<int>(wcslen(L"refs/remotes/")));
722 CString remoteName, remoteBranchToDelete;
723 if (!SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
724 remoteBranches[remoteName].push_back(remoteBranchToDelete);
726 else
728 allRemoteBranch = false;
729 break;
732 if (allRemoteBranch)
734 // delete multiple remote branches in batch, so it is faster, fewer password prompt
735 for (const auto& remotebranchlist : remoteBranches)
737 auto& remoteName = remotebranchlist.first;
738 if (CAppUtils::IsSSHPutty())
739 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &remoteName);
741 CSysProgressDlg sysProgressDlg;
742 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
743 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
744 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
745 sysProgressDlg.SetShowProgressBar(false);
746 sysProgressDlg.ShowModal(this, true);
748 STRING_VECTOR list;
749 list.reserve(remotebranchlist.second.size());
750 std::transform(remotebranchlist.second.cbegin(), remotebranchlist.second.cend(), std::back_inserter(list), [](auto& branch) { return L"refs/heads/" + branch; });
751 if (g_Git.DeleteRemoteRefs(remoteName, list))
753 MessageBox(g_Git.GetGitLastErr(L"Could not delete remote refs.", CGit::GIT_CMD_PUSH), L"TortoiseGit", MB_OK | MB_ICONERROR);
754 sysProgressDlg.Stop();
755 BringWindowToTop();
756 return false;
758 sysProgressDlg.Stop();
760 BringWindowToTop();
761 return true;
764 for (auto i = leafs.cbegin(); i != leafs.cend(); ++i)
765 if(!DoDeleteRef((*i)->GetRefName()))
766 return false;
767 return true;
770 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName)
772 bool bIsRemoteBranch = false;
773 bool bIsBranch = false;
774 if (CStringUtils::StartsWith(completeRefName, L"refs/remotes/"))
776 bIsBranch = true;
777 bIsRemoteBranch = true;
779 else if (CStringUtils::StartsWith(completeRefName, L"refs/heads/"))
780 bIsBranch = true;
782 if (bIsRemoteBranch)
784 CString branchToDelete = completeRefName.Mid(static_cast<int>(wcslen(L"refs/remotes/")));
785 CString remoteName, remoteBranchToDelete;
786 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
787 return false;
789 if (CAppUtils::IsSSHPutty())
790 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &remoteName);
792 CSysProgressDlg sysProgressDlg;
793 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
794 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
795 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
796 sysProgressDlg.SetShowProgressBar(false);
797 sysProgressDlg.ShowModal(this, true);
799 STRING_VECTOR list;
800 list.push_back(L"refs/heads/" + remoteBranchToDelete);
801 if (g_Git.DeleteRemoteRefs(remoteName, list))
803 MessageBox(g_Git.GetGitLastErr(L"Could not delete remote ref.", CGit::GIT_CMD_PUSH), L"TortoiseGit", MB_OK | MB_ICONERROR);
804 sysProgressDlg.Stop();
805 BringWindowToTop();
806 return false;
808 sysProgressDlg.Stop();
809 BringWindowToTop();
811 else if (bIsBranch)
813 if (g_Git.DeleteRef(completeRefName))
815 MessageBox(g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), L"TortoiseGit", MB_OK | MB_ICONERROR);
816 return false;
819 else if (CStringUtils::StartsWith(completeRefName, L"refs/tags/"))
821 if (g_Git.DeleteRef(completeRefName))
823 MessageBox(g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), L"TortoiseGit", MB_OK | MB_ICONERROR);
824 return false;
827 return true;
830 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
832 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
833 if (!pLeaf)
834 return CString();
835 return pLeaf->GetRefName();
839 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
841 if (pWndFrom == &m_RefTreeCtrl)
842 OnContextMenu_RefTreeCtrl(point);
845 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
847 CPoint clientPoint=point;
848 m_RefTreeCtrl.ScreenToClient(&clientPoint);
850 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
851 if (hTreeItem)
852 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
854 VectorPShadowTree tree;
855 ShowContextMenu(point,hTreeItem,tree);
858 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree& selectedLeafs)
860 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
861 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
862 while (pos)
864 selectedLeafs.push_back(GetListEntry(m_ListRefLeafs.GetNextSelectedItem(pos)));
868 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
870 std::vector<CShadowTree*> selectedLeafs;
871 GetSelectedLeaves(selectedLeafs);
872 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
875 CString CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
877 ASSERT(selectedLeafs.size() == 2);
879 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
880 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
881 else
882 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
885 int findVectorPosition(const STRING_VECTOR& vector, const CString& entry)
887 int i = 0;
888 for (auto it = vector.cbegin(); it != vector.cend(); ++it, ++i)
890 if (*it == entry)
891 return i;
893 return -1;
896 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
898 CIconMenu popupMenu;
899 popupMenu.CreatePopupMenu();
901 bool bAddSeparator = false;
902 CString remoteName;
904 if(selectedLeafs.size()==1)
906 bAddSeparator = true;
908 bool bShowReflogOption = false;
909 bool bShowFetchOption = false;
910 bool bShowRenameOption = false;
911 bool bShowCreateBranchOption = false;
912 bool bShowEditBranchDescriptionOption = false;
914 CString fetchFromCmd;
916 if(selectedLeafs[0]->IsFrom(L"refs/heads/"))
918 bShowReflogOption = true;
919 bShowRenameOption = true;
920 bShowEditBranchDescriptionOption = true;
922 else if(selectedLeafs[0]->IsFrom(L"refs/remotes/"))
924 bShowReflogOption = true;
925 bShowFetchOption = true;
926 bShowCreateBranchOption = true;
928 CString remoteBranch;
929 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
930 bShowFetchOption = false;
931 else
932 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, static_cast<LPCWSTR>(remoteName));
935 if (m_bWantPick)
937 popupMenu.AppendMenuIcon(eCmd_Select, IDS_SELECT);
938 popupMenu.AppendMenu(MF_SEPARATOR);
940 popupMenu.AppendMenuIcon(eCmd_ViewLog, IDS_MENULOG, IDI_LOG);
941 popupMenu.SetDefaultItem(0, TRUE);
942 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
943 if(bShowReflogOption)
944 popupMenu.AppendMenuIcon(eCmd_ShowReflog, IDS_MENUREFLOG, IDI_LOG);
946 if (m_bHasWC)
948 popupMenu.AppendMenu(MF_SEPARATOR);
949 popupMenu.AppendMenuIcon(eCmd_DiffWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
952 popupMenu.AppendMenu(MF_SEPARATOR);
953 bAddSeparator = false;
955 if(bShowFetchOption)
957 bAddSeparator = true;
958 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_UPDATE);
961 if(bAddSeparator)
962 popupMenu.AppendMenu(MF_SEPARATOR);
964 bAddSeparator = false;
965 if (m_bHasWC)
967 CString str;
968 if (selectedLeafs[0]->GetRefName() != L"refs/heads/" + g_Git.GetCurrentBranch())
970 str.Format(IDS_LOG_POPUP_MERGEREV, static_cast<LPCWSTR>(g_Git.GetCurrentBranch()));
971 popupMenu.AppendMenuIcon(eCmd_Merge, str, IDI_MERGE);
973 popupMenu.AppendMenuIcon(eCmd_Switch, IDS_SWITCH_TO_THIS, IDI_SWITCH);
974 popupMenu.AppendMenu(MF_SEPARATOR);
977 if(bShowCreateBranchOption)
979 bAddSeparator = true;
980 popupMenu.AppendMenuIcon(eCmd_CreateBranch, IDS_MENUBRANCH, IDI_COPY);
983 if (bShowEditBranchDescriptionOption)
985 bAddSeparator = true;
986 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, IDS_PROC_BROWSEREFS_EDITDESCRIPTION, IDI_RENAME);
988 if(bShowRenameOption)
990 bAddSeparator = true;
991 popupMenu.AppendMenuIcon(eCmd_Rename, IDS_PROC_BROWSEREFS_RENAME, IDI_RENAME);
994 if (m_bHasWC && selectedLeafs[0]->IsFrom(L"refs/heads/"))
996 if (bAddSeparator)
997 popupMenu.AppendMenu(MF_SEPARATOR);
998 bAddSeparator = true;
999 if (!selectedLeafs[0]->m_csUpstream.IsEmpty())
1000 popupMenu.AppendMenuIcon(eCmd_UpstreamDrop, IDS_PROC_BROWSEREFS_DROPTRACKEDBRANCH);
1001 popupMenu.AppendMenuIcon(eCmd_UpstreamSet, IDS_PROC_BROWSEREFS_SETTRACKEDBRANCH);
1004 else if(selectedLeafs.size() == 2)
1006 bAddSeparator = true;
1007 popupMenu.AppendMenuIcon(eCmd_Diff, IDS_PROC_BROWSEREFS_COMPAREREFS, IDI_DIFF);
1008 popupMenu.AppendMenuIcon(eCmd_UnifiedDiff, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1009 CString menu;
1010 menu.Format(IDS_SHOWLOG_OF, static_cast<LPCWSTR>(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, L"..")));
1011 popupMenu.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
1012 menu.Format(IDS_SHOWLOG_OF, static_cast<LPCWSTR>(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, L"...")));
1013 popupMenu.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
1016 if(!selectedLeafs.empty())
1018 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
1020 if(bAddSeparator)
1021 popupMenu.AppendMenu(MF_SEPARATOR);
1022 CString menuItemName;
1023 if(selectedLeafs.size() == 1)
1024 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
1025 else
1026 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
1028 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
1029 bAddSeparator = true;
1031 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
1033 if(bAddSeparator)
1034 popupMenu.AppendMenu(MF_SEPARATOR);
1035 CString menuItemName;
1036 if(selectedLeafs.size() == 1)
1037 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
1038 else
1039 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
1041 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
1042 bAddSeparator = true;
1044 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
1046 if(bAddSeparator)
1047 popupMenu.AppendMenu(MF_SEPARATOR);
1048 CString menuItemName;
1049 if(selectedLeafs.size() == 1)
1050 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
1051 else
1052 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
1054 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
1055 bAddSeparator = true;
1060 if (hTreePos && selectedLeafs.empty())
1062 auto pTree = GetTreeEntry(hTreePos);
1063 if(pTree->IsFrom(L"refs/remotes"))
1065 if(bAddSeparator)
1066 popupMenu.AppendMenu(MF_SEPARATOR);
1067 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, IDS_PROC_BROWSEREFS_MANAGEREMOTES, IDI_SETTINGS);
1068 bAddSeparator = true;
1069 if(selectedLeafs.empty())
1071 CString remoteBranch;
1072 if (SplitRemoteBranchName(pTree->GetRefName(), remoteName, remoteBranch))
1073 remoteName.Empty();
1074 const int pos = findVectorPosition(remotes, remoteName);
1075 if (pos >= 0)
1077 CString temp;
1078 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, static_cast<LPCWSTR>(remoteName));
1079 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_UPDATE);
1081 temp.LoadString(IDS_DELETEREMOTETAG);
1082 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag | (pos << 16), temp, IDI_DELETE);
1085 bAddSeparator = false;
1087 if(pTree->IsFrom(L"refs/heads"))
1089 if(bAddSeparator)
1090 popupMenu.AppendMenu(MF_SEPARATOR);
1091 CString temp;
1092 temp.LoadString(IDS_MENUBRANCH);
1093 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
1094 bAddSeparator = false;
1096 if(pTree->IsFrom(L"refs/tags"))
1098 if(bAddSeparator)
1099 popupMenu.AppendMenu(MF_SEPARATOR);
1100 popupMenu.AppendMenuIcon(eCmd_CreateTag, IDS_MENUTAG, IDI_TAG);
1101 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, IDS_PROC_BROWSEREFS_DELETEALLTAGS, IDI_DELETE);
1102 if (!remotes.empty())
1104 popupMenu.AppendMenu(MF_SEPARATOR);
1105 int i = 0;
1106 for (auto it = remotes.cbegin(); it != remotes.cend(); ++it, ++i)
1108 CString temp;
1109 temp.Format(IDS_DELETEREMOTETAGON, static_cast<LPCWSTR>(*it));
1110 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag | (i << 16), temp, IDI_DELETE);
1113 bAddSeparator = false;
1116 if (bAddSeparator)
1117 popupMenu.AppendMenu(MF_SEPARATOR);
1118 popupMenu.AppendMenuIcon(eCmd_Copy, IDS_COPY_REF_NAMES, IDI_COPYCLIP);
1120 const int selection = popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN | TPM_RETURNCMD, point.x, point.y, this, nullptr);
1121 switch (static_cast<eCmd>(selection & 0xFFFF))
1123 case eCmd_Select:
1124 EndDialog(IDOK);
1125 break;
1126 case eCmd_ViewLog:
1128 CString sCmd;
1129 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir), static_cast<LPCWSTR>(g_Git.FixBranchName(selectedLeafs[0]->GetRefName())));
1130 CAppUtils::RunTortoiseGitProc(sCmd);
1132 break;
1133 case eCmd_ViewLogRange:
1135 CString sCmd;
1136 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir), static_cast<LPCWSTR>(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, L"..")));
1137 CAppUtils::RunTortoiseGitProc(sCmd);
1139 break;
1140 case eCmd_ViewLogRangeReachableFromOnlyOne:
1142 CString sCmd;
1143 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir), static_cast<LPCWSTR>(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, L"...")));
1144 CAppUtils::RunTortoiseGitProc(sCmd);
1146 break;
1147 case eCmd_RepoBrowser:
1148 CAppUtils::RunTortoiseGitProc(L"/command:repobrowser /path:\"" + g_Git.m_CurrentDir + L"\" /rev:" + selectedLeafs[0]->GetRefName());
1149 break;
1150 case eCmd_DeleteBranch:
1151 case eCmd_DeleteRemoteBranch:
1153 if(ConfirmDeleteRef(selectedLeafs))
1154 DoDeleteRefs(selectedLeafs);
1155 Refresh();
1157 break;
1158 case eCmd_DeleteTag:
1160 if(ConfirmDeleteRef(selectedLeafs))
1161 DoDeleteRefs(selectedLeafs);
1162 Refresh();
1164 break;
1165 case eCmd_ShowReflog:
1167 CRefLogDlg refLogDlg(this);
1168 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
1169 refLogDlg.DoModal();
1171 break;
1172 case eCmd_Fetch:
1174 CAppUtils::Fetch(GetSafeHwnd(), remoteName);
1175 Refresh();
1177 break;
1178 case eCmd_DeleteRemoteTag:
1180 CDeleteRemoteTagDlg deleteRemoteTagDlg;
1181 const int remoteInx = selection >> 16;
1182 if (remoteInx < 0 || static_cast<size_t>(remoteInx) >= remotes.size())
1183 return;
1184 deleteRemoteTagDlg.m_sRemote = remotes[remoteInx];
1185 deleteRemoteTagDlg.DoModal();
1187 break;
1188 case eCmd_Merge:
1190 CString ref = selectedLeafs[0]->GetRefName();
1191 CAppUtils::Merge(GetSafeHwnd(), &ref);
1193 break;
1194 case eCmd_Switch:
1196 CAppUtils::Switch(GetSafeHwnd(), selectedLeafs[0]->GetRefName());
1198 break;
1199 case eCmd_Rename:
1201 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1202 if (pos)
1203 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1205 break;
1206 case eCmd_AddRemote:
1208 CAddRemoteDlg(this).DoModal();
1209 Refresh();
1211 break;
1212 case eCmd_ManageRemotes:
1214 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(), this).DoModal();
1215 // CSettingGitRemote W_Remotes(m_cmdPath);
1216 // W_Remotes.DoModal();
1217 Refresh();
1219 break;
1220 case eCmd_CreateBranch:
1222 CString* commitHash = nullptr;
1223 if (selectedLeafs.size() == 1)
1224 commitHash = &(selectedLeafs[0]->m_csRefHash);
1225 CAppUtils::CreateBranchTag(GetSafeHwnd(), false, commitHash);
1226 Refresh();
1228 break;
1229 case eCmd_CreateTag:
1231 CAppUtils::CreateBranchTag(GetSafeHwnd(), true);
1232 Refresh();
1234 break;
1235 case eCmd_DeleteAllTags:
1237 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1239 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1240 selectedLeafs.push_back(GetListEntry(i));
1242 if (ConfirmDeleteRef(selectedLeafs))
1243 DoDeleteRefs(selectedLeafs);
1244 Refresh();
1246 break;
1247 case eCmd_Diff:
1249 CFileDiffDlg dlg;
1250 dlg.SetDiff(
1251 nullptr,
1252 selectedLeafs[0]->GetRefName(),
1253 selectedLeafs[1]->GetRefName());
1254 dlg.DoModal();
1256 break;
1257 case eCmd_UnifiedDiff:
1259 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs[0]->m_csRefHash, CTGitPath(), selectedLeafs[1]->m_csRefHash, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1261 break;
1262 case eCmd_DiffWC:
1264 CString sCmd;
1265 sCmd.Format(L"/command:showcompare /path:\"%s\" /revision1:%s /revision2:%s", static_cast<LPCWSTR>(g_Git.m_CurrentDir), static_cast<LPCWSTR>(selectedLeafs[0]->GetRefName()), static_cast<LPCWSTR>(GitRev::GetWorkingCopy()));
1266 if (!!(GetAsyncKeyState(VK_SHIFT) & 0x8000))
1267 sCmd += L" /alternative";
1269 CAppUtils::RunTortoiseGitProc(sCmd);
1271 break;
1272 case eCmd_EditBranchDescription:
1274 CInputDlg dlg;
1275 dlg.m_sHintText.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION);
1276 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1277 dlg.m_sTitle.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION);
1278 dlg.m_bUseLogWidth = true;
1279 if(dlg.DoModal() == IDOK)
1281 CAppUtils::UpdateBranchDescription(selectedLeafs[0]->GetRefsHeadsName(), dlg.m_sInputText);
1282 Refresh();
1285 break;
1286 case eCmd_UpstreamDrop:
1288 CString key;
1289 key.Format(L"branch.%s.remote", static_cast<LPCWSTR>(selectedLeafs[0]->GetRefsHeadsName()));
1290 g_Git.UnsetConfigValue(key);
1291 key.Format(L"branch.%s.merge", static_cast<LPCWSTR>(selectedLeafs[0]->GetRefsHeadsName()));
1292 g_Git.UnsetConfigValue(key);
1294 Refresh();
1295 break;
1296 case eCmd_UpstreamSet:
1298 CString newRef = CBrowseRefsDlg::PickRef(false, L"", gPickRef_Remote, false);
1299 if (newRef.IsEmpty() || newRef.Find(L"refs/remotes/") != 0)
1300 return;
1301 CString remote, branch;
1302 if (SplitRemoteBranchName(newRef, remote, branch))
1303 return;
1304 // Setting the config keys directly might result in an invalid situation if the remote is not set to
1305 // fetch the desired upstream branch (in remote.x.fetch), cf. issue #3638
1306 if (CString errorMsg; g_Git.Run(L"git.exe branch --set-upstream-to=\"" + remote + L'/' + branch + L"\" -- \"" + selectedLeafs[0]->GetRefsHeadsName() + L"\"", &errorMsg, CP_UTF8) != 0)
1308 MessageBox(errorMsg + "\r\n\r\nThis is generally caused when remote." + remote + ".fetch does not include the desired branch.", L"TortoiseGit", MB_ICONERROR);
1309 return;
1311 Refresh();
1313 break;
1314 case eCmd_Copy:
1316 CString sClipdata;
1317 bool first = true;
1318 for (const auto& leaf : selectedLeafs)
1320 if (!first)
1321 sClipdata += L"\r\n";
1322 sClipdata += CGit::StripRefName(leaf->GetRefName());
1323 first = false;
1325 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
1327 break;
1331 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1333 for (auto i = leafs.cbegin(); i != leafs.cend(); ++i)
1334 if(!(*i)->IsFrom(from))
1335 return false;
1336 return true;
1339 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1341 if (pMsg->message == WM_KEYDOWN)
1343 switch (pMsg->wParam)
1345 /* case VK_RETURN:
1347 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1349 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1351 PostMessage(WM_COMMAND, IDOK);
1353 return TRUE;
1356 break;
1357 */ case VK_F2:
1359 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1361 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1362 if (pos)
1363 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1366 break;
1368 case VK_F5:
1370 Refresh();
1372 break;
1373 case L'E':
1375 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
1377 m_ctrlFilter.SetSel(0, -1, FALSE);
1378 m_ctrlFilter.SetFocus();
1379 return TRUE;
1382 break;
1383 case L'A':
1384 if (pMsg->hwnd == m_ListRefLeafs.m_hWnd && (GetAsyncKeyState(VK_CONTROL) & 0x8000))
1386 // select all entries
1387 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1388 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1389 return TRUE;
1391 break;
1392 case VK_ESCAPE:
1393 if (GetFocus() == GetDlgItem(IDC_BROWSEREFS_EDIT_FILTER) && m_ctrlFilter.GetWindowTextLength())
1395 OnClickedCancelFilter(NULL, NULL);
1396 return TRUE;
1398 break;
1403 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1406 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1408 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1409 *pResult = 0;
1411 if(m_currSortCol == pNMLV->iSubItem)
1412 m_currSortDesc = !m_currSortDesc;
1413 else
1415 m_currSortCol = pNMLV->iSubItem;
1416 m_currSortDesc = false;
1419 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1420 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, reinterpret_cast<DWORD_PTR>(&compareFunc));
1422 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1425 void CBrowseRefsDlg::OnDestroy()
1427 if (!m_bPickedRefSet)
1428 m_pickedRef = GetSelectedRef(true, false);
1430 CResizableStandAloneDialog::OnDestroy();
1433 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1435 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1436 *pResult = 0;
1438 if (!(pNMListView->uChanged & LVIF_STATE))
1439 return;
1441 auto item = GetListEntry(pNMListView->iItem);
1442 if (item && pNMListView->uNewState == LVIS_SELECTED)
1443 m_sLastSelected = item->GetRefName();
1445 UpdateInfoLabel();
1448 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1450 *pResult = 0;
1452 if (!m_ListRefLeafs.GetFirstSelectedItemPosition())
1453 return;
1455 if (m_bWantPick)
1457 EndDialog(IDOK);
1458 return;
1461 CString sCmd;
1462 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir), static_cast<LPCWSTR>(g_Git.FixBranchName(GetSelectedRef(true, false))));
1463 CAppUtils::RunTortoiseGitProc(sCmd);
1466 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefs, bool showRangeOptionWithTwoRefs)
1468 CBrowseRefsDlg dlg(CString(), nullptr);
1470 if(initialRef.IsEmpty())
1471 initialRef = L"HEAD";
1472 dlg.m_bWantPick = true;
1473 dlg.m_initialRef = initialRef;
1474 dlg.m_pickRef_Kind = pickRef_Kind;
1475 dlg.m_bPickOne = !pickMultipleRefs;
1476 dlg.m_bShowRangeOptionWithTwoRefs = showRangeOptionWithTwoRefs;
1478 if(dlg.DoModal() != IDOK)
1479 return CString();
1481 return dlg.m_pickedRef;
1484 bool CBrowseRefsDlg::PickRefForCombo(CHistoryCombo& refComboBox, int pickRef_Kind /* = gPickRef_All*/, int useShortName /* = gPickRef_Head*/)
1486 CString origRef;
1487 refComboBox.GetLBText(refComboBox.GetCurSel(), origRef);
1488 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1489 if(resultRef.IsEmpty())
1490 return false;
1492 if (useShortName)
1494 CGit::REF_TYPE refType;
1495 CString shortName = CGit::GetShortName(resultRef, &refType);
1496 switch (refType)
1498 case CGit::REF_TYPE::LOCAL_BRANCH:
1499 if (useShortName & gPickRef_Head)
1500 resultRef = shortName;
1501 break;
1502 case CGit::REF_TYPE::ANNOTATED_TAG:
1503 case CGit::REF_TYPE::TAG:
1504 if (useShortName & gPickRef_Tag)
1505 resultRef = shortName;
1506 break;
1507 case CGit::REMOTE_BRANCH:
1508 if (useShortName & gPickRef_Remote)
1509 resultRef = shortName;
1510 break;
1514 CGit::StripRefName(resultRef);
1516 refComboBox.AddString(resultRef);
1518 return true;
1521 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1523 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1524 *pResult = FALSE;
1526 if (!pDispInfo->item.pszText)
1527 return; //User canceled changing
1529 auto pTree = GetListEntry(pDispInfo->item.iItem);
1530 if(!pTree->IsFrom(L"refs/heads/"))
1532 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1533 return;
1536 CString selectedTreeRef;
1537 HTREEITEM hTree = m_RefTreeCtrl.GetSelectedItem();
1538 if (!hTree)
1540 auto pTree2 = GetTreeEntry(hTree);
1541 selectedTreeRef = pTree2->GetRefName();
1544 CString origName = pTree->GetRefName().Mid(static_cast<int>(wcslen(L"refs/heads/")));
1546 CString newName;
1547 if (m_pListCtrlRoot)
1548 newName = m_pListCtrlRoot->GetRefName() + L'/';
1549 newName += pDispInfo->item.pszText;
1551 if (!CStringUtils::StartsWith(newName, L"refs/heads/"))
1553 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1554 return;
1557 CString newNameTrunced = newName.Mid(static_cast<int>(wcslen(L"refs/heads/")));
1559 if (CString errorMsg; g_Git.Run(L"git.exe branch -m \"" + origName + L"\" -- \"" + newNameTrunced + L'"', &errorMsg, CP_UTF8) != 0)
1561 MessageBox(errorMsg, L"TortoiseGit", MB_OK | MB_ICONERROR);
1562 return;
1564 //Do as if it failed to rename. Let Refresh() do the job.
1565 //*pResult = TRUE;
1567 Refresh(selectedTreeRef);
1569 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1571 // AfxMessageBox(W_csPopup);
1574 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1576 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1577 *pResult = FALSE;
1579 auto pTree = GetListEntry(pDispInfo->item.iItem);
1580 if(!pTree->IsFrom(L"refs/heads/"))
1582 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1583 return;
1587 void CBrowseRefsDlg::OnEnChangeEditFilter()
1589 SetTimer(IDT_FILTER, 1000, nullptr);
1592 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1594 if (nIDEvent == IDT_FILTER)
1596 KillTimer(IDT_FILTER);
1597 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1600 CResizableStandAloneDialog::OnTimer(nIDEvent);
1603 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1605 // FIXME: x64 version would get this function called with unexpected parameters.
1606 if (!lParam)
1607 return 0;
1609 auto rect = reinterpret_cast<LPRECT>(lParam);
1610 CPoint point = CPoint(rect->left, rect->bottom);
1611 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | ((m_SelectedFilters & x) ? MF_CHECKED : MF_UNCHECKED))
1612 CMenu popup;
1613 if (popup.CreatePopupMenu())
1615 CString temp;
1616 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1617 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1619 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1620 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1622 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1623 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1625 temp.LoadString(IDS_LOG_FILTER_REVS);
1626 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1628 popup.AppendMenu(MF_SEPARATOR);
1630 temp.LoadString(IDS_LOG_FILTER_TOGGLE);
1631 popup.AppendMenu(MF_STRING | MF_ENABLED, LOGFILTER_TOGGLE, temp);
1633 const int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this);
1634 if (selection != 0)
1636 if (selection == LOGFILTER_TOGGLE)
1637 m_SelectedFilters = (~m_SelectedFilters) & LOGFILTER_ALL;
1638 else
1639 m_SelectedFilters ^= selection;
1640 SetFilterCueText();
1641 SetTimer(IDT_FILTER, 1000, nullptr);
1644 return 0L;
1647 LRESULT CBrowseRefsDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1649 KillTimer(LOGFILTER_TIMER);
1650 m_ctrlFilter.SetWindowText(L"");
1651 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1652 return 0L;
1655 void CBrowseRefsDlg::SetFilterCueText()
1657 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1658 temp += L' ';
1660 if (m_SelectedFilters & LOGFILTER_REFNAME)
1661 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1663 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1665 if (!CStringUtils::EndsWith(temp, L' '))
1666 temp += L", ";
1667 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1670 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1672 if (!CStringUtils::EndsWith(temp, L' '))
1673 temp += L", ";
1674 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1677 if (m_SelectedFilters & LOGFILTER_REVS)
1679 if (!CStringUtils::EndsWith(temp, L' '))
1680 temp += L", ";
1681 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1684 // to make the cue banner text appear more to the right of the edit control
1685 temp = L" " + temp;
1686 m_ctrlFilter.SetCueBanner(temp.TrimRight());
1689 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1691 m_pickedRef = g_Git.GetCurrentBranch(true);
1692 m_bPickedRefSet = true;
1693 OnOK();
1696 void CBrowseRefsDlg::UpdateInfoLabel()
1698 CString temp;
1699 temp.FormatMessage(IDS_REFBROWSE_INFO, m_ListRefLeafs.GetItemCount(), m_ListRefLeafs.GetSelectedCount());
1700 SetDlgItemText(IDC_INFOLABEL, temp);
1703 void CBrowseRefsDlg::OnBnClickedIncludeNestedRefs()
1705 UpdateData(TRUE);
1706 m_regIncludeNestedRefs = m_bIncludeNestedRefs;
1707 Refresh();
1710 CShadowTree* CBrowseRefsDlg::GetListEntry(int index)
1712 auto entry = reinterpret_cast<CShadowTree*>(m_ListRefLeafs.GetItemData(index));
1713 ASSERT(entry);
1714 return entry;
1717 CShadowTree* CBrowseRefsDlg::GetTreeEntry(HTREEITEM treeItem)
1719 auto entry = reinterpret_cast<CShadowTree*>(m_RefTreeCtrl.GetItemData(treeItem));
1720 ASSERT(entry);
1721 return entry;
1725 void CBrowseRefsDlg::OnCbnSelchangeBrowseRefsBranchfilter()
1727 Refresh();