示例#1
0
void QMLManager::saveChangesCloud()
{
	git_storage_update_progress(true, "start save change to cloud");
	if (!loadFromCloud()) {
		appendTextToLog("Don't save dives without loading from the cloud, first.");
		return;
	}
	bool glo = prefs.git_local_only;
	bool cbs = prefs.cloud_background_sync;
	// first we need to store any unsaved changes to the local repo
	saveChangesLocal();
	if (alreadySaving) {
		appendTextToLog("save operation in progress already, can't sync with server");
		return;
	}
	prefs.git_local_only = false;
	alreadySaving = true;
	loadDivesWithValidCredentials();
	alreadySaving = false;
	git_storage_update_progress(false, "finished syncing dive list to cloud server");
	setAccessingCloud(-1);
	prefs.git_local_only = glo;
	prefs.cloud_background_sync = cbs;
	alreadySaving = false;
}
示例#2
0
void QMLManager::saveChangesLocal()
{
	if (unsaved_changes()) {
		git_storage_update_progress(true, "saving dives locally"); // reset the timers
		if (!loadFromCloud()) {
			// this seems silly, but you need a common ancestor in the repository in
			// order to be able to merge che changes later
			appendTextToLog("Don't save dives without loading from the cloud, first.");
			return;
		}
		if (alreadySaving) {
			appendTextToLog("save operation already in progress, can't save locally");
			return;
		}
		alreadySaving = true;
		bool glo = prefs.git_local_only;
		prefs.git_local_only = true;
		if (save_dives(existing_filename)) {
			appendTextToLog(get_error_string());
			setAccessingCloud(-1);
			prefs.git_local_only = glo;
			alreadySaving = false;
			return;
		}
		prefs.git_local_only = glo;
		mark_divelist_changed(false);
		git_storage_update_progress(false, "done with local save");
		alreadySaving = false;
	} else {
		appendTextToLog("local save requested with no unsaved changes");
	}
}
示例#3
0
void QMLManager::saveChangesCloud(bool forceRemoteSync)
{
	if (!unsaved_changes() && !forceRemoteSync) {
		appendTextToLog("asked to save changes but no unsaved changes");
		return;
	}
	if (alreadySaving) {
		appendTextToLog("save operation in progress already");
		return;
	}
	// first we need to store any unsaved changes to the local repo
	saveChangesLocal();

	// if the user asked not to push to the cloud we are done
	if (prefs.git_local_only && !forceRemoteSync)
		return;

	if (!loadFromCloud()) {
		appendTextToLog("Don't save dives without loading from the cloud, first.");
		return;
	}

	bool glo = prefs.git_local_only;
	git_storage_update_progress(false, "start save change to cloud");
	prefs.git_local_only = false;
	alreadySaving = true;
	loadDivesWithValidCredentials();
	alreadySaving = false;
	git_storage_update_progress(false, "finished syncing dive list to cloud server");
	setAccessingCloud(-1);
	prefs.git_local_only = glo;
}
bool CheckCloudConnection::checkServer()
{
	if (verbose)
		fprintf(stderr, "Checking cloud connection...\n");

	QTimer timer;
	timer.setSingleShot(true);
	QEventLoop loop;
	QNetworkRequest request;
	request.setRawHeader("Accept", "text/plain");
	request.setRawHeader("User-Agent", getUserAgent().toUtf8());
	request.setRawHeader("Client-Id", getUUID().toUtf8());
	request.setUrl(QString(prefs.cloud_base_url) + TEAPOT);
	QNetworkAccessManager *mgr = new QNetworkAccessManager();
	reply = mgr->get(request);
	connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
	connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
	connect(reply, &QNetworkReply::sslErrors, this, &CheckCloudConnection::sslErrors);
	for (int seconds = 1; seconds <= prefs.cloud_timeout; seconds++) {
		timer.start(1000); // wait the given number of seconds (default 5)
		loop.exec();
		if (timer.isActive()) {
			// didn't time out, did we get the right response?
			timer.stop();
			if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == HTTP_I_AM_A_TEAPOT &&
			    reply->readAll() == QByteArray(MILK)) {
				reply->deleteLater();
				mgr->deleteLater();
				if (verbose > 1)
					qWarning() << "Cloud storage: successfully checked connection to cloud server";
				git_storage_update_progress(false, "successfully checked cloud connection");
				return true;
			}
		} else if (seconds < prefs.cloud_timeout) {
			QString text = QString("waited %1 sec for cloud connetion").arg(seconds);
			git_storage_update_progress(false, qPrintable(text));
		} else {
			disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
			reply->abort();
		}
	}
	git_storage_update_progress(false, "cloud connection failed");
	prefs.git_local_only = true;
	if (verbose)
		qDebug() << "connection test to cloud server failed" <<
			    reply->error() << reply->errorString() <<
			    reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() <<
			    reply->readAll();
	reply->deleteLater();
	mgr->deleteLater();
	if (verbose)
		qWarning() << "Cloud storage: unable to connect to cloud server";
	return false;
}
示例#5
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_remote_repo: accessing %s\n", remote);
	}
	git_storage_update_progress(false, "start git interaction");
	/* 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);
	}
	if (!prefs.git_local_only)
		return create_local_repo(localdir, remote, branch, rt);
	else
		return 0;

}
示例#6
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;
}
示例#7
0
// this randomly assumes that 80% of the time is spent on the objects and 20% on the deltas
// map the git progress to 20% of overall progress
// if the user cancels the dialog this is passed back to libgit2
static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
{
	UNUSED(payload);

	static int last_done = -1;
	char buf[80];
	int done = 0;
	int total = 0;

	if (stats->total_objects) {
		total = 60;
		done = 60 * stats->received_objects / stats->total_objects;
	}
	if (stats->total_deltas) {
		total += 20;
		done += 20 * stats->indexed_deltas / stats->total_deltas;
	}
	/* for debugging this is useful
	char buf[100];
	snprintf(buf, 100, "transfer cb rec_obj %d tot_obj %d idx_delta %d total_delta %d local obj %d", stats->received_objects, stats->total_objects, stats->indexed_deltas, stats->total_deltas, stats->local_objects);
	return git_storage_update_progress(buf);
	 */
	if (done > last_done) {
		last_done = done;
		snprintf(buf, sizeof(buf), translate("gettextFromC", "Transfer from storage (%d/%d)"), done, total);
		return git_storage_update_progress(buf);
	}
	return 0;
}
示例#8
0
void QMLManager::retrieveUserid()
{
	if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) {
		appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: (%1) %2")
				.arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())
				.arg(QString(reply->readAll())));
		setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT);
		revertToNoCloudIfNeeded();
		return;
	}
	setCredentialStatus(VALID);
	QString userid(prefs.userid);
	if (userid.isEmpty()) {
		if (same_string(prefs.cloud_storage_email, "") || same_string(prefs.cloud_storage_password, "")) {
			appendTextToLog("cloud user name or password are empty, can't retrieve web user id");
			revertToNoCloudIfNeeded();
			return;
		}
		appendTextToLog(QStringLiteral("calling getUserid with user %1").arg(prefs.cloud_storage_email));
		userid = locationProvider->getUserid(prefs.cloud_storage_email, prefs.cloud_storage_password);
	}
	if (!userid.isEmpty()) {
		// overwrite the existing userid
		free(prefs.userid);
		prefs.userid = strdup(qPrintable(userid));
		QSettings s;
		s.setValue("subsurface_webservice_uid", prefs.userid);
		s.sync();
	}
	setCredentialStatus(VALID);
	setStartPageText("Cloud credentials valid, loading dives...");
	git_storage_update_progress(true, "load dives with valid credentials");
	// this only gets called with "alreadySaving" already locked
	loadDivesWithValidCredentials();
}
示例#9
0
// the initial push to sync the repos is mapped to 10% of overall progress
static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void *payload)
{
	UNUSED(bytes);
	UNUSED(payload);
	char buf[80];
	snprintf(buf, sizeof(buf), translate("gettextFromC", "Transfer to storage (%d/%d)"), current, total);
	return git_storage_update_progress(buf);
}
示例#10
0
// the checkout_progress_cb doesn't allow canceling of the operation
// map the git progress to 20% of overall progress
static void progress_cb(const char *path, size_t completed_steps, size_t total_steps, void *payload)
{
	UNUSED(path);
	UNUSED(payload);
	char buf[80];
	snprintf(buf, sizeof(buf),  translate("gettextFromC", "Checkout from storage (%lu/%lu)"), completed_steps, total_steps);
	(void)git_storage_update_progress(buf);
}
示例#11
0
void QMLManager::saveChangesLocal()
{
	if (unsaved_changes()) {
		git_storage_update_progress(true, "saving dives locally"); // reset the timers
		if (credentialStatus() == NOCLOUD) {
			if (same_string(existing_filename, "")) {
				char *filename = NOCLOUD_LOCALSTORAGE;
				if (git_create_local_repo(filename))
					appendTextToLog(get_error_string());
				set_filename(filename, true);
				GeneralSettingsObjectWrapper s(this);
				s.setDefaultFilename(filename);
				s.setDefaultFileBehavior(LOCAL_DEFAULT_FILE);
				qDebug() << "setting default file to" << filename;
			}
		} else if (!loadFromCloud()) {
			// this seems silly, but you need a common ancestor in the repository in
			// order to be able to merge che changes later
			appendTextToLog("Don't save dives without loading from the cloud, first.");
			return;
		}
		if (alreadySaving) {
			appendTextToLog("save operation already in progress, can't save locally");
			return;
		}
		alreadySaving = true;
		bool glo = prefs.git_local_only;
		prefs.git_local_only = true;
		if (save_dives(existing_filename)) {
			appendTextToLog(get_error_string());
			set_filename(NULL, true);
			setAccessingCloud(-1);
			prefs.git_local_only = glo;
			alreadySaving = false;
			return;
		}
		prefs.git_local_only = glo;
		mark_divelist_changed(false);
		git_storage_update_progress(false, "done with local save");
		alreadySaving = false;
	} else {
		appendTextToLog("local save requested with no unsaved changes");
	}
}
示例#12
0
// the checkout_progress_cb doesn't allow canceling of the operation
// map the git progress to 20% of overall progress
static void progress_cb(const char *path, size_t completed_steps, size_t total_steps, void *payload)
{
	(void) path;
	(void) payload;
	static size_t last_percent = -1;

	if (total_steps && 20 * completed_steps / total_steps > last_percent) {
		(void)git_storage_update_progress(false, "checkout_progress_cb");
		last_percent = 20 * completed_steps / total_steps;
	}
}
示例#13
0
static int do_git_load(git_repository *repo, const char *branch)
{
	int ret;
	git_commit *commit;
	git_tree *tree;

	git_storage_update_progress(false, "do_git_load, find the commit");
	ret = find_commit(repo, branch, &commit);
	if (ret)
		return ret;
	git_storage_update_progress(false, "git commit tree");
	if (git_commit_tree(&tree, commit))
		return report_error("Could not look up tree of commit in branch '%s'", branch);
	git_storage_update_progress(false, "load dives from tree");
	ret = load_dives_from_tree(repo, tree);
	if (!ret)
		set_git_id(git_commit_id(commit));
	git_object_free((git_object *)tree);
	git_storage_update_progress(false, "done do_git_load");
	return ret;
}
示例#14
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");
	git_storage_update_progress(false, "git check remote status");

	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);
		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;
		git_storage_update_progress(false, "git remote push (no remote existed)");
		error = git_remote_push(origin, &refspec, &opts);
	} 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;
}
示例#15
0
// the initial push to sync the repos is mapped to 10% of overall progress
static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void *payload)
{
	(void) bytes;
	(void) payload;
	static int last_percent = -1;
	int percent = 0;

	if (total != 0)
		percent = 5 * current / total;
	if (percent > last_percent) {
		last_percent = percent;
		return git_storage_update_progress(false, "push trasfer cb");
	}
	return 0;
}
示例#16
0
// this randomly assumes that 80% of the time is spent on the objects and 20% on the deltas
// map the git progress to 20% of overall progress
// if the user cancels the dialog this is passed back to libgit2
static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
{
	(void) payload;
	static int last_percent = -1;

	int percent = 0;
	if (stats->total_objects)
		percent = 16 * stats->received_objects / stats->total_objects;
	if (stats->total_deltas)
		percent += 4 * stats->indexed_deltas / stats->total_deltas;
	/* for debugging this is useful
	char buf[100];
	snprintf(buf, 100, "transfer cb rec_obj %d tot_obj %d idx_delta %d total_delta %d local obj %d", stats->received_objects, stats->total_objects, stats->indexed_deltas, stats->total_deltas, stats->local_objects);
	return git_storage_update_progress(false, buf);
	 */
	if (percent > last_percent) {
		last_percent = percent;
		return git_storage_update_progress(false, "transfer cb");
	}
	return 0;
}
示例#17
0
void QMLManager::loadDivesWithValidCredentials()
{
	QString url;
	timestamp_t currentDiveTimestamp = selectedDiveTimestamp();
	if (getCloudURL(url)) {
		QString errorString(get_error_string());
		appendTextToLog(errorString);
		setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT);
		revertToNoCloudIfNeeded();
		return;
	}
	QByteArray fileNamePrt = QFile::encodeName(url);
	git_repository *git;
	const char *branch;
	int error;
	if (check_git_sha(fileNamePrt.data(), &git, &branch) == 0) {
		qDebug() << "local cache was current, no need to modify dive list";
		appendTextToLog("Cloud sync shows local cache was current");
		goto successful_exit;
	}
	appendTextToLog("Cloud sync brought newer data, reloading the dive list");

	clear_dive_file_data();
	if (git != dummy_git_repository) {
		appendTextToLog(QString("have repository and branch %1").arg(branch));
		error = git_load_dives(git, branch);
	} else {
		appendTextToLog(QString("didn't receive valid git repo, try again"));
		error = parse_file(fileNamePrt.data());
	}
	setAccessingCloud(-1);
	if (!error) {
		report_error("filename is now %s", fileNamePrt.data());
		const char *error_string = get_error_string();
		appendTextToLog(error_string);
		set_filename(fileNamePrt.data(), true);
	} else {
		report_error("failed to open file %s", fileNamePrt.data());
		QString errorString(get_error_string());
		appendTextToLog(errorString);
		revertToNoCloudIfNeeded();
		return;
	}
	consumeFinishedLoad(currentDiveTimestamp);

