Esempio n. 1
0
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;
}
Esempio n. 2
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);
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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);
}