1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - 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"
35 void SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
40 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
41 HDITEM HeaderItem
= {0};
42 HeaderItem
.mask
= HDI_FORMAT
;
43 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
45 pHeader
->GetItem(i
, &HeaderItem
);
46 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
47 pHeader
->SetItem(i
, &HeaderItem
);
51 pHeader
->GetItem(nColumn
, &HeaderItem
);
52 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
53 pHeader
->SetItem(nColumn
, &HeaderItem
);
57 // CBrowseRefsDlg dialog
59 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
61 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=NULL*/)
62 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
65 m_currSortDesc(false),
66 m_initialRef(L
"HEAD"),
67 m_pickRef_Kind(gPickRef_All
),
68 m_pListCtrlRoot(NULL
),
74 CBrowseRefsDlg::~CBrowseRefsDlg()
78 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
80 CDialog::DoDataExchange(pDX
);
81 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
82 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
86 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
87 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
88 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
90 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
92 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
93 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
94 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
98 // CBrowseRefsDlg message handlers
100 void CBrowseRefsDlg::OnBnClickedOk()
105 BOOL
CBrowseRefsDlg::OnInitDialog()
107 CResizableStandAloneDialog::OnInitDialog();
108 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
110 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
111 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
112 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
114 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
116 temp
.LoadString(IDS_BRANCHNAME
);
117 m_ListRefLeafs
.InsertColumn(eCol_Name
, temp
, 0, 150);
118 temp
.LoadString(IDS_DATELASTCOMMIT
);
119 m_ListRefLeafs
.InsertColumn(eCol_Date
, temp
, 0, 100);
120 temp
.LoadString(IDS_LASTCOMMIT
);
121 m_ListRefLeafs
.InsertColumn(eCol_Msg
, temp
, 0, 300);
122 temp
.LoadString(IDS_HASH
);
123 m_ListRefLeafs
.InsertColumn(eCol_Hash
, temp
, 0, 80);
125 AddAnchor(IDOK
,BOTTOM_RIGHT
);
126 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
128 Refresh(m_initialRef
);
130 EnableSaveRestore(L
"BrowseRefs");
132 CString sWindowTitle
;
133 GetWindowText(sWindowTitle
);
134 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
136 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
138 m_ListRefLeafs
.SetFocus();
142 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
144 int posSlash
=nameLeft
.Find('/');
149 nameLeft
.Empty();//Nothing left
153 nameSub
=nameLeft
.Left(posSlash
);
154 nameLeft
=nameLeft
.Mid(posSlash
+1);
156 if(nameSub
.IsEmpty())
159 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
162 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
163 nextNode
.m_csRefName
=nameSub
;
164 nextNode
.m_pParent
=this;
168 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
172 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
174 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
176 //Match of leaf name. Try match on total name.
177 CString totalRefName
= GetRefName();
178 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
179 return this; //Also match. Found.
184 //Not a leaf. Search all nodes.
185 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
187 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
189 return pSubtree
; //Found
192 return NULL
;//Not found
196 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
198 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
200 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
201 //List ctrl selection?
202 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
205 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
206 m_ListRefLeafs
.GetNextSelectedItem(pos
));
207 return pTree
->GetRefName();
211 //Tree ctrl selection?
212 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
215 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
216 return pTree
->GetRefName();
219 return CString();//None
222 void CBrowseRefsDlg::Refresh(CString selectRef
)
225 // g_Git.GetMapHashToFriendName(m_RefMap);
227 if(!selectRef
.IsEmpty())
229 if(selectRef
== "HEAD")
231 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
236 selectRef
= GetSelectedRef(false, true);
239 m_RefTreeCtrl
.DeleteAllItems();
240 m_ListRefLeafs
.DeleteAllItems();
241 m_TreeRoot
.m_ShadowTree
.clear();
242 m_TreeRoot
.m_csRefName
= "refs";
243 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"Refs",NULL
,NULL
);
244 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
247 g_Git
.Run(L
"git for-each-ref --format="
250 L
"%(authordate:relative)%04"
253 L
"%(authordate:iso8601)%03",
254 &allRefs
, NULL
, CP_UTF8
);
259 MAP_STRING_STRING refMap
;
261 //First sort on ref name
262 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
264 singleRef
.TrimLeft(L
"\r\n");
266 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
267 if(refName
.IsEmpty())
269 CString refRest
=singleRef
.Mid(valuePos
);
272 //Use ref based on m_pickRef_Kind
273 if(wcsncmp(refName
,L
"refs/heads",10)==0 && !(m_pickRef_Kind
& gPickRef_Head
) )
275 if(wcsncmp(refName
,L
"refs/tags",9)==0 && !(m_pickRef_Kind
& gPickRef_Tag
) )
277 if(wcsncmp(refName
,L
"refs/remotes",12)==0 && !(m_pickRef_Kind
& gPickRef_Remote
) )
280 refMap
[refName
] = refRest
; //Use
286 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
288 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
289 CString values
=iterRefMap
->second
;
290 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
293 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
294 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
295 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
296 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
297 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
301 if(selectRef
.IsEmpty() || !SelectRef(selectRef
, false))
302 //Probably not on a branch. Select root node.
303 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
307 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
311 CString newRefName
= GetFullRefName(refName
);
312 if(!newRefName
.IsEmpty())
313 refName
= newRefName
;
314 //else refName is not a valid ref. Try to select as good as possible.
316 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
317 return false; // Not a ref name
319 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
320 if(treeLeafHead
.m_hTree
!= NULL
)
322 //Not a leaf. Select tree node and return
323 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
327 if(treeLeafHead
.m_pParent
==NULL
)
328 return false; //Weird... should not occur.
330 //This is the current head.
331 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
333 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
335 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
336 if(pCurrShadowTree
== &treeLeafHead
)
338 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
339 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
346 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
350 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
351 refName
=refName
.Mid(5);
352 pTreePos
=&m_TreeRoot
;
354 if(refName
.IsEmpty())
355 return *pTreePos
;//Found leaf
357 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
360 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
361 ASSERT(!bCreateIfNotExist
);
365 if(!refName
.IsEmpty())
367 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
368 //Leafs are for the list control.
369 if(pNextTree
->m_hTree
==NULL
)
371 //New tree. Create node in control.
372 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
373 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
377 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
381 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
383 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
386 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
389 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
391 m_ListRefLeafs
.DeleteAllItems();
393 m_currSortDesc
= false;
394 SetSortArrow(&m_ListRefLeafs
,-1,false);
396 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
402 FillListCtrlForShadowTree(pTree
,L
"",true);
405 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
409 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
))
411 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
413 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
414 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, refNamePrefix
+pTree
->m_csRefName
);
415 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
416 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
417 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
425 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
427 m_pListCtrlRoot
= pTree
;
428 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
430 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
435 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
437 ASSERT(!leafs
.empty());
440 UINT mbIcon
=MB_ICONQUESTION
;
442 bool bIsRemoteBranch
= false;
443 bool bIsBranch
= false;
444 if (leafs
[0]->IsFrom(L
"refs/remotes")) {bIsBranch
= true; bIsRemoteBranch
= true;}
445 else if (leafs
[0]->IsFrom(L
"refs/heads")) {bIsBranch
= true;}
449 if(leafs
.size() == 1)
451 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
452 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
454 //Check if branch is fully merged in HEAD
455 CGitHash branchHash
= g_Git
.GetHash(leafs
[0]->GetRefName());
456 CGitHash commonAncestor
;
457 CString commonAncestorstr
;
459 cmd
.Format(L
"git.exe merge-base HEAD %s", leafs
[0]->GetRefName());
460 g_Git
.Run(cmd
, &commonAncestorstr
, NULL
, CP_UTF8
);
462 commonAncestor
=commonAncestorstr
;
464 if(commonAncestor
!= branchHash
)
466 csMessage
+= L
"\r\n\r\n";
467 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
468 mbIcon
= MB_ICONWARNING
;
473 csMessage
+= L
"\r\n\r\n";
474 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
475 mbIcon
= MB_ICONWARNING
;
480 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
482 csMessage
+= L
"\r\n\r\n";
483 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
484 mbIcon
= MB_ICONWARNING
;
488 csMessage
+= L
"\r\n\r\n";
489 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
490 mbIcon
= MB_ICONWARNING
;
495 else if(leafs
[0]->IsFrom(L
"refs/tags"))
497 if(leafs
.size() == 1)
499 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
500 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
504 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
508 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
512 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
514 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
515 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
520 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
522 bool bIsRemoteBranch
= false;
523 bool bIsBranch
= false;
524 if (wcsncmp(completeRefName
, L
"refs/remotes",12)==0) {bIsBranch
= true; bIsRemoteBranch
= true;}
525 else if (wcsncmp(completeRefName
, L
"refs/heads",10)==0) {bIsBranch
= true;}
529 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
533 int slash
= branchToDelete
.Find(L
'/');
536 CString remoteName
= branchToDelete
.Left(slash
);
537 CString remoteBranchToDelete
= branchToDelete
.Mid(slash
+ 1);
539 if(CAppUtils::IsSSHPutty())
541 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
544 cmd
.Format(L
"git.exe push \"%s\" :%s", remoteName
, remoteBranchToDelete
);
547 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
549 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
551 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
555 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
557 CString tagToDelete
= completeRefName
.Mid(10);
559 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
561 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
563 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
570 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
572 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
575 return pLeaf
->GetRefName();
579 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
581 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
582 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
585 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
587 CPoint clientPoint
=point
;
588 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
590 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
592 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
594 VectorPShadowTree tree
;
595 ShowContextMenu(point
,hTreeItem
,tree
);
599 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
601 std::vector
<CShadowTree
*> selectedLeafs
;
602 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
603 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
606 selectedLeafs
.push_back(
607 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
608 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
611 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
614 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
617 popupMenu
.CreatePopupMenu();
619 bool bAddSeparator
= false;
622 if(selectedLeafs
.size()==1)
624 bAddSeparator
= true;
626 bool bShowReflogOption
= false;
627 bool bShowFetchOption
= false;
628 bool bShowSwitchOption
= false;
629 bool bShowRenameOption
= false;
630 bool bShowCreateBranchOption
= false;
632 CString fetchFromCmd
;
634 if(selectedLeafs
[0]->IsFrom(L
"refs/heads"))
636 bShowReflogOption
= true;
637 bShowSwitchOption
= true;
638 bShowRenameOption
= true;
640 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes"))
642 bShowReflogOption
= true;
643 bShowFetchOption
= true;
644 bShowCreateBranchOption
= true;
646 int dummy
= 0;//Needed for tokenize
647 remoteName
= selectedLeafs
[0]->GetRefName();
648 remoteName
= remoteName
.Mid(13);
649 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
650 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
652 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
657 temp
.LoadString(IDS_MENULOG
);
658 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
659 if(bShowReflogOption
)
661 temp
.LoadString(IDS_MENUREFLOG
);
662 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
665 popupMenu
.AppendMenu(MF_SEPARATOR
);
666 bAddSeparator
= false;
670 bAddSeparator
= true;
671 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
675 popupMenu
.AppendMenu(MF_SEPARATOR
);
677 bAddSeparator
= false;
680 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
681 popupMenu
.AppendMenu(MF_SEPARATOR
);
684 if(bShowCreateBranchOption
)
686 bAddSeparator
= true;
687 temp
.LoadString(IDS_MENUBRANCH
);
688 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
691 if(bShowRenameOption
)
693 bAddSeparator
= true;
694 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
697 else if(selectedLeafs
.size() == 2)
699 bAddSeparator
= true;
700 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
703 if(!selectedLeafs
.empty())
705 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
708 popupMenu
.AppendMenu(MF_SEPARATOR
);
709 CString menuItemName
;
710 if(selectedLeafs
.size() == 1)
711 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
713 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
715 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
716 bAddSeparator
= true;
718 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
721 popupMenu
.AppendMenu(MF_SEPARATOR
);
722 CString menuItemName
;
723 if(selectedLeafs
.size() == 1)
724 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
726 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
728 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
729 bAddSeparator
= true;
731 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
734 popupMenu
.AppendMenu(MF_SEPARATOR
);
735 CString menuItemName
;
736 if(selectedLeafs
.size() == 1)
737 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
739 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
741 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
742 bAddSeparator
= true;
747 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
749 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
750 if(pTree
->IsFrom(L
"refs/remotes"))
753 popupMenu
.AppendMenu(MF_SEPARATOR
);
754 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
755 bAddSeparator
= true;
756 if(selectedLeafs
.empty())
758 int dummy
= 0;//Needed for tokenize
759 remoteName
= pTree
->GetRefName();
760 remoteName
= remoteName
.Mid(13);
761 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
762 if(!remoteName
.IsEmpty())
764 CString fetchFromCmd
;
765 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
766 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
770 if(pTree
->IsFrom(L
"refs/heads"))
773 popupMenu
.AppendMenu(MF_SEPARATOR
);
775 temp
.LoadString(IDS_MENUBRANCH
);
776 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
778 if(pTree
->IsFrom(L
"refs/tags"))
781 popupMenu
.AppendMenu(MF_SEPARATOR
);
783 temp
.LoadString(IDS_MENUTAG
);
784 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
789 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
795 dlg
.SetStartRef(selectedLeafs
[0]->GetRefName());
799 case eCmd_DeleteBranch
:
800 case eCmd_DeleteRemoteBranch
:
802 if(ConfirmDeleteRef(selectedLeafs
))
803 DoDeleteRefs(selectedLeafs
, true);
809 if(ConfirmDeleteRef(selectedLeafs
))
810 DoDeleteRefs(selectedLeafs
, true);
814 case eCmd_ShowReflog
:
816 CRefLogDlg
refLogDlg(this);
817 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
823 CAppUtils::Fetch(remoteName
);
829 CAppUtils::Switch(NULL
, selectedLeafs
[0]->GetRefName());
834 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
836 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
841 CAddRemoteDlg(this).DoModal();
845 case eCmd_ManageRemotes
:
847 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
848 // CSettingGitRemote W_Remotes(m_cmdPath);
849 // W_Remotes.DoModal();
853 case eCmd_CreateBranch
:
855 CString
*commitHash
= NULL
;
856 if (selectedLeafs
.size() == 1)
857 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
858 CAppUtils::CreateBranchTag(false, commitHash
);
864 CAppUtils::CreateBranchTag(true);
873 selectedLeafs
[0]->m_csRefHash
,
874 selectedLeafs
[1]->m_csRefHash
);
881 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
883 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
884 if(!(*i
)->IsFrom(from
))
889 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
891 if (pMsg
->message
== WM_KEYDOWN
)
893 switch (pMsg
->wParam
)
897 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
899 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
901 PostMessage(WM_COMMAND, IDOK);
909 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
911 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
913 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
927 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
930 class CRefLeafListCompareFunc
933 CRefLeafListCompareFunc(CListCtrl
* pList
, int col
, bool desc
):m_col(col
),m_desc(desc
),m_pList(pList
){}
935 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
937 return ((CRefLeafListCompareFunc
*)lParamSort
)->Compare(lParam1
,lParam2
);
940 int Compare(LPARAM lParam1
, LPARAM lParam2
)
943 (CShadowTree
*)m_pList
->GetItemData(lParam1
),
944 (CShadowTree
*)m_pList
->GetItemData(lParam2
));
947 int Compare(CShadowTree
* pLeft
, CShadowTree
* pRight
)
949 int result
=CompareNoDesc(pLeft
,pRight
);
955 int CompareNoDesc(CShadowTree
* pLeft
, CShadowTree
* pRight
)
959 case CBrowseRefsDlg::eCol_Name
: return pLeft
->GetRefName().CompareNoCase(pRight
->GetRefName());
960 case CBrowseRefsDlg::eCol_Date
: return pLeft
->m_csDate_Iso8601
.CompareNoCase(pRight
->m_csDate_Iso8601
);
961 case CBrowseRefsDlg::eCol_Msg
: return pLeft
->m_csSubject
.CompareNoCase(pRight
->m_csSubject
);
962 case CBrowseRefsDlg::eCol_Hash
: return pLeft
->m_csRefHash
.CompareNoCase(pRight
->m_csRefHash
);
975 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
977 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
980 if(m_currSortCol
== pNMLV
->iSubItem
)
981 m_currSortDesc
= !m_currSortDesc
;
984 m_currSortCol
= pNMLV
->iSubItem
;
985 m_currSortDesc
= false;
988 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
989 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
991 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
994 void CBrowseRefsDlg::OnDestroy()
996 m_pickedRef
= GetSelectedRef(true, false);
998 CResizableStandAloneDialog::OnDestroy();
1001 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1003 UNREFERENCED_PARAMETER(pNMHDR
);
1009 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
)
1011 CBrowseRefsDlg
dlg(CString(),NULL
);
1013 if(initialRef
.IsEmpty())
1014 initialRef
= L
"HEAD";
1015 dlg
.m_initialRef
= initialRef
;
1016 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1018 if(dlg
.DoModal() != IDOK
)
1021 return dlg
.m_pickedRef
;
1024 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1027 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1028 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1029 if(resultRef
.IsEmpty())
1031 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1032 resultRef
= resultRef
.Mid(5);
1033 // if(wcsncmp(resultRef,L"heads/",6)==0)
1034 // resultRef = resultRef.Mid(6);
1036 //Find closest match of choice in combobox
1038 int matchLength
= 0;
1039 CString comboRefName
;
1040 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1042 pComboBox
->GetLBText(i
, comboRefName
);
1043 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1044 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1045 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1047 matchLength
= comboRefName
.GetLength();
1052 pComboBox
->SetCurSel(ixFound
);
1054 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)
1059 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1061 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1064 if(pDispInfo
->item
.pszText
== NULL
)
1065 return; //User canceled changing
1067 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1069 if(!pTree
->IsFrom(L
"refs/heads"))
1071 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1075 CString origName
= pTree
->GetRefName().Mid(11);
1078 if(m_pListCtrlRoot
!= NULL
)
1079 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1080 newName
+= pDispInfo
->item
.pszText
;
1082 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1084 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1088 CString newNameTrunced
= newName
.Mid(11);
1091 if(g_Git
.Run(L
"git branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1093 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1096 //Do as if it failed to rename. Let Refresh() do the job.
1101 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1103 // AfxMessageBox(W_csPopup);
1107 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1109 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1112 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1114 if(!pTree
->IsFrom(L
"refs/heads"))
1116 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.