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 if (m_GitCommit
.m_pGitCommit
!= commit
.m_pGitCommit
)
181 git_free_commit(&commit
);
186 int GitRevLoglist::SafeFetchFullInfo(CGit
* git
)
188 if (InterlockedExchange(&m_IsUpdateing
, TRUE
) == TRUE
)
192 if (git
->UsingLibGit2(CGit::GIT_CMD_LOGLISTDIFF
))
194 CAutoRepository
repo(git
->GetGitRepository());
198 if (git_commit_lookup(commit
.GetPointer(), repo
, (const git_oid
*)m_CommitHash
.m_hash
) < 0)
201 CAutoTree commitTree
;
202 if (git_commit_tree(commitTree
.GetPointer(), commit
) < 0)
205 bool isRoot
= git_commit_parentcount(commit
) == 0;
206 for (unsigned int parentId
= 0; isRoot
|| parentId
< git_commit_parentcount(commit
); ++parentId
)
208 CAutoTree parentTree
;
211 CAutoCommit parentCommit
;
212 if (git_commit_parent(parentCommit
.GetPointer(), commit
, parentId
) < 0)
215 if (git_commit_tree(parentTree
.GetPointer(), parentCommit
) < 0)
221 if (git_diff_tree_to_tree(diff
.GetPointer(), repo
, parentTree
, commitTree
, nullptr) < 0)
224 if (git_diff_find_similar(diff
, nullptr) < 0)
227 const git_diff_delta
* lastDelta
= nullptr;
229 size_t deltas
= git_diff_num_deltas(diff
);
230 for (size_t i
= 0; i
< deltas
; ++i
)
233 if (git_patch_from_diff(patch
.GetPointer(), diff
, i
) < 0)
236 const git_diff_delta
* delta
= git_patch_get_delta(patch
);
238 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
))
240 CTGitPath path
= m_Files
[m_Files
.GetCount() - 1];
241 m_Files
.RemoveItem(path
);
242 path
.m_StatAdd
= _T("-");
243 path
.m_StatDel
= _T("-");
244 path
.m_Action
= CTGitPath::LOGACTIONS_MODIFIED
;
245 m_Action
= oldAction
| CTGitPath::LOGACTIONS_MODIFIED
;
246 m_Files
.AddPath(path
);
252 CString newname
= CUnicodeUtils::GetUnicode(delta
->new_file
.path
);
254 if (delta
->new_file
.path
== delta
->old_file
.path
)
255 path
.SetFromGit(newname
);
258 CString oldname
= CUnicodeUtils::GetUnicode(delta
->old_file
.path
);
259 path
.SetFromGit(newname
, &oldname
);
261 oldAction
= m_Action
;
262 m_Action
|= path
.ParserAction(delta
->status
);
263 path
.m_ParentNo
= parentId
;
265 if (delta
->flags
& GIT_DIFF_FLAG_BINARY
)
267 path
.m_StatAdd
= _T("-");
268 path
.m_StatDel
= _T("-");
273 if (git_patch_line_stats(nullptr, &adds
, &dels
, patch
) < 0)
275 path
.m_StatAdd
.Format(_T("%d"), adds
);
276 path
.m_StatDel
.Format(_T("%d"), dels
);
278 m_Files
.AddPath(path
);
282 InterlockedExchange(&m_IsUpdateing
, FALSE
);
283 InterlockedExchange(&m_IsFull
, TRUE
);
287 git
->CheckAndInitDll();
288 GIT_COMMIT commit
= { 0 };
289 GIT_COMMIT_LIST list
;
292 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
296 if (git_get_commit_from_hash(&commit
, m_CommitHash
.m_hash
))
305 git_get_commit_first_parent(&commit
, &list
);
306 bool isRoot
= (list
== nullptr);
308 while (git_get_commit_next_parent(&list
, parent
) == 0 || isRoot
)
316 git_root_diff(git
->GetGitDiff(), m_CommitHash
.m_hash
, &file
, &count
, 1);
318 git_do_diff(git
->GetGitDiff(), parent
, commit
.m_hash
, &file
, &count
, 1);
322 git_free_commit(&commit
);
331 for (int j
= 0; j
< count
; ++j
)
340 int mode
, IsBin
, inc
, dec
;
341 git_get_diff_file(git
->GetGitDiff(), file
, j
, &newname
, &oldname
, &mode
, &IsBin
, &inc
, &dec
);
343 git
->StringAppend(&strnewname
, (BYTE
*)newname
, CP_UTF8
);
344 if (newname
== oldname
)
345 path
.SetFromGit(strnewname
);
348 git
->StringAppend(&stroldname
, (BYTE
*)oldname
, CP_UTF8
);
349 path
.SetFromGit(strnewname
, &stroldname
);
351 path
.ParserAction((BYTE
)mode
);
354 m_Action
|= path
.m_Action
;
358 path
.m_StatAdd
= _T("-");
359 path
.m_StatDel
= _T("-");
363 path
.m_StatAdd
.Format(_T("%d"), inc
);
364 path
.m_StatDel
.Format(_T("%d"), dec
);
366 m_Files
.AddPath(path
);
368 git_diff_flush(git
->GetGitDiff());
372 InterlockedExchange(&m_IsUpdateing
, FALSE
);
373 InterlockedExchange(&m_IsFull
, TRUE
);
374 if (m_GitCommit
.m_pGitCommit
!= commit
.m_pGitCommit
)
375 git_free_commit(&commit
);
380 int GitRevLoglist::GetRefLog(const CString
& ref
, std::vector
<GitRevLoglist
>& refloglist
, CString
& error
)
383 if (g_Git
.m_IsUseLibGit2
)
385 CAutoRepository
repo(g_Git
.GetGitRepository());
388 error
= g_Git
.GetLibGit2LastErr();
393 if (git_reflog_read(reflog
.GetPointer(), repo
, CUnicodeUtils::GetUTF8(ref
)) < 0)
395 error
= g_Git
.GetLibGit2LastErr();
399 for (size_t i
= 0; i
< git_reflog_entrycount(reflog
); ++i
)
401 const git_reflog_entry
*entry
= git_reflog_entry_byindex(reflog
, i
);
406 rev
.m_CommitHash
= (const unsigned char*)git_reflog_entry_id_new(entry
)->id
;
407 rev
.m_Ref
.Format(_T("%s@{%d}"), (LPCTSTR
)ref
, i
);
408 rev
.GetCommitterDate() = CTime(git_reflog_entry_committer(entry
)->when
.time
);
409 if (git_reflog_entry_message(entry
) != nullptr)
411 CString one
= CUnicodeUtils::GetUnicode(git_reflog_entry_message(entry
));
412 int message
= one
.Find(_T(": "), 0);
415 rev
.m_RefAction
= one
.Left(message
);
416 rev
.GetSubject() = one
.Mid(message
+ 2);
419 rev
.m_RefAction
= one
;
421 refloglist
.push_back(rev
);
425 else if (g_Git
.m_IsUseGitDLL
)
427 g_Git
.CheckAndInitDll();
428 std::vector
<GitRevLoglist
> tmp
;
429 // no error checking, because the only error which could occour is file not found
430 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
)
432 std::vector
<GitRevLoglist
>* vector
= (std::vector
<GitRevLoglist
>*)data
;
434 rev
.m_CommitHash
= (const unsigned char*)nsha1
;
435 rev
.GetCommitterDate() = CTime(time
);
437 CString one
= CUnicodeUtils::GetUnicode(msg
);
438 int message
= one
.Find(_T(": "), 0);
441 rev
.m_RefAction
= one
.Left(message
);
442 rev
.GetSubject() = one
.Mid(message
+ 2).TrimRight(L
'\n');
445 rev
.m_RefAction
= one
.TrimRight(L
'\n');
446 vector
->push_back(rev
);
451 for (size_t i
= tmp
.size(), id
= 0; i
> 0; --i
, ++id
)
453 GitRevLoglist rev
= tmp
[i
- 1];
454 rev
.m_Ref
.Format(_T("%s@{%ld}"), (LPCTSTR
)ref
, id
);
455 refloglist
.push_back(rev
);
461 if (!GitAdminDir::GetAdminDirPath(g_Git
.m_CurrentDir
, dotGitDir
))
463 error
= _T(".git directory not found");
468 // git.exe would fail with "branch not known"
469 if (!PathFileExists(dotGitDir
+ ref
))
473 cmd
.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), (LPCTSTR
)ref
);
474 if (g_Git
.Run(cmd
, &out
, &error
, CP_UTF8
))
478 CString prefix
= ref
+ _T("@{");
482 CString one
= out
.Tokenize(_T("\n"), pos
);
483 int refPos
= one
.Find(_T(' '), 0);
488 rev
.m_CommitHash
= one
.Left(refPos
);
489 rev
.m_Ref
.Format(_T("%s@{%d}"), (LPCTSTR
)ref
, i
++);
490 int prefixPos
= one
.Find(prefix
, refPos
+ 1);
491 if (prefixPos
!= refPos
+ 1)
494 int spacePos
= one
.Find(_T(' '), prefixPos
+ prefix
.GetLength() + 1);
498 CString timeStr
= one
.Mid(prefixPos
+ prefix
.GetLength(), spacePos
- prefixPos
- prefix
.GetLength());
499 rev
.GetCommitterDate() = CTime(_ttoi(timeStr
));
500 int action
= one
.Find(_T("}: "), spacePos
+ 1);
504 int message
= one
.Find(_T(": "), action
);
507 rev
.m_RefAction
= one
.Mid(action
+ 1, message
- action
- 1);
508 rev
.GetSubject() = one
.Right(one
.GetLength() - message
- 2);
511 rev
.m_RefAction
= one
.Mid(action
+ 1);
514 refloglist
.push_back(rev
);