49 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformFetch() - No repository found" );
53 std::unique_lock<std::mutex> lock(
GetCommon()->m_gitActionMutex, std::try_to_lock );
55 if( !aSkipLock && !lock.owns_lock() )
57 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformFetch() - Could not lock mutex" );
62 git_remote* remote =
nullptr;
64 if( git_remote_lookup( &remote,
GetRepo(),
"origin" ) != 0 )
66 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformFetch() - Failed to lookup remote 'origin'" );
67 AddErrorString( wxString::Format(
_(
"Could not lookup remote '%s'" ),
"origin" ) );
73 git_remote_callbacks remoteCallbacks;
74 git_remote_init_callbacks( &remoteCallbacks, GIT_REMOTE_CALLBACKS_VERSION );
78 remoteCallbacks.payload =
this;
84 if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks,
nullptr,
nullptr ) )
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 ) );
92 git_fetch_options fetchOptions;
93 git_fetch_init_options( &fetchOptions, GIT_FETCH_OPTIONS_VERSION );
94 fetchOptions.callbacks = remoteCallbacks;
96 if( git_remote_fetch( remote,
nullptr, &fetchOptions,
nullptr ) )
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 ) );
104 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformFetch() - Fetch completed successfully" );
112 std::unique_lock<std::mutex> lock(
GetCommon()->m_gitActionMutex, std::try_to_lock );
114 if( !lock.owns_lock() )
116 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Could not lock mutex" );
117 return PullResult::Error;
121 return PullResult::Error;
123 git_oid pull_merge_oid = {};
129 return PullResult::Error;
132 git_annotated_commit* fetchhead_commit;
134 if( git_annotated_commit_lookup( &fetchhead_commit,
GetRepo(), &pull_merge_oid ) )
137 return PullResult::Error;
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;
145 if( git_merge_analysis( &merge_analysis, &merge_preference,
GetRepo(), merge_commits, 1 ) )
148 return PullResult::Error;
151 if( merge_analysis & GIT_MERGE_ANALYSIS_UNBORN )
154 return PullResult::MergeFailed;
158 if( merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE )
160 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Repository is up to date" );
161 git_repository_state_cleanup(
GetRepo() );
162 return PullResult::UpToDate;
166 if( merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD )
168 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Fast-forward merge" );
172 if( merge_analysis & GIT_MERGE_ANALYSIS_NORMAL )
174 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Normal merge" );
177 git_config*
config =
nullptr;
181 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Failed to get repository config" );
183 return PullResult::Error;
189 int rebase_value = 0;
190 int ret = git_config_get_bool( &rebase_value,
config,
"pull.rebase" );
193 if( ret == GIT_OK && rebase_value )
195 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Using rebase based on config" );
199 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Using merge based on config" );
203 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::PerformPull() - Merge needs resolution" );
209const std::vector<std::pair<std::string, std::vector<CommitDetails>>>&
218 if( aMessage.empty() )
221 size_t firstLineEnd = aMessage.find_first_of(
'\n' );
223 if( firstLineEnd != std::string::npos )
224 return aMessage.substr( 0, firstLineEnd );
233 time_t time =
static_cast<time_t
>( aTime.time );
236 localtime_s( &timeInfo, &time );
238 gmtime_r( &time, &timeInfo );
240 strftime( dateBuffer,
sizeof( dateBuffer ),
"%Y-%b-%d %H:%M:%S", &timeInfo );
247 git_reference* rawRef =
nullptr;
250 if( git_repository_head( &rawRef,
GetRepo() ) )
253 return PullResult::Error;
258 git_oid updatedRefOid;
259 const char* currentBranchName = git_reference_name( rawRef );
260 const char* branch_shorthand = git_reference_shorthand( rawRef );
262 wxString remoteBranchName = wxString::Format(
"refs/remotes/%s/%s",
263 remote_name, branch_shorthand );
266 if( git_reference_name_to_id( &updatedRefOid,
GetRepo(), remoteBranchName.c_str() ) != GIT_OK )
268 AddErrorString( wxString::Format(
_(
"Could not get reference OID for reference '%s'" ),
269 remoteBranchName ) );
270 return PullResult::Error;
274 git_commit* targetCommit =
nullptr;
276 if( git_commit_lookup( &targetCommit,
GetRepo(), &updatedRefOid ) != GIT_OK )
279 return PullResult::Error;
285 git_tree* targetTree =
nullptr;
287 if( git_commit_tree( &targetTree, targetCommit ) != GIT_OK )
289 git_commit_free( targetCommit );
291 return PullResult::Error;
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
304 case GIT_CHECKOUT_NOTIFY_CONFLICT:
307 case GIT_CHECKOUT_NOTIFY_DIRTY:
310 case GIT_CHECKOUT_NOTIFY_UPDATED:
313 case GIT_CHECKOUT_NOTIFY_UNTRACKED:
316 case GIT_CHECKOUT_NOTIFY_IGNORED:
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;
330 if( git_checkout_tree(
GetRepo(),
reinterpret_cast<git_object*
>( targetTree ), &checkoutOptions ) != GIT_OK )
333 return PullResult::Error;
336 git_reference* updatedRef =
nullptr;
339 if (git_reference_set_target(&updatedRef, rawRef, &updatedRefOid,
nullptr) != GIT_OK)
341 AddErrorString( wxString::Format(
_(
"Failed to update reference '%s' to point to '%s'" ), currentBranchName,
342 git_oid_tostr_s( &updatedRefOid ) ) );
343 return PullResult::Error;
349 if( git_repository_state_cleanup(
GetRepo() ) != GIT_OK )
351 AddErrorString(
_(
"Failed to clean up repository state after fast-forward." ) );
352 return PullResult::Error;
355 git_revwalk* revWalker =
nullptr;
358 if( git_revwalk_new( &revWalker,
GetRepo() ) != GIT_OK )
361 return PullResult::Error;
365 git_revwalk_sorting( revWalker, GIT_SORT_TIME );
367 if( git_revwalk_push_glob( revWalker, currentBranchName ) != GIT_OK )
370 return PullResult::Error;
373 std::pair<std::string, std::vector<CommitDetails>>& branchCommits =
m_fetchResults.emplace_back();
374 branchCommits.first = currentBranchName;
378 while( git_revwalk_next( &commitOid, revWalker ) == GIT_OK )
380 git_commit* commit =
nullptr;
382 if( git_commit_lookup( &commit,
GetRepo(), &commitOid ) )
385 git_oid_tostr_s( &commitOid ) ) );
386 return PullResult::Error;
392 details.
m_sha = git_oid_tostr_s( &commitOid );
394 details.
m_author = git_commit_author( commit )->name;
397 branchCommits.second.push_back( details );
400 return PullResult::FastForward;
405 size_t aMergeHeadsCount )
407 git_merge_options merge_opts;
408 git_merge_options_init( &merge_opts, GIT_MERGE_OPTIONS_VERSION );
410 git_checkout_options checkout_opts;
411 git_checkout_init_options( &checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION );
413 checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
415 if( git_merge(
GetRepo(), aMergeHeads, aMergeHeadsCount, &merge_opts, &checkout_opts ) )
418 return PullResult::Error;
422 git_index* index =
nullptr;
424 if( git_repository_index( &index,
GetRepo() ) )
427 return PullResult::Error;
433 git_index_conflict_iterator* conflicts =
nullptr;
435 if( git_index_conflict_iterator_new( &conflicts, index ) )
438 return PullResult::Error;
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;
448 while( git_index_conflict_next( &ancestor, &our, &their, conflicts ) == 0 )
451 if( ancestor && our && their )
454 conflict_datum.
filename = our->path;
455 conflict_datum.
our_oid = our->id;
463 conflict_data.push_back( conflict_datum );
466 else if( !ancestor && our && their )
469 conflict_datum.
filename = our->path;
470 conflict_datum.
our_oid = our->id;
478 conflict_data.push_back( conflict_datum );
481 else if( their && !our )
484 git_index_add( index, their );
487 else if( our && !their )
490 git_index_add( index, our );
494 wxLogError( wxS(
"Unexpected conflict state" ) );
498 if( conflict_data.empty() )
500 git_index_conflict_cleanup( index );
501 git_index_write( index );
504 return conflict_data.empty() ? PullResult::Success : PullResult::MergeFailed;
511 git_reference* head_ref =
nullptr;
513 if( git_repository_head( &head_ref,
GetRepo() ) )
516 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to get HEAD: %s", errorMsg );
517 return PullResult::Error;
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;
527 if( git_rebase_init( &rebase,
GetRepo(),
nullptr,
nullptr, aMergeHeads[0], &rebase_opts ) )
530 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to initialize rebase: %s", errorMsg );
531 return PullResult::Error;
535 git_rebase_operation* operation =
nullptr;
537 while( git_rebase_next( &operation, rebase ) != GIT_ITEROVER )
540 git_index* index =
nullptr;
541 if( git_repository_index( &index,
GetRepo() ) )
543 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to get index: %s",
545 return PullResult::Error;
549 if( git_index_has_conflicts( index ) )
552 git_rebase_abort( rebase );
554 return PullResult::MergeFailed;
558 git_signature* committer =
nullptr;
560 if( git_signature_default( &committer,
GetRepo() ) )
562 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to create signature: %s",
564 return PullResult::Error;
569 if( git_rebase_commit( &commit_id, rebase,
nullptr, committer,
nullptr,
nullptr ) != GIT_OK )
572 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to commit operation: %s", errorMsg );
573 git_rebase_abort( rebase );
574 return PullResult::Error;
579 if( git_rebase_finish( rebase,
nullptr ) )
581 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Failed to finish rebase: %s",
583 return PullResult::Error;
586 wxLogTrace(
traceGit,
"GIT_PULL_HANDLER::handleRebase() - Rebase completed successfully" );
587 git_repository_state_cleanup(
GetRepo() );
588 return PullResult::Success;
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
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.
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.
git_time_t their_commit_time
git_time_t our_commit_time
wxLogTrace helper definitions.