This is just part of the story, but it's a step: when we fetch the
remote, and notice that it's different from our local cached copy of
the git tree, we now update the local cache if the remote is more
recent (and if the remote is old, we report that).

I'll try to get the "push to remote" done too.

On an unrelated note, I had my EON Steel with the new firmware in the
pool with me today, and the new parsing code seems to work. I say
"seems" because it downloaded the pool time fine, but since I didn't
have a cylinder and tank transmitter, I obviously cannot say that it
actually got the cylinder pressures right. But at least it didn't get
them wrong ;)

                       Linus
From b1d94a5ef5a8a9d7ce6f3132832603c44f067fe3 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torva...@linux-foundation.org>
Date: Wed, 10 Jun 2015 13:37:22 -0700
Subject: [PATCH] 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 <torva...@linux-foundation.org>
---
 git-access.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 77 insertions(+), 2 deletions(-)

diff --git a/git-access.c b/git-access.c
index 580badd2b1d9..bf238a191983 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
-- 
2.4.2.337.gfae46aa

_______________________________________________
subsurface mailing list
subsurface@subsurface-divelog.org
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to