Use CUnicodeUtils::GetUnicode instead of CGit::StringAppend if possible
[TortoiseGit.git] / src / TortoiseProc / RefLogDlg.cpp
blob71427af033c352e32303d4b10dc4dcb096e0d489
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 = CUnicodeUtils::GetUnicode(msg);
146 int message = one.Find(_T(":"), 0);
147 if (message > 0)
149 rev.m_RefAction = one.Left(message);
150 rev.GetSubject() = one.Mid(message + 1);
153 vector->insert(vector->begin(), rev);
155 return 0;
158 int ParserFromRefLog(CString ref, std::vector<GitRev> &refloglist)
160 refloglist.clear();
161 if (g_Git.m_IsUseLibGit2)
163 CAutoRepository repo(g_Git.GetGitRepository());
164 if (!repo)
166 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR);
167 return -1;
170 CAutoReflog reflog;
171 if (git_reflog_read(reflog.GetPointer(), repo, CUnicodeUtils::GetUTF8(ref)) < 0)
173 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not read reflog.")), _T("TortoiseGit"), MB_ICONERROR);
174 return -1;
177 for (size_t i = 0; i < git_reflog_entrycount(reflog); ++i)
179 const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i);
180 if (!entry)
181 continue;
183 GitRev rev;
184 rev.m_CommitHash = (char *)git_reflog_entry_id_new(entry)->id;
185 rev.m_Ref.Format(_T("%s@{%d}"), ref, i);
186 rev.GetCommitterDate() = CTime(git_reflog_entry_committer(entry)->when.time);
187 if (git_reflog_entry_message(entry) != nullptr)
189 CString one = CUnicodeUtils::GetUnicode(git_reflog_entry_message(entry));
190 int message = one.Find(_T(":"), 0);
191 if (message > 0)
193 rev.m_RefAction = one.Left(message);
194 rev.GetSubject() = one.Mid(message + 1);
197 refloglist.push_back(rev);
200 else if (g_Git.m_IsUseGitDLL)
202 git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref), AddToRefLoglist, &refloglist);
203 for (size_t i = 0; i < refloglist.size(); ++i)
204 refloglist[i].m_Ref.Format(_T("%s@{%d}"), ref, i);
206 else
208 CString cmd, out;
209 GitRev rev;
210 cmd.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), ref);
211 if (g_Git.Run(cmd, &out, NULL, CP_UTF8))
212 return -1;
214 int i = 0;
215 CString prefix = ref + _T("@{");
216 int pos = 0;
217 while (pos >= 0)
219 CString one = out.Tokenize(_T("\n"), pos);
220 int refPos = one.Find(_T(' '), 0);
221 if (refPos < 0)
222 continue;
224 rev.Clear();
226 CString hashStr = one.Left(refPos);
227 rev.m_CommitHash = hashStr;
228 rev.m_Ref.Format(_T("%s@{%d}"), ref, i++);
229 int prefixPos = one.Find(prefix, refPos + 1);
230 if (prefixPos != refPos + 1)
231 continue;
233 int spacePos = one.Find(_T(' '), prefixPos + prefix.GetLength() + 1);
234 if (spacePos < 0)
235 continue;
237 CString timeStr = one.Mid(prefixPos + prefix.GetLength(), spacePos - prefixPos - prefix.GetLength());
238 rev.GetCommitterDate() = CTime(_ttoi(timeStr));
239 int action = one.Find(_T("}: "), spacePos + 1);
240 if (action > 0)
242 action += 2;
243 int message = one.Find(_T(":"), action);
244 if (message > 0)
246 rev.m_RefAction = one.Mid(action + 1, message - action - 1);
247 rev.GetSubject() = one.Right(one.GetLength() - message - 1);
251 refloglist.push_back(rev);
254 return 0;
258 void CRefLogDlg::OnCbnSelchangeRef()
260 CString ref=m_ChooseRef.GetString();
261 m_RefList.ClearText();
263 m_RefList.SetRedraw(false);
265 ParserFromRefLog(ref, m_RefList.m_RevCache);
267 m_RefList.SetItemCountEx((int)m_RefList.m_RevCache.size());
269 this->m_RefList.m_arShownList.RemoveAll();
271 for (unsigned int i = 0; i < m_RefList.m_RevCache.size(); ++i)
273 GitRev *rev = &m_RefList.m_RevCache[i];
274 rev->m_IsFull = TRUE;
275 this->m_RefList.m_arShownList.Add(rev);
278 m_RefList.SetRedraw(true);
280 m_RefList.Invalidate();
282 if (ref == _T("refs/stash"))
284 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->ShowWindow(SW_SHOW);
285 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->EnableWindow((m_RefList.m_arShownList.GetSize() > 0));
287 else
288 GetDlgItem(IDC_REFLOG_BUTTONCLEARSTASH)->ShowWindow(SW_HIDE);
291 BOOL CRefLogDlg::PreTranslateMessage(MSG* pMsg)
293 if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F5)
294 Refresh();
295 else if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_F3 || (pMsg->wParam == 'F' && (GetAsyncKeyState(VK_CONTROL) & 0x8000))))
296 OnFind();
298 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
301 void CRefLogDlg::Refresh()
303 STRING_VECTOR list;
304 list.push_back(_T("HEAD"));
305 if (g_Git.GetRefList(list))
306 MessageBox(g_Git.GetGitLastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
308 m_ChooseRef.SetList(list);
310 if (m_CurrentBranch.IsEmpty())
312 m_CurrentBranch.Format(_T("refs/heads/%s"), g_Git.GetCurrentBranch());
313 m_ChooseRef.SetCurSel(0); /* Choose HEAD */
315 else
317 bool found = false;
318 for (int i = 0; i < (int)list.size(); ++i)
320 if(list[i] == m_CurrentBranch)
322 m_ChooseRef.SetCurSel(i);
323 found = true;
324 break;
327 if (!found)
328 m_ChooseRef.SetCurSel(0);
331 m_RefList.m_RevCache.clear();
333 OnCbnSelchangeRef();
336 void CRefLogDlg::OnFind()
338 m_nSearchLine = 0;
339 m_pFindDialog = new CFindReplaceDialog();
340 m_pFindDialog->Create(TRUE, _T(""), NULL, FR_DOWN | FR_HIDEWHOLEWORD | FR_HIDEUPDOWN, this);
343 LRESULT CRefLogDlg::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
345 ASSERT(m_pFindDialog != NULL);
347 if (m_RefList.m_arShownList.IsEmpty())
348 return 0;
350 // If the FR_DIALOGTERM flag is set,
351 // invalidate the handle identifying the dialog box.
352 if (m_pFindDialog->IsTerminating())
354 m_pFindDialog = NULL;
355 return 0;
358 // If the FR_FINDNEXT flag is set,
359 // call the application-defined search routine
360 // to search for the requested string.
361 if (m_pFindDialog->FindNext())
363 //read data from dialog
364 CString findString = m_pFindDialog->GetFindString();
366 bool bFound = false;
367 bool bCaseSensitive = !!(m_pFindDialog->m_nFlags & FR_MATCHCASE);
369 if (!bCaseSensitive)
370 findString.MakeLower();
372 int i = m_nSearchLine;
373 if (i < 0 || i >= m_RefList.m_arShownList.GetCount())
374 i = 0;
378 GitRev * data = (GitRev*)m_RefList.m_arShownList.SafeGetAt(i);
380 CString str;
381 str += data->m_Ref;
382 str += _T("\n");
383 str += data->m_RefAction;
384 str += _T("\n");
385 str += data->m_CommitHash.ToString();
386 str += _T("\n");
387 str += data->GetSubject();
388 str += _T("\n");
389 str += data->GetBody();
390 str += _T("\n");
392 if (!bCaseSensitive)
393 str.MakeLower();
395 if (str.Find(findString) >= 0)
396 bFound = true;
398 ++i;
399 if(!bFound && i >= m_RefList.m_arShownList.GetCount())
400 i=0;
401 } while (i != m_nSearchLine && (!bFound));
403 if (bFound)
405 m_RefList.SetHotItem(i - 1);
406 m_RefList.EnsureVisible(i - 1, FALSE);
407 m_nSearchLine = i;
409 else
410 MessageBox(_T("\"") + findString + _T("\" ") + CString(MAKEINTRESOURCE(IDS_NOTFOUND)), _T("TortoiseGit"), MB_ICONINFORMATION);
413 return 0;