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
, (LPCTSTR
)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
, (LPCTSTR
)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
, (LPCTSTR
)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 str
.Format(IDS_LOG_POPUP_MERGEREV
, (LPCTSTR
)g_Git
.GetCurrentBranch());
941 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
943 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
944 popupMenu
.AppendMenu(MF_SEPARATOR
);
947 if(bShowCreateBranchOption
)
949 bAddSeparator
= true;
950 temp
.LoadString(IDS_MENUBRANCH
);
951 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
954 if (bShowEditBranchDescriptionOption
)
956 bAddSeparator
= true;
957 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
959 if(bShowRenameOption
)
961 bAddSeparator
= true;
962 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
965 if (m_bHasWC
&& selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
968 popupMenu
.AppendMenu(MF_SEPARATOR
);
969 bAddSeparator
= true;
970 if (!selectedLeafs
[0]->m_csUpstream
.IsEmpty())
971 popupMenu
.AppendMenuIcon(eCmd_UpstreamDrop
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_DROPTRACKEDBRANCH
)));
972 popupMenu
.AppendMenuIcon(eCmd_UpstreamSet
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_SETTRACKEDBRANCH
)));
975 else if(selectedLeafs
.size() == 2)
977 bAddSeparator
= true;
978 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
979 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
981 menu
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
982 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
983 menu
.Format(IDS_SHOWLOG_OF
, (LPCTSTR
)GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
984 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
987 if(!selectedLeafs
.empty())
989 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
992 popupMenu
.AppendMenu(MF_SEPARATOR
);
993 CString menuItemName
;
994 if(selectedLeafs
.size() == 1)
995 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
997 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
999 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
1000 bAddSeparator
= true;
1002 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
1005 popupMenu
.AppendMenu(MF_SEPARATOR
);
1006 CString menuItemName
;
1007 if(selectedLeafs
.size() == 1)
1008 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1010 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1012 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1013 bAddSeparator
= true;
1015 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1018 popupMenu
.AppendMenu(MF_SEPARATOR
);
1019 CString menuItemName
;
1020 if(selectedLeafs
.size() == 1)
1021 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1023 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1025 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1026 bAddSeparator
= true;
1031 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1033 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1034 if(pTree
->IsFrom(L
"refs/remotes"))
1037 popupMenu
.AppendMenu(MF_SEPARATOR
);
1038 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1039 bAddSeparator
= true;
1040 if(selectedLeafs
.empty())
1042 CString remoteBranch
;
1043 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1045 int pos
= findVectorPosition(remotes
, remoteName
);
1049 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, (LPCTSTR
)remoteName
);
1050 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1052 temp
.LoadString(IDS_DELETEREMOTETAG
);
1053 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (pos
<< 16), temp
, IDI_DELETE
);
1057 if(pTree
->IsFrom(L
"refs/heads"))
1060 popupMenu
.AppendMenu(MF_SEPARATOR
);
1062 temp
.LoadString(IDS_MENUBRANCH
);
1063 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1065 if(pTree
->IsFrom(L
"refs/tags"))
1068 popupMenu
.AppendMenu(MF_SEPARATOR
);
1070 temp
.LoadString(IDS_MENUTAG
);
1071 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1072 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1073 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1074 if (!remotes
.empty())
1076 popupMenu
.AppendMenu(MF_SEPARATOR
);
1078 for (auto it
= remotes
.cbegin(); it
!= remotes
.cend(); ++it
, ++i
)
1080 temp
.Format(IDS_DELETEREMOTETAGON
, (LPCTSTR
)*it
);
1081 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (i
<< 16), temp
, IDI_DELETE
);
1088 int selection
= popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
| TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1089 switch ((eCmd
)(selection
& 0xFFFF))
1094 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1098 case eCmd_ViewLogRange
:
1101 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1105 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1108 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1112 case eCmd_RepoBrowser
:
1113 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1115 case eCmd_DeleteBranch
:
1116 case eCmd_DeleteRemoteBranch
:
1118 if(ConfirmDeleteRef(selectedLeafs
))
1119 DoDeleteRefs(selectedLeafs
);
1123 case eCmd_DeleteTag
:
1125 if(ConfirmDeleteRef(selectedLeafs
))
1126 DoDeleteRefs(selectedLeafs
);
1130 case eCmd_ShowReflog
:
1132 CRefLogDlg
refLogDlg(this);
1133 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1134 refLogDlg
.DoModal();
1139 CAppUtils::Fetch(remoteName
);
1143 case eCmd_DeleteRemoteTag
:
1145 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1146 int remoteInx
= selection
>> 16;
1147 if (remoteInx
< 0 || remoteInx
>= remotes
.size())
1149 deleteRemoteTagDlg
.m_sRemote
= remotes
[remoteInx
];
1150 deleteRemoteTagDlg
.DoModal();
1155 CString ref
= selectedLeafs
[0]->GetRefName();
1156 CAppUtils::Merge(&ref
);
1161 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1166 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1168 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1171 case eCmd_AddRemote
:
1173 CAddRemoteDlg(this).DoModal();
1177 case eCmd_ManageRemotes
:
1179 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(), this).DoModal();
1180 // CSettingGitRemote W_Remotes(m_cmdPath);
1181 // W_Remotes.DoModal();
1185 case eCmd_CreateBranch
:
1187 CString
*commitHash
= NULL
;
1188 if (selectedLeafs
.size() == 1)
1189 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1190 CAppUtils::CreateBranchTag(false, commitHash
);
1194 case eCmd_CreateTag
:
1196 CAppUtils::CreateBranchTag(true);
1200 case eCmd_DeleteAllTags
:
1202 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1204 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1205 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1207 if (ConfirmDeleteRef(selectedLeafs
))
1208 DoDeleteRefs(selectedLeafs
);
1217 selectedLeafs
[1]->GetRefName() + L
"^{}",
1218 selectedLeafs
[0]->GetRefName() + L
"^{}");
1222 case eCmd_UnifiedDiff
:
1224 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1227 case eCmd_EditBranchDescription
:
1230 dlg
.m_sHintText
.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
);
1231 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1232 dlg
.m_sTitle
.LoadString(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
);
1233 dlg
.m_bUseLogWidth
= true;
1234 if(dlg
.DoModal() == IDOK
)
1237 key
.Format(_T("branch.%s.description"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1238 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1239 dlg
.m_sInputText
.Trim();
1240 if (dlg
.m_sInputText
.IsEmpty())
1241 g_Git
.UnsetConfigValue(key
);
1243 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1248 case eCmd_UpstreamDrop
:
1251 key
.Format(_T("branch.%s.remote"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1252 g_Git
.UnsetConfigValue(key
);
1253 key
.Format(_T("branch.%s.merge"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1254 g_Git
.UnsetConfigValue(key
);
1258 case eCmd_UpstreamSet
:
1260 CString newRef
= CBrowseRefsDlg::PickRef(false, _T(""), gPickRef_Remote
, false);
1261 if (newRef
.IsEmpty() || newRef
.Find(_T("refs/remotes/")) != 0)
1263 CString remote
, branch
;
1264 if (SplitRemoteBranchName(newRef
, remote
, branch
))
1267 key
.Format(_T("branch.%s.remote"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1268 g_Git
.SetConfigValue(key
, remote
);
1269 key
.Format(_T("branch.%s.merge"), (LPCTSTR
)selectedLeafs
[0]->GetRefsHeadsName());
1270 g_Git
.SetConfigValue(key
, _T("refs/heads/") + branch
);
1277 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1279 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1280 if(!(*i
)->IsFrom(from
))
1285 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1287 if (pMsg
->message
== WM_KEYDOWN
)
1289 switch (pMsg
->wParam
)
1293 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1295 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1297 PostMessage(WM_COMMAND, IDOK);
1305 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1307 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1309 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1321 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
1323 m_ctrlFilter
.SetSel(0, -1, FALSE
);
1324 m_ctrlFilter
.SetFocus();
1333 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1336 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1338 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1341 if(m_currSortCol
== pNMLV
->iSubItem
)
1342 m_currSortDesc
= !m_currSortDesc
;
1345 m_currSortCol
= pNMLV
->iSubItem
;
1346 m_currSortDesc
= false;
1349 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1350 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1352 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1355 void CBrowseRefsDlg::OnDestroy()
1357 if (!m_bPickedRefSet
)
1358 m_pickedRef
= GetSelectedRef(true, false);
1360 int maxcol
= m_ColumnManager
.GetColumnCount();
1361 for (int col
= 0; col
< maxcol
; ++col
)
1362 if (m_ColumnManager
.IsVisible(col
))
1363 m_ColumnManager
.ColumnResized(col
);
1364 m_ColumnManager
.WriteSettings();
1366 CResizableStandAloneDialog::OnDestroy();
1369 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1371 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1374 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1375 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1376 m_sLastSelected
= item
->GetRefName();
1381 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1385 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1390 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1392 CBrowseRefsDlg
dlg(CString(),NULL
);
1394 if(initialRef
.IsEmpty())
1395 initialRef
= L
"HEAD";
1396 dlg
.m_initialRef
= initialRef
;
1397 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1398 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1400 if(dlg
.DoModal() != IDOK
)
1403 return dlg
.m_pickedRef
;
1406 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1409 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1410 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1411 if(resultRef
.IsEmpty())
1413 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1414 resultRef
= resultRef
.Mid(5);
1415 // if(wcsncmp(resultRef,L"heads/",6)==0)
1416 // resultRef = resultRef.Mid(6);
1418 //Find closest match of choice in combobox
1420 int matchLength
= 0;
1421 CString comboRefName
;
1422 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1424 pComboBox
->GetLBText(i
, comboRefName
);
1425 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1426 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1427 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1429 matchLength
= comboRefName
.GetLength();
1434 pComboBox
->SetCurSel(ixFound
);
1436 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)
1441 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1443 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1446 if(pDispInfo
->item
.pszText
== NULL
)
1447 return; //User canceled changing
1449 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1451 if(!pTree
->IsFrom(L
"refs/heads/"))
1453 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1457 CString origName
= pTree
->GetRefName().Mid(11);
1460 if(m_pListCtrlRoot
!= NULL
)
1461 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1462 newName
+= pDispInfo
->item
.pszText
;
1464 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1466 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1470 CString newNameTrunced
= newName
.Mid(11);
1473 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1475 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1478 //Do as if it failed to rename. Let Refresh() do the job.
1483 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1485 // AfxMessageBox(W_csPopup);
1489 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1491 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1494 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1496 if(!pTree
->IsFrom(L
"refs/heads/"))
1498 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1503 void CBrowseRefsDlg::OnEnChangeEditFilter()
1505 SetTimer(IDT_FILTER
, 1000, NULL
);
1508 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1510 if (nIDEvent
== IDT_FILTER
)
1512 KillTimer(IDT_FILTER
);
1513 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1516 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1519 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1521 // FIXME: x64 version would get this function called with unexpected parameters.
1525 RECT
* rect
= (LPRECT
)lParam
;
1528 point
= CPoint(rect
->left
, rect
->bottom
);
1529 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1531 if (popup
.CreatePopupMenu())
1533 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1534 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1536 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1537 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1539 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1540 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1542 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1543 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1545 temp
.LoadString(IDS_LOG_FILTER_TOGGLE
);
1546 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, LOGFILTER_TOGGLE
, temp
);
1548 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1551 if (selection
== LOGFILTER_TOGGLE
)
1552 m_SelectedFilters
= (~m_SelectedFilters
) & LOGFILTER_ALL
;
1554 m_SelectedFilters
^= selection
;
1556 SetTimer(IDT_FILTER
, 1000, NULL
);
1562 LRESULT
CBrowseRefsDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
1564 KillTimer(LOGFILTER_TIMER
);
1565 m_ctrlFilter
.SetWindowText(_T(""));
1566 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1570 void CBrowseRefsDlg::SetFilterCueText()
1572 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1575 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1576 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1578 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1580 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1582 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1585 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1587 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1589 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1592 if (m_SelectedFilters
& LOGFILTER_REVS
)
1594 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1596 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1599 // to make the cue banner text appear more to the right of the edit control
1600 temp
= _T(" ") + temp
;
1601 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1604 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1606 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1607 m_bPickedRefSet
= true;
1611 void CBrowseRefsDlg::UpdateInfoLabel()
1614 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1615 SetDlgItemText(IDC_INFOLABEL
, temp
);
1619 void CBrowseRefsDlg::OnBnClickedIncludeNestedRefs()
1622 m_regIncludeNestedRefs
= m_bIncludeNestedRefs
;