1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2014 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // 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"
39 static int SplitRemoteBranchName(CString ref
, CString
&remote
, CString
&branch
)
41 if (ref
.Left(13) == _T("refs/remotes/"))
43 else if (ref
.Left(8) == _T("remotes/"))
47 int result
= g_Git
.GetRemoteList(list
);
51 for (size_t i
= 0; i
< list
.size(); ++i
)
53 if (ref
.Left(list
[i
].GetLength() + 1) == list
[i
] + _T("/"))
56 branch
= ref
.Mid(list
[i
].GetLength() + 1);
70 void SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
75 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
76 HDITEM HeaderItem
= {0};
77 HeaderItem
.mask
= HDI_FORMAT
;
78 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
80 pHeader
->GetItem(i
, &HeaderItem
);
81 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
82 pHeader
->SetItem(i
, &HeaderItem
);
86 pHeader
->GetItem(nColumn
, &HeaderItem
);
87 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
88 pHeader
->SetItem(nColumn
, &HeaderItem
);
92 class CRefLeafListCompareFunc
95 CRefLeafListCompareFunc(CListCtrl
* pList
, int col
, bool desc
):m_col(col
),m_desc(desc
),m_pList(pList
){
96 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
98 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
101 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
103 return ((CRefLeafListCompareFunc
*)lParamSort
)->Compare(lParam1
,lParam2
);
106 int Compare(LPARAM lParam1
, LPARAM lParam2
)
109 (CShadowTree
*)m_pList
->GetItemData((int)lParam1
),
110 (CShadowTree
*)m_pList
->GetItemData((int)lParam2
));
113 int Compare(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
115 int result
=CompareNoDesc(pLeft
,pRight
);
121 int CompareNoDesc(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
125 case CBrowseRefsDlg::eCol_Name
: return SortStrCmp(pLeft
->GetRefName(), pRight
->GetRefName());
126 case CBrowseRefsDlg::eCol_Upstream
: return SortStrCmp(pLeft
->m_csUpstream
, pRight
->m_csUpstream
);
127 case CBrowseRefsDlg::eCol_Date
: return pLeft
->m_csDate_Iso8601
.CompareNoCase(pRight
->m_csDate_Iso8601
);
128 case CBrowseRefsDlg::eCol_Msg
: return SortStrCmp(pLeft
->m_csSubject
, pRight
->m_csSubject
);
129 case CBrowseRefsDlg::eCol_LastAuthor
: return SortStrCmp(pLeft
->m_csAuthor
, pRight
->m_csAuthor
);
130 case CBrowseRefsDlg::eCol_Hash
: return pLeft
->m_csRefHash
.CompareNoCase(pRight
->m_csRefHash
);
131 case CBrowseRefsDlg::eCol_Description
: return SortStrCmp(pLeft
->m_csDescription
, pRight
->m_csDescription
);
135 int SortStrCmp(const CString
& left
, const CString
& right
)
138 return StrCmpLogicalW(left
, right
);
139 return StrCmpI(left
, right
);
148 // CBrowseRefsDlg dialog
150 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
152 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=NULL*/)
153 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
156 m_currSortDesc(false),
157 m_initialRef(L
"HEAD"),
158 m_pickRef_Kind(gPickRef_All
),
159 m_pListCtrlRoot(NULL
),
161 m_SelectedFilters(LOGFILTER_ALL
),
162 m_ColumnManager(&m_ListRefLeafs
),
164 m_bPickedRefSet(false)
169 CBrowseRefsDlg::~CBrowseRefsDlg()
173 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
175 CDialog::DoDataExchange(pDX
);
176 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
177 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
178 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
182 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
183 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
184 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
186 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
188 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
189 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
190 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
191 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
192 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
193 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
194 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED
, OnClickedCancelFilter
)
196 ON_BN_CLICKED(IDC_CURRENTBRANCH
, OnBnClickedCurrentbranch
)
200 // CBrowseRefsDlg message handlers
202 void CBrowseRefsDlg::OnBnClickedOk()
204 if (m_bPickOne
|| m_ListRefLeafs
.GetSelectedCount() != 2)
211 popupMenu
.CreatePopupMenu();
213 std::vector
<CShadowTree
*> selectedLeafs
;
214 GetSelectedLeaves(selectedLeafs
);
216 popupMenu
.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG
);
217 popupMenu
.SetDefaultItem(1);
218 popupMenu
.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")), IDI_LOG
);
219 popupMenu
.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")), IDI_LOG
);
222 GetDlgItem(IDOK
)->GetWindowRect(&rect
);
223 int selection
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, rect
.left
, rect
.top
, this, 0);
231 m_bPickedRefSet
= true;
232 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T(".."));
238 m_bPickedRefSet
= true;
239 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..."));
248 BOOL
CBrowseRefsDlg::OnInitDialog()
250 CResizableStandAloneDialog::OnInitDialog();
251 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
253 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
254 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
255 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
258 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
259 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
260 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
261 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
262 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
263 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
265 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
266 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_TRACKEDBRANCH
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
267 static int columnWidths
[] = { 150, 100, 100, 300, 100, 80, 80 };
268 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Upstream
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
269 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
270 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
271 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
272 m_bPickedRefSet
= false;
274 AddAnchor(IDOK
,BOTTOM_RIGHT
);
275 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
276 AddAnchor(IDC_CURRENTBRANCH
, BOTTOM_RIGHT
);
278 Refresh(m_initialRef
);
280 EnableSaveRestore(L
"BrowseRefs");
282 CString sWindowTitle
;
283 GetWindowText(sWindowTitle
);
284 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
286 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
289 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
291 m_ListRefLeafs
.SetFocus();
295 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
297 int posSlash
=nameLeft
.Find('/');
302 nameLeft
.Empty();//Nothing left
306 nameSub
=nameLeft
.Left(posSlash
);
307 nameLeft
=nameLeft
.Mid(posSlash
+1);
309 if(nameSub
.IsEmpty())
312 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
315 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
316 nextNode
.m_csRefName
=nameSub
;
317 nextNode
.m_pParent
=this;
321 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
325 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
327 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
329 //Match of leaf name. Try match on total name.
330 CString totalRefName
= GetRefName();
331 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
332 return this; //Also match. Found.
337 //Not a leaf. Search all nodes.
338 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
340 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
342 return pSubtree
; //Found
345 return NULL
;//Not found
349 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
351 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
353 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
354 //List ctrl selection?
355 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
358 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
359 m_ListRefLeafs
.GetNextSelectedItem(pos
));
360 return pTree
->GetRefName();
362 else if (pos
&& !pickFirstSelIfMultiSel
)
364 // at least one leaf is selected
367 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
369 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
370 if(wcsncmp(ref
, L
"refs/", 5) == 0)
372 if(wcsncmp(ref
, L
"heads/", 6) == 0)
374 refs
+= ref
+ _T(" ");
380 //Tree ctrl selection?
381 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
384 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
385 return pTree
->GetRefName();
388 return CString();//None
391 static int GetBranchDescriptionsCallback(const git_config_entry
*entry
, void *data
)
393 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
394 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
395 CString val
= CUnicodeUtils::GetUnicode(entry
->value
, CP_UTF8
);
396 descriptions
->insert(std::make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
400 MAP_STRING_STRING
GetBranchDescriptions()
402 MAP_STRING_STRING descriptions
;
403 CAutoConfig
config(true);
404 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
405 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
409 void CBrowseRefsDlg::Refresh(CString selectRef
)
412 // g_Git.GetMapHashToFriendName(m_RefMap);
415 if (g_Git
.GetRemoteList(remotes
))
416 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get a list of remotes.")), _T("TortoiseGit"), MB_ICONERROR
);
418 if(!selectRef
.IsEmpty())
420 if(selectRef
== "HEAD")
422 if (g_Git
.GetCurrentBranchFromFile(g_Git
.m_CurrentDir
, selectRef
))
425 selectRef
= L
"refs/heads/" + selectRef
;
430 selectRef
= GetSelectedRef(false, true);
433 m_RefTreeCtrl
.DeleteAllItems();
434 m_ListRefLeafs
.DeleteAllItems();
435 m_TreeRoot
.m_ShadowTree
.clear();
436 m_TreeRoot
.m_csRefName
= "refs";
437 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"refs",NULL
,NULL
);
438 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
440 CString allRefs
, error
;
441 if (g_Git
.Run(L
"git.exe for-each-ref --format="
445 L
"%(authordate:relative)%04"
448 L
"%(authordate:iso8601)%03",
449 &allRefs
, &error
, CP_UTF8
))
451 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
457 MAP_STRING_STRING refMap
;
459 //First sort on ref name
460 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
462 singleRef
.TrimLeft(L
"\r\n");
464 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
465 if(refName
.IsEmpty())
467 CString refRest
=singleRef
.Mid(valuePos
);
470 //Use ref based on m_pickRef_Kind
471 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
473 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
475 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
478 refMap
[refName
] = refRest
; //Use
481 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
484 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
486 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
487 CString values
=iterRefMap
->second
;
488 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
491 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
492 treeLeaf
.m_csUpstream
= values
.Tokenize(L
"\04", valuePos
); if (valuePos
< 0) continue;
493 CGit::GetShortName(treeLeaf
.m_csUpstream
, treeLeaf
.m_csUpstream
, L
"refs/remotes/");
494 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
495 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
496 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
497 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
499 if (wcsncmp(iterRefMap
->first
, L
"refs/heads/", 11) == 0)
500 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
503 // always expand the tree first
504 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
506 // try exact match first
507 if (!selectRef
.IsEmpty() && !SelectRef(selectRef
, true))
508 SelectRef(selectRef
, false);
511 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
515 CString newRefName
= GetFullRefName(refName
);
516 if(!newRefName
.IsEmpty())
517 refName
= newRefName
;
518 //else refName is not a valid ref. Try to select as good as possible.
520 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
521 return false; // Not a ref name
523 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
524 if(treeLeafHead
.m_hTree
!= NULL
)
526 //Not a leaf. Select tree node and return
527 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
531 if(treeLeafHead
.m_pParent
==NULL
)
532 return false; //Weird... should not occur.
534 //This is the current head.
535 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
537 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
539 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
540 if(pCurrShadowTree
== &treeLeafHead
)
542 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
543 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
550 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
554 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
555 refName
=refName
.Mid(5);
556 pTreePos
=&m_TreeRoot
;
558 if(refName
.IsEmpty())
559 return *pTreePos
;//Found leaf
561 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
564 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
565 ASSERT(!bCreateIfNotExist
);
569 if(!refName
.IsEmpty())
571 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
572 //Leafs are for the list control.
573 if(pNextTree
->m_hTree
==NULL
)
575 //New tree. Create node in control.
576 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
577 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
581 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
585 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
587 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
590 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
593 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
595 m_ListRefLeafs
.DeleteAllItems();
597 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
603 FillListCtrlForShadowTree(pTree
,L
"",true);
604 m_ColumnManager
.SetVisible(eCol_Upstream
, (wcsncmp(pTree
->GetRefName(), L
"refs/heads", 11) == 0));
607 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
612 m_ctrlFilter
.GetWindowText(filter
);
614 bool positive
= filter
[0] != '!';
616 filter
= filter
.Mid(1);
617 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
618 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
, positive
))
620 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
622 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
623 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
624 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Upstream
, pTree
->m_csUpstream
);
625 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
626 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
627 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
628 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
630 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
638 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
640 m_pListCtrlRoot
= pTree
;
641 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
643 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
648 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
649 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
651 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
655 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
, bool positive
)
657 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
660 msg
= msg
.MakeLower();
662 if (msg
.Find(filter
) >= 0)
666 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
668 CString msg
= pTree
->m_csSubject
;
669 msg
= msg
.MakeLower();
671 if (msg
.Find(filter
) >= 0)
675 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
677 CString msg
= pTree
->m_csAuthor
;
678 msg
= msg
.MakeLower();
680 if (msg
.Find(filter
) >= 0)
684 if (m_SelectedFilters
& LOGFILTER_REVS
)
686 CString msg
= pTree
->m_csRefHash
;
687 msg
= msg
.MakeLower();
689 if (msg
.Find(filter
) >= 0)
695 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
697 ASSERT(!leafs
.empty());
700 UINT mbIcon
=MB_ICONQUESTION
;
702 bool bIsRemoteBranch
= false;
703 bool bIsBranch
= false;
704 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
705 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
709 if(leafs
.size() == 1)
711 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
712 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
714 //Check if branch is fully merged in HEAD
715 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
717 csMessage
+= L
"\r\n\r\n";
718 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
719 mbIcon
= MB_ICONWARNING
;
724 csMessage
+= L
"\r\n\r\n";
725 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
726 mbIcon
= MB_ICONWARNING
;
731 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
733 csMessage
+= L
"\r\n\r\n";
734 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
735 mbIcon
= MB_ICONWARNING
;
739 csMessage
+= L
"\r\n\r\n";
740 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
741 mbIcon
= MB_ICONWARNING
;
746 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
748 if(leafs
.size() == 1)
750 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
751 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
755 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
759 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
763 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
)
765 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
766 if(!DoDeleteRef((*i
)->GetRefName()))
771 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
)
773 bool bIsRemoteBranch
= false;
774 bool bIsBranch
= false;
775 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
776 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
780 CString branchToDelete
= completeRefName
.Mid(13);
781 CString remoteName
, remoteBranchToDelete
;
782 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
785 if (CAppUtils::IsSSHPutty())
786 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
788 CSysProgressDlg sysProgressDlg
;
789 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
790 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
791 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
792 sysProgressDlg
.SetShowProgressBar(false);
793 sysProgressDlg
.ShowModal(this, true);
796 list
.push_back(_T("refs/heads/") + remoteBranchToDelete
);
797 if (g_Git
.DeleteRemoteRefs(remoteName
, list
))
799 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(_T("Could not delete remote ref."), CGit::GIT_CMD_PUSH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
800 sysProgressDlg
.Stop();
804 sysProgressDlg
.Stop();
809 if (g_Git
.DeleteRef(completeRefName
))
811 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
815 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
817 if (g_Git
.DeleteRef(completeRefName
))
819 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
826 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
828 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
831 return pLeaf
->GetRefName();
835 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
837 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
838 else if (pWndFrom
== &m_ListRefLeafs
)
840 CRect headerPosition
;
841 m_ListRefLeafs
.GetHeaderCtrl()->GetWindowRect(headerPosition
);
842 if (!headerPosition
.PtInRect(point
))
843 OnContextMenu_ListRefLeafs(point
);
847 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
849 CPoint clientPoint
=point
;
850 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
852 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
854 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
856 VectorPShadowTree tree
;
857 ShowContextMenu(point
,hTreeItem
,tree
);
860 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
862 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
863 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
866 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
870 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
872 std::vector
<CShadowTree
*> selectedLeafs
;
873 GetSelectedLeaves(selectedLeafs
);
874 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
877 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
879 ASSERT(selectedLeafs
.size() == 2);
881 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
882 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
884 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
887 int findVectorPosition(const STRING_VECTOR
& vector
, const CString
& entry
)
890 for (auto it
= vector
.cbegin(); it
!= vector
.cend(); ++it
, ++i
)
898 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
901 popupMenu
.CreatePopupMenu();
903 bool bAddSeparator
= false;
906 if(selectedLeafs
.size()==1)
908 bAddSeparator
= true;
910 bool bShowReflogOption
= false;
911 bool bShowFetchOption
= false;
912 bool bShowRenameOption
= false;
913 bool bShowCreateBranchOption
= false;
914 bool bShowEditBranchDescriptionOption
= false;
916 CString fetchFromCmd
;
918 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
920 bShowReflogOption
= true;
921 bShowRenameOption
= true;
922 bShowEditBranchDescriptionOption
= true;
924 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
926 bShowReflogOption
= true;
927 bShowFetchOption
= true;
928 bShowCreateBranchOption
= true;
930 CString remoteBranch
;
931 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
932 bShowFetchOption
= false;
934 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
936 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
941 temp
.LoadString(IDS_MENULOG
);
942 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
943 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
944 if(bShowReflogOption
)
946 temp
.LoadString(IDS_MENUREFLOG
);
947 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
950 popupMenu
.AppendMenu(MF_SEPARATOR
);
951 bAddSeparator
= false;
955 bAddSeparator
= true;
956 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
960 popupMenu
.AppendMenu(MF_SEPARATOR
);
962 bAddSeparator
= false;
966 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
968 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
969 str
.Format(format
, g_Git
.GetCurrentBranch());
970 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
972 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
973 popupMenu
.AppendMenu(MF_SEPARATOR
);
976 if(bShowCreateBranchOption
)
978 bAddSeparator
= true;
979 temp
.LoadString(IDS_MENUBRANCH
);
980 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
983 if (bShowEditBranchDescriptionOption
)
985 bAddSeparator
= true;
986 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
988 if(bShowRenameOption
)
990 bAddSeparator
= true;
991 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
994 else if(selectedLeafs
.size() == 2)
996 bAddSeparator
= true;
997 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
998 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
1000 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1001 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
1002 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1003 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
1006 if(!selectedLeafs
.empty())
1008 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
1011 popupMenu
.AppendMenu(MF_SEPARATOR
);
1012 CString menuItemName
;
1013 if(selectedLeafs
.size() == 1)
1014 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
1016 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
1018 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
1019 bAddSeparator
= true;
1021 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
1024 popupMenu
.AppendMenu(MF_SEPARATOR
);
1025 CString menuItemName
;
1026 if(selectedLeafs
.size() == 1)
1027 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1029 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1031 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1032 bAddSeparator
= true;
1034 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1037 popupMenu
.AppendMenu(MF_SEPARATOR
);
1038 CString menuItemName
;
1039 if(selectedLeafs
.size() == 1)
1040 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1042 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1044 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1045 bAddSeparator
= true;
1050 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1052 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1053 if(pTree
->IsFrom(L
"refs/remotes"))
1056 popupMenu
.AppendMenu(MF_SEPARATOR
);
1057 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1058 bAddSeparator
= true;
1059 if(selectedLeafs
.empty())
1061 CString remoteBranch
;
1062 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1063 remoteName
= _T("");
1064 int pos
= findVectorPosition(remotes
, remoteName
);
1068 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
1069 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1071 temp
.LoadString(IDS_DELETEREMOTETAG
);
1072 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (pos
<< 16), temp
, IDI_DELETE
);
1076 if(pTree
->IsFrom(L
"refs/heads"))
1079 popupMenu
.AppendMenu(MF_SEPARATOR
);
1081 temp
.LoadString(IDS_MENUBRANCH
);
1082 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1084 if(pTree
->IsFrom(L
"refs/tags"))
1087 popupMenu
.AppendMenu(MF_SEPARATOR
);
1089 temp
.LoadString(IDS_MENUTAG
);
1090 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1091 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1092 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1093 if (!remotes
.empty())
1095 popupMenu
.AppendMenu(MF_SEPARATOR
);
1097 for (auto it
= remotes
.cbegin(); it
!= remotes
.cend(); ++it
, ++i
)
1099 temp
.Format(IDS_DELETEREMOTETAGON
, *it
);
1100 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
| (i
<< 16), temp
, IDI_DELETE
);
1107 int selection
= popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
| TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1108 switch ((eCmd
)(selection
& 0xFFFF))
1113 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1117 case eCmd_ViewLogRange
:
1120 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1124 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1127 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1131 case eCmd_RepoBrowser
:
1132 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1134 case eCmd_DeleteBranch
:
1135 case eCmd_DeleteRemoteBranch
:
1137 if(ConfirmDeleteRef(selectedLeafs
))
1138 DoDeleteRefs(selectedLeafs
);
1142 case eCmd_DeleteTag
:
1144 if(ConfirmDeleteRef(selectedLeafs
))
1145 DoDeleteRefs(selectedLeafs
);
1149 case eCmd_ShowReflog
:
1151 CRefLogDlg
refLogDlg(this);
1152 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1153 refLogDlg
.DoModal();
1158 CAppUtils::Fetch(remoteName
);
1162 case eCmd_DeleteRemoteTag
:
1164 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1165 int remoteInx
= selection
>> 16;
1166 if (remoteInx
< 0 || remoteInx
>= remotes
.size())
1168 deleteRemoteTagDlg
.m_sRemote
= remotes
[remoteInx
];
1169 deleteRemoteTagDlg
.DoModal();
1174 CString ref
= selectedLeafs
[0]->GetRefName();
1175 CAppUtils::Merge(&ref
);
1180 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1185 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1187 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1190 case eCmd_AddRemote
:
1192 CAddRemoteDlg(this).DoModal();
1196 case eCmd_ManageRemotes
:
1198 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(), this).DoModal();
1199 // CSettingGitRemote W_Remotes(m_cmdPath);
1200 // W_Remotes.DoModal();
1204 case eCmd_CreateBranch
:
1206 CString
*commitHash
= NULL
;
1207 if (selectedLeafs
.size() == 1)
1208 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1209 CAppUtils::CreateBranchTag(false, commitHash
);
1213 case eCmd_CreateTag
:
1215 CAppUtils::CreateBranchTag(true);
1219 case eCmd_DeleteAllTags
:
1221 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1223 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1224 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1226 if (ConfirmDeleteRef(selectedLeafs
))
1227 DoDeleteRefs(selectedLeafs
);
1236 selectedLeafs
[1]->GetRefName() + L
"^{}",
1237 selectedLeafs
[0]->GetRefName() + L
"^{}");
1241 case eCmd_UnifiedDiff
:
1243 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1246 case eCmd_EditBranchDescription
:
1249 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1250 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1251 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1252 dlg
.m_bUseLogWidth
= true;
1253 if(dlg
.DoModal() == IDOK
)
1256 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1257 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1258 dlg
.m_sInputText
.Trim();
1259 if (dlg
.m_sInputText
.IsEmpty())
1260 g_Git
.UnsetConfigValue(key
);
1262 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1270 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1272 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1273 if(!(*i
)->IsFrom(from
))
1278 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1280 if (pMsg
->message
== WM_KEYDOWN
)
1282 switch (pMsg
->wParam
)
1286 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1288 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1290 PostMessage(WM_COMMAND, IDOK);
1298 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1300 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1302 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1314 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
1316 m_ctrlFilter
.SetSel(0, -1, FALSE
);
1317 m_ctrlFilter
.SetFocus();
1326 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1329 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1331 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1334 if(m_currSortCol
== pNMLV
->iSubItem
)
1335 m_currSortDesc
= !m_currSortDesc
;
1338 m_currSortCol
= pNMLV
->iSubItem
;
1339 m_currSortDesc
= false;
1342 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1343 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1345 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1348 void CBrowseRefsDlg::OnDestroy()
1350 if (!m_bPickedRefSet
)
1351 m_pickedRef
= GetSelectedRef(true, false);
1353 int maxcol
= m_ColumnManager
.GetColumnCount();
1354 for (int col
= 0; col
< maxcol
; ++col
)
1355 if (m_ColumnManager
.IsVisible(col
))
1356 m_ColumnManager
.ColumnResized(col
);
1357 m_ColumnManager
.WriteSettings();
1359 CResizableStandAloneDialog::OnDestroy();
1362 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1364 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1367 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1368 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1369 m_sLastSelected
= item
->GetRefName();
1374 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1378 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1383 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1385 CBrowseRefsDlg
dlg(CString(),NULL
);
1387 if(initialRef
.IsEmpty())
1388 initialRef
= L
"HEAD";
1389 dlg
.m_initialRef
= initialRef
;
1390 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1391 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1393 if(dlg
.DoModal() != IDOK
)
1396 return dlg
.m_pickedRef
;
1399 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1402 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1403 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1404 if(resultRef
.IsEmpty())
1406 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1407 resultRef
= resultRef
.Mid(5);
1408 // if(wcsncmp(resultRef,L"heads/",6)==0)
1409 // resultRef = resultRef.Mid(6);
1411 //Find closest match of choice in combobox
1413 int matchLength
= 0;
1414 CString comboRefName
;
1415 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1417 pComboBox
->GetLBText(i
, comboRefName
);
1418 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1419 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1420 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1422 matchLength
= comboRefName
.GetLength();
1427 pComboBox
->SetCurSel(ixFound
);
1429 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)
1434 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1436 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1439 if(pDispInfo
->item
.pszText
== NULL
)
1440 return; //User canceled changing
1442 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1444 if(!pTree
->IsFrom(L
"refs/heads/"))
1446 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1450 CString origName
= pTree
->GetRefName().Mid(11);
1453 if(m_pListCtrlRoot
!= NULL
)
1454 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1455 newName
+= pDispInfo
->item
.pszText
;
1457 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1459 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1463 CString newNameTrunced
= newName
.Mid(11);
1466 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1468 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1471 //Do as if it failed to rename. Let Refresh() do the job.
1476 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1478 // AfxMessageBox(W_csPopup);
1482 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1484 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1487 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1489 if(!pTree
->IsFrom(L
"refs/heads/"))
1491 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1496 void CBrowseRefsDlg::OnEnChangeEditFilter()
1498 SetTimer(IDT_FILTER
, 1000, NULL
);
1501 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1503 if (nIDEvent
== IDT_FILTER
)
1505 KillTimer(IDT_FILTER
);
1506 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1509 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1512 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1514 // FIXME: x64 version would get this function called with unexpected parameters.
1518 RECT
* rect
= (LPRECT
)lParam
;
1521 point
= CPoint(rect
->left
, rect
->bottom
);
1522 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1524 if (popup
.CreatePopupMenu())
1526 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1527 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1529 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1530 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1532 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1533 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1535 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1536 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1538 temp
.LoadString(IDS_LOG_FILTER_TOGGLE
);
1539 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, LOGFILTER_TOGGLE
, temp
);
1541 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1544 if (selection
== LOGFILTER_TOGGLE
)
1545 m_SelectedFilters
= (~m_SelectedFilters
) & LOGFILTER_ALL
;
1547 m_SelectedFilters
^= selection
;
1549 SetTimer(IDT_FILTER
, 1000, NULL
);
1555 LRESULT
CBrowseRefsDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
1557 KillTimer(LOGFILTER_TIMER
);
1558 m_ctrlFilter
.SetWindowText(_T(""));
1559 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1563 void CBrowseRefsDlg::SetFilterCueText()
1565 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1568 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1569 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1571 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1573 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1575 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1578 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1580 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1582 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1585 if (m_SelectedFilters
& LOGFILTER_REVS
)
1587 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1589 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1592 // to make the cue banner text appear more to the right of the edit control
1593 temp
= _T(" ") + temp
;
1594 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1597 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1599 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1600 m_bPickedRefSet
= true;
1604 void CBrowseRefsDlg::UpdateInfoLabel()
1607 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1608 SetDlgItemText(IDC_INFOLABEL
, temp
);