1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2015 - 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
/*=NULL*/)
155 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
158 m_currSortDesc(false),
159 m_initialRef(L
"HEAD"),
160 m_pickRef_Kind(gPickRef_All
),
161 m_pListCtrlRoot(NULL
),
163 m_SelectedFilters(LOGFILTER_ALL
),
164 m_ColumnManager(&m_ListRefLeafs
),
166 m_bIncludeNestedRefs(TRUE
),
167 m_bPickedRefSet(false)
169 // get short/long datetime setting from registry
170 DWORD RegUseShortDateFormat
= CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE
);
171 if (RegUseShortDateFormat
)
172 m_DateFormat
= DATE_SHORTDATE
;
174 m_DateFormat
= DATE_LONGDATE
;
175 // get relative time display setting from registry
176 DWORD regRelativeTimes
= CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE
);
177 m_bRelativeTimes
= (regRelativeTimes
!= 0);
179 m_regIncludeNestedRefs
= CRegDWORD(_T("Software\\TortoiseGit\\RefBrowserIncludeNestedRefs"), TRUE
);
182 CBrowseRefsDlg::~CBrowseRefsDlg()
186 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
188 CDialog::DoDataExchange(pDX
);
189 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
190 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
191 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
192 DDX_Check(pDX
, IDC_INCLUDENESTEDREFS
, m_bIncludeNestedRefs
);
196 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
197 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
198 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
200 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
202 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
203 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
204 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
205 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
206 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
207 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
208 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED
, OnClickedCancelFilter
)
210 ON_BN_CLICKED(IDC_CURRENTBRANCH
, OnBnClickedCurrentbranch
)
211 ON_BN_CLICKED(IDC_INCLUDENESTEDREFS
, &CBrowseRefsDlg::OnBnClickedIncludeNestedRefs
)
215 // CBrowseRefsDlg message handlers
217 void CBrowseRefsDlg::OnBnClickedOk()
219 if (m_bPickOne
|| m_ListRefLeafs
.GetSelectedCount() != 2)
226 popupMenu
.CreatePopupMenu();
228 std::vector
<CShadowTree
*> selectedLeafs
;
229 GetSelectedLeaves(selectedLeafs
);
231 popupMenu
.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG
);
232 popupMenu
.SetDefaultItem(1);
233 popupMenu
.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")), IDI_LOG
);
234 popupMenu
.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")), IDI_LOG
);
237 GetDlgItem(IDOK
)->GetWindowRect(&rect
);
238 int selection
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, rect
.left
, rect
.top
, this, 0);
246 m_bPickedRefSet
= true;
247 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T(".."));
253 m_bPickedRefSet
= true;
254 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..."));
263 BOOL
CBrowseRefsDlg::OnInitDialog()
265 CResizableStandAloneDialog::OnInitDialog();
266 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
268 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
269 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
270 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
273 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
274 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
275 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
276 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
277 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
278 AddAnchor(IDC_INCLUDENESTEDREFS
, BOTTOM_LEFT
);
279 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
281 CRegDWORD
regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE
);
282 DWORD exStyle
= LVS_EX_INFOTIP
;
283 if (DWORD(regFullRowSelect
))
284 exStyle
|= LVS_EX_FULLROWSELECT
;
285 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle() | exStyle
);
286 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_TRACKEDBRANCH
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
287 static int columnWidths
[] = { 150, 100, 100, 300, 100, 80, 80 };
288 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Upstream
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
289 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
290 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
291 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
292 m_bPickedRefSet
= false;
294 AddAnchor(IDOK
,BOTTOM_RIGHT
);
295 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
296 AddAnchor(IDC_CURRENTBRANCH
, BOTTOM_RIGHT
);
298 m_bIncludeNestedRefs
= !!m_regIncludeNestedRefs
;
301 Refresh(m_initialRef
);
303 EnableSaveRestore(L
"BrowseRefs");
305 CString sWindowTitle
;
306 GetWindowText(sWindowTitle
);
307 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
309 m_bHasWC
= !GitAdminDir::IsBareRepo(g_Git
.m_CurrentDir
);
312 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
314 m_ListRefLeafs
.SetFocus();
318 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
320 int posSlash
=nameLeft
.Find('/');
325 nameLeft
.Empty();//Nothing left
329 nameSub
=nameLeft
.Left(posSlash
);
330 nameLeft
=nameLeft
.Mid(posSlash
+1);
332 if(nameSub
.IsEmpty())
335 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
338 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
339 nextNode
.m_csRefName
=nameSub
;
340 nextNode
.m_pParent
=this;
344 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
348 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
350 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
352 //Match of leaf name. Try match on total name.
353 CString totalRefName
= GetRefName();
354 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
355 return this; //Also match. Found.
360 //Not a leaf. Search all nodes.
361 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
363 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
365 return pSubtree
; //Found
368 return NULL
;//Not found
371 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
373 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
374 //List ctrl selection?
375 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
378 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
379 m_ListRefLeafs
.GetNextSelectedItem(pos
));
380 return pTree
->GetRefName();
382 else if (pos
&& !pickFirstSelIfMultiSel
)
384 // at least one leaf is selected
387 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
389 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
390 if(wcsncmp(ref
, L
"refs/", 5) == 0)
392 if(wcsncmp(ref
, L
"heads/", 6) == 0)
394 refs
+= ref
+ _T(" ");
400 //Tree ctrl selection?
401 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
404 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
405 return pTree
->GetRefName();
408 return CString();//None
411 void CBrowseRefsDlg::Refresh(CString selectRef
)
414 if (g_Git
.GetRemoteList(remotes
))
415 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get a list of remotes.")), _T("TortoiseGit"), MB_ICONERROR
);
417 if(!selectRef
.IsEmpty())
419 if(selectRef
== "HEAD")
421 if (g_Git
.GetCurrentBranchFromFile(g_Git
.m_CurrentDir
, selectRef
))
424 selectRef
= L
"refs/heads/" + selectRef
;
429 selectRef
= GetSelectedRef(false, true);
432 m_RefTreeCtrl
.DeleteAllItems();
433 m_ListRefLeafs
.DeleteAllItems();
434 m_TreeRoot
.m_ShadowTree
.clear();
435 m_TreeRoot
.m_csRefName
= "refs";
436 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"refs",NULL
,NULL
);
437 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
440 MAP_REF_GITREVREFBROWSER refMap
;
441 if (GitRevRefBrowser::GetGitRevRefMap(refMap
, err
, [&](const CString
& refName
)
443 //Use ref based on m_pickRef_Kind
444 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
446 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
448 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
450 if (m_pickRef_Kind
== gPickRef_Remote
&& wcsncmp(refName
, L
"refs/remotes/", 13) != 0) // do not show refs/stash if only remote branches are requested
455 MessageBox(_T("Get refs failed:") + err
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
459 for (auto iterRefMap
= refMap
.cbegin(); iterRefMap
!= refMap
.cend(); ++iterRefMap
)
461 CShadowTree
& treeLeaf
= GetTreeNode(iterRefMap
->first
, nullptr, true);
462 GitRevRefBrowser ref
= iterRefMap
->second
;
464 treeLeaf
.m_csRefHash
= ref
.m_CommitHash
.ToString();
465 treeLeaf
.m_csUpstream
= ref
.m_UpstreamRef
;
466 CGit::GetShortName(treeLeaf
.m_csUpstream
, treeLeaf
.m_csUpstream
, L
"refs/remotes/");
467 treeLeaf
.m_csSubject
= ref
.GetSubject();
468 treeLeaf
.m_csAuthor
= ref
.GetAuthorName();
469 treeLeaf
.m_csDate
= ref
.GetAuthorDate();
470 treeLeaf
.m_csDescription
= ref
.m_Description
;
473 // always expand the tree first
474 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
476 // try exact match first
477 if (!selectRef
.IsEmpty() && !SelectRef(selectRef
, true))
478 SelectRef(selectRef
, false);
481 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
485 CString newRefName
= GetFullRefName(refName
);
486 if(!newRefName
.IsEmpty())
487 refName
= newRefName
;
488 //else refName is not a valid ref. Try to select as good as possible.
490 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
491 return false; // Not a ref name
493 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
494 if(treeLeafHead
.m_hTree
!= NULL
)
496 //Not a leaf. Select tree node and return
497 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
501 if(treeLeafHead
.m_pParent
==NULL
)
502 return false; //Weird... should not occur.
504 //This is the current head.
505 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
507 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
509 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
510 if(pCurrShadowTree
== &treeLeafHead
)
512 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
513 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
520 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
524 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
525 refName
=refName
.Mid(5);
526 pTreePos
=&m_TreeRoot
;
528 if(refName
.IsEmpty())
529 return *pTreePos
;//Found leaf
531 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
534 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
535 ASSERT(!bCreateIfNotExist
);
539 if(!refName
.IsEmpty())
541 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
542 //Leafs are for the list control.
543 if(pNextTree
->m_hTree
==NULL
)
545 //New tree. Create node in control.
546 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
547 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
551 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
555 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
557 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
560 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
563 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
565 m_ListRefLeafs
.DeleteAllItems();
567 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
573 FillListCtrlForShadowTree(pTree
,L
"",true);
574 m_ColumnManager
.SetVisible(eCol_Upstream
, pTree
->IsFrom(L
"refs/heads"));
577 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
582 m_ctrlFilter
.GetWindowText(filter
);
584 bool positive
= filter
[0] != '!';
586 filter
= filter
.Mid(1);
587 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
588 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
, positive
))
590 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
592 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
593 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
594 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Upstream
, pTree
->m_csUpstream
);
595 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Date
, pTree
->m_csDate
!= 0 ? CLoglistUtils::FormatDateAndTime(pTree
->m_csDate
, m_DateFormat
, true, m_bRelativeTimes
) : _T(""));
596 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
597 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
598 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
600 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
607 if (!isFirstLevel
&& !m_bIncludeNestedRefs
)
609 else if (!isFirstLevel
)
610 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
612 m_pListCtrlRoot
= pTree
;
613 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
615 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
620 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
621 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
623 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
627 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
, bool positive
)
629 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
632 msg
= msg
.MakeLower();
634 if (msg
.Find(filter
) >= 0)
638 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
640 CString msg
= pTree
->m_csSubject
;
641 msg
= msg
.MakeLower();
643 if (msg
.Find(filter
) >= 0)
647 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
649 CString msg
= pTree
->m_csAuthor
;
650 msg
= msg
.MakeLower();
652 if (msg
.Find(filter
) >= 0)
656 if (m_SelectedFilters
& LOGFILTER_REVS
)
658 CString msg
= pTree
->m_csRefHash
;
659 msg
= msg
.MakeLower();
661 if (msg
.Find(filter
) >= 0)
667 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
669 ASSERT(!leafs
.empty());
672 UINT mbIcon
=MB_ICONQUESTION
;
674 bool bIsRemoteBranch
= false;
675 bool bIsBranch
= false;
676 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
677 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
681 if(leafs
.size() == 1)
683 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
684 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
686 //Check if branch is fully merged in HEAD
687 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
689 csMessage
+= L
"\r\n\r\n";
690 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
691 mbIcon
= MB_ICONWARNING
;
696 csMessage
+= L
"\r\n\r\n";
697 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
698 mbIcon
= MB_ICONWARNING
;
703 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
705 csMessage
+= L
"\r\n\r\n";
706 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
707 mbIcon
= MB_ICONWARNING
;
711 csMessage
+= L
"\r\n\r\n";
712 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
713 mbIcon
= MB_ICONWARNING
;
718 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
720 if(leafs
.size() == 1)
722 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
723 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
727 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
731 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
735 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
)
737 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
738 if(!DoDeleteRef((*i
)->GetRefName()))
743 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
)
745 bool bIsRemoteBranch
= false;
746 bool bIsBranch
= false;
747 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
748 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
752 CString branchToDelete
= completeRefName
.Mid(13);
753 CString remoteName
, remoteBranchToDelete
;
754 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
757 if (CAppUtils::IsSSHPutty())
758 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
760 CSysProgressDlg sysProgressDlg
;
761 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
762 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
763 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
764 sysProgressDlg
.SetShowProgressBar(false);
765 sysProgressDlg
.ShowModal(this, true);
768 list
.push_back(_T("refs/heads/") + remoteBranchToDelete
);
769 if (g_Git
.DeleteRemoteRefs(remoteName
, list
))
771 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(_T("Could not delete remote ref."), CGit::GIT_CMD_PUSH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
772 sysProgressDlg
.Stop();
776 sysProgressDlg
.Stop();
781 if (g_Git
.DeleteRef(completeRefName
))
783 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
787 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
789 if (g_Git
.DeleteRef(completeRefName
))
791 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
798 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
800 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
803 return pLeaf
->GetRefName();
807 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
809 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
810 else if (pWndFrom
== &m_ListRefLeafs
)
812 CRect headerPosition
;
813 m_ListRefLeafs
.GetHeaderCtrl()->GetWindowRect(headerPosition
);
814 if (!headerPosition
.PtInRect(point
))
815 OnContextMenu_ListRefLeafs(point
);
819 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
821 CPoint clientPoint
=point
;
822 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
824 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
826 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
828 VectorPShadowTree tree
;
829 ShowContextMenu(point
,hTreeItem
,tree
);
832 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
834 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
835 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
838 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
842 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
844 std::vector
<CShadowTree
*> selectedLeafs
;
845 GetSelectedLeaves(selectedLeafs
);
846 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
849 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
851 ASSERT(selectedLeafs
.size() == 2);
853 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
854 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
856 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
859 int findVectorPosition(const STRING_VECTOR
& vector
, const CString
& entry
)
862 for (auto it
= vector
.cbegin(); it
!= vector
.cend(); ++it
, ++i
)
870 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
873 popupMenu
.CreatePopupMenu();
875 bool bAddSeparator
= false;
878 if(selectedLeafs
.size()==1)
880 bAddSeparator
= true;
882 bool bShowReflogOption
= false;
883 bool bShowFetchOption
= false;
884 bool bShowRenameOption
= false;
885 bool bShowCreateBranchOption
= false;
886 bool bShowEditBranchDescriptionOption
= false;
888 CString fetchFromCmd
;
890 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
892 bShowReflogOption
= true;
893 bShowRenameOption
= true;
894 bShowEditBranchDescriptionOption
= true;
896 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
898 bShowReflogOption
= true;
899 bShowFetchOption
= true;
900 bShowCreateBranchOption
= true;
902 CString remoteBranch
;
903 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
904 bShowFetchOption
= false;
906 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
908 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
913 temp
.LoadString(IDS_MENULOG
);
914 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
915 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
916 if(bShowReflogOption
)
918 temp
.LoadString(IDS_MENUREFLOG
);
919 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
922 popupMenu
.AppendMenu(MF_SEPARATOR
);
923 bAddSeparator
= false;
927 bAddSeparator
= true;
928 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
932 popupMenu
.AppendMenu(MF_SEPARATOR
);
934 bAddSeparator
= false;
938 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
940 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
941 str
.Format(format
, g_Git
.GetCurrentBranch());
942 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
944 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
945 popupMenu
.AppendMenu(MF_SEPARATOR
);
948 if(bShowCreateBranchOption
)
950 bAddSeparator
= true;
951 temp
.LoadString(IDS_MENUBRANCH
);
952 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
955 if (bShowEditBranchDescriptionOption
)
957 bAddSeparator
= true;
958 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
960 if(bShowRenameOption
)
962 bAddSeparator
= true;
963 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
966 if (m_bHasWC
&& selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
969 popupMenu
.AppendMenu(MF_SEPARATOR
);
970 bAddSeparator
= true;
971 if (!selectedLeafs
[0]->m_csUpstream
.IsEmpty())
972 popupMenu
.AppendMenuIcon(eCmd_UpstreamDrop
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_DROPTRACKEDBRANCH
)));
973 popupMenu
.AppendMenuIcon(eCmd_UpstreamSet
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_SETTRACKEDBRANCH
)));
976 else if(selectedLeafs
.size() == 2)
978 bAddSeparator
= true;
979 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
980 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
982 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
983 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
984 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
985 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
988 if(!selectedLeafs
.empty())
990 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
993 popupMenu
.AppendMenu(MF_SEPARATOR
);
994 CString menuItemName
;
995 if(selectedLeafs
.size() == 1)
996 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
998 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
1000 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
1001 bAddSeparator
= true;
1003 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
1006 popupMenu
.AppendMenu(MF_SEPARATOR
);
1007 CString menuItemName
;
1008 if(selectedLeafs
.size() == 1)
1009 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1011 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1013 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1014 bAddSeparator
= true;
1016 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1019 popupMenu
.AppendMenu(MF_SEPARATOR
);
1020 CString menuItemName
;
1021 if(selectedLeafs
.size() == 1)
1022 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1024 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1026 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1027 bAddSeparator
= true;
1032 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1034 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1035 if(pTree
->IsFrom(L
"refs/remotes"))
1038 popupMenu
.AppendMenu(MF_SEPARATOR
);
1039 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1040 bAddSeparator
= true;
1041 if(selectedLeafs
.empty())
1043 CString remoteBranch
;
1044 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1045 remoteName
= _T("");
1046 int pos
= findVectorPosition(remotes
, remoteName
);
1050 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
1051 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1053 temp
.LoadString(IDS_DELETEREMOTETAG
);
1054 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (pos
<< 16), temp
, IDI_DELETE
);
1058 if(pTree
->IsFrom(L
"refs/heads"))
1061 popupMenu
.AppendMenu(MF_SEPARATOR
);
1063 temp
.LoadString(IDS_MENUBRANCH
);
1064 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1066 if(pTree
->IsFrom(L
"refs/tags"))
1069 popupMenu
.AppendMenu(MF_SEPARATOR
);
1071 temp
.LoadString(IDS_MENUTAG
);
1072 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1073 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1074 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1075 if (!remotes
.empty())
1077 popupMenu
.AppendMenu(MF_SEPARATOR
);
1079 for (auto it
= remotes
.cbegin(); it
!= remotes
.cend(); ++it
, ++i
)
1081 temp
.Format(IDS_DELETEREMOTETAGON
, *it
);
1082 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (i
<< 16), temp
, IDI_DELETE
);
1089 int selection
= popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
| TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1090 switch ((eCmd
)(selection
& 0xFFFF))
1095 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1099 case eCmd_ViewLogRange
:
1102 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1106 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1109 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1113 case eCmd_RepoBrowser
:
1114 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1116 case eCmd_DeleteBranch
:
1117 case eCmd_DeleteRemoteBranch
:
1119 if(ConfirmDeleteRef(selectedLeafs
))
1120 DoDeleteRefs(selectedLeafs
);
1124 case eCmd_DeleteTag
:
1126 if(ConfirmDeleteRef(selectedLeafs
))
1127 DoDeleteRefs(selectedLeafs
);
1131 case eCmd_ShowReflog
:
1133 CRefLogDlg
refLogDlg(this);
1134 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1135 refLogDlg
.DoModal();
1140 CAppUtils::Fetch(remoteName
);
1144 case eCmd_DeleteRemoteTag
:
1146 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1147 int remoteInx
= selection
>> 16;
1148 if (remoteInx
< 0 || remoteInx
>= remotes
.size())
1150 deleteRemoteTagDlg
.m_sRemote
= remotes
[remoteInx
];
1151 deleteRemoteTagDlg
.DoModal();
1156 CString ref
= selectedLeafs
[0]->GetRefName();
1157 CAppUtils::Merge(&ref
);
1162 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1167 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1169 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1172 case eCmd_AddRemote
:
1174 CAddRemoteDlg(this).DoModal();
1178 case eCmd_ManageRemotes
:
1180 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(), this).DoModal();
1181 // CSettingGitRemote W_Remotes(m_cmdPath);
1182 // W_Remotes.DoModal();
1186 case eCmd_CreateBranch
:
1188 CString
*commitHash
= NULL
;
1189 if (selectedLeafs
.size() == 1)
1190 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1191 CAppUtils::CreateBranchTag(false, commitHash
);
1195 case eCmd_CreateTag
:
1197 CAppUtils::CreateBranchTag(true);
1201 case eCmd_DeleteAllTags
:
1203 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1205 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1206 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1208 if (ConfirmDeleteRef(selectedLeafs
))
1209 DoDeleteRefs(selectedLeafs
);
1218 selectedLeafs
[1]->GetRefName() + L
"^{}",
1219 selectedLeafs
[0]->GetRefName() + L
"^{}");
1223 case eCmd_UnifiedDiff
:
1225 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1228 case eCmd_EditBranchDescription
:
1231 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1232 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1233 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1234 dlg
.m_bUseLogWidth
= true;
1235 if(dlg
.DoModal() == IDOK
)
1238 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->GetRefsHeadsName());
1239 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1240 dlg
.m_sInputText
.Trim();
1241 if (dlg
.m_sInputText
.IsEmpty())
1242 g_Git
.UnsetConfigValue(key
);
1244 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1249 case eCmd_UpstreamDrop
:
1252 key
.Format(_T("branch.%s.remote"), selectedLeafs
[0]->GetRefsHeadsName());
1253 g_Git
.UnsetConfigValue(key
);
1254 key
.Format(_T("branch.%s.merge"), selectedLeafs
[0]->GetRefsHeadsName());
1255 g_Git
.UnsetConfigValue(key
);
1259 case eCmd_UpstreamSet
:
1261 CString newRef
= CBrowseRefsDlg::PickRef(false, _T(""), gPickRef_Remote
, false);
1262 if (newRef
.IsEmpty() || newRef
.Find(_T("refs/remotes/")) != 0)
1264 CString remote
, branch
;
1265 if (SplitRemoteBranchName(newRef
, remote
, branch
))
1268 key
.Format(_T("branch.%s.remote"), selectedLeafs
[0]->GetRefsHeadsName());
1269 g_Git
.SetConfigValue(key
, remote
);
1270 key
.Format(_T("branch.%s.merge"), selectedLeafs
[0]->GetRefsHeadsName());
1271 g_Git
.SetConfigValue(key
, _T("refs/heads/") + branch
);
1278 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1280 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1281 if(!(*i
)->IsFrom(from
))
1286 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1288 if (pMsg
->message
== WM_KEYDOWN
)
1290 switch (pMsg
->wParam
)
1294 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1296 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1298 PostMessage(WM_COMMAND, IDOK);
1306 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1308 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1310 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1322 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
1324 m_ctrlFilter
.SetSel(0, -1, FALSE
);
1325 m_ctrlFilter
.SetFocus();
1334 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1337 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1339 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1342 if(m_currSortCol
== pNMLV
->iSubItem
)
1343 m_currSortDesc
= !m_currSortDesc
;
1346 m_currSortCol
= pNMLV
->iSubItem
;
1347 m_currSortDesc
= false;
1350 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1351 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1353 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1356 void CBrowseRefsDlg::OnDestroy()
1358 if (!m_bPickedRefSet
)
1359 m_pickedRef
= GetSelectedRef(true, false);
1361 int maxcol
= m_ColumnManager
.GetColumnCount();
1362 for (int col
= 0; col
< maxcol
; ++col
)
1363 if (m_ColumnManager
.IsVisible(col
))
1364 m_ColumnManager
.ColumnResized(col
);
1365 m_ColumnManager
.WriteSettings();
1367 CResizableStandAloneDialog::OnDestroy();
1370 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1372 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1375 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1376 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1377 m_sLastSelected
= item
->GetRefName();
1382 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1386 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1391 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1393 CBrowseRefsDlg
dlg(CString(),NULL
);
1395 if(initialRef
.IsEmpty())
1396 initialRef
= L
"HEAD";
1397 dlg
.m_initialRef
= initialRef
;
1398 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1399 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1401 if(dlg
.DoModal() != IDOK
)
1404 return dlg
.m_pickedRef
;
1407 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1410 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1411 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1412 if(resultRef
.IsEmpty())
1414 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1415 resultRef
= resultRef
.Mid(5);
1416 // if(wcsncmp(resultRef,L"heads/",6)==0)
1417 // resultRef = resultRef.Mid(6);
1419 //Find closest match of choice in combobox
1421 int matchLength
= 0;
1422 CString comboRefName
;
1423 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1425 pComboBox
->GetLBText(i
, comboRefName
);
1426 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1427 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1428 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1430 matchLength
= comboRefName
.GetLength();
1435 pComboBox
->SetCurSel(ixFound
);
1437 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)
1442 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1444 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1447 if(pDispInfo
->item
.pszText
== NULL
)
1448 return; //User canceled changing
1450 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1452 if(!pTree
->IsFrom(L
"refs/heads/"))
1454 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1458 CString origName
= pTree
->GetRefName().Mid(11);
1461 if(m_pListCtrlRoot
!= NULL
)
1462 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1463 newName
+= pDispInfo
->item
.pszText
;
1465 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1467 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1471 CString newNameTrunced
= newName
.Mid(11);
1474 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1476 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1479 //Do as if it failed to rename. Let Refresh() do the job.
1484 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1486 // AfxMessageBox(W_csPopup);
1490 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1492 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1495 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1497 if(!pTree
->IsFrom(L
"refs/heads/"))
1499 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1504 void CBrowseRefsDlg::OnEnChangeEditFilter()
1506 SetTimer(IDT_FILTER
, 1000, NULL
);
1509 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1511 if (nIDEvent
== IDT_FILTER
)
1513 KillTimer(IDT_FILTER
);
1514 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1517 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1520 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1522 // FIXME: x64 version would get this function called with unexpected parameters.
1526 RECT
* rect
= (LPRECT
)lParam
;
1529 point
= CPoint(rect
->left
, rect
->bottom
);
1530 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1532 if (popup
.CreatePopupMenu())
1534 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1535 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1537 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1538 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1540 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1541 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1543 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1544 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1546 temp
.LoadString(IDS_LOG_FILTER_TOGGLE
);
1547 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, LOGFILTER_TOGGLE
, temp
);
1549 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1552 if (selection
== LOGFILTER_TOGGLE
)
1553 m_SelectedFilters
= (~m_SelectedFilters
) & LOGFILTER_ALL
;
1555 m_SelectedFilters
^= selection
;
1557 SetTimer(IDT_FILTER
, 1000, NULL
);
1563 LRESULT
CBrowseRefsDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
1565 KillTimer(LOGFILTER_TIMER
);
1566 m_ctrlFilter
.SetWindowText(_T(""));
1567 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1571 void CBrowseRefsDlg::SetFilterCueText()
1573 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1576 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1577 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1579 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1581 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1583 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1586 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1588 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1590 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1593 if (m_SelectedFilters
& LOGFILTER_REVS
)
1595 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1597 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1600 // to make the cue banner text appear more to the right of the edit control
1601 temp
= _T(" ") + temp
;
1602 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1605 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1607 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1608 m_bPickedRefSet
= true;
1612 void CBrowseRefsDlg::UpdateInfoLabel()
1615 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1616 SetDlgItemText(IDC_INFOLABEL
, temp
);
1620 void CBrowseRefsDlg::OnBnClickedIncludeNestedRefs()
1623 m_regIncludeNestedRefs
= m_bIncludeNestedRefs
;