Do not use GitAdminDir objects
[TortoiseGit.git] / src / TortoiseProc / RefLogDlg.cpp
blobd044152af1152c2e251abf48456e2be90f055a62
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2015 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // RefLogDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "resource.h"
24 #include "RefLogDlg.h"
25 #include "Git.h"
26 #include "AppUtils.h"
27 #include "MessageBox.h"
28 #include "UnicodeUtils.h"
30 // CRefLogDlg dialog
32 IMPLEMENT_DYNAMIC(CRefLogDlg, CResizableStandAloneDialog)
34 UINT CRefLogDlg::m_FindDialogMessage = ::RegisterWindowMessage(FINDMSGSTRING);
36 CRefLogDlg::CRefLogDlg(CWnd* pParent /*=NULL*/)
37 : CResizableStandAloneDialog(CRefLogDlg::IDD, pParent)
38 , m_pFindDialog(NULL)
39 , m_nSearchLine(0)
44 CRefLogDlg::~CRefLogDlg()
48 void CRefLogDlg::DoDataExchange(CDataExchange* pDX)
50 CDialog::DoDataExchange(pDX);
51 DDX_Control(pDX, IDC_COMBOBOXEX_REF, m_ChooseRef);
52 DDX_Control(pDX, IDC_REFLOG_LIST, m_RefList);
56 BEGIN_MESSAGE_MAP(CRefLogDlg, CResizableStandAloneDialog)
57 ON_BN_CLICKED(IDOK, &CRefLogDlg::OnBnClickedOk)
58 ON_BN_CLICKED(IDC_REFLOG_BUTTONCLEARSTASH, &CRefLogDlg::OnBnClickedClearStash)
59 ON_CBN_SELCHANGE(IDC_COMBOBOXEX_REF, &CRefLogDlg::OnCbnSelchangeRef)
60 ON_MESSAGE(MSG_REFLOG_CHANGED,OnRefLogChanged)
61 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
62 END_MESSAGE_MAP()
64 LRESULT CRefLogDlg::OnRefLogChanged(WPARAM /*wParam*/, LPARAM /*lParam*/)
66 m_RefList.m_RevCache.clear();
67 OnCbnSelchangeRef();
68 return 0;
71 BOOL CRefLogDlg::OnInitDialog()
73 CResizableStandAloneDialog::OnInitDialog();
74 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
76 AddAnchor(IDOK,BOTTOM_RIGHT);
77 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
78 AddAnchor(IDC_REFLOG_BUTTONCLEARSTASH, BOTTOM_LEFT);
79 AddAnchor(IDC_REFLOG_LIST,TOP_LEFT,BOTTOM_RIGHT);
80 AddAnchor(IDHELP, BOTTOM_RIGHT);
81 AddAnchor(IDC_COMBOBOXEX_REF, TOP_LEFT, TOP_RIGHT);
83 AddOthersToAnchor();
84 this->EnableSaveRestore(_T("RefLogDlg"));
86 CString sWindowTitle;
87 GetWindowText(sWindowTitle);
88 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
90 m_ChooseRef.SetMaxHistoryItems(0x7FFFFFFF);
92 m_RefList.m_hasWC = !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
94 this->m_RefList.InsertRefLogColumn();
96 Refresh();
98 return TRUE;
100 // CRefLogDlg message handlers
102 void CRefLogDlg::OnBnClickedOk()
104 if (m_RefList.GetSelectedCount() == 1)
106 // get the selected row
107 POSITION pos = m_RefList.GetFirstSelectedItemPosition();
108 int selIndex = m_RefList.GetNextSelectedItem(pos);
109 if (selIndex < m_RefList.m_arShownList.GetCount())
111 // all ok, pick up the revision
112 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_RefList.m_arShownList.GetAt(selIndex));
113 // extract the hash
114 m_SelectedHash = pLogEntry->m_CommitHash;
118 OnOK();
120 void CRefLogDlg::OnBnClickedClearStash()
122 if (CMessageBox::Show(this->GetSafeHwnd(), IDS_PROC_DELETEALLSTASH, IDS_APPNAME, 2, IDI_QUESTION, IDS_DELETEBUTTON, IDS_ABORTBUTTON) == 1)
124 CString cmdOut;
125 if (g_Git.Run(_T("git.exe stash clear"), &cmdOut, CP_UTF8))
127 MessageBox(cmdOut, _T("TortoiseGit"), MB_ICONERROR);
128 return;
131 m_RefList.m_RevCache.clear();
133 OnCbnSelchangeRef();
137 int AddToRefLoglist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long time, int /*sz*/, const char *msg, void *data)
139 std::vector<GitRev> *vector = (std::vector<GitRev> *)data;
140 GitRev rev;
141 rev.m_CommitHash = (char *)nsha1;
142 rev.GetCommitterDate() = CTime(time);
144 CString one;
145 g_Git.StringAppend(&one, (BYTE *)msg);
147 int message = one.Find(_T(":"), 0);
148 if (message > 0)
150 rev.m_RefAction = one.Left(message);
151 rev.GetSubject() = one.Mid(message + 1);
154 vector->insert(vector->begin(), rev);
156 return 0;
159 int ParserFromRefLog(CString ref, std::vector<GitRev> &refloglist)
161 refloglist.clear();
162 if (g_Git.m_IsUseLibGit2)
164 CAutoRepository repo(g_Git.GetGitRepository());
165 if (!repo)
167 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR);
168 return -1;
171 CAutoReflog reflog;
172 if (git_reflog_read(reflog.GetPointer(), repo, CUnicodeUtils::GetUTF8(ref)) < 0)
174 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not read reflog.")), _T("TortoiseGit"), MB_ICONERROR);
175 return -1;
178 for (size_t i = 0; i < git_reflog_entrycount(reflog); ++i)
180 const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i);
181 if (!entry)
182 continue;
184 GitRev rev;
185 rev.m_CommitHash = (char *)git_reflog_entry_id_new(entry)->id;
186 rev.m_Ref.Format(_T("%s@{%d}"), ref, i);
187 rev.GetCommitterDate() = CTime(git_reflog_entry_committer(entry)->when.time);
188 if (git_reflog_entry_message(entry) != nullptr)
190 CString one;
191 g_Git.StringAppend(&one, (BYTE *)git_reflog_entry_message(entry));
192 int message = one.Find(_T(":"), 0);
193 if (message > 0)
195 rev.m_RefAction = one.Left(message);
196 rev.GetSubject() = one.Mid(message + 1);
199 refloglist.push_back(rev);
202 else if (g_Git.m_IsUseGitDLL)
204 git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref), AddToRefLoglist, &refloglist);
205 for (size_t i = 0; i < refloglist.size(); ++i)
206 refloglist[i].m_Ref.Format(_T("%s@{%d}"), ref, i);
208 else
210 CString cmd, out;
211 GitRev rev;
212 cmd.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), ref);
213 if (g_Git.Run(cmd, &out, NULL, CP_UTF8))
214 return -1;
216 int i = 0;
217 CString prefix = ref + _T("@{");
218 int pos = 0;
219 while (pos >= 0)
221 CString one = out.Tokenize(_T("\n"), pos);
222 int refPos = one.Find(_T(' '), 0);
223 if (refPos < 0)
224 continue;
226 rev.Clear();
228 CString hashStr = one.Left(refPos);
229 rev.m_CommitHash = hashStr;
230 rev.m_Ref.Format(_T("%s@{%d}"), ref, i++);
231 int prefixPos = one.Find(prefix, refPos + 1);
232 if (prefixPos != refPos + 1)
233 continue;
235 int spacePos = one.Find(_T(' '), prefixPos + prefix.GetLength() + 1);
236 if (spacePos < 0)
237 continue;
239 CString timeStr = one.Mid(prefixPos + prefix.GetLength(), spacePos - prefixPos - prefix.GetLength());
240 rev.GetCommitterDate() = CTime(_ttoi(timeStr));
241 int action = one.Find(_T("}: "), spacePos + 1);
242 if (action > 0)
244 action += 2;
245 int message = one.Find(_T(":"), action);
246 if (message > 0)
248 rev.m_RefAction = one.Mid(action + 1, message - action - 1);
249 rev.GetSubject() = one.Right(one.GetLength() - message - 1);
253 refloglist.push_back(rev);
256 return 0;
260 void CRefLogDlg::OnCbnSelchangeRef()
262 CString ref=m_ChooseRef.GetString();
263 m_RefList.ClearText();
265 m_RefList.SetRedraw(false);
267 ParserFromRefLog(ref, m_RefList.m_RevCache);
269 m_RefList.SetItemCountEx((int)m_RefList.m_RevCache.size());
271 this->m_RefList.m_arShownList.RemoveAll();
273 for (unsigned int i = 0; i < m_RefList.m_RevCache.size(); ++i)
275 GitRev *rev = &m_RefList.m_RevCache[i];
276 rev->m_IsFull = TRUE;
277 this->m_RefList.m_arShownList.Add(rev);
280 m_RefList.SetRedraw(true);
282 m_RefList.Invalidate();
284 if (ref == _T("refs/stash"))
286 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->ShowWindow(SW_SHOW);
287 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->EnableWindow((m_RefList.m_arShownList.GetSize() > 0));
289 else
290 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->ShowWindow(SW_HIDE);
293 BOOL CRefLogDlg::PreTranslateMessage(MSG* pMsg)
295 if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F5)
296 Refresh();
297 else if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_F3 || (pMsg->wParam == 'F' && (GetAsyncKeyState(VK_CONTROL) & 0x8000))))
298 OnFind();
300 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
303 void CRefLogDlg::Refresh()
305 STRING_VECTOR list;
306 list.push_back(_T("HEAD"));
307 if (g_Git.GetRefList(list))
308 MessageBox(g_Git.GetGitLastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
310 m_ChooseRef.SetList(list);
312 if (m_CurrentBranch.IsEmpty())
314 m_CurrentBranch.Format(_T("refs/heads/%s"), g_Git.GetCurrentBranch());
315 m_ChooseRef.SetCurSel(0); /* Choose HEAD */
317 else
319 bool found = false;
320 for (int i = 0; i < (int)list.size(); ++i)
322 if(list[i] == m_CurrentBranch)
324 m_ChooseRef.SetCurSel(i);
325 found = true;
326 break;
329 if (!found)
330 m_ChooseRef.SetCurSel(0);
333 m_RefList.m_RevCache.clear();
335 OnCbnSelchangeRef();
338 void CRefLogDlg::OnFind()
340 m_nSearchLine = 0;
341 m_pFindDialog = new CFindReplaceDialog();
342 m_pFindDialog->Create(TRUE, _T(""), NULL, FR_DOWN | FR_HIDEWHOLEWORD | FR_HIDEUPDOWN, this);
345 LRESULT CRefLogDlg::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
347 ASSERT(m_pFindDialog != NULL);
349 if (m_RefList.m_arShownList.IsEmpty())
350 return 0;
352 // If the FR_DIALOGTERM flag is set,
353 // invalidate the handle identifying the dialog box.
354 if (m_pFindDialog->IsTerminating())
356 m_pFindDialog = NULL;
357 return 0;
360 // If the FR_FINDNEXT flag is set,
361 // call the application-defined search routine
362 // to search for the requested string.
363 if (m_pFindDialog->FindNext())
365 //read data from dialog
366 CString findString = m_pFindDialog->GetFindString();
368 bool bFound = false;
369 bool bCaseSensitive = !!(m_pFindDialog->m_nFlags & FR_MATCHCASE);
371 if (!bCaseSensitive)
372 findString.MakeLower();
374 int i = m_nSearchLine;
375 if (i < 0 || i >= m_RefList.m_arShownList.GetCount())
376 i = 0;
380 GitRev * data = (GitRev*)m_RefList.m_arShownList.SafeGetAt(i);
382 CString str;
383 str += data->m_Ref;
384 str += _T("\n");
385 str += data->m_RefAction;
386 str += _T("\n");
387 str += data->m_CommitHash.ToString();
388 str += _T("\n");
389 str += data->GetSubject();
390 str += _T("\n");
391 str += data->GetBody();
392 str += _T("\n");
394 if (!bCaseSensitive)
395 str.MakeLower();
397 if (str.Find(findString) >= 0)
398 bFound = true;
400 ++i;
401 if(!bFound && i >= m_RefList.m_arShownList.GetCount())
402 i=0;
403 } while (i != m_nSearchLine && (!bFound));
405 if (bFound)
407 m_RefList.SetHotItem(i - 1);
408 m_RefList.EnsureVisible(i - 1, FALSE);
409 m_nSearchLine = i;
411 else
412 MessageBox(_T("\"") + findString + _T("\" ") + CString(MAKEINTRESOURCE(IDS_NOTFOUND)), _T("TortoiseGit"), MB_ICONINFORMATION);
415 return 0;