1 // BrowseRefsDlg.cpp : implementation file
\r
5 #include "TortoiseProc.h"
\r
6 #include "BrowseRefsDlg.h"
\r
8 #include "AddRemoteDlg.h"
\r
9 #include "AppUtils.h"
\r
10 #include "Settings\SettingGitRemote.h"
\r
11 #include "SinglePropSheetDlg.h"
\r
12 #include "MessageBox.h"
\r
13 #include "RefLogDlg.h"
\r
14 #include "IconMenu.h"
\r
15 #include "FileDiffDlg.h"
\r
17 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
\r
19 if (control == NULL)
\r
21 // set the sort arrow
\r
22 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
\r
23 HDITEM HeaderItem = {0};
\r
24 HeaderItem.mask = HDI_FORMAT;
\r
25 for (int i=0; i<pHeader->GetItemCount(); ++i)
\r
27 pHeader->GetItem(i, &HeaderItem);
\r
28 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
\r
29 pHeader->SetItem(i, &HeaderItem);
\r
33 pHeader->GetItem(nColumn, &HeaderItem);
\r
34 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
\r
35 pHeader->SetItem(nColumn, &HeaderItem);
\r
39 // CBrowseRefsDlg dialog
\r
41 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
\r
43 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
\r
44 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
\r
47 m_currSortDesc(false),
\r
48 m_initialRef(L"HEAD"),
\r
49 m_pickRef_Kind(gPickRef_All)
\r
54 CBrowseRefsDlg::~CBrowseRefsDlg()
\r
58 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
\r
60 CDialog::DoDataExchange(pDX);
\r
61 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
\r
62 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
\r
66 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
\r
67 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
\r
68 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
\r
70 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
\r
72 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
\r
76 // CBrowseRefsDlg message handlers
\r
78 void CBrowseRefsDlg::OnBnClickedOk()
\r
83 BOOL CBrowseRefsDlg::OnInitDialog()
\r
85 CResizableStandAloneDialog::OnInitDialog();
\r
87 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
\r
88 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
\r
89 AddAnchor(IDHELP, BOTTOM_RIGHT);
\r
91 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
\r
92 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
\r
93 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
\r
94 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
\r
95 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
\r
97 AddAnchor(IDOK,BOTTOM_RIGHT);
\r
98 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
\r
100 Refresh(m_initialRef);
\r
102 EnableSaveRestore(L"BrowseRefs");
\r
105 m_ListRefLeafs.SetFocus();
\r
109 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
\r
111 int posSlash=nameLeft.Find('/');
\r
116 nameLeft.Empty();//Nothing left
\r
120 nameSub=nameLeft.Left(posSlash);
\r
121 nameLeft=nameLeft.Mid(posSlash+1);
\r
123 if(nameSub.IsEmpty())
\r
126 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
\r
129 CShadowTree& nextNode=m_ShadowTree[nameSub];
\r
130 nextNode.m_csRefName=nameSub;
\r
131 nextNode.m_pParent=this;
\r
135 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
\r
139 if(m_csRefName.GetLength() > partialRefName.GetLength())
\r
141 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
\r
143 //Match of leaf name. Try match on total name.
\r
144 CString totalRefName = GetRefName();
\r
145 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
\r
146 return this; //Also match. Found.
\r
151 //Not a leaf. Search all nodes.
\r
152 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
\r
154 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
\r
155 if(pSubtree != NULL)
\r
156 return pSubtree; //Found
\r
159 return NULL;//Not found
\r
163 typedef std::map<CString,CString> MAP_STRING_STRING;
\r
165 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
\r
167 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
\r
168 //List ctrl selection?
\r
169 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
\r
171 //A leaf is selected
\r
172 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
\r
173 m_ListRefLeafs.GetNextSelectedItem(pos));
\r
174 return pTree->GetRefName();
\r
176 else if(!onlyIfLeaf)
\r
178 //Tree ctrl selection?
\r
179 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
\r
182 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
\r
183 return pTree->GetRefName();
\r
186 return CString();//None
\r
189 void CBrowseRefsDlg::Refresh(CString selectRef)
\r
191 // m_RefMap.clear();
\r
192 // g_Git.GetMapHashToFriendName(m_RefMap);
\r
194 if(!selectRef.IsEmpty())
\r
196 if(selectRef == "HEAD")
\r
198 selectRef = g_Git.GetSymbolicRef(selectRef, false);
\r
203 selectRef = GetSelectedRef(false, true);
\r
206 m_RefTreeCtrl.DeleteAllItems();
\r
207 m_ListRefLeafs.DeleteAllItems();
\r
208 m_TreeRoot.m_ShadowTree.clear();
\r
209 m_TreeRoot.m_csRefName="refs";
\r
210 // m_TreeRoot.m_csShowName="Refs";
\r
211 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
\r
212 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
\r
215 g_Git.Run(L"git for-each-ref --format="
\r
217 L"%(objectname)%04"
\r
218 L"%(authordate:relative)%04"
\r
220 L"%(authorname)%04"
\r
221 L"%(authordate:iso8601)",
\r
227 MAP_STRING_STRING refMap;
\r
229 //First sort on ref name
\r
230 while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())
\r
233 CString refName=singleRef.Tokenize(L"\04",valuePos);
\r
234 CString refRest=singleRef.Mid(valuePos);
\r
236 //Use ref based on m_pickRef_Kind
\r
237 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
\r
239 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
\r
241 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
\r
244 refMap[refName] = refRest; //Use
\r
249 //Populate ref tree
\r
250 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
\r
252 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
\r
253 CString values=iterRefMap->second;
\r
254 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
\r
257 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
\r
258 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
\r
259 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
\r
260 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
\r
261 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
\r
265 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
\r
266 //Probably not on a branch. Select root node.
\r
267 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
\r
271 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
\r
274 refName = GetFullRefName(refName);
\r
275 if(wcsnicmp(refName,L"refs/",5)!=0)
\r
276 return false; // Not a ref name
\r
278 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
\r
279 if(treeLeafHead.m_hTree != NULL)
\r
281 //Not a leaf. Select tree node and return
\r
282 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
\r
286 if(treeLeafHead.m_pParent==NULL)
\r
287 return false; //Weird... should not occur.
\r
289 //This is the current head.
\r
290 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
\r
292 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
\r
294 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
\r
295 if(pCurrShadowTree == &treeLeafHead)
\r
297 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
\r
298 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
\r
305 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
\r
309 if(wcsnicmp(refName,L"refs/",5)==0)
\r
310 refName=refName.Mid(5);
\r
311 pTreePos=&m_TreeRoot;
\r
313 if(refName.IsEmpty())
\r
314 return *pTreePos;//Found leaf
\r
316 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
\r
317 if(pNextTree==NULL)
\r
319 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
\r
320 ASSERT(!bCreateIfNotExist);
\r
324 if(!refName.IsEmpty())
\r
326 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
\r
327 //Leafs are for the list control.
\r
328 if(pNextTree->m_hTree==NULL)
\r
330 //New tree. Create node in control.
\r
331 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
\r
332 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
\r
336 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
\r
340 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
\r
342 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
\r
345 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
\r
348 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
\r
350 m_ListRefLeafs.DeleteAllItems();
\r
351 m_currSortCol = -1;
\r
352 m_currSortDesc = false;
\r
353 SetSortArrow(&m_ListRefLeafs,-1,false);
\r
355 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
\r
361 FillListCtrlForShadowTree(pTree,L"",true);
\r
364 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
\r
366 if(pTree->IsLeaf())
\r
368 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
\r
370 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
\r
371 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
\r
372 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
\r
373 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
\r
374 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
\r
379 CString csThisName;
\r
381 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
\r
382 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
\r
384 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
\r
389 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
\r
391 ASSERT(!leafs.empty());
\r
396 UINT mbIcon=MB_ICONQUESTION;
\r
397 csMessage = L"Are you sure you want to delete ";
\r
399 bool bIsRemoteBranch = false;
\r
400 bool bIsBranch = false;
\r
401 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
\r
402 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
\r
406 if(leafs.size() == 1)
\r
408 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
\r
409 csTitle.Format(L"Confirm deletion of %sbranch %s",
\r
410 bIsRemoteBranch? L"remote ": L"",
\r
413 csMessage += "the ";
\r
414 if(bIsRemoteBranch)
\r
415 csMessage += L"<ct=0x0000FF><i>remote</i></ct> ";
\r
416 csMessage += L"branch:\r\n\r\n<b>";
\r
417 csMessage += branchToDelete;
\r
418 csMessage += L"</b>";
\r
420 //Check if branch is fully merged in HEAD
\r
421 CString branchHash = g_Git.GetHash(leafs[0]->GetRefName());
\r
422 CString commonAncestor;
\r
424 cmd.Format(L"git.exe merge-base HEAD %s", leafs[0]->GetRefName());
\r
425 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
\r
427 branchHash=branchHash.Left(40);
\r
428 commonAncestor=commonAncestor.Left(40);
\r
430 if(commonAncestor != branchHash)
\r
432 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
\r
433 mbIcon = MB_ICONWARNING;
\r
435 if(bIsRemoteBranch)
\r
437 csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branch on the remote.</b>";
\r
438 mbIcon = MB_ICONWARNING;
\r
443 csTitle.Format(L"Confirm deletion of %d %sbranches",
\r
445 bIsRemoteBranch? L"remote ": L"");
\r
447 CString csMoreMsgText;
\r
448 csMoreMsgText.Format(L"<b>%d</b> ", leafs.size());
\r
449 csMessage += csMoreMsgText;
\r
450 if(bIsRemoteBranch)
\r
451 csMessage += L"<ct=0x0000FF><i>remote</i></ct> ";
\r
452 csMessage += L"branches";
\r
454 csMessage += L"\r\n\r\n<b>Warning:\r\nIt has not been checked if these branches have been fully merged into HEAD.</b>";
\r
455 mbIcon = MB_ICONWARNING;
\r
457 if(bIsRemoteBranch)
\r
459 csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branches on the remote.</b>";
\r
460 mbIcon = MB_ICONWARNING;
\r
465 else if(leafs[0]->IsFrom(L"refs/tags"))
\r
467 if(leafs.size() == 1)
\r
469 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
\r
470 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
\r
471 csMessage += "the tag:\r\n\r\n<b>";
\r
472 csMessage += tagToDelete;
\r
473 csMessage += "</b>";
\r
477 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
\r
478 csTitle.Format(L"Confirm deletion of %d tags", leafs.size());
\r
479 CString csMoreMsgText;
\r
480 csMoreMsgText.Format(L"<b>%d</b> ", leafs.size());
\r
481 csMessage += csMoreMsgText;
\r
482 csMessage += L"tags";
\r
486 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
\r
490 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
\r
492 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
\r
493 if(!DoDeleteRef((*i)->GetRefName(), bForce))
\r
498 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
\r
500 bool bIsRemoteBranch = false;
\r
501 bool bIsBranch = false;
\r
502 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
\r
503 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
\r
507 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
\r
509 if(bIsRemoteBranch)
\r
511 int slash = branchToDelete.Find(L'/');
\r
514 CString remoteName = branchToDelete.Left(slash);
\r
515 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
\r
516 cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);
\r
519 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
\r
520 CString resultDummy;
\r
521 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
\r
524 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
\r
525 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
\r
529 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
\r
531 CString tagToDelete = completeRefName.Mid(10);
\r
533 cmd.Format(L"git.exe tag -d %s",tagToDelete);
\r
534 CString resultDummy;
\r
535 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
\r
538 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
\r
539 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
\r
546 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
\r
548 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
\r
551 return pLeaf->GetRefName();
\r
555 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
\r
557 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
\r
558 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
\r
561 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
\r
563 CPoint clientPoint=point;
\r
564 m_RefTreeCtrl.ScreenToClient(&clientPoint);
\r
566 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
\r
567 if(hTreeItem!=NULL)
\r
568 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
\r
570 ShowContextMenu(point,hTreeItem,VectorPShadowTree());
\r
574 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
\r
576 std::vector<CShadowTree*> selectedLeafs;
\r
577 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
\r
578 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
\r
581 selectedLeafs.push_back(
\r
582 (CShadowTree*)m_ListRefLeafs.GetItemData(
\r
583 m_ListRefLeafs.GetNextSelectedItem(pos)));
\r
586 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
\r
589 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
\r
591 CIconMenu popupMenu;
\r
592 popupMenu.CreatePopupMenu();
\r
594 bool bAddSeparator = false;
\r
595 CString remoteName;
\r
597 if(selectedLeafs.size()==1)
\r
599 bAddSeparator = true;
\r
601 bool bShowReflogOption = false;
\r
602 bool bShowFetchOption = false;
\r
603 bool bShowSwitchOption = false;
\r
605 CString fetchFromCmd;
\r
607 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
\r
609 bShowReflogOption = true;
\r
610 bShowSwitchOption = true;
\r
612 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
\r
614 bShowReflogOption = true;
\r
615 bShowFetchOption = true;
\r
617 int dummy = 0;//Needed for tokenize
\r
618 remoteName = selectedLeafs[0]->GetRefName();
\r
619 remoteName = remoteName.Mid(13);
\r
620 remoteName = remoteName.Tokenize(L"/", dummy);
\r
621 fetchFromCmd.Format(L"Fetch from %s", remoteName);
\r
623 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
\r
627 popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);
\r
628 if(bShowReflogOption) popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);
\r
629 if(bShowFetchOption) popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
\r
630 if(bShowSwitchOption) popupMenu.AppendMenuIcon(eCmd_Switch, L"Switch to this Ref", IDI_SWITCH);
\r
633 else if(selectedLeafs.size() == 2)
\r
635 bAddSeparator = true;
\r
637 popupMenu.AppendMenuIcon(eCmd_Diff, L"Compare These Refs", IDI_DIFF);
\r
640 if(!selectedLeafs.empty())
\r
642 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
\r
644 CString menuItemName;
\r
645 if(selectedLeafs.size() == 1)
\r
646 menuItemName = L"Delete Remote Branch";
\r
648 menuItemName.Format(L"Delete %d Remote Branches", selectedLeafs.size());
\r
650 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
\r
653 if(AreAllFrom(selectedLeafs, L"refs/heads/"))
\r
655 CString menuItemName;
\r
656 if(selectedLeafs.size() == 1)
\r
657 menuItemName = L"Delete Branch";
\r
659 menuItemName.Format(L"Delete %d Branches", selectedLeafs.size());
\r
661 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
\r
664 if(AreAllFrom(selectedLeafs, L"refs/tags/"))
\r
666 CString menuItemName;
\r
667 if(selectedLeafs.size() == 1)
\r
668 menuItemName = L"Delete Tag";
\r
670 menuItemName.Format(L"Delete %d Tags", selectedLeafs.size());
\r
672 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
\r
676 if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);
\r
680 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
\r
681 if(pTree->IsFrom(L"refs/remotes"))
\r
683 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
\r
684 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);
\r
685 if(selectedLeafs.empty())
\r
687 int dummy = 0;//Needed for tokenize
\r
688 remoteName = pTree->GetRefName();
\r
689 remoteName = remoteName.Mid(13);
\r
690 remoteName = remoteName.Tokenize(L"/", dummy);
\r
691 if(!remoteName.IsEmpty())
\r
693 CString fetchFromCmd;
\r
694 fetchFromCmd.Format(L"Fetch from %s", remoteName);
\r
695 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
\r
699 else if(pTree->IsFrom(L"refs/heads"))
\r
700 popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);
\r
701 else if(pTree->IsFrom(L"refs/tags"))
\r
702 popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);
\r
706 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
\r
712 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
\r
716 case eCmd_DeleteBranch:
\r
717 case eCmd_DeleteRemoteBranch:
\r
719 if(ConfirmDeleteRef(selectedLeafs))
\r
720 DoDeleteRefs(selectedLeafs, true);
\r
724 case eCmd_DeleteTag:
\r
726 if(ConfirmDeleteRef(selectedLeafs))
\r
727 DoDeleteRefs(selectedLeafs, true);
\r
731 case eCmd_ShowReflog:
\r
733 CRefLogDlg refLogDlg(this);
\r
734 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
\r
735 refLogDlg.DoModal();
\r
741 cmd.Format(_T("git.exe fetch %s"), remoteName);
\r
742 CProgressDlg progress;
\r
743 progress.m_GitCmd=cmd;
\r
744 progress.DoModal();
\r
750 CAppUtils::Switch(NULL, selectedLeafs[0]->GetRefName());
\r
753 case eCmd_AddRemote:
\r
755 CAddRemoteDlg(this).DoModal();
\r
759 case eCmd_ManageRemotes:
\r
761 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();
\r
762 // CSettingGitRemote W_Remotes(m_cmdPath);
\r
763 // W_Remotes.DoModal();
\r
767 case eCmd_CreateBranch:
\r
769 CAppUtils::CreateBranchTag(false);
\r
773 case eCmd_CreateTag:
\r
775 CAppUtils::CreateBranchTag(true);
\r
784 selectedLeafs[0]->m_csRefHash,
\r
785 selectedLeafs[1]->m_csRefHash);
\r
792 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
\r
794 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
\r
795 if(!(*i)->IsFrom(from))
\r
800 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
\r
802 if (pMsg->message == WM_KEYDOWN)
\r
804 switch (pMsg->wParam)
\r
808 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
\r
810 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
\r
812 PostMessage(WM_COMMAND, IDOK);
\r
827 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
\r
830 class CRefLeafListCompareFunc
\r
833 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
\r
835 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
837 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
\r
840 int Compare(LPARAM lParam1, LPARAM lParam2)
\r
843 (CShadowTree*)m_pList->GetItemData(lParam1),
\r
844 (CShadowTree*)m_pList->GetItemData(lParam2));
\r
847 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
\r
849 int result=CompareNoDesc(pLeft,pRight);
\r
855 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
\r
859 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
\r
860 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
\r
861 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
\r
862 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
\r
869 CListCtrl* m_pList;
\r
875 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
\r
877 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
\r
880 if(m_currSortCol == pNMLV->iSubItem)
\r
881 m_currSortDesc = !m_currSortDesc;
\r
884 m_currSortCol = pNMLV->iSubItem;
\r
885 m_currSortDesc = false;
\r
888 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
\r
889 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
\r
891 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
\r
894 void CBrowseRefsDlg::OnDestroy()
\r
896 m_pickedRef = GetSelectedRef(true, false);
\r
898 CResizableStandAloneDialog::OnDestroy();
\r
901 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
\r
903 LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
\r
909 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)
\r
911 CBrowseRefsDlg dlg(CString(),NULL);
\r
913 if(initialRef.IsEmpty())
\r
914 initialRef = L"HEAD";
\r
915 dlg.m_initialRef = initialRef;
\r
916 dlg.m_pickRef_Kind = pickRef_Kind;
\r
918 if(dlg.DoModal() != IDOK)
\r
921 return dlg.m_pickedRef;
\r
924 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
\r
927 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
\r
928 CString resultRef = PickRef(false,origRef,pickRef_Kind);
\r
929 if(resultRef.IsEmpty())
\r
931 if(wcsncmp(resultRef,L"refs/",5)==0)
\r
932 resultRef = resultRef.Mid(5);
\r
933 // if(wcsncmp(resultRef,L"heads/",6)==0)
\r
934 // resultRef = resultRef.Mid(6);
\r
936 //Find closest match of choice in combobox
\r
938 int matchLength = 0;
\r
939 CString comboRefName;
\r
940 for(int i = 0; i < pComboBox->GetCount(); ++i)
\r
942 pComboBox->GetLBText(i, comboRefName);
\r
943 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
\r
944 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
\r
945 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
\r
947 matchLength = comboRefName.GetLength();
\r
952 pComboBox->SetCurSel(ixFound);
\r
954 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)
\r