summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2014-03-13 15:42:45 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2014-03-13 20:48:31 -0700
commit3fe0019bc2939aea4e89cbb8bc5aeb5edeb456d9 (patch)
treecbd3b2b6fa7f2b31977256f1e909920ebe916840
parent0b114a198eb3a648c507bc801a0343bf04dc0191 (diff)
downloadsubsurface-3fe0019bc2939aea4e89cbb8bc5aeb5edeb456d9.tar.gz
git object format: make sure parenthood isn't lost when saving
This makes subsurface remember the git source commit of the dive data. If you save to an existing branch, subsurface will now complain and refuse to save if you try to save if the existing branch is not related to the original source. That would destroy the history of the dive data, which in turn would make it impossible to do sane merging of the data. If you save to a new branch, it will see if the previous parent commit is known in the repository you are saving to, and will save parenthood information if so. Otherwise it will save it as a new parentless commit ("root commit" in git parlance). Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.h4
-rw-r--r--load-git.c30
-rw-r--r--qt-ui/mainwindow.cpp1
-rw-r--r--save-git.c53
-rw-r--r--save-xml.c5
5 files changed, 75 insertions, 18 deletions
diff --git a/dive.h b/dive.h
index 5ac43f16d..41b467d29 100644
--- a/dive.h
+++ b/dive.h
@@ -686,10 +686,14 @@ extern void save_dives_logic(const char *filename, bool select_only);
extern void save_dive(FILE *f, struct dive *dive);
extern void export_dives_uddf(const char *filename, const bool selected);
+struct git_oid;
struct git_repository;
extern struct git_repository *is_git_repository(const char *filename, const char **branchp);
extern int git_save_dives(struct git_repository *, const char *, bool select_only);
extern int git_load_dives(struct git_repository *, const char *);
+extern const char *saved_git_id;
+extern void clear_git_id(void);
+extern void set_git_id(const struct git_oid *);
extern int subsurface_rename(const char *path, const char *newpath);
extern int subsurface_open(const char *path, int oflags, mode_t mode);
diff --git a/load-git.c b/load-git.c
index c268cf1ef..12bf66141 100644
--- a/load-git.c
+++ b/load-git.c
@@ -14,6 +14,8 @@
#include "device.h"
#include "membuffer.h"
+const char *saved_git_id = NULL;
+
struct keyword_action {
const char *keyword;
void (*fn)(char *, struct membuffer *, void *);
@@ -1198,11 +1200,25 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree)
return 0;
}
+void clear_git_id(void)
+{
+ saved_git_id = NULL;
+}
+
+void set_git_id(const struct git_oid * id)
+{
+ static char git_id_buffer[GIT_OID_HEXSZ+1];
+
+ git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), id);
+ saved_git_id = git_id_buffer;
+}
+
static int do_git_load(git_repository *repo, const char *branch)
{
int ret;
git_reference *ref;
- git_object *tree;
+ git_commit *commit;
+ git_tree *tree;
ret = git_branch_lookup(&ref, repo, branch, GIT_BRANCH_LOCAL);
if (ret) {
@@ -1210,10 +1226,14 @@ static int do_git_load(git_repository *repo, const char *branch)
if (ret)
return report_error("Unable to look up branch '%s'", branch);
}
- if (git_reference_peel(&tree, ref, GIT_OBJ_TREE))
- return report_error("Could not look up tree of branch '%s'", branch);
- ret = load_dives_from_tree(repo, (git_tree *) tree);
- git_object_free(tree);
+ if (git_reference_peel((git_object **)&commit, ref, GIT_OBJ_COMMIT))
+ return report_error("Could not look up commit of branch '%s'", branch);
+ if (git_commit_tree(&tree, commit))
+ return report_error("Could not look up tree of commit in branch '%s'", branch);
+ ret = load_dives_from_tree(repo, tree);
+ if (!ret)
+ set_git_id(git_commit_id(commit));
+ git_object_free((git_object *)tree);
return ret;
}
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 4d4d0baa2..b67de1588 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -214,6 +214,7 @@ void MainWindow::on_actionClose_triggered()
ui.newProfile->setEmptyState();
/* free the dives and trips */
+ clear_git_id();
while (dive_table.nr)
delete_single_dive(0);
diff --git a/save-git.c b/save-git.c
index 4760fe32a..ab00ba951 100644
--- a/save-git.c
+++ b/save-git.c
@@ -741,6 +741,23 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o
}
/*
+ * See if we can find the parent ID that the git data came from
+ */
+static git_object *try_to_find_parent(const char *hex_id, git_repository *repo)
+{
+ git_oid object_id;
+ git_commit *commit;
+
+ if (!hex_id)
+ return NULL;
+ if (git_oid_fromstr(&object_id, hex_id))
+ return NULL;
+ if (git_commit_lookup(&commit, repo, &object_id))
+ return NULL;
+ return (git_object *)commit;
+}
+
+/*
* libgit2 revision 0.20 and earlier do not have the signature and
* message log arguments.
*/
@@ -769,15 +786,18 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid *
case GIT_EINVALIDSPEC:
return report_error("Invalid branch name '%s'", branch);
case GIT_ENOTFOUND: /* We'll happily create it */
- ref = NULL; parent = NULL;
+ ref = NULL;
+ parent = try_to_find_parent(saved_git_id, repo);
break;
case 0:
if (git_reference_peel(&parent, ref, GIT_OBJ_COMMIT))
return report_error("Unable to look up parent in branch '%s'", branch);
- /* If the parent commit has the same tree ID, do nothing */
- if (git_oid_equal(tree_id, git_commit_tree_id((const git_commit *) parent)))
- return 0;
+ if (saved_git_id) {
+ const git_oid *id = git_commit_id((const git_commit *) parent);
+ if (git_oid_strcmp(id, saved_git_id))
+ return report_error("The git branch does not match the git parent of the source");
+ }
/* all good */
break;
@@ -790,12 +810,21 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid *
if (git_signature_now(&author, "Subsurface", "subsurface@hohndel.org"))
return report_error("No user name configuration in git repo");
- put_format(&commit_msg, "Created by subsurface %s\n", VERSION_STRING);
- if (git_commit_create_v(&commit_id, repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent))
- return report_error("Git commit create failed (%s)", strerror(errno));
-
- if (git_commit_lookup(&commit, repo, &commit_id))
- return report_error("Could not look up newly created commit");
+ /* If the parent commit has the same tree ID, do not create a new commit */
+ if (parent && git_oid_equal(tree_id, git_commit_tree_id((const git_commit *) parent))) {
+ /* If the parent already came from the ref, the commit is already there */
+ if (ref)
+ return 0;
+ /* Else we do want to create the new branch, but with the old commit */
+ commit = (git_commit *) parent;
+ } else {
+ put_format(&commit_msg, "Created by subsurface %s\n", VERSION_STRING);
+ if (git_commit_create_v(&commit_id, repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent))
+ return report_error("Git commit create failed (%s)", strerror(errno));
+
+ if (git_commit_lookup(&commit, repo, &commit_id))
+ return report_error("Could not look up newly created commit");
+ }
if (!ref) {
if (git_branch_create(&ref, repo, branch, commit, 0, author, "Create branch"))
@@ -803,6 +832,7 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid *
}
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);
return 0;
}
@@ -850,8 +880,7 @@ static int do_git_save(git_repository *repo, const char *branch, bool select_onl
return report_error("git tree write failed");
/* And save the tree! */
- create_new_commit(repo, branch, &id);
- return 0;
+ return create_new_commit(repo, branch, &id);
}
/*
diff --git a/save-xml.c b/save-xml.c
index a27dcfa76..ef7396bf9 100644
--- a/save-xml.c
+++ b/save-xml.c
@@ -601,8 +601,11 @@ void save_dives_logic(const char *filename, const bool select_only)
const char *branch;
git = is_git_repository(filename, &branch);
- if (git && !git_save_dives(git, branch, select_only))
+ if (git) {
+ /* error returns, anybody? */
+ git_save_dives(git, branch, select_only);
return;
+ }
try_to_backup(filename);