Only store old filename if it differs from current one
[TortoiseGit.git] / src / Git / GitRevLoglist.cpp
blob4eb6225272d0c8971633068d7000510788e78d31
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-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.
20 #include "stdafx.h"
21 #include "GitRevLoglist.h"
22 #include "Git.h"
23 #include "gitdll.h"
24 #include "UnicodeUtils.h"
26 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
28 GitRevLoglist::GitRevLoglist(void)
30 GitRev();
31 m_Action = 0;
32 m_RebaseAction = 0;
33 m_IsFull = FALSE;
34 m_IsUpdateing = FALSE;
35 m_IsCommitParsed = FALSE;
36 m_IsDiffFiles = FALSE;
37 m_CallDiffAsync = nullptr;
38 m_IsSimpleListReady = FALSE;
39 m_Mark = 0;
41 SecureZeroMemory(&m_GitCommit, sizeof(GIT_COMMIT));
44 GitRevLoglist::~GitRevLoglist(void)
46 if (!m_IsCommitParsed && m_GitCommit.m_pGitCommit)
47 git_free_commit(&m_GitCommit);
50 void GitRevLoglist::Clear()
52 GitRev::Clear();
53 m_Action = 0;
54 m_Files.Clear();
55 m_UnRevFiles.Clear();
56 m_Ref.Empty();
57 m_RefAction.Empty();
58 m_Mark = 0;
59 m_IsFull = FALSE;
60 m_IsUpdateing = FALSE;
61 m_IsCommitParsed = FALSE;
62 m_IsDiffFiles = FALSE;
63 m_CallDiffAsync = nullptr;
64 m_IsSimpleListReady = FALSE;
68 int GitRevLoglist::SafeGetSimpleList(CGit* git)
70 if (InterlockedExchange(&m_IsUpdateing, TRUE) == TRUE)
71 return 0;
73 m_SimpleFileList.clear();
74 if (git->UsingLibGit2(CGit::GIT_CMD_LOGLISTDIFF))
76 CAutoRepository repo(git->GetGitRepository());
77 if (!repo)
78 return -1;
79 CAutoCommit commit;
80 if (git_commit_lookup(commit.GetPointer(), repo, (const git_oid*)m_CommitHash.m_hash) < 0)
81 return -1;
83 CAutoTree commitTree;
84 if (git_commit_tree(commitTree.GetPointer(), commit) < 0)
85 return -1;
87 bool isRoot = git_commit_parentcount(commit) == 0;
88 for (unsigned int parentId = 0; isRoot || parentId < git_commit_parentcount(commit); ++parentId)
90 CAutoTree parentTree;
91 if (!isRoot)
93 CAutoCommit parentCommit;
94 if (git_commit_parent(parentCommit.GetPointer(), commit, parentId) < 0)
95 return -1;
97 if (git_commit_tree(parentTree.GetPointer(), parentCommit) < 0)
98 return -1;
100 isRoot = false;
102 CAutoDiff diff;
103 if (git_diff_tree_to_tree(diff.GetPointer(), repo, parentTree, commitTree, nullptr) < 0)
104 return -1;
106 size_t deltas = git_diff_num_deltas(diff);
107 for (size_t i = 0; i < deltas; ++i)
109 const git_diff_delta* delta = git_diff_get_delta(diff, i);
110 m_SimpleFileList.push_back(CUnicodeUtils::GetUnicode(delta->new_file.path));
114 InterlockedExchange(&m_IsUpdateing, FALSE);
115 InterlockedExchange(&m_IsSimpleListReady, TRUE);
116 return 0;
118 git->CheckAndInitDll();
119 GIT_COMMIT commit = { 0 };
120 GIT_COMMIT_LIST list;
121 GIT_HASH parent;
123 CAutoLocker lock(g_Git.m_critGitDllSec);
127 if (git_get_commit_from_hash(&commit, m_CommitHash.m_hash))
128 return -1;
130 catch (char *)
132 return -1;
135 int i = 0;
136 bool isRoot = m_ParentHash.empty();
137 git_get_commit_first_parent(&commit, &list);
138 while (git_get_commit_next_parent(&list, parent) == 0 || isRoot)
140 GIT_FILE file = 0;
141 int count = 0;
144 if (isRoot)
145 git_root_diff(git->GetGitSimpleListDiff(), commit.m_hash, &file, &count, 0);
146 else
147 git_do_diff(git->GetGitSimpleListDiff(), parent, commit.m_hash, &file, &count, 0);
149 catch (char *)
151 return -1;
154 isRoot = false;
156 for (int j = 0; j < count; ++j)
158 char* newname;
159 char* oldname;
160 int mode, IsBin, inc, dec;
164 git_get_diff_file(git->GetGitSimpleListDiff(), file, j, &newname, &oldname, &mode, &IsBin, &inc, &dec);
166 catch (char *)
168 return -1;
171 m_SimpleFileList.push_back(CUnicodeUtils::GetUnicode(newname));
174 git_diff_flush(git->GetGitSimpleListDiff());
175 ++i;
178 InterlockedExchange(&m_IsUpdateing, FALSE);
179 InterlockedExchange(&m_IsSimpleListReady, TRUE);
180 git_free_commit(&commit);
182 return 0;
185 int GitRevLoglist::SafeFetchFullInfo(CGit* git)
187 if (InterlockedExchange(&m_IsUpdateing, TRUE) == TRUE)
188 return 0;
190 m_Files.Clear();
191 if (git->UsingLibGit2(CGit::GIT_CMD_LOGLISTDIFF))
193 CAutoRepository repo(git->GetGitRepository());
194 if (!repo)
195 return -1;
196 CAutoCommit commit;
197 if (git_commit_lookup(commit.GetPointer(), repo, (const git_oid*)m_CommitHash.m_hash) < 0)
198 return -1;
200 CAutoTree commitTree;
201 if (git_commit_tree(commitTree.GetPointer(), commit) < 0)
202 return -1;
204 bool isRoot = git_commit_parentcount(commit) == 0;
205 for (unsigned int parentId = 0; isRoot || parentId < git_commit_parentcount(commit); ++parentId)
207 CAutoTree parentTree;
208 if (!isRoot)
210 CAutoCommit parentCommit;
211 if (git_commit_parent(parentCommit.GetPointer(), commit, parentId) < 0)
212 return -1;
214 if (git_commit_tree(parentTree.GetPointer(), parentCommit) < 0)
215 return -1;
217 isRoot = false;
219 CAutoDiff diff;
220 if (git_diff_tree_to_tree(diff.GetPointer(), repo, parentTree, commitTree, nullptr) < 0)
221 return -1;
223 if (git_diff_find_similar(diff, nullptr) < 0)
224 return-1;
226 const git_diff_delta* lastDelta = nullptr;
227 int oldAction = 0;
228 size_t deltas = git_diff_num_deltas(diff);
229 for (size_t i = 0; i < deltas; ++i)
231 CAutoPatch patch;
232 if (git_patch_from_diff(patch.GetPointer(), diff, i) < 0)
233 return -1;
235 const git_diff_delta* delta = git_patch_get_delta(patch);
237 if (lastDelta && strcmp(lastDelta->new_file.path, delta->new_file.path) == 0 && (lastDelta->status == GIT_DELTA_DELETED && delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_DELETED && lastDelta->status == GIT_DELTA_ADDED))
239 CTGitPath path = m_Files[m_Files.GetCount() - 1];
240 m_Files.RemoveItem(path);
241 path.m_StatAdd = _T("-");
242 path.m_StatDel = _T("-");
243 path.m_Action = CTGitPath::LOGACTIONS_MODIFIED;
244 m_Action = oldAction | CTGitPath::LOGACTIONS_MODIFIED;
245 m_Files.AddPath(path);
246 lastDelta = nullptr;
247 continue;
249 lastDelta = delta;
251 CString newname = CUnicodeUtils::GetUnicode(delta->new_file.path);
252 CTGitPath path;
253 if (delta->new_file.path == delta->old_file.path)
254 path.SetFromGit(newname);
255 else
257 CString oldname = CUnicodeUtils::GetUnicode(delta->old_file.path);
258 path.SetFromGit(newname, &oldname);
260 oldAction = m_Action;
261 m_Action |= path.ParserAction(delta->status);
262 path.m_ParentNo = parentId;
264 if (delta->flags & GIT_DIFF_FLAG_BINARY)
266 path.m_StatAdd = _T("-");
267 path.m_StatDel = _T("-");
269 else
271 size_t adds, dels;
272 if (git_patch_line_stats(nullptr, &adds, &dels, patch) < 0)
273 return -1;
274 path.m_StatAdd.Format(_T("%d"), adds);
275 path.m_StatDel.Format(_T("%d"), dels);
277 m_Files.AddPath(path);
281 InterlockedExchange(&m_IsUpdateing, FALSE);
282 InterlockedExchange(&m_IsFull, TRUE);
283 return 0;
286 git->CheckAndInitDll();
287 GIT_COMMIT commit = { 0 };
288 GIT_COMMIT_LIST list;
289 GIT_HASH parent;
291 CAutoLocker lock(g_Git.m_critGitDllSec);
295 if (git_get_commit_from_hash(&commit, m_CommitHash.m_hash))
296 return -1;
298 catch (char *)
300 return -1;
303 int i = 0;
304 git_get_commit_first_parent(&commit, &list);
305 bool isRoot = (list == nullptr);
307 while (git_get_commit_next_parent(&list, parent) == 0 || isRoot)
309 GIT_FILE file = 0;
310 int count = 0;
314 if (isRoot)
315 git_root_diff(git->GetGitDiff(), m_CommitHash.m_hash, &file, &count, 1);
316 else
317 git_do_diff(git->GetGitDiff(), parent, commit.m_hash, &file, &count, 1);
319 catch (char*)
321 git_free_commit(&commit);
322 return -1;
324 isRoot = false;
326 CTGitPath path;
327 CString strnewname;
328 CString stroldname;
330 for (int j = 0; j < count; ++j)
332 path.Reset();
333 char* newname;
334 char* oldname;
336 strnewname.Empty();
337 stroldname.Empty();
339 int mode, IsBin, inc, dec;
340 git_get_diff_file(git->GetGitDiff(), file, j, &newname, &oldname, &mode, &IsBin, &inc, &dec);
342 git->StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
343 if (newname == oldname)
344 path.SetFromGit(strnewname);
345 else
347 git->StringAppend(&stroldname, (BYTE*)oldname, CP_UTF8);
348 path.SetFromGit(strnewname, &stroldname);
350 path.ParserAction((BYTE)mode);
351 path.m_ParentNo = i;
353 m_Action |= path.m_Action;
355 if (IsBin)
357 path.m_StatAdd = _T("-");
358 path.m_StatDel = _T("-");
360 else
362 path.m_StatAdd.Format(_T("%d"), inc);
363 path.m_StatDel.Format(_T("%d"), dec);
365 m_Files.AddPath(path);
367 git_diff_flush(git->GetGitDiff());
368 ++i;
371 InterlockedExchange(&m_IsUpdateing, FALSE);
372 InterlockedExchange(&m_IsFull, TRUE);
373 git_free_commit(&commit);
375 return 0;
378 int GitRevLoglist::GetRefLog(const CString& ref, std::vector<GitRevLoglist>& refloglist, CString& error)
380 refloglist.clear();
381 if (g_Git.m_IsUseLibGit2)
383 CAutoRepository repo(g_Git.GetGitRepository());
384 if (!repo)
386 error = g_Git.GetLibGit2LastErr();
387 return -1;
390 CAutoReflog reflog;
391 if (git_reflog_read(reflog.GetPointer(), repo, CUnicodeUtils::GetUTF8(ref)) < 0)
393 error = g_Git.GetLibGit2LastErr();
394 return -1;
397 for (size_t i = 0; i < git_reflog_entrycount(reflog); ++i)
399 const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i);
400 if (!entry)
401 continue;
403 GitRevLoglist rev;
404 rev.m_CommitHash = (const unsigned char*)git_reflog_entry_id_new(entry)->id;
405 rev.m_Ref.Format(_T("%s@{%d}"), ref, i);
406 rev.GetCommitterDate() = CTime(git_reflog_entry_committer(entry)->when.time);
407 if (git_reflog_entry_message(entry) != nullptr)
409 CString one = CUnicodeUtils::GetUnicode(git_reflog_entry_message(entry));
410 int message = one.Find(_T(":"), 0);
411 if (message > 0)
413 rev.m_RefAction = one.Left(message);
414 rev.GetSubject() = one.Mid(message + 1);
417 refloglist.push_back(rev);
419 return 0;
421 else if (g_Git.m_IsUseGitDLL)
423 g_Git.CheckAndInitDll();
424 std::vector<GitRevLoglist> tmp;
425 // no error checking, because the only error which could occour is file not found
426 git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref), [](unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long time, int /*sz*/, const char *msg, void *data)
428 std::vector<GitRevLoglist>* vector = (std::vector<GitRevLoglist>*)data;
429 GitRevLoglist rev;
430 rev.m_CommitHash = (const unsigned char*)nsha1;
431 rev.GetCommitterDate() = CTime(time);
433 CString one = CUnicodeUtils::GetUnicode(msg);
434 int message = one.Find(_T(":"), 0);
435 if (message > 0)
437 rev.m_RefAction = one.Left(message);
438 rev.GetSubject() = one.Mid(message + 1).TrimRight(L'\n');
440 vector->push_back(rev);
442 return 0;
443 }, &tmp);
445 for (size_t i = tmp.size(), id = 0; i > 0; --i, ++id)
447 GitRevLoglist rev = tmp[i - 1];
448 rev.m_Ref.Format(_T("%s@{%ld}"), ref, id);
449 refloglist.push_back(rev);
451 return 0;
454 CString dotGitDir;
455 if (!GitAdminDir::GetAdminDirPath(g_Git.m_CurrentDir, dotGitDir))
457 error = _T(".git directory not found");
458 return -1;
461 error.Empty();
462 // git.exe would fail with "branch not known"
463 if (!PathFileExists(dotGitDir + ref))
464 return 0;
466 CString cmd, out;
467 cmd.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), ref);
468 if (g_Git.Run(cmd, &out, &error, CP_UTF8))
469 return -1;
471 int i = 0;
472 CString prefix = ref + _T("@{");
473 int pos = 0;
474 while (pos >= 0)
476 CString one = out.Tokenize(_T("\n"), pos);
477 int refPos = one.Find(_T(' '), 0);
478 if (refPos < 0)
479 continue;
481 GitRevLoglist rev;
482 rev.m_CommitHash = one.Left(refPos);
483 rev.m_Ref.Format(_T("%s@{%d}"), ref, i++);
484 int prefixPos = one.Find(prefix, refPos + 1);
485 if (prefixPos != refPos + 1)
486 continue;
488 int spacePos = one.Find(_T(' '), prefixPos + prefix.GetLength() + 1);
489 if (spacePos < 0)
490 continue;
492 CString timeStr = one.Mid(prefixPos + prefix.GetLength(), spacePos - prefixPos - prefix.GetLength());
493 rev.GetCommitterDate() = CTime(_ttoi(timeStr));
494 int action = one.Find(_T("}: "), spacePos + 1);
495 if (action > 0)
497 action += 2;
498 int message = one.Find(_T(":"), action);
499 if (message > 0)
501 rev.m_RefAction = one.Mid(action + 1, message - action - 1);
502 rev.GetSubject() = one.Right(one.GetLength() - message - 1);
506 refloglist.push_back(rev);
508 return 0;