static struct git_repository *get_remote_repo(const char *localdir, const char *remote, const char *branch) { struct stat st; enum remote_transport rt = url_to_remote_transport(remote); if (verbose > 1) { fprintf(stderr, "git_remote_repo: accessing %s\n", remote); } git_storage_update_progress(translate("gettextFromC", "Synchronising data file")); /* Do we already have a local cache? */ if (!subsurface_stat(localdir, &st)) { if (!S_ISDIR(st.st_mode)) { if (is_subsurface_cloud) (void)cleanup_local_cache(remote, branch); else report_error("local git cache at '%s' is corrupt", localdir); return NULL; } return update_local_repo(localdir, remote, branch, rt); } else { /* We have no local cache yet. * Take us temporarly online to create a local and * remote cloud repo. */ git_repository *ret; bool glo = git_local_only; git_local_only = false; ret = create_local_repo(localdir, remote, branch, rt); git_local_only = glo; return ret; } /* all normal cases are handled above */ return 0; }
static struct git_repository *get_remote_repo(const char *localdir, const char *remote, const char *branch) { struct stat st; enum remote_transport rt; /* figure out the remote transport */ if (strncmp(remote, "ssh://", 6) == 0) rt = RT_SSH; else if (strncmp(remote, "https://", 8) == 0) rt = RT_HTTPS; else rt = RT_OTHER; if (verbose > 1) { fprintf(stderr, "git storage: accessing %s\n", remote); } /* Do we already have a local cache? */ if (!stat(localdir, &st)) { if (!S_ISDIR(st.st_mode)) { if (is_subsurface_cloud) (void)cleanup_local_cache(remote, branch); else report_error("local git cache at '%s' is corrupt"); return NULL; } return update_local_repo(localdir, remote, branch, rt); } return create_local_repo(localdir, remote, branch, rt); }
static git_repository *update_local_repo(const char *localdir, const char *remote, const char *branch, enum remote_transport rt) { int error; git_repository *repo = NULL; if (verbose) fprintf(stderr, "git storage: update local repo\n"); error = git_repository_open(&repo, localdir); if (error) { if (is_subsurface_cloud) (void)cleanup_local_cache(remote, branch); else report_error("Unable to open git cache repository at %s: %s", localdir, giterr_last()->message); return NULL; } sync_with_remote(repo, remote, branch, rt); return repo; }
static int check_remote_status(git_repository *repo, git_remote *origin, const char *remote, const char *branch, enum remote_transport rt) { int error = 0; git_reference *local_ref, *remote_ref; if (verbose) fprintf(stderr, "git storage: check remote status\n"); if (git_branch_lookup(&local_ref, repo, branch, GIT_BRANCH_LOCAL)) { if (is_subsurface_cloud) return cleanup_local_cache(remote, branch); else return report_error("Git cache branch %s no longer exists", branch); } if (git_branch_upstream(&remote_ref, local_ref)) { /* so there is no upstream branch for our branch; that's a problem. * let's push our branch */ git_strarray refspec; git_reference_list(&refspec, repo); #if USE_LIBGIT23_API git_push_options opts = GIT_PUSH_OPTIONS_INIT; opts.callbacks.transfer_progress = &transfer_progress_cb; if (rt == RT_SSH) opts.callbacks.credentials = credential_ssh_cb; else if (rt == RT_HTTPS) opts.callbacks.credentials = credential_https_cb; opts.callbacks.certificate_check = certificate_check_cb; error = git_remote_push(origin, &refspec, &opts); #else error = git_remote_push(origin, &refspec, NULL); #endif } else { error = try_to_update(repo, origin, local_ref, remote_ref, remote, branch, rt); git_reference_free(remote_ref); } git_reference_free(local_ref); return error; }
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); }