KiCad PCB EDA Suite
Loading...
Searching...
No Matches
git_pull_handler.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "git_pull_handler.h"
27#include <trace_helpers.h>
28
29#include <wx/log.h>
30
31#include <iostream>
32#include <time.h>
33#include <memory>
34
36{
37}
38
39
41{
42}
43
44
45bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock )
46{
47 if( !GetRepo() )
48 {
49 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - No repository found" );
50 return false;
51 }
52
53 std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
54
55 if( !aSkipLock && !lock.owns_lock() )
56 {
57 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Could not lock mutex" );
58 return false;
59 }
60
61 // Fetch updates from remote repository
62 git_remote* remote = nullptr;
63
64 if( git_remote_lookup( &remote, GetRepo(), "origin" ) != 0 )
65 {
66 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to lookup remote 'origin'" );
67 AddErrorString( wxString::Format( _( "Could not lookup remote '%s'" ), "origin" ) );
68 return false;
69 }
70
71 KIGIT::GitRemotePtr remotePtr( remote );
72
73 git_remote_callbacks remoteCallbacks;
74 git_remote_init_callbacks( &remoteCallbacks, GIT_REMOTE_CALLBACKS_VERSION );
75 remoteCallbacks.sideband_progress = progress_cb;
76 remoteCallbacks.transfer_progress = transfer_progress_cb;
77 remoteCallbacks.credentials = credentials_cb;
78 remoteCallbacks.payload = this;
79 GetCommon()->SetCancelled( false );
80
81 TestedTypes() = 0;
83
84 if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks, nullptr, nullptr ) )
85 {
86 wxString errorMsg = KIGIT_COMMON::GetLastGitError();
87 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to connect to remote: %s", errorMsg );
88 AddErrorString( wxString::Format( _( "Could not connect to remote '%s': %s" ), "origin", errorMsg ) );
89 return false;
90 }
91
92 git_fetch_options fetchOptions;
93 git_fetch_init_options( &fetchOptions, GIT_FETCH_OPTIONS_VERSION );
94 fetchOptions.callbacks = remoteCallbacks;
95
96 if( git_remote_fetch( remote, nullptr, &fetchOptions, nullptr ) )
97 {
98 wxString errorMsg = KIGIT_COMMON::GetLastGitError();
99 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to fetch from remote: %s", errorMsg );
100 AddErrorString( wxString::Format( _( "Could not fetch data from remote '%s': %s" ), "origin", errorMsg ) );
101 return false;
102 }
103
104 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Fetch completed successfully" );
105 return true;
106}
107
108
110{
111 PullResult result = PullResult::Success;
112 std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
113
114 if( !lock.owns_lock() )
115 {
116 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Could not lock mutex" );
117 return PullResult::Error;
118 }
119
120 if( !PerformFetch( true ) )
121 return PullResult::Error;
122
123 git_oid pull_merge_oid = {};
124
125 if( git_repository_fetchhead_foreach( GetRepo(), fetchhead_foreach_cb,
126 &pull_merge_oid ) )
127 {
128 AddErrorString( _( "Could not read 'FETCH_HEAD'" ) );
129 return PullResult::Error;
130 }
131
132 git_annotated_commit* fetchhead_commit;
133
134 if( git_annotated_commit_lookup( &fetchhead_commit, GetRepo(), &pull_merge_oid ) )
135 {
136 AddErrorString( _( "Could not lookup commit" ) );
137 return PullResult::Error;
138 }
139
140 KIGIT::GitAnnotatedCommitPtr fetchheadCommitPtr( fetchhead_commit );
141 const git_annotated_commit* merge_commits[] = { fetchhead_commit };
142 git_merge_analysis_t merge_analysis;
143 git_merge_preference_t merge_preference = GIT_MERGE_PREFERENCE_NONE;
144
145 if( git_merge_analysis( &merge_analysis, &merge_preference, GetRepo(), merge_commits, 1 ) )
146 {
147 AddErrorString( _( "Could not analyze merge" ) );
148 return PullResult::Error;
149 }
150
151 if( merge_analysis & GIT_MERGE_ANALYSIS_UNBORN )
152 {
153 AddErrorString( _( "Invalid HEAD. Cannot merge." ) );
154 return PullResult::MergeFailed;
155 }
156
157 // Nothing to do if the repository is up to date
158 if( merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE )
159 {
160 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Repository is up to date" );
161 git_repository_state_cleanup( GetRepo() );
162 return PullResult::UpToDate;
163 }
164
165 // Fast-forward is easy, just update the local reference
166 if( merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD )
167 {
168 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Fast-forward merge" );
169 return handleFastForward();
170 }
171
172 if( merge_analysis & GIT_MERGE_ANALYSIS_NORMAL )
173 {
174 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Normal merge" );
175
176 // Check git config to determine if we should rebase or merge
177 git_config* config = nullptr;
178
179 if( git_repository_config( &config, GetRepo() ) != GIT_OK )
180 {
181 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Failed to get repository config" );
182 AddErrorString( _( "Could not access repository configuration" ) );
183 return PullResult::Error;
184 }
185
186 KIGIT::GitConfigPtr configPtr( config );
187
188 // Check for pull.rebase config
189 int rebase_value = 0;
190 int ret = git_config_get_bool( &rebase_value, config, "pull.rebase" );
191
192 // If pull.rebase is set to true, use rebase; otherwise use merge
193 if( ret == GIT_OK && rebase_value )
194 {
195 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Using rebase based on config" );
196 return handleRebase( merge_commits, 1 );
197 }
198
199 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Using merge based on config" );
200 return handleMerge( merge_commits, 1 );
201 }
202
203 wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Merge needs resolution" );
204 //TODO: handle merges when they need to be resolved
205
206 return result;
207}
208
209const std::vector<std::pair<std::string, std::vector<CommitDetails>>>&
211{
212 return m_fetchResults;
213}
214
215
216std::string GIT_PULL_HANDLER::getFirstLineFromCommitMessage( const std::string& aMessage )
217{
218 if( aMessage.empty() )
219 return aMessage;
220
221 size_t firstLineEnd = aMessage.find_first_of( '\n' );
222
223 if( firstLineEnd != std::string::npos )
224 return aMessage.substr( 0, firstLineEnd );
225
226 return aMessage;
227}
228
229
230std::string GIT_PULL_HANDLER::getFormattedCommitDate( const git_time& aTime )
231{
232 char dateBuffer[64];
233 time_t time = static_cast<time_t>( aTime.time );
234 struct tm timeInfo;
235 #ifdef _WIN32
236 localtime_s( &timeInfo, &time );
237 #else
238 gmtime_r( &time, &timeInfo );
239 #endif
240 strftime( dateBuffer, sizeof( dateBuffer ), "%Y-%b-%d %H:%M:%S", &timeInfo );
241 return dateBuffer;
242}
243
244
246{
247 git_reference* rawRef = nullptr;
248
249 // Get the current HEAD reference
250 if( git_repository_head( &rawRef, GetRepo() ) )
251 {
252 AddErrorString( _( "Could not get repository head" ) );
253 return PullResult::Error;
254 }
255
256 KIGIT::GitReferencePtr headRef( rawRef );
257
258 git_oid updatedRefOid;
259 const char* currentBranchName = git_reference_name( rawRef );
260 const char* branch_shorthand = git_reference_shorthand( rawRef );
261 wxString remote_name = GetRemotename();
262 wxString remoteBranchName = wxString::Format( "refs/remotes/%s/%s",
263 remote_name, branch_shorthand );
264
265 // Get the OID of the updated reference (remote-tracking branch)
266 if( git_reference_name_to_id( &updatedRefOid, GetRepo(), remoteBranchName.c_str() ) != GIT_OK )
267 {
268 AddErrorString( wxString::Format( _( "Could not get reference OID for reference '%s'" ),
269 remoteBranchName ) );
270 return PullResult::Error;
271 }
272
273 // Get the target commit object
274 git_commit* targetCommit = nullptr;
275
276 if( git_commit_lookup( &targetCommit, GetRepo(), &updatedRefOid ) != GIT_OK )
277 {
278 AddErrorString( _( "Could not look up target commit" ) );
279 return PullResult::Error;
280 }
281
282 KIGIT::GitCommitPtr targetCommitPtr( targetCommit );
283
284 // Get the tree from the target commit
285 git_tree* targetTree = nullptr;
286
287 if( git_commit_tree( &targetTree, targetCommit ) != GIT_OK )
288 {
289 git_commit_free( targetCommit );
290 AddErrorString( _( "Could not get tree from target commit" ) );
291 return PullResult::Error;
292 }
293
294 KIGIT::GitTreePtr targetTreePtr( targetTree );
295
296 // Perform a checkout to update the working directory
297 git_checkout_options checkoutOptions;
298 git_checkout_init_options( &checkoutOptions, GIT_CHECKOUT_OPTIONS_VERSION );
299 auto notify_cb = []( git_checkout_notify_t why, const char* path, const git_diff_file* baseline,
300 const git_diff_file* target, const git_diff_file* workdir, void* payload ) -> int
301 {
302 switch( why )
303 {
304 case GIT_CHECKOUT_NOTIFY_CONFLICT:
305 wxLogTrace( traceGit, "Checkout conflict: %s", path ? path : "unknown" );
306 break;
307 case GIT_CHECKOUT_NOTIFY_DIRTY:
308 wxLogTrace( traceGit, "Checkout dirty: %s", path ? path : "unknown" );
309 break;
310 case GIT_CHECKOUT_NOTIFY_UPDATED:
311 wxLogTrace( traceGit, "Checkout updated: %s", path ? path : "unknown" );
312 break;
313 case GIT_CHECKOUT_NOTIFY_UNTRACKED:
314 wxLogTrace( traceGit, "Checkout untracked: %s", path ? path : "unknown" );
315 break;
316 case GIT_CHECKOUT_NOTIFY_IGNORED:
317 wxLogTrace( traceGit, "Checkout ignored: %s", path ? path : "unknown" );
318 break;
319 default:
320 break;
321 }
322
323 return 0;
324 };
325
326 checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
327 checkoutOptions.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
328 checkoutOptions.notify_cb = notify_cb;
329
330 if( git_checkout_tree( GetRepo(), reinterpret_cast<git_object*>( targetTree ), &checkoutOptions ) != GIT_OK )
331 {
332 AddErrorString( _( "Failed to perform checkout operation." ) );
333 return PullResult::Error;
334 }
335
336 git_reference* updatedRef = nullptr;
337
338 // Update the current branch to point to the new commit
339 if (git_reference_set_target(&updatedRef, rawRef, &updatedRefOid, nullptr) != GIT_OK)
340 {
341 AddErrorString( wxString::Format( _( "Failed to update reference '%s' to point to '%s'" ), currentBranchName,
342 git_oid_tostr_s( &updatedRefOid ) ) );
343 return PullResult::Error;
344 }
345
346 KIGIT::GitReferencePtr updatedRefPtr( updatedRef );
347
348 // Clean up the repository state
349 if( git_repository_state_cleanup( GetRepo() ) != GIT_OK )
350 {
351 AddErrorString( _( "Failed to clean up repository state after fast-forward." ) );
352 return PullResult::Error;
353 }
354
355 git_revwalk* revWalker = nullptr;
356
357 // Collect commit details for updated references
358 if( git_revwalk_new( &revWalker, GetRepo() ) != GIT_OK )
359 {
360 AddErrorString( _( "Failed to initialize revision walker." ) );
361 return PullResult::Error;
362 }
363
364 KIGIT::GitRevWalkPtr revWalkerPtr( revWalker );
365 git_revwalk_sorting( revWalker, GIT_SORT_TIME );
366
367 if( git_revwalk_push_glob( revWalker, currentBranchName ) != GIT_OK )
368 {
369 AddErrorString( _( "Failed to push reference to revision walker." ) );
370 return PullResult::Error;
371 }
372
373 std::pair<std::string, std::vector<CommitDetails>>& branchCommits = m_fetchResults.emplace_back();
374 branchCommits.first = currentBranchName;
375
376 git_oid commitOid;
377
378 while( git_revwalk_next( &commitOid, revWalker ) == GIT_OK )
379 {
380 git_commit* commit = nullptr;
381
382 if( git_commit_lookup( &commit, GetRepo(), &commitOid ) )
383 {
384 AddErrorString( wxString::Format( _( "Could not lookup commit '%s'" ),
385 git_oid_tostr_s( &commitOid ) ) );
386 return PullResult::Error;
387 }
388
389 KIGIT::GitCommitPtr commitPtr( commit );
390
391 CommitDetails details;
392 details.m_sha = git_oid_tostr_s( &commitOid );
393 details.m_firstLine = getFirstLineFromCommitMessage( git_commit_message( commit ) );
394 details.m_author = git_commit_author( commit )->name;
395 details.m_date = getFormattedCommitDate( git_commit_author( commit )->when );
396
397 branchCommits.second.push_back( details );
398 }
399
400 return PullResult::FastForward;
401}
402
403
404PullResult GIT_PULL_HANDLER::handleMerge( const git_annotated_commit** aMergeHeads,
405 size_t aMergeHeadsCount )
406{
407 git_merge_options merge_opts;
408 git_merge_options_init( &merge_opts, GIT_MERGE_OPTIONS_VERSION );
409
410 git_checkout_options checkout_opts;
411 git_checkout_init_options( &checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION );
412
413 checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
414
415 if( git_merge( GetRepo(), aMergeHeads, aMergeHeadsCount, &merge_opts, &checkout_opts ) )
416 {
417 AddErrorString( _( "Could not merge commits" ) );
418 return PullResult::Error;
419 }
420
421 // Get the repository index
422 git_index* index = nullptr;
423
424 if( git_repository_index( &index, GetRepo() ) )
425 {
426 AddErrorString( _( "Could not get repository index" ) );
427 return PullResult::Error;
428 }
429
430 KIGIT::GitIndexPtr indexPtr( index );
431
432 // Check for conflicts
433 git_index_conflict_iterator* conflicts = nullptr;
434
435 if( git_index_conflict_iterator_new( &conflicts, index ) )
436 {
437 AddErrorString( _( "Could not get conflict iterator" ) );
438 return PullResult::Error;
439 }
440
441 KIGIT::GitIndexConflictIteratorPtr conflictsPtr( conflicts );
442
443 const git_index_entry* ancestor = nullptr;
444 const git_index_entry* our = nullptr;
445 const git_index_entry* their = nullptr;
446 std::vector<ConflictData> conflict_data;
447
448 while( git_index_conflict_next( &ancestor, &our, &their, conflicts ) == 0 )
449 {
450 // Case 3: Both files have changed
451 if( ancestor && our && their )
452 {
453 ConflictData conflict_datum;
454 conflict_datum.filename = our->path;
455 conflict_datum.our_oid = our->id;
456 conflict_datum.their_oid = their->id;
457 conflict_datum.our_commit_time = our->mtime.seconds;
458 conflict_datum.their_commit_time = their->mtime.seconds;
459 conflict_datum.our_status = _( "Changed" );
460 conflict_datum.their_status = _( "Changed" );
461 conflict_datum.use_ours = true;
462
463 conflict_data.push_back( conflict_datum );
464 }
465 // Case 4: File added in both ours and theirs
466 else if( !ancestor && our && their )
467 {
468 ConflictData conflict_datum;
469 conflict_datum.filename = our->path;
470 conflict_datum.our_oid = our->id;
471 conflict_datum.their_oid = their->id;
472 conflict_datum.our_commit_time = our->mtime.seconds;
473 conflict_datum.their_commit_time = their->mtime.seconds;
474 conflict_datum.our_status = _( "Added" );
475 conflict_datum.their_status = _( "Added" );
476 conflict_datum.use_ours = true;
477
478 conflict_data.push_back( conflict_datum );
479 }
480 // Case 1: Remote file has changed or been added, local file has not
481 else if( their && !our )
482 {
483 // Accept their changes
484 git_index_add( index, their );
485 }
486 // Case 2: Local file has changed or been added, remote file has not
487 else if( our && !their )
488 {
489 // Accept our changes
490 git_index_add( index, our );
491 }
492 else
493 {
494 wxLogError( wxS( "Unexpected conflict state" ) );
495 }
496 }
497
498 if( conflict_data.empty() )
499 {
500 git_index_conflict_cleanup( index );
501 git_index_write( index );
502 }
503
504 return conflict_data.empty() ? PullResult::Success : PullResult::MergeFailed;
505}
506
507
508PullResult GIT_PULL_HANDLER::handleRebase( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount )
509{
510 // Get the current branch reference
511 git_reference* head_ref = nullptr;
512
513 if( git_repository_head( &head_ref, GetRepo() ) )
514 {
515 wxString errorMsg = KIGIT_COMMON::GetLastGitError();
516 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to get HEAD: %s", errorMsg );
517 return PullResult::Error;
518 }
519
520 KIGIT::GitReferencePtr headRefPtr(head_ref);
521
522 // Initialize rebase operation
523 git_rebase* rebase = nullptr;
524 git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
525 rebase_opts.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
526
527 if( git_rebase_init( &rebase, GetRepo(), nullptr, nullptr, aMergeHeads[0], &rebase_opts ) )
528 {
529 wxString errorMsg = KIGIT_COMMON::GetLastGitError();
530 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to initialize rebase: %s", errorMsg );
531 return PullResult::Error;
532 }
533
534 KIGIT::GitRebasePtr rebasePtr( rebase );
535 git_rebase_operation* operation = nullptr;
536
537 while( git_rebase_next( &operation, rebase ) != GIT_ITEROVER )
538 {
539 // Check for conflicts
540 git_index* index = nullptr;
541 if( git_repository_index( &index, GetRepo() ) )
542 {
543 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to get index: %s",
545 return PullResult::Error;
546 }
547 KIGIT::GitIndexPtr indexPtr( index );
548
549 if( git_index_has_conflicts( index ) )
550 {
551 // Abort the rebase if there are conflicts because we need to merge manually
552 git_rebase_abort( rebase );
553 AddErrorString( _( "Conflicts detected during rebase" ) );
554 return PullResult::MergeFailed;
555 }
556
557 git_oid commit_id;
558 git_signature* committer = nullptr;
559
560 if( git_signature_default( &committer, GetRepo() ) )
561 {
562 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to create signature: %s",
564 return PullResult::Error;
565 }
566
567 KIGIT::GitSignaturePtr committerPtr( committer );
568
569 if( git_rebase_commit( &commit_id, rebase, nullptr, committer, nullptr, nullptr ) != GIT_OK )
570 {
571 wxString errorMsg = KIGIT_COMMON::GetLastGitError();
572 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to commit operation: %s", errorMsg );
573 git_rebase_abort( rebase );
574 return PullResult::Error;
575 }
576 }
577
578 // Finish the rebase
579 if( git_rebase_finish( rebase, nullptr ) )
580 {
581 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to finish rebase: %s",
583 return PullResult::Error;
584 }
585
586 wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Rebase completed successfully" );
587 git_repository_state_cleanup( GetRepo() );
588 return PullResult::Success;
589}
590
591
592void GIT_PULL_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
593{
594
595}
bool PerformFetch(bool aSkipLock=false)
std::string getFirstLineFromCommitMessage(const std::string &aMessage)
PullResult handleRebase(const git_annotated_commit **aMergeHeads, size_t aMergeHeadsCount)
PullResult handleFastForward()
PullResult handleMerge(const git_annotated_commit **aMergeHeads, size_t aMergeHeadsCount)
const std::vector< std::pair< std::string, std::vector< CommitDetails > > > & GetFetchResults() const
GIT_PULL_HANDLER(KIGIT_COMMON *aCommon)
void UpdateProgress(int aCurrent, int aTotal, const wxString &aMessage) override
std::string getFormattedCommitDate(const git_time &aTime)
std::vector< std::pair< std::string, std::vector< CommitDetails > > > m_fetchResults
PullResult PerformPull()
static wxString GetLastGitError()
void SetCancelled(bool aCancel)
void AddErrorString(const wxString aErrorString)
git_repository * GetRepo() const
Get a pointer to the git repository.
unsigned & TestedTypes()
Return the connection types that have been tested for authentication.
KIGIT_COMMON * GetCommon() const
Get the common object.
wxString GetRemotename() const
Get the remote name.
void ResetNextKey()
Reset the next public key to test.
#define _(s)
PullResult
const wxChar *const traceGit
Flag to enable Git debugging output.
int fetchhead_foreach_cb(const char *, const char *, const git_oid *aOID, unsigned int aIsMerge, void *aPayload)
int progress_cb(const char *str, int len, void *aPayload)
int transfer_progress_cb(const git_transfer_progress *aStats, void *aPayload)
int credentials_cb(git_cred **aOut, const char *aUrl, const char *aUsername, unsigned int aAllowedTypes, void *aPayload)
std::unique_ptr< git_commit, decltype([](git_commit *aCommit) { git_commit_free(aCommit) GitCommitPtr
A unique pointer for git_commit objects with automatic cleanup.
std::unique_ptr< git_annotated_commit, decltype([](git_annotated_commit *aCommit) { git_annotated_commit_free(aCommit) GitAnnotatedCommitPtr
A unique pointer for git_annotated_commit objects with automatic cleanup.
std::unique_ptr< git_tree, decltype([](git_tree *aTree) { git_tree_free(aTree) GitTreePtr
A unique pointer for git_tree objects with automatic cleanup.
std::unique_ptr< git_index, decltype([](git_index *aIndex) { git_index_free(aIndex) GitIndexPtr
A unique pointer for git_index objects with automatic cleanup.
std::unique_ptr< git_signature, decltype([](git_signature *aSignature) { git_signature_free(aSignature) GitSignaturePtr
A unique pointer for git_signature objects with automatic cleanup.
std::unique_ptr< git_rebase, decltype([](git_rebase *aRebase) { git_rebase_free(aRebase) GitRebasePtr
A unique pointer for git_rebase objects with automatic cleanup.
std::unique_ptr< git_config, decltype([](git_config *aConfig) { git_config_free(aConfig) GitConfigPtr
A unique pointer for git_config objects with automatic cleanup.
std::unique_ptr< git_reference, decltype([](git_reference *aRef) { git_reference_free(aRef) GitReferencePtr
A unique pointer for git_reference objects with automatic cleanup.
std::unique_ptr< git_index_conflict_iterator, decltype([](git_index_conflict_iterator *aIterator) { git_index_conflict_iterator_free(aIterator) GitIndexConflictIteratorPtr
A unique pointer for git_index_conflict_iterator objects with automatic cleanup.
std::unique_ptr< git_revwalk, decltype([](git_revwalk *aWalker) { git_revwalk_free(aWalker) GitRevWalkPtr
A unique pointer for git_revwalk objects with automatic cleanup.
std::unique_ptr< git_remote, decltype([](git_remote *aRemote) { git_remote_free(aRemote) GitRemotePtr
A unique pointer for git_remote objects with automatic cleanup.
std::string m_sha
std::string m_date
std::string m_firstLine
std::string m_author
std::string their_status
std::string our_status
std::string filename
git_time_t their_commit_time
git_time_t our_commit_time
wxLogTrace helper definitions.