From fe0864be601064e1662802eb520f6c5e61f6a87c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 20 Aug 2015 22:23:12 -0700 Subject: Cloud storage: merge local and remote changes if there is no conflict We still punt if there are any conflicts (which are likely to occur if we have touched dive sites in both changes). But in my testing at least for fairly simple, non-conflicting changes this works and creates a correctly merged tree. Signed-off-by: Dirk Hohndel --- git-access.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index c6a1648b1..ff00b85a3 100644 --- a/git-access.c +++ b/git-access.c @@ -214,16 +214,41 @@ static int try_to_update(git_repository *repo, git_remote *origin, git_reference 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 and clean, so we *could* try - * to merge. - */ - return report_error("Local and remote have diverged, need to merge"); + /* Ok, let's try to merge these */ + git_tree *local_tree, *remote_tree, *base_tree; + git_commit *local_commit, *remote_commit, *base_commit; + git_index *merged_index; + if (git_commit_lookup(&local_commit, repo, local_id)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); + if (git_commit_tree(&local_tree, local_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed local tree lookup (%s)"), giterr_last()->message); + if (git_commit_lookup(&remote_commit, repo, remote_id)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); + if (git_commit_tree(&remote_tree, remote_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed remote tree lookup (%s)"), giterr_last()->message); + if (git_commit_lookup(&base_commit, repo, &base)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message); + if (git_commit_tree(&base_tree, base_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message); + if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, 0)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); + if (git_index_has_conflicts(merged_index)) { + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); + } else { + git_oid merge_oid, commit_oid; + git_tree *merged_tree; + git_signature *author; + + if (git_index_write_tree_to(&merge_oid, merged_index, repo)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); + if (git_tree_lookup(&merged_tree, repo, &merge_oid)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); + if (git_signature_default(&author, repo) < 0) + return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); + if (git_commit_create_v(&commit_oid, repo, "HEAD", author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); + } + return 0; } static int check_remote_status(git_repository *repo, git_remote *origin, const char *branch, enum remote_transport rt) -- cgit v1.2.3-70-g09d2 From b8575221b19bf0a77319318a505a27c00e33e375 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 22 Aug 2015 16:19:17 -0700 Subject: Cloud storage: use merge options to avoid conflicts In many cases Subsurface will do something "reasonable" if we have conflicting edits and then try to merge. GIT_MERGE_FILE_FAVOR_UNION means that both edits will be added to the final file and then Subsurface should quietly take one or the other - this will need quite a bit of testing. Signed-off-by: Dirk Hohndel --- git-access.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index ff00b85a3..3602b80f0 100644 --- a/git-access.c +++ b/git-access.c @@ -218,6 +218,10 @@ static int try_to_update(git_repository *repo, git_remote *origin, git_reference git_tree *local_tree, *remote_tree, *base_tree; git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; + git_merge_options merge_options; + git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); + merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; + merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; if (git_commit_lookup(&local_commit, repo, local_id)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); if (git_commit_tree(&local_tree, local_commit)) @@ -230,7 +234,7 @@ static int try_to_update(git_repository *repo, git_remote *origin, git_reference return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message); if (git_commit_tree(&base_tree, base_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message); - if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, 0)) + if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); if (git_index_has_conflicts(merged_index)) { return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); -- cgit v1.2.3-70-g09d2 From 4c31c0113901fd3cbad8ab5c66efbc221d49d378 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 11:29:49 -0700 Subject: Cloud storage: better error message for dirty state Tell us WHAT is wrong with the state. Signed-off-by: Dirk Hohndel --- git-access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 3602b80f0..08000ed88 100644 --- a/git-access.c +++ b/git-access.c @@ -69,7 +69,7 @@ static int check_clean(const char *path, unsigned int status, void *payload) status &= ~GIT_STATUS_CURRENT | GIT_STATUS_IGNORED; if (!status) return 0; - report_error("WARNING: Git cache directory modified (path %s)", path); + report_error("WARNING: Git cache directory modified (path %s) status %0x", path, status); return 1; } -- cgit v1.2.3-70-g09d2 From f5eb0e2bbb6224b69bcf95b4d89d443c576808ad Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 11:32:14 -0700 Subject: Cloud storage: move git merge into its own function Just to keep things more readable. Signed-off-by: Dirk Hohndel --- git-access.c | 84 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 38 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 08000ed88..07eb76a68 100644 --- a/git-access.c +++ b/git-access.c @@ -176,6 +176,51 @@ static int update_remote(git_repository *repo, git_remote *origin, git_reference return 0; } +extern int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree); + +static int try_to_git_merge(git_repository *repo, git_reference *local, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id) +{ + git_tree *local_tree, *remote_tree, *base_tree; + git_commit *local_commit, *remote_commit, *base_commit; + git_index *merged_index; + git_merge_options merge_options; + git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); + merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; + merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; + if (git_commit_lookup(&local_commit, repo, local_id)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); + if (git_commit_tree(&local_tree, local_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed local tree lookup (%s)"), giterr_last()->message); + if (git_commit_lookup(&remote_commit, repo, remote_id)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); + if (git_commit_tree(&remote_tree, remote_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed remote tree lookup (%s)"), giterr_last()->message); + if (git_commit_lookup(&base_commit, repo, base)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message); + if (git_commit_tree(&base_tree, base_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message); + if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); + if (git_index_has_conflicts(merged_index)) { + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); + } else { + git_oid merge_oid, commit_oid; + git_tree *merged_tree; + git_signature *author; + git_commit *commit; + + if (git_index_write_tree_to(&merge_oid, merged_index, repo)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); + if (git_tree_lookup(&merged_tree, repo, &merge_oid)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); + if (git_signature_default(&author, repo) < 0) + return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); + if (git_commit_create_v(&commit_oid, repo, "HEAD", author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); + } + return 0; +} + static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt) { git_oid base; @@ -215,44 +260,7 @@ static int try_to_update(git_repository *repo, git_remote *origin, git_reference return report_error("Local and remote do not match, local branch not HEAD - cannot update"); /* Ok, let's try to merge these */ - git_tree *local_tree, *remote_tree, *base_tree; - git_commit *local_commit, *remote_commit, *base_commit; - git_index *merged_index; - git_merge_options merge_options; - git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); - merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; - merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; - if (git_commit_lookup(&local_commit, repo, local_id)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); - if (git_commit_tree(&local_tree, local_commit)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed local tree lookup (%s)"), giterr_last()->message); - if (git_commit_lookup(&remote_commit, repo, remote_id)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); - if (git_commit_tree(&remote_tree, remote_commit)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed remote tree lookup (%s)"), giterr_last()->message); - if (git_commit_lookup(&base_commit, repo, &base)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message); - if (git_commit_tree(&base_tree, base_commit)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message); - if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); - if (git_index_has_conflicts(merged_index)) { - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); - } else { - git_oid merge_oid, commit_oid; - git_tree *merged_tree; - git_signature *author; - - if (git_index_write_tree_to(&merge_oid, merged_index, repo)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); - if (git_tree_lookup(&merged_tree, repo, &merge_oid)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); - if (git_signature_default(&author, repo) < 0) - return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); - if (git_commit_create_v(&commit_oid, repo, "HEAD", author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); - } - return 0; + return try_to_git_merge(repo, local, remote, &base, local_id, remote_id); } static int check_remote_status(git_repository *repo, git_remote *origin, const char *branch, enum remote_transport rt) -- cgit v1.2.3-70-g09d2 From 471af6c2fd63fa6139682aade26aa5039df911c0 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 11:33:15 -0700 Subject: Cloud storage: clean up after the merge Make sure the branch is pointing at the merge commit, etc. Signed-off-by: Dirk Hohndel --- git-access.c | 13 +++++++++++++ save-git.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 07eb76a68..2b44cb6b3 100644 --- a/git-access.c +++ b/git-access.c @@ -217,6 +217,19 @@ static int try_to_git_merge(git_repository *repo, git_reference *local, git_refe return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); if (git_commit_create_v(&commit_oid, repo, "HEAD", author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); + if (git_commit_lookup(&commit, repo, &commit_oid)) + return report_error(translate("gettextFromC", "Error: could not lookup the merge commit I just created (%s)"), giterr_last()->message); + if (git_branch_is_head(local) && !git_repository_is_bare(repo)) { + git_object *parent; + git_reference_peel(&parent, local, GIT_OBJ_COMMIT); + if (update_git_checkout(repo, parent, merged_tree)) { + report_error("Warning: checked out branch is inconsistent with git data"); + } + } + if (git_reference_set_target(&local, local, &commit_oid, "Subsurface merge event")) + return report_error("Error: failed to update branch (%s)", giterr_last()->message); + set_git_id(&commit_oid); + git_signature_free(author); } return 0; } diff --git a/save-git.c b/save-git.c index 9ae1d572e..3cd8a8a2c 100644 --- a/save-git.c +++ b/save-git.c @@ -997,7 +997,7 @@ static git_tree *get_git_tree(git_repository *repo, git_object *parent) return tree; } -static int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree) +int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; -- cgit v1.2.3-70-g09d2 From a45c5f1acf728d28449f00d6ed37d9449c9e0ee1 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 18:14:27 -0700 Subject: When building against libgit2 v0.23 or newer we can assume API23 Signed-off-by: Dirk Hohndel --- git-access.c | 2 +- save-git.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 2b44cb6b3..b98b373a1 100644 --- a/git-access.c +++ b/git-access.c @@ -40,7 +40,7 @@ /* * api break introduced in libgit2 master after 0.22 - let's guess this is the v0.23 API */ -#if USE_LIBGIT23_API +#if USE_LIBGIT23_API || (!LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR >= 23) #define git_branch_create(out, repo, branch_name, target, force, signature, log_message) \ git_branch_create(out, repo, branch_name, target, force) #endif diff --git a/save-git.c b/save-git.c index 3cd8a8a2c..9e6819322 100644 --- a/save-git.c +++ b/save-git.c @@ -40,7 +40,7 @@ /* * api break introduced in libgit2 master after 0.22 - let's guess this is the v0.23 API */ -#if USE_LIBGIT23_API +#if USE_LIBGIT23_API || (!LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR >= 23) #define git_branch_create(out, repo, branch_name, target, force, signature, log_message) \ git_branch_create(out, repo, branch_name, target, force) #define git_reference_set_target(out, ref, id, author, log_message) \ -- cgit v1.2.3-70-g09d2 From 70c38de3a1880b5920fd799b0059518a79ca2acd Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 18:15:33 -0700 Subject: Cloud storage: be very strict about renames Because of the structure of some of our files git too easily assumed that they were renames and that confused the merge algorithm. Signed-off-by: Dirk Hohndel --- git-access.c | 1 + 1 file changed, 1 insertion(+) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index b98b373a1..47973d577 100644 --- a/git-access.c +++ b/git-access.c @@ -187,6 +187,7 @@ static int try_to_git_merge(git_repository *repo, git_reference *local, git_refe git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; + merge_options.rename_threshold = 100; if (git_commit_lookup(&local_commit, repo, local_id)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); if (git_commit_tree(&local_tree, local_commit)) -- cgit v1.2.3-70-g09d2 From f177b2ec533dea8b61648a856ac8e77bf6407a48 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 18:16:59 -0700 Subject: Cloud storage: try to brute force your way around merge issues This seems to do the right thing in several cases that I tested, but I'm worried if it might end up causing us data loss in other cases. This needs a TON of testing. Signed-off-by: Dirk Hohndel --- git-access.c | 70 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 27 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 47973d577..879330a6a 100644 --- a/git-access.c +++ b/git-access.c @@ -203,35 +203,51 @@ static int try_to_git_merge(git_repository *repo, git_reference *local, git_refe if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); if (git_index_has_conflicts(merged_index)) { - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); - } else { - git_oid merge_oid, commit_oid; - git_tree *merged_tree; - git_signature *author; - git_commit *commit; - - if (git_index_write_tree_to(&merge_oid, merged_index, repo)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); - if (git_tree_lookup(&merged_tree, repo, &merge_oid)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); - if (git_signature_default(&author, repo) < 0) - return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); - if (git_commit_create_v(&commit_oid, repo, "HEAD", author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) - return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); - if (git_commit_lookup(&commit, repo, &commit_oid)) - return report_error(translate("gettextFromC", "Error: could not lookup the merge commit I just created (%s)"), giterr_last()->message); - if (git_branch_is_head(local) && !git_repository_is_bare(repo)) { - git_object *parent; - git_reference_peel(&parent, local, GIT_OBJ_COMMIT); - if (update_git_checkout(repo, parent, merged_tree)) { - report_error("Warning: checked out branch is inconsistent with git data"); - } + int error; + const git_index_entry *ancestor = NULL, + *ours = NULL, + *theirs = NULL; + git_index_conflict_iterator *iter = NULL; + error = git_index_conflict_iterator_new(&iter, merged_index); + while (git_index_conflict_next(&ancestor, &ours, &theirs, iter) + != GIT_ITEROVER) { + /* Mark this conflict as resolved */ + fprintf(stderr, "conflict in %s / %s / %s\n", + ours ? ours->path : "-", + theirs ? theirs->path : "-", + ancestor ? ancestor->path : "-"); + error = git_index_conflict_remove(merged_index, ours->path); } - if (git_reference_set_target(&local, local, &commit_oid, "Subsurface merge event")) - return report_error("Error: failed to update branch (%s)", giterr_last()->message); - set_git_id(&commit_oid); - git_signature_free(author); + git_index_conflict_iterator_free(iter); + report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); } + git_oid merge_oid, commit_oid; + git_tree *merged_tree; + git_signature *author; + git_commit *commit; + + if (git_index_write_tree_to(&merge_oid, merged_index, repo)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); + if (git_tree_lookup(&merged_tree, repo, &merge_oid)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); + if (git_signature_default(&author, repo) < 0) + return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); + if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) + return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); + if (git_commit_lookup(&commit, repo, &commit_oid)) + return report_error(translate("gettextFromC", "Error: could not lookup the merge commit I just created (%s)"), giterr_last()->message); + if (git_branch_is_head(local) && !git_repository_is_bare(repo)) { + git_object *parent; + git_reference_peel(&parent, local, GIT_OBJ_COMMIT); + if (update_git_checkout(repo, parent, merged_tree)) { + report_error("Warning: checked out branch is inconsistent with git data"); + } + } + if (git_reference_set_target(&local, local, &commit_oid, "Subsurface merge event")) + return report_error("Error: failed to update branch (%s)", giterr_last()->message); + set_git_id(&commit_oid); + git_signature_free(author); + return 0; } -- cgit v1.2.3-70-g09d2 From eb205c1b090b94f4812358c8835065491d149c6b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 23 Aug 2015 18:19:36 -0700 Subject: Cloud storage: be more verbose if asked to be verbose Signed-off-by: Dirk Hohndel --- git-access.c | 9 +++++++++ load-git.c | 2 ++ 2 files changed, 11 insertions(+) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 879330a6a..cf6868af8 100644 --- a/git-access.c +++ b/git-access.c @@ -184,6 +184,15 @@ static int try_to_git_merge(git_repository *repo, git_reference *local, git_refe git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; git_merge_options merge_options; + + if (verbose) { + char outlocal[41], outremote[41]; + outlocal[40] = outremote[40] = 0; + git_oid_fmt(outlocal, local_id); + git_oid_fmt(outremote, remote_id); + fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote); + } + git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; diff --git a/load-git.c b/load-git.c index c4bbf1616..24fef24de 100644 --- a/load-git.c +++ b/load-git.c @@ -1531,6 +1531,8 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, git_rep struct dive *dive = active_dive; dive_trip_t *trip = active_trip; const char *name = git_tree_entry_name(entry); + if (verbose) + fprintf(stderr, "git load handling file %s\n", name); switch (*name) { /* Picture file? They are saved as time offsets in the dive */ case '-': case '+': -- cgit v1.2.3-70-g09d2 From 39863089ed707cd58ae5ccfa700590f4e9248c4c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 24 Aug 2015 13:55:50 -0700 Subject: Cloud storage: adjust debugging messages We shouldn't always tell the user about the perceived validity of the cloud certificate - we force it anyway. But it's nice to be easily able to see if we tried to update the remote, so add another debug output when run with -v Signed-off-by: Dirk Hohndel --- git-access.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index cf6868af8..36309fa42 100644 --- a/git-access.c +++ b/git-access.c @@ -143,11 +143,12 @@ int certificate_check_cb(git_cert *cert, int valid, const char *host, void *payl SHA1_Update(&ctx, cert509->data, cert509->len); SHA1_Final(hash, &ctx); hash[20] = 0; - if (same_string((char *)hash, KNOWN_CERT)) { - fprintf(stderr, "cloud certificate considered %s, forcing it valid\n", - valid ? "valid" : "not valid"); - return 1; - } + if (verbose > 1) + if (same_string((char *)hash, KNOWN_CERT)) { + fprintf(stderr, "cloud certificate considered %s, forcing it valid\n", + valid ? "valid" : "not valid"); + return 1; + } } return valid; } @@ -342,6 +343,8 @@ int sync_with_remote(git_repository *repo, const char *remote, const char *branc char *proxy_string; git_config *conf; + if (verbose) + fprintf(stderr, "sync with remote %s[%s]\n", remote, branch); git_repository_config(&conf, repo); if (rt == RT_HTTPS && getProxyString(&proxy_string)) { git_config_set_string(conf, "http.proxy", proxy_string); -- cgit v1.2.3-70-g09d2 From 449ba2876fd8d41262b440270e804a2f5acfbc12 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 24 Aug 2015 13:59:20 -0700 Subject: Cloud storage: test offline operation All this really does is make sure that the fast forward works if the local cache has received updates that haven't made it to the server, yet. Signed-off-by: Dirk Hohndel --- git-access.c | 2 +- tests/testgitstorage.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/testgitstorage.h | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 36309fa42..9c5b703a2 100644 --- a/git-access.c +++ b/git-access.c @@ -45,7 +45,7 @@ git_branch_create(out, repo, branch_name, target, force) #endif -static char *get_local_dir(const char *remote, const char *branch) +char *get_local_dir(const char *remote, const char *branch) { SHA_CTX ctx; unsigned char hash[20]; diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index 08dbcaaa0..5bc7924af 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -96,4 +96,57 @@ void TestGitStorage::testGitStorageCloud() clear_dive_file_data(); } +// this is a local helper function in git-access.c +extern "C" char *get_local_dir(const char *remote, const char *branch); + +void TestGitStorage::testGitStorageCloudOfflineSync() +{ + // make a change to local cache repo (pretending that we did some offline changes) + // and then open the remote one again and check that things were propagated correctly + QString cloudTestRepo("https://cloud.subsurface-divelog.org/git/ssrftest@hohndel.org[ssrftest@hohndel.org]"); + QString localCacheDir(get_local_dir("https://cloud.subsurface-divelog.org/git/ssrftest@hohndel.org", "ssrftest@hohndel.org")); + QString localCacheRepo = localCacheDir + "[ssrftest@hohndel.org]"; + // read the local repo from the previous test and add dive 10 + QCOMPARE(parse_file(qPrintable(localCacheRepo)), 0); + QCOMPARE(parse_file(SUBSURFACE_SOURCE "/dives/test10.xml"), 0); + // calling process_dive() sorts the table, but calling it with + // is_imported == true causes it to try to update the window title... let's not do that + process_dives(false, false); + // now save only to the local cache but not to the remote server + QCOMPARE(save_dives(qPrintable(localCacheRepo)), 0); + QCOMPARE(save_dives("./SampleDivesV3plus10local.ssrf"), 0); + clear_dive_file_data(); + // open the cloud storage and compare + QCOMPARE(parse_file(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives("./SampleDivesV3plus10viacloud.ssrf"), 0); + QFile org("./SampleDivesV3plus10local.ssrf"); + org.open(QFile::ReadOnly); + QFile out("./SampleDivesV3plus10viacloud.ssrf"); + out.open(QFile::ReadOnly); + QTextStream orgS(&org); + QTextStream outS(&out); + QString readin = orgS.readAll(); + QString written = outS.readAll(); + QCOMPARE(readin, written); + // write back out to cloud storage, move away the local cache, open again and compare + QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + clear_dive_file_data(); + QDir localCacheDirectory(localCacheDir); + QDir localCacheDirectorySave(localCacheDir + "save"); + QCOMPARE(localCacheDirectorySave.removeRecursively(), true); + QCOMPARE(localCacheDirectory.rename(localCacheDir, localCacheDir + "save"), true); + QCOMPARE(parse_file(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives("./SampleDivesV3plus10fromcloud.ssrf"), 0); + org.close(); + org.open(QFile::ReadOnly); + QFile out2("./SampleDivesV3plus10fromcloud.ssrf"); + out2.open(QFile::ReadOnly); + QTextStream orgS2(&org); + QTextStream outS2(&out2); + readin = orgS2.readAll(); + written = outS2.readAll(); + QCOMPARE(readin, written); + clear_dive_file_data(); +} + QTEST_MAIN(TestGitStorage) diff --git a/tests/testgitstorage.h b/tests/testgitstorage.h index a701363e9..43c33c228 100644 --- a/tests/testgitstorage.h +++ b/tests/testgitstorage.h @@ -9,6 +9,7 @@ class TestGitStorage : public QObject private slots: void testGitStorageLocal(); void testGitStorageCloud(); + void testGitStorageCloudOfflineSync(); }; #endif // TESTGITSTORAGE_H -- cgit v1.2.3-70-g09d2 From a2c638f63fd94861e60d8dfdad517e6e14785d9f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 24 Aug 2015 16:21:53 -0700 Subject: Cloud storage: attempt to deal with conflicts about deleted files This doesn't seem right, but it works. Definitely needs more analysis. Signed-off-by: Dirk Hohndel --- git-access.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'git-access.c') diff --git a/git-access.c b/git-access.c index 9c5b703a2..388fa27e2 100644 --- a/git-access.c +++ b/git-access.c @@ -222,12 +222,23 @@ static int try_to_git_merge(git_repository *repo, git_reference *local, git_refe while (git_index_conflict_next(&ancestor, &ours, &theirs, iter) != GIT_ITEROVER) { /* Mark this conflict as resolved */ - fprintf(stderr, "conflict in %s / %s / %s\n", + fprintf(stderr, "conflict in %s / %s / %s -- ", ours ? ours->path : "-", theirs ? theirs->path : "-", ancestor ? ancestor->path : "-"); - error = git_index_conflict_remove(merged_index, ours->path); + if ((!ours && theirs && ancestor) || + (ours && !theirs && ancestor)) { + // the file was removed on one side or the other - just remove it + fprintf(stderr, "looks like a delete on one side; removing the file from the index\n"); + error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY); + } else { + error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path); + } + if (error) { + fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message); + } } + git_index_conflict_cleanup(merged_index); git_index_conflict_iterator_free(iter); report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); } -- cgit v1.2.3-70-g09d2