summaryrefslogtreecommitdiffstats
path: root/git-access.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2015-06-10 13:37:22 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2015-06-10 18:13:25 -0700
commit1379f3479278b4a7c6de1ada9e21161d2117c210 (patch)
tree75298c05b98ad6820543880b4fd5b6a51a59ef97 /git-access.c
parentec8ba5f830efc01f2db627e82e34c353c0dd13f7 (diff)
downloadsubsurface-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.c79
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