diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-10 13:37:22 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2015-06-10 18:13:25 -0700 |
commit | 1379f3479278b4a7c6de1ada9e21161d2117c210 (patch) | |
tree | 75298c05b98ad6820543880b4fd5b6a51a59ef97 /git-access.c | |
parent | ec8ba5f830efc01f2db627e82e34c353c0dd13f7 (diff) | |
download | subsurface-1379f3479278b4a7c6de1ada9e21161d2117c210.tar.gz |
git storage: actually update cached copy from remote
We used to fetch the remote information but not actually do anything
about it, except report when it wasn't up-to-date.
Now we actually update the local cached copy if the remote has changed.
The code does not try to actually merge things, so only fast-forward
updates are done, but that should be the normal case. We might
eventually do some simple merging on our own, but I suspect manual
merging may be the safer option.
We don't currently ever update the remote repository, and only inform
users that our local repository is ahead of the remote. Fixing that is
the next step.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'git-access.c')
-rw-r--r-- | git-access.c | 79 |
1 files changed, 77 insertions, 2 deletions
diff --git a/git-access.c b/git-access.c index 580badd2b..bf238a191 100644 --- a/git-access.c +++ b/git-access.c @@ -63,11 +63,86 @@ static int check_clean(const char *path, unsigned int status, void *payload) return 1; } -static int try_to_update(git_repository *rep, git_reference *local, git_reference *remote) +/* + * The remote is strictly newer than the local branch. + */ +static int reset_to_remote(git_repository *repo, git_reference *local, const git_oid *new_id) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_object *target; + + // If it's not checked out (bare or not HEAD), just update the reference */ + if (git_repository_is_bare(repo) || git_branch_is_head(local) != 1) { + git_reference *out; + + if (git_reference_set_target(&out, local, new_id, "Update to remote")) + return report_error("Could not update local ref to newer remote ref"); + + git_reference_free(out); + + // Not really an error, just informational + report_error("Updated local branch from remote"); + + return 0; + } + + if (git_object_lookup(&target, repo, new_id, GIT_OBJ_COMMIT)) + return report_error("Could not look up remote commit"); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE; + if (git_reset(repo, target, GIT_RESET_HARD, &opts)) + return report_error("Local head checkout failed after update"); + + // Not really an error, just informational + report_error("Updated local information from remote"); + + return 0; +} + +static int try_to_update(git_repository *repo, git_reference *local, git_reference *remote) { + git_oid base; + const git_oid *local_id, *remote_id; + if (!git_reference_cmp(local, remote)) return 0; - return report_error("Local and remote do not match, not updating"); + + local_id = git_reference_target(local); + remote_id = git_reference_target(remote); + + if (!local_id || !remote_id) + return report_error("Unable to get local or remote SHA1"); + + if (git_merge_base(&base, repo, local_id, remote_id)) + return report_error("Unable to find common commit of local and remote branches"); + + /* Is the remote strictly newer? Use it */ + if (git_oid_equal(&base, local_id)) + return reset_to_remote(repo, local, remote_id); + + /* Is the local repo the more recent one? We're done */ + if (git_oid_equal(&base, remote_id)) { + report_error("Local cache more recent than remote"); + return 0; + } + + /* Merging a bare repository always needs user action */ + if (git_repository_is_bare(repo)) + return report_error("Local and remote have diverged, merge of bare branch needed"); + + /* Merging will definitely need the head branch too */ + if (git_branch_is_head(local) != 1) + return report_error("Local and remote do not match, local branch not HEAD - cannot update"); + + /* + * Some day we migth try a clean merge here. + * + * But I couldn't find any good examples of this, so for now + * you'd need to merge divergent histories manually. But we've + * at least verified above that we have a working tree and the + * current branch is checked out, so we *could* try to merge. + */ + return report_error("Local and remote have diverged, need to merge"); } #if USE_LIBGIT23_API |