static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) return report_error("local cached copy is dirty, skipping update"); 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? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) return update_remote(repo, origin, local, remote, rt); /* 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 and clean, so we *could* try * to merge. */ return report_error("Local and remote have diverged, need to merge"); }
static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) return report_error("local cached copy is dirty, skipping update"); 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? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) return update_remote(repo, origin, local, remote, rt); /* 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"); /* Ok, let's try to merge these */ return try_to_git_merge(repo, local, remote, &base, local_id, remote_id); }
static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, const char *remote_url, const char *branch, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; int ret = 0; if (verbose) fprintf(stderr, "git storage: try to update\n"); if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("local cached copy is dirty, skipping update"); } local_id = git_reference_target(local); remote_id = git_reference_target(remote); if (!local_id || !remote_id) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Unable to get local or remote SHA1"); } if (git_merge_base(&base, repo, local_id, remote_id)) { // TODO: // if they have no merge base, they actually are different repos // so instead merge this as merging a commit into a repo - git_merge() appears to do that // but needs testing and cleanup afterwards // if (is_subsurface_cloud) goto cloud_data_error; else 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)) { git_storage_update_progress(translate("gettextFromC", "Update local storage to match cloud storage")); return reset_to_remote(repo, local, remote_id); } /* Is the local repo the more recent one? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) { if (verbose) fprintf(stderr, "local is newer than remote, update remote\n"); git_storage_update_progress(translate("gettextFromC", "Push local changes to cloud storage")); return update_remote(repo, origin, local, remote, rt); } /* Merging a bare repository always needs user action */ if (git_repository_is_bare(repo)) { if (is_subsurface_cloud) goto cloud_data_error; else 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) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Local and remote do not match, local branch not HEAD - cannot update"); } /* Ok, let's try to merge these */ git_storage_update_progress(translate("gettextFromC", "Try to merge local changes into cloud storage")); ret = try_to_git_merge(repo, &local, remote, &base, local_id, remote_id); if (ret == 0) return update_remote(repo, origin, local, remote, rt); else return ret; cloud_data_error: // since we are working with Subsurface cloud storage we want to make the user interaction // as painless as possible. So if something went wrong with the local cache, tell the user // about it an move it away return cleanup_local_cache(remote_url, branch); }