1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - TortoiseGit
4 // Copyright (C) 2005-2007 Marco Costalba
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // GitLogList.cpp : implementation file
23 #include "TortoiseProc.h"
24 #include "GitLogList.h"
26 //#include "VssStyle.h"
31 #include "GITProgressDlg.h"
32 #include "ProgressDlg.h"
33 #include "SysProgressDlg.h"
34 //#include "RepositoryBrowser.h"
35 //#include "CopyDlg.h"
36 //#include "StatGraphDlg.h"
38 #include "MessageBox.h"
41 #include "PathUtils.h"
42 #include "StringUtils.h"
43 #include "UnicodeUtils.h"
45 //#include "GitInfo.h"
46 //#include "GitDiff.h"
47 //#include "RevisionRangeDlg.h"
48 //#include "BrowseFolder.h"
49 //#include "BlameDlg.h"
51 //#include "GitHelpers.h"
52 #include "GitStatus.h"
53 //#include "LogDlgHelper.h"
54 //#include "CachedLogInfo.h"
55 //#include "RepositoryInfo.h"
56 //#include "EditPropertiesDlg.h"
57 #include "FileDiffDlg.h"
58 #include "CommitDlg.h"
59 #include "RebaseDlg.h"
61 #include "../TGitCache/CacheInterface.h"
63 IMPLEMENT_DYNAMIC(CGitLogList
, CHintListCtrl
)
65 int CGitLogList::RevertSelectedCommits()
67 CSysProgressDlg progress
;
71 if(!g_Git
.CheckCleanWorkTree())
73 CMessageBox::Show(NULL
, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
78 if (progress
.IsValid() && (this->GetSelectedCount() > 1) )
80 progress
.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_REVERTCOMMIT
)));
81 progress
.SetAnimation(IDR_MOVEANI
);
82 progress
.SetTime(true);
83 progress
.ShowModeless(this);
86 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
88 POSITION pos
= GetFirstSelectedItemPosition();
92 int index
= GetNextSelectedItem(pos
);
93 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(index
));
95 if (progress
.IsValid() && (this->GetSelectedCount() > 1) )
98 temp
.Format(IDS_PROC_REVERTCOMMIT
, r1
->m_CommitHash
.ToString());
99 progress
.FormatPathLine(1, temp
);
100 progress
.FormatPathLine(2, _T("%s"), r1
->GetSubject());
101 progress
.SetProgress(i
, this->GetSelectedCount());
105 if(r1
->m_CommitHash
.IsEmpty())
109 cmd
.Format(_T("git.exe revert --no-edit --no-commit %s"), r1
->m_CommitHash
.ToString());
110 if (g_Git
.Run(cmd
, &output
, CP_UTF8
))
113 str
.LoadString(IDS_SVNACTION_FAILEDREVERT
);
116 str
+= _T("\n")+output
;
117 if( GetSelectedCount() == 1)
118 CMessageBox::Show(NULL
, str
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
121 if(CMessageBox::Show(NULL
, str
, _T("TortoiseGit"),2 , IDI_ERROR
, CString(MAKEINTRESOURCE(IDS_SKIPBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 2)
132 if ((progress
.IsValid())&&(progress
.HasUserCancelled()))
137 int CGitLogList::CherryPickFrom(CString from
, CString to
)
139 CLogDataVector
logs(&m_LogCache
);
140 if(logs
.ParserFromLog(NULL
,-1,0,&from
,&to
))
146 CSysProgressDlg progress
;
147 if (progress
.IsValid())
149 progress
.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_CHERRYPICK
)));
150 progress
.SetAnimation(IDR_MOVEANI
);
151 progress
.SetTime(true);
152 progress
.ShowModeless(this);
155 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
157 for(int i
=logs
.size()-1;i
>=0;i
--)
159 if (progress
.IsValid())
162 temp
.Format(IDS_PROC_PICK
, logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
163 progress
.FormatPathLine(1, temp
);
164 progress
.FormatPathLine(2, _T("%s"), logs
.GetGitRevAt(i
).GetSubject());
165 progress
.SetProgress(logs
.size()-i
, logs
.size());
167 if ((progress
.IsValid())&&(progress
.HasUserCancelled()))
169 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_SVN_USERCANCELLED
))));
173 cmd
.Format(_T("git.exe cherry-pick %s"),logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
175 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
177 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_CHERRYPICKFAILED
)) + _T(":\r\n\r\n") + out
));
185 void CGitLogList::ContextMenuAction(int cmd
,int FirstSelect
, int LastSelect
, CMenu
*popmenu
)
187 POSITION pos
= GetFirstSelectedItemPosition();
188 int indexNext
= GetNextSelectedItem(pos
);
192 GitRev
* pSelLogEntry
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(indexNext
));
194 theApp
.DoWaitCursor(1);
199 CTGitPathList pathlist
;
200 CTGitPathList selectedlist
;
201 pathlist
.AddPath(this->m_Path
);
202 bool bSelectFilesForCommit
= !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE
));
204 CAppUtils::Commit(CString(),false,str
,
205 pathlist
,selectedlist
,bSelectFilesForCommit
);
207 this->GetParent()->PostMessage(WM_COMMAND
,ID_LOGDLG_REFRESH
,0);
210 case ID_GNUDIFF1
: // compare with WC, unified
212 CString tempfile
=GetTempFile();
214 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
215 if(!r1
->m_CommitHash
.IsEmpty())
220 if( (cmd
&0xFFFF) == 0xFFFF)
224 else if((cmd
&0xFFFF) == 0xFFFE)
230 if(cmd
> r1
->m_ParentHash
.size())
233 str
.Format(IDS_PROC_NOPARENT
, cmd
);
234 MessageBox(str
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
240 hash2
= r1
->m_ParentHash
[cmd
-1].ToString();
243 command
.Format(_T("git.exe diff-tree %s -r -p --stat %s %s"), merge
, hash2
, r1
->m_CommitHash
.ToString());
246 command
.Format(_T("git.exe diff -r -p --stat"));
248 g_Git
.RunLogFile(command
,tempfile
);
249 CAppUtils::StartUnifiedDiffViewer(tempfile
,r1
->m_CommitHash
.ToString().Left(6)+_T(":")+r1
->GetSubject());
253 case ID_GNUDIFF2
: // compare two revisions, unified
255 CString tempfile
=GetTempFile();
257 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
258 GitRev
* r2
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
260 if( r1
->m_CommitHash
.IsEmpty()) {
261 cmd
.Format(_T("git.exe diff -r -p --stat %s"),r2
->m_CommitHash
.ToString());
263 else if( r2
->m_CommitHash
.IsEmpty()) {
264 cmd
.Format(_T("git.exe diff -r -p --stat %s"),r1
->m_CommitHash
.ToString());
268 cmd
.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r2
->m_CommitHash
.ToString(),r1
->m_CommitHash
.ToString());
271 g_Git
.RunLogFile(cmd
,tempfile
);
272 CAppUtils::StartUnifiedDiffViewer(tempfile
,r2
->m_CommitHash
.ToString().Left(6)+_T(":")+r1
->m_CommitHash
.ToString().Left(6));
277 case ID_COMPARETWO
: // compare two revisions
279 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
280 GitRev
* r2
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
281 CGitDiff::DiffCommit(this->m_Path
, r1
,r2
);
286 case ID_COMPARE
: // compare revision with WC
288 GitRev
* r1
= &m_wcRev
;
289 GitRev
* r2
= pSelLogEntry
;
291 CGitDiff::DiffCommit(this->m_Path
, r1
,r2
);
293 //user clicked on the menu item "compare with working copy"
296 // GitDiff diff(this, m_hWnd, true);
297 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
298 // diff.SetHEADPeg(m_LogRevision);
299 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
302 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
306 case ID_COMPAREWITHPREVIOUS
:
311 if(pSelLogEntry
->m_ParentHash
.size()>0)
312 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
320 CGitDiff::DiffCommit(this->m_Path
, pSelLogEntry
->m_CommitHash
.ToString(),pSelLogEntry
->m_ParentHash
[cmd
-1].ToString());
325 CMessageBox::Show(NULL
, IDS_PROC_NOPREVIOUSVERSION
, IDS_APPNAME
, MB_OK
);
329 // GitDiff diff(this, m_hWnd, true);
330 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
331 // diff.SetHEADPeg(m_LogRevision);
332 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
335 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
338 case ID_COPYCLIPBOARD
:
340 CopySelectionToClipBoard();
345 CopySelectionToClipBoard(TRUE
);
350 CString str
=pSelLogEntry
->m_CommitHash
.ToString();
351 CAppUtils::Export(&str
);
354 case ID_CREATE_BRANCH
:
356 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
357 CAppUtils::CreateBranchTag(FALSE
,&str
);
364 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
365 CAppUtils::CreateBranchTag(TRUE
,&str
);
368 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
373 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
374 CAppUtils::Switch(&str
);
378 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
380 case ID_SWITCHBRANCH
:
383 CString
*branch
= (CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
);
387 if(branch
->Find(_T("refs/heads/")) ==0 )
388 name
= branch
->Mid(11);
392 CAppUtils::PerformSwitch(name
);
396 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
401 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
402 CAppUtils::GitReset(&str
);
408 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK
);
411 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT
);
413 case ID_REBASE_SQUASH
:
414 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH
);
417 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP
);
419 case ID_COMBINE_COMMIT
:
423 CGitHash hashFirst
,hashLast
;
425 int headindex
=GetHeadIndex();
426 if(headindex
>=0) //incase show all branch, head is not the first commits.
428 head
.Format(_T("HEAD~%d"),FirstSelect
-headindex
);
429 hashFirst
=g_Git
.GetHash(head
);
431 head
.Format(_T("HEAD~%d"),LastSelect
-headindex
);
432 hashLast
=g_Git
.GetHash(head
);
435 GitRev
* pFirstEntry
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
436 GitRev
* pLastEntry
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
437 if(pFirstEntry
->m_CommitHash
!= hashFirst
|| pLastEntry
->m_CommitHash
!= hashLast
)
439 CMessageBox::Show(NULL
, IDS_PROC_CANNOTCOMBINE
, IDS_APPNAME
, MB_OK
);
443 headhash
=g_Git
.GetHash(_T("HEAD"));
445 if(!g_Git
.CheckCleanWorkTree())
447 CMessageBox::Show(NULL
, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
452 //Use throw to abort this process (reset back to original HEAD)
455 cmd
.Format(_T("git.exe reset --hard %s"),pFirstEntry
->m_CommitHash
.ToString());
456 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
458 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
459 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP1
)) + _T("\r\n\r\n") + out
));
461 cmd
.Format(_T("git.exe reset --mixed %s"),hashLast
.ToString());
462 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
464 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
465 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP2
)) + _T("\r\n\r\n")+out
));
468 CTGitPathList PathList
;
469 /* don't why must add --stat to get action status*/
470 /* first -z will be omitted by gitdll*/
471 if(g_Git
.GetDiffPath(&PathList
,&pFirstEntry
->m_CommitHash
,&hashLast
,"-z --stat -r"))
473 CMessageBox::Show(NULL
,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK
);
474 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out
));
477 for(int i
=0;i
<PathList
.GetCount();i
++)
479 if(PathList
[i
].m_Action
& CTGitPath::LOGACTIONS_ADDED
)
481 cmd
.Format(_T("git.exe add -- \"%s\""), PathList
[i
].GetGitPathString());
482 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
484 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
485 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out
));
489 if(PathList
[i
].m_Action
& CTGitPath::LOGACTIONS_DELETED
)
491 cmd
.Format(_T("git.exe rm -- \"%s\""), PathList
[i
].GetGitPathString());
492 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
494 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
495 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out
));
501 for(int i
=FirstSelect
;i
<=LastSelect
;i
++)
503 GitRev
* pRev
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(i
));
504 dlg
.m_sLogMessage
+=pRev
->GetSubject()+_T("\n")+pRev
->GetBody();
505 dlg
.m_sLogMessage
+=_T("\n");
507 dlg
.m_bWholeProject
=true;
508 dlg
.m_bSelectFilesForCommit
= true;
509 dlg
.m_bCommitAmend
=true;
510 dlg
.m_bNoPostActions
=true;
511 dlg
.m_AmendStr
=dlg
.m_sLogMessage
;
513 if (dlg
.DoModal() == IDOK
)
515 if(pFirstEntry
->m_CommitHash
!=headhash
)
517 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
518 //on top of new commit.
519 //Use the rebase --onto command for it.
521 //All this can be done in one step using the following command:
522 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
523 // pFirstEntry->m_CommitHash,
525 //But I am not sure if a '|' is going to work in a CreateProcess() call.
527 //Later the progress dialog could be used to execute these steps.
529 if(CherryPickFrom(pFirstEntry
->m_CommitHash
.ToString(),headhash
))
532 msg
.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
533 throw std::exception(CUnicodeUtils::GetUTF8(msg
));
536 CString currentBranch
=g_Git
.GetCurrentBranch();
537 cmd
.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
539 pFirstEntry
->m_CommitHash
,
541 if(g_Git
.Run(cmd
,&out
,CP_UTF8
)!=0)
544 msg
.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out
);
545 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
546 g_Git
.Run(_T("git.exe rebase --abort"),&out
,CP_UTF8
);
547 throw std::exception(CUnicodeUtils::GetUTF8(msg
));
550 //HEAD is now on <no branch>.
551 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
552 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
553 //And then git checkout <original branch>
554 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
557 CString newHead
=g_Git
.GetHash(CString(_T("HEAD")));
559 //Checkout working branch
560 cmd
.Format(_T("git.exe checkout -f \"%s\""),currentBranch
);
561 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
562 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out
));
565 cmd
.Format(_T("git.exe reset --hard %s"),newHead
);
566 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
567 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out
));
572 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_SVN_USERCANCELLED
))));
574 catch(std::exception
& e
)
576 CMessageBox::Show(NULL
, CUnicodeUtils::GetUnicode(CStringA(e
.what())), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
577 cmd
.Format(_T("git.exe reset --hard %s"),headhash
.ToString());
579 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
581 CMessageBox::Show(NULL
, CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORRESETHEAD
)) + _T("\r\n\r\n") + out
, _T("TortoiseGit"), MB_OK
);
589 if(!g_Git
.CheckCleanWorkTree())
591 CMessageBox::Show(NULL
, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
597 dlg
.m_IsCherryPick
= TRUE
;
598 dlg
.m_Upstream
= this->m_CurrentBranch
;
599 POSITION pos
= GetFirstSelectedItemPosition();
602 int indexNext
= GetNextSelectedItem(pos
);
603 dlg
.m_CommitList
.m_logEntries
.push_back( ((GitRev
*)m_arShownList
[indexNext
])->m_CommitHash
);
604 dlg
.m_CommitList
.m_LogCache
.m_HashMap
[((GitRev
*)m_arShownList
[indexNext
])->m_CommitHash
]=*(GitRev
*)m_arShownList
[indexNext
];
605 dlg
.m_CommitList
.m_logEntries
.GetGitRevAt(dlg
.m_CommitList
.m_logEntries
.size()-1).GetAction(this) |= CTGitPath::LOGACTIONS_REBASE_PICK
;
608 if(dlg
.DoModal() == IDOK
)
614 case ID_REBASE_TO_VERSION
:
615 if(!g_Git
.CheckCleanWorkTree())
617 CMessageBox::Show(NULL
, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
623 dlg
.m_Upstream
= pSelLogEntry
->m_CommitHash
;
625 if(dlg
.DoModal() == IDOK
)
634 if (CAppUtils::StashSave())
639 if (CAppUtils::StashPop())
644 CAppUtils::RunTortoiseProc(_T("/command:reflog /ref:refs/stash"));
647 case ID_REFLOG_STASH_APPLY
:
648 CAppUtils::StashApply(pSelLogEntry
->m_Ref
);
654 if (GetSelectedCount() > 1)
655 str
.Format(IDS_PROC_DELETENREFS
, GetSelectedCount());
657 str
.Format(IDS_PROC_DELETEREF
, pSelLogEntry
->m_Ref
);
659 if (CMessageBox::Show(NULL
, str
, _T("TortoiseGit"), 1, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 2)
662 POSITION pos
= GetFirstSelectedItemPosition();
665 CString ref
= ((GitRev
*)m_arShownList
[GetNextSelectedItem(pos
)])->m_Ref
;
666 if (ref
.Find(_T("refs/")) == 0)
668 int refpos
= ref
.ReverseFind('{');
669 if (refpos
> 0 && ref
.Mid(refpos
, 2) != _T("@{"))
670 ref
= ref
.Left(refpos
) + _T("@")+ ref
.Mid(refpos
);
673 if (ref
.Find(_T("stash")) == 0)
674 cmd
.Format(_T("git.exe stash drop %s"), ref
);
676 cmd
.Format(_T("git.exe reflog delete %s"), ref
);
678 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
679 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
681 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
687 CString cmd
= _T("/command:log");
688 cmd
+= _T(" /path:\"")+g_Git
.m_CurrentDir
+_T("\" ");
689 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
690 cmd
+= _T(" /endrev:")+r1
->m_CommitHash
.ToString();
691 CAppUtils::RunTortoiseProc(cmd
);
694 case ID_CREATE_PATCH
:
696 int select
=this->GetSelectedCount();
697 CString cmd
= _T("/command:formatpatch");
698 cmd
+= _T(" /path:\"")+g_Git
.m_CurrentDir
+_T("\" ");
700 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
704 cmd
+= _T(" /startrev:")+r1
->m_CommitHash
.ToString();
708 r2
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
709 if( this->m_IsOldFirst
)
711 cmd
+= _T(" /startrev:")+r1
->m_CommitHash
.ToString()+_T("~1");
712 cmd
+= _T(" /endrev:")+r2
->m_CommitHash
.ToString();
717 cmd
+= _T(" /startrev:")+r2
->m_CommitHash
.ToString()+_T("~1");
718 cmd
+= _T(" /endrev:")+r1
->m_CommitHash
.ToString();
723 CAppUtils::RunTortoiseProc(cmd
);
728 CString guessAssociatedBranch
;
729 if (m_HashMap
[pSelLogEntry
->m_CommitHash
].size() > 0)
730 guessAssociatedBranch
= m_HashMap
[pSelLogEntry
->m_CommitHash
].at(0);
731 if (CAppUtils::Push(guessAssociatedBranch
))
737 if (CAppUtils::Fetch(_T(""), true))
743 CString
*branch
= (CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
);
746 CMessageBox::Show(NULL
,IDS_ERROR_NOREF
,IDS_APPNAME
,MB_OK
|MB_ICONERROR
);
751 if (this->GetShortName(*branch
, shortname
, _T("refs/remotes/")))
754 msg
.Format(IDS_PROC_DELETEREMOTEBRANCH
, *branch
);
755 int result
= CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), 3, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCALREMOTE
)), CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCAL
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
758 CString remoteName
= shortname
.Left(shortname
.Find('/'));
759 shortname
= shortname
.Mid(shortname
.Find('/') + 1);
760 if(CAppUtils::IsSSHPutty())
761 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
763 cmd
.Format(L
"git.exe push \"%s\" :%s", remoteName
, shortname
);
765 else if (result
== 2)
766 cmd
.Format(_T("git.exe branch -r -D -- %s"), shortname
);
770 else if (this->GetShortName(*branch
, shortname
, _T("refs/stash")))
772 if (CMessageBox::Show(NULL
, IDS_PROC_DELETEALLSTASH
, IDS_APPNAME
, 2, IDI_QUESTION
, IDS_DELETEBUTTON
, IDS_ABORTBUTTON
) == 1)
773 cmd
.Format(_T("git.exe stash clear"));
780 msg
.Format(IDS_PROC_DELETEBRANCHTAG
, *branch
);
781 if (CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), 2, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 1)
783 if(this->GetShortName(*branch
,shortname
,_T("refs/heads/")))
785 cmd
.Format(_T("git.exe branch -D -- %s"),shortname
);
788 if(this->GetShortName(*branch
,shortname
,_T("refs/tags/")))
790 cmd
.Format(_T("git.exe tag -d -- %s"),shortname
);
797 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
799 CMessageBox::Show(NULL
,out
,_T("TortoiseGit"),MB_OK
);
801 this->ReloadHashMap();
803 this->GetItemRect(FirstSelect
,&rect
,LVIR_BOUNDS
);
804 this->InvalidateRect(rect
);
811 m_nSearchIndex
= GetSelectionMark();
812 if (m_nSearchIndex
< 0)
820 m_pFindDialog
= new CFindDlg();
821 m_pFindDialog
->Create(this);
827 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
828 if (m_HashMap
[pSelLogEntry
->m_CommitHash
].size() > 0)
829 str
= m_HashMap
[pSelLogEntry
->m_CommitHash
].at(0);
830 // we need an URL to complete this command, so error out if we can't get an URL
831 if(CAppUtils::Merge(&str
))
839 if(!this->RevertSelectedCommits())
845 CAppUtils::EditNote(pSelLogEntry
);
846 this->SetItemState(FirstSelect
, 0, LVIS_SELECTED
);
847 this->SetItemState(FirstSelect
, LVIS_SELECTED
, LVIS_SELECTED
);
851 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
855 case ID_BLAMECOMPARE
:
857 //user clicked on the menu item "compare with working copy"
858 //now first get the revision which is selected
861 GitDiff
diff(this, this->m_hWnd
, true);
862 diff
.SetHEADPeg(m_LogRevision
);
863 diff
.ShowCompare(m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), false, true);
866 CAppUtils::StartShowCompare(m_hWnd
, m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), m_LogRevision
, false, false, true);
871 //user clicked on the menu item "compare and blame revisions"
874 GitDiff
diff(this, this->m_hWnd
, true);
875 diff
.SetHEADPeg(m_LogRevision
);
876 diff
.ShowCompare(CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
879 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
882 case ID_BLAMEWITHPREVIOUS
:
884 //user clicked on the menu item "Compare and Blame with previous revision"
887 GitDiff
diff(this, this->m_hWnd
, true);
888 diff
.SetHEADPeg(m_LogRevision
);
889 diff
.ShowCompare(CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
892 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
900 CProgressDlg progDlg
;
901 progDlg
.SetTitle(IDS_APPNAME
);
902 progDlg
.SetAnimation(IDR_DOWNLOAD
);
904 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, m_path
.GetWinPath(), (LPCTSTR
)revSelected
.ToString());
905 progDlg
.SetLine(1, sInfoLine
, true);
906 SetAndClearProgressInfo(&progDlg
);
907 progDlg
.ShowModeless(m_hWnd
);
908 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path
, revSelected
);
909 bool bSuccess
= true;
910 if (!Cat(m_path
, GitRev(GitRev::REV_HEAD
), revSelected
, tempfile
))
913 // try again, but with the selected revision as the peg revision
914 if (!Cat(m_path
, revSelected
, revSelected
, tempfile
))
917 SetAndClearProgressInfo((HWND
)NULL
);
918 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
927 SetAndClearProgressInfo((HWND
)NULL
);
928 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
931 ret
= (int)ShellExecute(this->m_hWnd
, NULL
, tempfile
.GetWinPath(), NULL
, NULL
, SW_SHOWNORMAL
);
932 if ((ret
<= HINSTANCE_ERROR
)||bOpenWith
)
934 CString cmd
= _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
935 cmd
+= tempfile
.GetWinPathString() + _T(" ");
936 CAppUtils::LaunchApplication(cmd
, NULL
, false);
944 dlg
.EndRev
= revSelected
;
945 if (dlg
.DoModal() == IDOK
)
950 tempfile
= blame
.BlameToTempFile(m_path
, dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, _T(""), dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
951 if (!tempfile
.IsEmpty())
955 //open the default text editor for the result file
956 CAppUtils::StartTextViewer(tempfile
);
960 CString sParams
= _T("/path:\"") + m_path
.GetGitPathString() + _T("\" ");
961 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(m_path
.GetFileOrDirectoryName()),sParams
))
969 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
977 CString url
= _T("tgit:")+pathURL
;
978 sCmd
.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
979 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
980 (LPCTSTR
)m_path
.GetWinPath(), (LONG
)revSelected
);
981 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
987 EditLogMessage(selIndex
);
992 EditAuthor(selEntries
);
997 CEditPropertiesDlg dlg
;
998 dlg
.SetProjectProperties(&m_ProjectProperties
);
999 CTGitPathList escapedlist
;
1000 dlg
.SetPathList(CTGitPathList(CTGitPath(pathURL
)));
1001 dlg
.SetRevision(revSelected
);
1010 sCmd
.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
1011 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
1012 (LPCTSTR
)pathURL
, (LONG
)revSelected
);
1013 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
1018 CString url
= m_ProjectProperties
.sWebViewerRev
;
1019 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1020 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
1022 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
1025 case ID_VIEWPATHREV
:
1027 CString relurl
= pathURL
;
1028 CString sRoot
= GetRepositoryRoot(CTGitPath(relurl
));
1029 relurl
= relurl
.Mid(sRoot
.GetLength());
1030 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
1031 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1032 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
1033 url
.Replace(_T("%PATH%"), relurl
);
1035 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
1042 theApp
.DoWaitCursor(-1);
1045 void CGitLogList::SetSelectedAction(int action
)
1047 POSITION pos
= GetFirstSelectedItemPosition();
1051 index
= GetNextSelectedItem(pos
);
1052 ((GitRev
*)m_arShownList
[index
])->GetAction(this) = action
;
1054 this->GetItemRect(index
,&rect
,LVIR_BOUNDS
);
1055 this->InvalidateRect(rect
);
1059 void CGitLogList::ShiftSelectedAction()
1061 POSITION pos
= GetFirstSelectedItemPosition();
1065 index
= GetNextSelectedItem(pos
);
1066 int action
= ((GitRev
*)m_arShownList
[index
])->GetAction(this);
1069 case CTGitPath::LOGACTIONS_REBASE_PICK
:
1070 action
= CTGitPath::LOGACTIONS_REBASE_SKIP
;
1072 case CTGitPath::LOGACTIONS_REBASE_SKIP
:
1073 action
= CTGitPath::LOGACTIONS_REBASE_EDIT
;
1075 case CTGitPath::LOGACTIONS_REBASE_EDIT
:
1076 action
= CTGitPath::LOGACTIONS_REBASE_SQUASH
;
1078 case CTGitPath::LOGACTIONS_REBASE_SQUASH
:
1079 action
= CTGitPath::LOGACTIONS_REBASE_PICK
;
1082 ((GitRev
*)m_arShownList
[index
])->GetAction(this) = action
;
1084 this->GetItemRect(index
, &rect
, LVIR_BOUNDS
);
1085 this->InvalidateRect(rect
);