summaryrefslogtreecommitdiffstats
path: root/save-git.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2014-03-19 13:28:47 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2014-03-19 14:32:27 -0700
commita29c4ddba32b59bfffaf08b5b8160f80e2e7dc2b (patch)
tree0f37ade98215cf203f6086c6193538fed21b850c /save-git.c
parentee2e43f11aedf615df0f21a8fc32e8fe6bc9782c (diff)
downloadsubsurface-a29c4ddba32b59bfffaf08b5b8160f80e2e7dc2b.tar.gz
git object store: make it possible to work with checked-out git branches
This makes the git object save logic also check out the changes in the working tree and index if the branch we save to is checked out. It used to be that we would just update the object store (and the branch ref, of course), but leave any checked-out state untouched. Note that if the working directory is dirty (ie you have made changes by hand and not committed them), the checkout will skip any dirty files and report it as a warning to the user. However, the save still succeeds (since the _real_ save goes to the backing store). NOTE NOTE NOTE! Both loading and saving very fundamentally work on the git object store level, and if you are working with a checked-out branch and make modifications to the working tree, saving will not touch those dirty files (so that you can try to recover your edits manually in the working tree), but it's worth pointing out that subsufrace loading state will totally ignore the working tree. So the only way to make subsurface *see* your changes is to commit them. Having edited state checked out in the working tree will only confuse you when subsurface first ignores it on reading, and then refuses to touch the checked-out state on writing. Put another way: working with a checked-out branch is now _possible_, but you need to be aware of the limitations. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'save-git.c')
-rw-r--r--save-git.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/save-git.c b/save-git.c
index 74eab55bb..1488b43bb 100644
--- a/save-git.c
+++ b/save-git.c
@@ -776,6 +776,38 @@ static git_object *try_to_find_parent(const char *hex_id, git_repository *repo)
return (git_object *)commit;
}
+static int notify_cb(git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ report_error("File '%s' does not match in working tree", path);
+ return 0; /* Continue with checkout */
+}
+
+static git_tree *get_git_tree(git_repository *repo, git_object *parent)
+{
+ git_tree *tree;
+ if (!parent)
+ return NULL;
+ if (git_tree_lookup(&tree, repo, git_commit_tree_id((const git_commit *) parent)))
+ return NULL;
+ return tree;
+}
+
+static int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_DIRTY;
+ opts.notify_cb = notify_cb;
+ opts.baseline = get_git_tree(repo, parent);
+ return git_checkout_tree(repo, (git_object *) tree, &opts);
+}
+
/*
* libgit2 revision 0.20 and earlier do not have the signature and
* message log arguments.
@@ -849,6 +881,22 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid *
if (git_branch_create(&ref, repo, branch, commit, 0, author, "Create branch"))
return report_error("Failed to create branch '%s'", branch);
}
+ /*
+ * If it's a checked-out branch, try to also update the working
+ * tree and index. If that fails (dirty working tree or whatever),
+ * this is not technically a save error (we did save things in
+ * the object database), but it can cause extreme confusion, so
+ * warn about it.
+ */
+ if (git_branch_is_head(ref) && !git_repository_is_bare(repo)) {
+ if (update_git_checkout(repo, parent, tree)) {
+ const git_error *err = giterr_last();
+ const char *errstr = err ? err->message : strerror(errno);
+ report_error("Git branch '%s' is checked out, but worktree is dirty (%s)",
+ branch, errstr);
+ }
+ }
+
if (git_reference_set_target(&ref, ref, &commit_id, author, "Subsurface save event"))
return report_error("Failed to update branch '%s'", branch);
set_git_id(&commit_id);