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.
21 #include "GitRevLoglist.h"
24 #include "UnicodeUtils.h"
26 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
28 GitRevLoglist::GitRevLoglist(void)
34 m_IsUpdateing
= FALSE
;
35 m_IsCommitParsed
= FALSE
;
36 m_IsDiffFiles
= FALSE
;
37 m_CallDiffAsync
= nullptr;
38 m_IsSimpleListReady
= FALSE
;
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()
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
)
73 m_SimpleFileList
.clear();
74 if (git
->UsingLibGit2(CGit::GIT_CMD_LOGLISTDIFF
))
76 CAutoRepository
repo(git
->GetGitRepository());
80 if (git_commit_lookup(commit
.GetPointer(), repo
, (const git_oid
*)m_CommitHash
.m_hash
) < 0)
84 if (git_commit_tree(commitTree
.GetPointer(), commit
) < 0)
87 bool isRoot
= git_commit_parentcount(commit
) == 0;
88 for (unsigned int parentId
= 0; isRoot
|| parentId
< git_commit_parentcount(commit
); ++parentId
)
93 CAutoCommit parentCommit
;
94 if (git_commit_parent(parentCommit
.GetPointer(), commit
, parentId
) < 0)
97 if (git_commit_tree(parentTree
.GetPointer(), parentCommit
) < 0)
103 if (git_diff_tree_to_tree(diff
.GetPointer(), repo
, parentTree
, commitTree
, nullptr) < 0)
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
);
118 git
->CheckAndInitDll();
119 GIT_COMMIT commit
= { 0 };
120 GIT_COMMIT_LIST list
;
123 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
127 if (git_get_commit_from_hash(&commit
, m_CommitHash
.m_hash
))
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
)
145 git_root_diff(git
->GetGitSimpleListDiff(), commit
.m_hash
, &file
, &count
, 0);
147 git_do_diff(git
->GetGitSimpleListDiff(), parent
, commit
.m_hash
, &file
, &count
, 0);
156 for (int j
= 0; j
< count
; ++j
)
160 int mode
, IsBin
, inc
, dec
;
164 git_get_diff_file(git
->GetGitSimpleListDiff(), file
, j
, &newname
, &oldname
, &mode
, &IsBin
, &inc
, &dec
);
171 m_SimpleFileList
.push_back(CUnicodeUtils::GetUnicode(newname
));
174 git_diff_flush(git
->GetGitSimpleListDiff());
178 InterlockedExchange(&m_IsUpdateing
, FALSE
);
179 InterlockedExchange(&m_IsSimpleListReady
, TRUE
);
180 git_free_commit(&commit
);
185 int GitRevLoglist::SafeFetchFullInfo(CGit
* git
)
187 if (InterlockedExchange(&m_IsUpdateing
, TRUE
) == TRUE
)
191 if (git
->UsingLibGit2(CGit::GIT_CMD_LOGLISTDIFF
))
193 CAutoRepository
repo(git
->GetGitRepository());
197 if (git_commit_lookup(commit
.GetPointer(), repo
, (const git_oid
*)m_CommitHash
.m_hash
) < 0)
200 CAutoTree commitTree
;
201 if (git_commit_tree(commitTree
.GetPointer(), commit
) < 0)
204 bool isRoot
= git_commit_parentcount(commit
) == 0;
205 for (unsigned int parentId
= 0; isRoot
|| parentId
< git_commit_parentcount(commit
); ++parentId
)
207 CAutoTree parentTree
;
210 CAutoCommit parentCommit
;
211 if (git_commit_parent(parentCommit
.GetPointer(), commit
, parentId
) < 0)
214 if (git_commit_tree(parentTree
.GetPointer(), parentCommit
) < 0)
220 if (git_diff_tree_to_tree(diff
.GetPointer(), repo
, parentTree
, commitTree
, nullptr) < 0)
223 if (git_diff_find_similar(diff
, nullptr) < 0)
226 const git_diff_delta
* lastDelta
= nullptr;
228 size_t deltas
= git_diff_num_deltas(diff
);
229 for (size_t i
= 0; i
< deltas
; ++i
)
232 if (git_patch_from_diff(patch
.GetPointer(), diff
, i
) < 0)
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
);
251 CString newname
= CUnicodeUtils::GetUnicode(delta
->new_file
.path
);
253 if (delta
->new_file
.path
== delta
->old_file
.path
)
254 path
.SetFromGit(newname
);
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("-");
272 if (git_patch_line_stats(nullptr, &adds
, &dels
, patch
) < 0)
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
);
286 git
->CheckAndInitDll();
287 GIT_COMMIT commit
= { 0 };
288 GIT_COMMIT_LIST list
;
291 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
295 if (git_get_commit_from_hash(&commit
, m_CommitHash
.m_hash
))
304 git_get_commit_first_parent(&commit
, &list
);
305 bool isRoot
= (list
== nullptr);
307 while (git_get_commit_next_parent(&list
, parent
) == 0 || isRoot
)
315 git_root_diff(git
->GetGitDiff(), m_CommitHash
.m_hash
, &file
, &count
, 1);
317 git_do_diff(git
->GetGitDiff(), parent
, commit
.m_hash
, &file
, &count
, 1);
321 git_free_commit(&commit
);
330 for (int j
= 0; j
< count
; ++j
)
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
);
347 git
->StringAppend(&stroldname
, (BYTE
*)oldname
, CP_UTF8
);
348 path
.SetFromGit(strnewname
, &stroldname
);
350 path
.ParserAction((BYTE
)mode
);
353 m_Action
|= path
.m_Action
;
357 path
.m_StatAdd
= _T("-");
358 path
.m_StatDel
= _T("-");
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());
371 InterlockedExchange(&m_IsUpdateing
, FALSE
);
372 InterlockedExchange(&m_IsFull
, TRUE
);
373 git_free_commit(&commit
);
378 int GitRevLoglist::GetRefLog(const CString
& ref
, std::vector
<GitRevLoglist
>& refloglist
, CString
& error
)
381 if (g_Git
.m_IsUseLibGit2
)
383 CAutoRepository
repo(g_Git
.GetGitRepository());
386 error
= g_Git
.GetLibGit2LastErr();
391 if (git_reflog_read(reflog
.GetPointer(), repo
, CUnicodeUtils::GetUTF8(ref
)) < 0)
393 error
= g_Git
.GetLibGit2LastErr();
397 for (size_t i
= 0; i
< git_reflog_entrycount(reflog
); ++i
)
399 const git_reflog_entry
*entry
= git_reflog_entry_byindex(reflog
, i
);
404 rev
.m_CommitHash
= (const unsigned char*)git_reflog_entry_id_new(entry
)->id
;
405 rev
.m_Ref
.Format(_T("%s@{%d}"), (LPCTSTR
)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);
413 rev
.m_RefAction
= one
.Left(message
);
414 rev
.GetSubject() = one
.Mid(message
+ 2);
417 rev
.m_RefAction
= one
;
419 refloglist
.push_back(rev
);
423 else if (g_Git
.m_IsUseGitDLL
)
425 g_Git
.CheckAndInitDll();
426 std::vector
<GitRevLoglist
> tmp
;
427 // no error checking, because the only error which could occour is file not found
428 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
)
430 std::vector
<GitRevLoglist
>* vector
= (std::vector
<GitRevLoglist
>*)data
;
432 rev
.m_CommitHash
= (const unsigned char*)nsha1
;
433 rev
.GetCommitterDate() = CTime(time
);
435 CString one
= CUnicodeUtils::GetUnicode(msg
);
436 int message
= one
.Find(_T(": "), 0);
439 rev
.m_RefAction
= one
.Left(message
);
440 rev
.GetSubject() = one
.Mid(message
+ 2).TrimRight(L
'\n');
443 rev
.m_RefAction
= one
.TrimRight(L
'\n');
444 vector
->push_back(rev
);
449 for (size_t i
= tmp
.size(), id
= 0; i
> 0; --i
, ++id
)
451 GitRevLoglist rev
= tmp
[i
- 1];
452 rev
.m_Ref
.Format(_T("%s@{%ld}"), (LPCTSTR
)ref
, id
);
453 refloglist
.push_back(rev
);
459 if (!GitAdminDir::GetAdminDirPath(g_Git
.m_CurrentDir
, dotGitDir
))
461 error
= _T(".git directory not found");
466 // git.exe would fail with "branch not known"
467 if (!PathFileExists(dotGitDir
+ ref
))
471 cmd
.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), (LPCTSTR
)ref
);
472 if (g_Git
.Run(cmd
, &out
, &error
, CP_UTF8
))
476 CString prefix
= ref
+ _T("@{");
480 CString one
= out
.Tokenize(_T("\n"), pos
);
481 int refPos
= one
.Find(_T(' '), 0);
486 rev
.m_CommitHash
= one
.Left(refPos
);
487 rev
.m_Ref
.Format(_T("%s@{%d}"), (LPCTSTR
)ref
, i
++);
488 int prefixPos
= one
.Find(prefix
, refPos
+ 1);
489 if (prefixPos
!= refPos
+ 1)
492 int spacePos
= one
.Find(_T(' '), prefixPos
+ prefix
.GetLength() + 1);
496 CString timeStr
= one
.Mid(prefixPos
+ prefix
.GetLength(), spacePos
- prefixPos
- prefix
.GetLength());
497 rev
.GetCommitterDate() = CTime(_ttoi(timeStr
));
498 int action
= one
.Find(_T("}: "), spacePos
+ 1);
502 int message
= one
.Find(_T(": "), action
);
505 rev
.m_RefAction
= one
.Mid(action
+ 1, message
- action
- 1);
506 rev
.GetSubject() = one
.Right(one
.GetLength() - message
- 2);
509 rev
.m_RefAction
= one
.Mid(action
+ 1);
512 refloglist
.push_back(rev
);