successful_exit:
	alreadySaving = false;
	setLoadFromCloud(true);
	// if we came from local storage mode, let's merge the local data into the local cache
	// for the remote data - which then later gets merged with the remote data if necessary
	if (oldStatus() == NOCLOUD) {
		git_storage_update_progress(false, "import dives from nocloud local storage");
		dive_table.preexisting = dive_table.nr;
		mergeLocalRepo();
		DiveListModel::instance()->clear();
		DiveListModel::instance()->addAllDives();
		appendTextToLog(QStringLiteral("%1 dives loaded after importing nocloud local storage").arg(dive_table.nr));
		saveChangesLocal();
		if (syncToCloud() == false) {
			appendTextToLog(QStringLiteral("taking things back offline now that storage is synced"));
			prefs.git_local_only = syncToCloud();
		}
	}
	setAccessingCloud(-1);
	// if we got here just for an initial connection to the cloud, reset to offline
	if (currentGitLocalOnly) {
		currentGitLocalOnly = false;
		prefs.git_local_only = true;
	}
	return;
}
示例#18
0
int sync_with_remote(git_repository *repo, const char *remote, const char *branch, enum remote_transport rt)
{
	int error;
	git_remote *origin;
	char *proxy_string;
	git_config *conf;

	if (git_local_only) {
		if (verbose)
			fprintf(stderr, "don't sync with remote - read from cache only\n");
		return 0;
	}
	if (verbose)
		fprintf(stderr, "sync with remote %s[%s]\n", remote, branch);
	git_storage_update_progress(translate("gettextFromC", "Sync with cloud storage"));
	git_repository_config(&conf, repo);
	if (rt == RT_HTTPS && getProxyString(&proxy_string)) {
		if (verbose)
			fprintf(stderr, "set proxy to \"%s\"\n", proxy_string);
		git_config_set_string(conf, "http.proxy", proxy_string);
		free(proxy_string);
	} else {
		if (verbose)
			fprintf(stderr, "delete proxy setting\n");
		git_config_delete_entry(conf, "http.proxy");
	}

	/*
	 * NOTE! Remote errors are reported, but are nonfatal:
	 * we still successfully return the local repository.
	 */
	error = git_remote_lookup(&origin, repo, "origin");
	if (error) {
		if (!is_subsurface_cloud)
			report_error("Repository '%s' origin lookup failed (%s)", remote, giterr_last()->message);
		return 0;
	}

	if (is_subsurface_cloud && !canReachCloudServer()) {
		// this is not an error, just a warning message, so return 0
		report_error("Cannot connect to cloud server, working with local copy");
		git_storage_update_progress(translate("gettextFromC", "Can't reach cloud server, working with local data"));
		return 0;
	}
	if (verbose)
		fprintf(stderr, "git storage: fetch remote\n");
	git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
	opts.callbacks.transfer_progress = &transfer_progress_cb;
	auth_attempt = 0;
	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;
	git_storage_update_progress(translate("gettextFromC", "Successful cloud connection, fetch remote"));
	error = git_remote_fetch(origin, NULL, &opts, NULL);
	// NOTE! A fetch error is not fatal, we just report it
	if (error) {
		if (is_subsurface_cloud)
			report_error("Cannot sync with cloud server, working with offline copy");
		else
			report_error("Unable to fetch remote '%s'", remote);
		if (verbose)
			// If we returned GIT_EUSER during authentication, giterr_last() returns NULL
			fprintf(stderr, "remote fetch failed (%s)\n",
				giterr_last() ? giterr_last()->message : "authentication failed");
		// Since we failed to sync with online repository, enter offline mode
		git_local_only = true;
		error = 0;
	} else {
		error = check_remote_status(repo, origin, remote, branch, rt);
	}
	git_remote_free(origin);
	git_storage_update_progress(translate("gettextFromC", "Done syncing with cloud storage"));
	return error;
}
示例#19
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);
}