1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-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 // BrowseRefsDlg.cpp : implementation file
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
26 #include "AddRemoteDlg.h"
28 #include "Settings\SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
33 #include "FileDiffDlg.h"
34 #include "DeleteRemoteTagDlg.h"
35 #include "UnicodeUtils.h"
37 #include "SysProgressDlg.h"
38 #include "LoglistUtils.h"
39 #include "GitRevRefBrowser.h"
41 static int SplitRemoteBranchName(CString ref
, CString
&remote
, CString
&branch
)
43 if (ref
.Left(13) == _T("refs/remotes/"))
45 else if (ref
.Left(8) == _T("remotes/"))
49 int result
= g_Git
.GetRemoteList(list
);
53 for (size_t i
= 0; i
< list
.size(); ++i
)
55 if (ref
.Left(list
[i
].GetLength() + 1) == list
[i
] + _T("/"))
58 branch
= ref
.Mid(list
[i
].GetLength() + 1);
72 void SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
77 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
78 HDITEM HeaderItem
= {0};
79 HeaderItem
.mask
= HDI_FORMAT
;
80 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
82 pHeader
->GetItem(i
, &HeaderItem
);
83 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
84 pHeader
->SetItem(i
, &HeaderItem
);
88 pHeader
->GetItem(nColumn
, &HeaderItem
);
89 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
90 pHeader
->SetItem(nColumn
, &HeaderItem
);
94 class CRefLeafListCompareFunc
97 CRefLeafListCompareFunc(CListCtrl
* pList
, int col
, bool desc
):m_col(col
),m_desc(desc
),m_pList(pList
){
98 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
100 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
103 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
105 return ((CRefLeafListCompareFunc
*)lParamSort
)->Compare(lParam1
,lParam2
);
108 int Compare(LPARAM lParam1
, LPARAM lParam2
)
111 (CShadowTree
*)m_pList
->GetItemData((int)lParam1
),
112 (CShadowTree
*)m_pList
->GetItemData((int)lParam2
));
115 int Compare(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
117 int result
=CompareNoDesc(pLeft
,pRight
);
123 int CompareNoDesc(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
127 case CBrowseRefsDlg::eCol_Name
: return SortStrCmp(pLeft
->GetRefName(), pRight
->GetRefName());
128 case CBrowseRefsDlg::eCol_Upstream
: return SortStrCmp(pLeft
->m_csUpstream
, pRight
->m_csUpstream
);
129 case CBrowseRefsDlg::eCol_Date
: return ((pLeft
->m_csDate
== pRight
->m_csDate
) ? 0 : ((pLeft
->m_csDate
> pRight
->m_csDate
) ? 1 : -1));
130 case CBrowseRefsDlg::eCol_Msg
: return SortStrCmp(pLeft
->m_csSubject
, pRight
->m_csSubject
);
131 case CBrowseRefsDlg::eCol_LastAuthor
: return SortStrCmp(pLeft
->m_csAuthor
, pRight
->m_csAuthor
);
132 case CBrowseRefsDlg::eCol_Hash
: return pLeft
->m_csRefHash
.CompareNoCase(pRight
->m_csRefHash
);
133 case CBrowseRefsDlg::eCol_Description
: return SortStrCmp(pLeft
->m_csDescription
, pRight
->m_csDescription
);
137 int SortStrCmp(const CString
& left
, const CString
& right
)
140 return StrCmpLogicalW(left
, right
);
141 return StrCmpI(left
, right
);
150 // CBrowseRefsDlg dialog
152 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
154 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=nullptr*/)
155 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
158 m_currSortDesc(false),
159 m_regCurrSortCol(L
"Software\\TortoiseGit\\RefBrowserSortCol", 0),
160 m_regCurrSortDesc(L
"Software\\TortoiseGit\\RefBrowserSortDesc", FALSE
),
161 m_initialRef(L
"HEAD"),
162 m_pickRef_Kind(gPickRef_All
),
163 m_pListCtrlRoot(nullptr),
165 m_SelectedFilters(LOGFILTER_ALL
),
167 m_bIncludeNestedRefs(TRUE
),
168 m_bPickedRefSet(false)
170 // get short/long datetime setting from registry
171 DWORD RegUseShortDateFormat
= CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE
);
172 if (RegUseShortDateFormat
)
173 m_DateFormat
= DATE_SHORTDATE
;
175 m_DateFormat
= DATE_LONGDATE
;
176 // get relative time display setting from registry
177 DWORD regRelativeTimes
= CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE
);
178 m_bRelativeTimes
= (regRelativeTimes
!= 0);
180 m_regIncludeNestedRefs
= CRegDWORD(_T("Software\\TortoiseGit\\RefBrowserIncludeNestedRefs"), TRUE
);
182 m_currSortCol
= m_regCurrSortCol
;
183 m_currSortDesc
= m_regCurrSortDesc
== TRUE
;
186 CBrowseRefsDlg::~CBrowseRefsDlg()
188 m_regCurrSortCol
= m_currSortCol
;
189 m_regCurrSortDesc
= m_currSortDesc
;
192 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
194 CDialog::DoDataExchange(pDX
);
195 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
196 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
197 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
198 DDX_Check(pDX
, IDC_INCLUDENESTEDREFS
, m_bIncludeNestedRefs
);
202 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
203 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
204 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
206 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
208 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
209 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
210 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
211 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
212 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
213 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
214 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED
, OnClickedCancelFilter
)
216 ON_BN_CLICKED(IDC_CURRENTBRANCH
, OnBnClickedCurrentbranch
)
217 ON_BN_CLICKED(IDC_INCLUDENESTEDREFS
, &CBrowseRefsDlg::OnBnClickedIncludeNestedRefs
)
221 // CBrowseRefsDlg message handlers
223 void CBrowseRefsDlg::OnBnClickedOk()
225 if (m_bPickOne
|| m_ListRefLeafs
.GetSelectedCount() != 2)
232 popupMenu
.CreatePopupMenu();
234 std::vector
<CShadowTree
*> selectedLeafs
;
235 GetSelectedLeaves(selectedLeafs
);
237 popupMenu
.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG
);
238 popupMenu
.SetDefaultItem(1);
239 popupMenu
.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")), IDI_LOG
);
240 popupMenu
.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")), IDI_LOG
);
243 GetDlgItem(IDOK
)->GetWindowRect(&rect
);
244 int selection
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, rect
.left
, rect
.top
, this, 0);
252 m_bPickedRefSet
= true;
253 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T(".."));
259 m_bPickedRefSet
= true;
260 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..."));
269 BOOL
CBrowseRefsDlg::OnInitDialog()
271 CResizableStandAloneDialog::OnInitDialog();
272 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
274 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
275 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
276 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
279 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
280 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
281 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
282 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
283 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
284 AddAnchor(IDC_INCLUDENESTEDREFS
, BOTTOM_LEFT
);
285 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
287 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle() | LVS_EX_INFOTIP
);
288 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_TRACKEDBRANCH
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
289 static int columnWidths
[] = { 0, 0, 0, 300, 0, 0, 80 };
290 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Upstream
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
291 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
292 m_ListRefLeafs
.m_bAllowHiding
= false;
293 m_ListRefLeafs
.Init();
294 m_ListRefLeafs
.SetListContextMenuHandler([&](CPoint point
) {OnContextMenu_ListRefLeafs(point
); });
295 m_ListRefLeafs
.m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
296 m_ListRefLeafs
.m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
297 m_bPickedRefSet
= false;
299 AddAnchor(IDOK
,BOTTOM_RIGHT
);
300 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
301 AddAnchor(IDC_CURRENTBRANCH
, BOTTOM_RIGHT
);
303 m_bIncludeNestedRefs
= !!m_regIncludeNestedRefs
;
306 Refresh(m_initialRef
);
308 EnableSaveRestore(L
"BrowseRefs");
310 CString sWindowTitle
;
311 GetWindowText(sWindowTitle
);
312 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
314 m_bHasWC
= !GitAdminDir::IsBareRepo(g_Git
.m_CurrentDir
);
317 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
319 m_ListRefLeafs
.SetFocus();
323 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
325 int posSlash
=nameLeft
.Find('/');
330 nameLeft
.Empty();//Nothing left
334 nameSub
=nameLeft
.Left(posSlash
);
335 nameLeft
=nameLeft
.Mid(posSlash
+1);
337 if(nameSub
.IsEmpty())
340 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
343 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
344 nextNode
.m_csRefName
=nameSub
;
345 nextNode
.m_pParent
=this;
349 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
353 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
355 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
357 //Match of leaf name. Try match on total name.
358 CString totalRefName
= GetRefName();
359 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
360 return this; //Also match. Found.
365 //Not a leaf. Search all nodes.
366 for (auto itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
368 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
370 return pSubtree
; //Found
373 return nullptr; //Not found
376 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
378 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
379 //List ctrl selection?
380 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
383 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
384 m_ListRefLeafs
.GetNextSelectedItem(pos
));
385 return pTree
->GetRefName();
387 else if (pos
&& !pickFirstSelIfMultiSel
)
389 // at least one leaf is selected
392 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
394 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
395 if(wcsncmp(ref
, L
"refs/", 5) == 0)
397 if(wcsncmp(ref
, L
"heads/", 6) == 0)
399 refs
+= ref
+ _T(" ");
405 //Tree ctrl selection?
406 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
409 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
410 return pTree
->GetRefName();
413 return CString();//None
416 void CBrowseRefsDlg::Refresh(CString selectRef
)
419 if (g_Git
.GetRemoteList(remotes
))
420 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get a list of remotes.")), _T("TortoiseGit"), MB_ICONERROR
);
422 if(!selectRef
.IsEmpty())
424 if(selectRef
== "HEAD")
426 if (g_Git
.GetCurrentBranchFromFile(g_Git
.m_CurrentDir
, selectRef
))
429 selectRef
= L
"refs/heads/" + selectRef
;
433 selectRef
= GetSelectedRef(false, true);
435 m_RefTreeCtrl
.DeleteAllItems();
436 m_ListRefLeafs
.DeleteAllItems();
437 m_TreeRoot
.m_ShadowTree
.clear();
438 m_TreeRoot
.m_csRefName
= "refs";
439 m_TreeRoot
.m_hTree
= m_RefTreeCtrl
.InsertItem(L
"refs", nullptr, nullptr);
440 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
443 MAP_REF_GITREVREFBROWSER refMap
;
444 if (GitRevRefBrowser::GetGitRevRefMap(refMap
, err
, [&](const CString
& refName
)
446 //Use ref based on m_pickRef_Kind
447 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
449 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
451 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
453 if (m_pickRef_Kind
== gPickRef_Remote
&& wcsncmp(refName
, L
"refs/remotes/", 13) != 0) // do not show refs/stash if only remote branches are requested
458 MessageBox(_T("Get refs failed:") + err
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
462 for (auto iterRefMap
= refMap
.cbegin(); iterRefMap
!= refMap
.cend(); ++iterRefMap
)
464 CShadowTree
& treeLeaf
= GetTreeNode(iterRefMap
->first
, nullptr, true);
465 GitRevRefBrowser ref
= iterRefMap
->second
;
467 treeLeaf
.m_csRefHash
= ref
.m_CommitHash
.ToString();
468 treeLeaf
.m_csUpstream
= ref
.m_UpstreamRef
;
469 CGit::GetShortName(treeLeaf
.m_csUpstream
, treeLeaf
.m_csUpstream
, L
"refs/remotes/");
470 treeLeaf
.m_csSubject
= ref
.GetSubject();
471 treeLeaf
.m_csAuthor
= ref
.GetAuthorName();
472 treeLeaf
.m_csDate
= ref
.GetAuthorDate();
473 treeLeaf
.m_csDescription
= ref
.m_Description
;
476 // always expand the tree first
477 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
479 // try exact match first
480 if (!selectRef
.IsEmpty() && !SelectRef(selectRef
, true))
481 SelectRef(selectRef
, false);
484 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
488 CString newRefName
= GetFullRefName(refName
);
489 if(!newRefName
.IsEmpty())
490 refName
= newRefName
;
491 //else refName is not a valid ref. Try to select as good as possible.
493 if (_wcsnicmp(refName
, L
"refs", 4) != 0)
494 return false; // Not a ref name
496 CShadowTree
& treeLeafHead
= GetTreeNode(refName
, nullptr, false);
497 if (treeLeafHead
.m_hTree
)
499 //Not a leaf. Select tree node and return
500 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
504 if (!treeLeafHead
.m_pParent
)
505 return false; //Weird... should not occur.
507 //This is the current head.
508 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
510 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
512 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
513 if(pCurrShadowTree
== &treeLeafHead
)
515 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
516 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
523 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
527 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
528 refName
=refName
.Mid(5);
529 pTreePos
=&m_TreeRoot
;
531 if(refName
.IsEmpty())
532 return *pTreePos
;//Found leaf
534 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
537 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
538 ASSERT(!bCreateIfNotExist
);
542 if(!refName
.IsEmpty())
544 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
545 //Leafs are for the list control.
546 if (!pNextTree
->m_hTree
)
548 //New tree. Create node in control.
549 pNextTree
->m_hTree
= m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
, pTreePos
->m_hTree
, nullptr);
550 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
554 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
558 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
560 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
563 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
566 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
568 m_ListRefLeafs
.DeleteAllItems();
570 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
576 FillListCtrlForShadowTree(pTree
,L
"",true);
577 m_ListRefLeafs
.m_ColumnManager
.SetVisible(eCol_Upstream
, pTree
->IsFrom(L
"refs/heads"));
578 m_ListRefLeafs
.AdjustColumnWidths();
581 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
586 m_ctrlFilter
.GetWindowText(filter
);
588 bool positive
= filter
[0] != '!';
590 filter
= filter
.Mid(1);
591 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
592 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && !pTree
->m_pParent
) && IsMatchFilter(pTree
, ref
, filter
, positive
))
594 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
596 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
597 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
598 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Upstream
, pTree
->m_csUpstream
);
599 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Date
, pTree
->m_csDate
!= 0 ? CLoglistUtils::FormatDateAndTime(pTree
->m_csDate
, m_DateFormat
, true, m_bRelativeTimes
) : _T(""));
600 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
601 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
602 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
603 CString descrition
= pTree
->m_csDescription
;
604 descrition
.Replace(L
"\n", L
" ");
605 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Description
, descrition
);
611 if (!isFirstLevel
&& !m_bIncludeNestedRefs
)
613 else if (!isFirstLevel
)
614 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
616 m_pListCtrlRoot
= pTree
;
617 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
619 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
624 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
625 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
627 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
631 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
, bool positive
)
633 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
636 msg
= msg
.MakeLower();
638 if (msg
.Find(filter
) >= 0)
642 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
644 CString msg
= pTree
->m_csSubject
;
645 msg
= msg
.MakeLower();
647 if (msg
.Find(filter
) >= 0)
651 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
653 CString msg
= pTree
->m_csAuthor
;
654 msg
= msg
.MakeLower();
656 if (msg
.Find(filter
) >= 0)
660 if (m_SelectedFilters
& LOGFILTER_REVS
)
662 CString msg
= pTree
->m_csRefHash
;
663 msg
= msg
.MakeLower();
665 if (msg
.Find(filter
) >= 0)
671 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
673 ASSERT(!leafs
.empty());
676 UINT mbIcon
=MB_ICONQUESTION
;
678 bool bIsRemoteBranch
= false;
679 bool bIsBranch
= false;
680 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
681 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
685 if(leafs
.size() == 1)
687 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
688 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, (LPCTSTR
)branchToDelete
);
690 //Check if branch is fully merged in HEAD
691 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
693 csMessage
+= L
"\r\n\r\n";
694 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
695 mbIcon
= MB_ICONWARNING
;
700 csMessage
+= L
"\r\n\r\n";
701 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
702 mbIcon
= MB_ICONWARNING
;
707 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
709 csMessage
+= L
"\r\n\r\n";
710 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
711 mbIcon
= MB_ICONWARNING
;
715 csMessage
+= L
"\r\n\r\n";
716 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
717 mbIcon
= MB_ICONWARNING
;
722 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
724 if(leafs
.size() == 1)
726 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
727 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, (LPCTSTR
)tagToDelete
);
730 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
733 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
736 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
)
738 for (auto i
= leafs
.cbegin(); i
!= leafs
.cend(); ++i
)
739 if(!DoDeleteRef((*i
)->GetRefName()))
744 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
)
746 bool bIsRemoteBranch
= false;
747 bool bIsBranch
= false;
748 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
749 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
753 CString branchToDelete
= completeRefName
.Mid(13);
754 CString remoteName
, remoteBranchToDelete
;
755 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
758 if (CAppUtils::IsSSHPutty())
759 CAppUtils::LaunchPAgent(nullptr, &remoteName
);
761 CSysProgressDlg sysProgressDlg
;
762 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
763 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
764 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
765 sysProgressDlg
.SetShowProgressBar(false);
766 sysProgressDlg
.ShowModal(this, true);
769 list
.push_back(_T("refs/heads/") + remoteBranchToDelete
);
770 if (g_Git
.DeleteRemoteRefs(remoteName
, list
))
772 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(_T("Could not delete remote ref."), CGit::GIT_CMD_PUSH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
773 sysProgressDlg
.Stop();
777 sysProgressDlg
.Stop();
782 if (g_Git
.DeleteRef(completeRefName
))
784 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
788 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
790 if (g_Git
.DeleteRef(completeRefName
))
792 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
799 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
801 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
804 return pLeaf
->GetRefName();
808 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
810 if (pWndFrom
== &m_RefTreeCtrl
)
811 OnContextMenu_RefTreeCtrl(point
);
814 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
816 CPoint clientPoint
=point
;
817 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
819 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
821 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
823 VectorPShadowTree tree
;
824 ShowContextMenu(point
,hTreeItem
,tree
);
827 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
829 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
830 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
833 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
837 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
839 std::vector
<CShadowTree
*> selectedLeafs
;
840 GetSelectedLeaves(selectedLeafs
);
841 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
844 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
846 ASSERT(selectedLeafs
.size() == 2);
848 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
849 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
851 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
854 int findVectorPosition(const STRING_VECTOR
& vector
, const CString
& entry
)
857 for (auto it
= vector
.cbegin(); it
!= vector
.cend(); ++it
, ++i
)
865 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
868 popupMenu
.CreatePopupMenu();
870 bool bAddSeparator
= false;
873 if(selectedLeafs
.size()==1)
875 bAddSeparator
= true;
877 bool bShowReflogOption
= false;
878 bool bShowFetchOption
= false;
879 bool bShowRenameOption
= false;
880 bool bShowCreateBranchOption
= false;
881 bool bShowEditBranchDescriptionOption
= false;
883 CString fetchFromCmd
;
885 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
887 bShowReflogOption
= true;
888 bShowRenameOption
= true;
889 bShowEditBranchDescriptionOption
= true;
891 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
893 bShowReflogOption
= true;
894 bShowFetchOption
= true;
895 bShowCreateBranchOption
= true;
897 CString remoteBranch
;
898 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
899 bShowFetchOption
= false;
901 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, (LPCTSTR
)remoteName
);
905 temp
.LoadString(IDS_MENULOG
);
906 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
907 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
908 if(bShowReflogOption
)
910 temp
.LoadString(IDS_MENUREFLOG
);
911 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
914 popupMenu
.AppendMenu(MF_SEPARATOR
);
915 bAddSeparator
= false;
919 bAddSeparator
= true;
920 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
924 popupMenu
.AppendMenu(MF_SEPARATOR
);
926 bAddSeparator
= false;
930 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
932 str
.Format(IDS_LOG_POPUP_MERGEREV
, (LPCTSTR
)g_Git
.GetCurrentBranch());
933 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
935 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
936 popupMenu
.AppendMenu(MF_SEPARATOR
);
939 if(bShowCreateBranchOption
)
941 bAddSeparator
= true;
942 temp
.LoadString(IDS_MENUBRANCH
);
943 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
946 if (bShowEditBranchDescriptionOption
)
948 bAddSeparator
= true;
949 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
951 if(bShowRenameOption
)
953 bAddSeparator
= true;
954 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
957 if (m_bHasWC
&& selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
960 popupMenu
.AppendMenu(MF_SEPARATOR
);
961 bAddSeparator
= true;
962 if (!selectedLeafs
[0]->m_csUpstream
.IsEmpty())
963 popupMenu
.AppendMenuIcon(eCmd_UpstreamDrop
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_DROPTRACKEDBRANCH
)));
964 popupMenu
.AppendMenuIcon(eCmd_UpstreamSet
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_SETTRACKEDBRANCH
)));
967 else if(selectedLeafs
.size() == 2)
969 bAddSeparator
= true;
970 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
971 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
973 menu
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
974 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
975 menu
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
976 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
979 if(!selectedLeafs
.empty())
981 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
984 popupMenu
.AppendMenu(MF_SEPARATOR
);
985 CString menuItemName
;
986 if(selectedLeafs
.size() == 1)
987 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
989 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
991 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
992 bAddSeparator
= true;
994 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
997 popupMenu
.AppendMenu(MF_SEPARATOR
);
998 CString menuItemName
;
999 if(selectedLeafs
.size() == 1)
1000 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1002 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1004 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1005 bAddSeparator
= true;
1007 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1010 popupMenu
.AppendMenu(MF_SEPARATOR
);
1011 CString menuItemName
;
1012 if(selectedLeafs
.size() == 1)
1013 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1015 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1017 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1018 bAddSeparator
= true;
1023 if (hTreePos
&& selectedLeafs
.empty())
1025 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1026 if(pTree
->IsFrom(L
"refs/remotes"))
1029 popupMenu
.AppendMenu(MF_SEPARATOR
);
1030 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1031 bAddSeparator
= true;
1032 if(selectedLeafs
.empty())
1034 CString remoteBranch
;
1035 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1037 int pos
= findVectorPosition(remotes
, remoteName
);
1041 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, (LPCTSTR
)remoteName
);
1042 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1044 temp
.LoadString(IDS_DELETEREMOTETAG
);
1045 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (pos
<< 16), temp
, IDI_DELETE
);
1049 if(pTree
->IsFrom(L
"refs/heads"))
1052 popupMenu
.AppendMenu(MF_SEPARATOR
);
1054 temp
.LoadString(IDS_MENUBRANCH
);
1055 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1057 if(pTree
->IsFrom(L
"refs/tags"))
1060 popupMenu
.AppendMenu(MF_SEPARATOR
);
1062 temp
.LoadString(IDS_MENUTAG
);
1063 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1064 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1065 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1066 if (!remotes
.empty())
1068 popupMenu
.AppendMenu(MF_SEPARATOR
);
1070 for (auto it
= remotes
.cbegin(); it
!= remotes
.cend(); ++it
, ++i
)
1072 temp
.Format(IDS_DELETEREMOTETAGON
, (LPCTSTR
)*it
);
1073 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (i
<< 16), temp
, IDI_DELETE
);
1080 int selection
= popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
| TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1081 switch ((eCmd
)(selection
& 0xFFFF))
1086 sCmd
.Format(_T("/command:log /path:\"%s\" /range:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1087 CAppUtils::RunTortoiseGitProc(sCmd
);
1090 case eCmd_ViewLogRange
:
1093 sCmd
.Format(_T("/command:log /path:\"%s\" /range:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1094 CAppUtils::RunTortoiseGitProc(sCmd
);
1097 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1100 sCmd
.Format(_T("/command:log /path:\"%s\" /range:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1101 CAppUtils::RunTortoiseGitProc(sCmd
);
1104 case eCmd_RepoBrowser
:
1105 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1107 case eCmd_DeleteBranch
:
1108 case eCmd_DeleteRemoteBranch
:
1110 if(ConfirmDeleteRef(selectedLeafs
))
1111 DoDeleteRefs(selectedLeafs
);
1115 case eCmd_DeleteTag
:
1117 if(ConfirmDeleteRef(selectedLeafs
))
1118 DoDeleteRefs(selectedLeafs
);
1122 case eCmd_ShowReflog
:
1124 CRefLogDlg
refLogDlg(this);
1125 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1126 refLogDlg
.DoModal();
1131 CAppUtils::Fetch(remoteName
);
1135 case eCmd_DeleteRemoteTag
:
1137 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1138 int remoteInx
= selection
>> 16;
1139 if (remoteInx
< 0 || (size_t)remoteInx
>= remotes
.size())
1141 deleteRemoteTagDlg
.m_sRemote
= remotes
[remoteInx
];
1142 deleteRemoteTagDlg
.DoModal();
1147 CString ref
= selectedLeafs
[0]->GetRefName();
1148 CAppUtils::Merge(&ref
);
1153 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1158 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1160 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1163 case eCmd_AddRemote
:
1165 CAddRemoteDlg(this).DoModal();
1169 case eCmd_ManageRemotes
:
1171 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(), this).DoModal();
1172 // CSettingGitRemote W_Remotes(m_cmdPath);
1173 // W_Remotes.DoModal();
1177 case eCmd_CreateBranch
:
1179 CString
* commitHash
= nullptr;
1180 if (selectedLeafs
.size() == 1)
1181 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1182 CAppUtils::CreateBranchTag(false, commitHash
);
1186 case eCmd_CreateTag
:
1188 CAppUtils::CreateBranchTag(true);
1192 case eCmd_DeleteAllTags
:
1194 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1196 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1197 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1199 if (ConfirmDeleteRef(selectedLeafs
))
1200 DoDeleteRefs(selectedLeafs
);
1209 selectedLeafs
[0]->GetRefName() + L
"^{}",
1210 selectedLeafs
[1]->GetRefName() + L
"^{}");
1214 case eCmd_UnifiedDiff
:
1216 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
, !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
1219 case eCmd_EditBranchDescription
:
1222 dlg
.m_sHintText
.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
);
1223 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1224 dlg
.m_sTitle
.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
);
1225 dlg
.m_bUseLogWidth
= true;
1226 if(dlg
.DoModal() == IDOK
)
1228 CAppUtils::UpdateBranchDescription(selectedLeafs
[0]->GetRefsHeadsName(), dlg
.m_sInputText
);
1233 case eCmd_UpstreamDrop
:
1236 key
.Format(_T("branch.%s.remote"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1237 g_Git
.UnsetConfigValue(key
);
1238 key
.Format(_T("branch.%s.merge"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1239 g_Git
.UnsetConfigValue(key
);
1243 case eCmd_UpstreamSet
:
1245 CString newRef
= CBrowseRefsDlg::PickRef(false, _T(""), gPickRef_Remote
, false);
1246 if (newRef
.IsEmpty() || newRef
.Find(_T("refs/remotes/")) != 0)
1248 CString remote
, branch
;
1249 if (SplitRemoteBranchName(newRef
, remote
, branch
))
1252 key
.Format(_T("branch.%s.remote"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1253 g_Git
.SetConfigValue(key
, remote
);
1254 key
.Format(_T("branch.%s.merge"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1255 g_Git
.SetConfigValue(key
, _T("refs/heads/") + branch
);
1262 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1264 for (auto i
= leafs
.cbegin(); i
!= leafs
.cend(); ++i
)
1265 if(!(*i
)->IsFrom(from
))
1270 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1272 if (pMsg
->message
== WM_KEYDOWN
)
1274 switch (pMsg
->wParam
)
1278 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1280 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1282 PostMessage(WM_COMMAND, IDOK);
1290 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1292 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1294 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1306 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
1308 m_ctrlFilter
.SetSel(0, -1, FALSE
);
1309 m_ctrlFilter
.SetFocus();
1318 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1321 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1323 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1326 if(m_currSortCol
== pNMLV
->iSubItem
)
1327 m_currSortDesc
= !m_currSortDesc
;
1330 m_currSortCol
= pNMLV
->iSubItem
;
1331 m_currSortDesc
= false;
1334 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1335 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1337 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1340 void CBrowseRefsDlg::OnDestroy()
1342 if (!m_bPickedRefSet
)
1343 m_pickedRef
= GetSelectedRef(true, false);
1345 CResizableStandAloneDialog::OnDestroy();
1348 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1350 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1353 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1354 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1355 m_sLastSelected
= item
->GetRefName();
1360 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1364 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1369 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1371 CBrowseRefsDlg
dlg(CString(), nullptr);
1373 if(initialRef
.IsEmpty())
1374 initialRef
= L
"HEAD";
1375 dlg
.m_initialRef
= initialRef
;
1376 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1377 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1379 if(dlg
.DoModal() != IDOK
)
1382 return dlg
.m_pickedRef
;
1385 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1388 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1389 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1390 if(resultRef
.IsEmpty())
1392 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1393 resultRef
= resultRef
.Mid(5);
1394 // if(wcsncmp(resultRef,L"heads/",6)==0)
1395 // resultRef = resultRef.Mid(6);
1397 //Find closest match of choice in combobox
1399 int matchLength
= 0;
1400 CString comboRefName
;
1401 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1403 pComboBox
->GetLBText(i
, comboRefName
);
1404 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1405 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1406 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1408 matchLength
= comboRefName
.GetLength();
1413 pComboBox
->SetCurSel(ixFound
);
1415 ASSERT(FALSE
);//No match found. So either pickRef_Kind is wrong or the combobox does not contain the ref specified in the picker (which it should unless the repo has changed before creating the CBrowseRef dialog)
1420 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1422 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1425 if (!pDispInfo
->item
.pszText
)
1426 return; //User canceled changing
1428 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1430 if(!pTree
->IsFrom(L
"refs/heads/"))
1432 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1436 CString selectedTreeRef
;
1437 HTREEITEM hTree
= m_RefTreeCtrl
.GetSelectedItem();
1440 CShadowTree
* pTree2
= (CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
1441 selectedTreeRef
= pTree2
->GetRefName();
1444 CString origName
= pTree
->GetRefName().Mid(11);
1447 if (m_pListCtrlRoot
)
1448 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1449 newName
+= pDispInfo
->item
.pszText
;
1451 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1453 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1457 CString newNameTrunced
= newName
.Mid(11);
1460 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1462 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1465 //Do as if it failed to rename. Let Refresh() do the job.
1468 Refresh(selectedTreeRef
);
1470 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1472 // AfxMessageBox(W_csPopup);
1475 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1477 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1480 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1482 if(!pTree
->IsFrom(L
"refs/heads/"))
1484 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1489 void CBrowseRefsDlg::OnEnChangeEditFilter()
1491 SetTimer(IDT_FILTER
, 1000, nullptr);
1494 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1496 if (nIDEvent
== IDT_FILTER
)
1498 KillTimer(IDT_FILTER
);
1499 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1502 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1505 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1507 // FIXME: x64 version would get this function called with unexpected parameters.
1511 RECT
* rect
= (LPRECT
)lParam
;
1514 point
= CPoint(rect
->left
, rect
->bottom
);
1515 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1517 if (popup
.CreatePopupMenu())
1519 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1520 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1522 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1523 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1525 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1526 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1528 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1529 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1531 temp
.LoadString(IDS_LOG_FILTER_TOGGLE
);
1532 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, LOGFILTER_TOGGLE
, temp
);
1534 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1537 if (selection
== LOGFILTER_TOGGLE
)
1538 m_SelectedFilters
= (~m_SelectedFilters
) & LOGFILTER_ALL
;
1540 m_SelectedFilters
^= selection
;
1542 SetTimer(IDT_FILTER
, 1000, nullptr);
1548 LRESULT
CBrowseRefsDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
1550 KillTimer(LOGFILTER_TIMER
);
1551 m_ctrlFilter
.SetWindowText(_T(""));
1552 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1556 void CBrowseRefsDlg::SetFilterCueText()
1558 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1561 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1562 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1564 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1566 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1568 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1571 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1573 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1575 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1578 if (m_SelectedFilters
& LOGFILTER_REVS
)
1580 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1582 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1585 // to make the cue banner text appear more to the right of the edit control
1586 temp
= _T(" ") + temp
;
1587 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1590 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1592 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1593 m_bPickedRefSet
= true;
1597 void CBrowseRefsDlg::UpdateInfoLabel()
1600 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1601 SetDlgItemText(IDC_INFOLABEL
, temp
);
1604 void CBrowseRefsDlg::OnBnClickedIncludeNestedRefs()
1607 m_regIncludeNestedRefs
= m_bIncludeNestedRefs
;