static int fast_forward_or_merge (const char *repo_id, SeafCommit *base, SeafCommit *new_commit) { #define MAX_RETRY_COUNT 3 SeafRepo *repo = NULL; SeafCommit *current_head = NULL, *merged_commit = NULL; int retry_cnt = 0; int ret = 0; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Repo %s doesn't exist.\n", repo_id); ret = -1; goto out; } retry: current_head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!current_head) { seaf_warning ("Failed to find head commit of %s.\n", repo_id); ret = -1; goto out; } /* Merge if base and head are not the same. */ if (strcmp (base->commit_id, current_head->commit_id) != 0) { MergeOptions opt; const char *roots[3]; char *desc = NULL; memset (&opt, 0, sizeof(opt)); opt.n_ways = 3; memcpy (opt.remote_repo_id, repo_id, 36); memcpy (opt.remote_head, new_commit->commit_id, 40); opt.do_merge = TRUE; roots[0] = base->root_id; /* base */ roots[1] = current_head->root_id; /* head */ roots[2] = new_commit->root_id; /* remote */ if (seaf_merge_trees (repo->store_id, repo->version, 3, roots, &opt) < 0) { seaf_warning ("Failed to merge.\n"); ret = -1; goto out; } if (!opt.conflict) desc = g_strdup("Auto merge by system"); else { desc = gen_merge_description (repo, opt.merged_tree_root, current_head->root_id, new_commit->root_id); if (!desc) desc = g_strdup("Auto merge by system"); } merged_commit = seaf_commit_new(NULL, repo->id, opt.merged_tree_root, new_commit->creator_name, EMPTY_SHA1, desc, 0); g_free (desc); merged_commit->parent_id = g_strdup (current_head->commit_id); merged_commit->second_parent_id = g_strdup (new_commit->commit_id); merged_commit->new_merge = TRUE; if (opt.conflict) merged_commit->conflict = TRUE; seaf_repo_to_commit (repo, merged_commit); if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged_commit) < 0) { seaf_warning ("Failed to add commit.\n"); ret = -1; goto out; } } else { seaf_commit_ref (new_commit); merged_commit = new_commit; } seaf_branch_set_commit(repo->head, merged_commit->commit_id); if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr, repo->head, current_head->commit_id) < 0) { seaf_repo_unref (repo); repo = NULL; seaf_commit_unref (current_head); current_head = NULL; seaf_commit_unref (merged_commit); merged_commit = NULL; repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { seaf_warning ("Repo %s doesn't exist.\n", repo_id); ret = -1; goto out; } if (++retry_cnt <= MAX_RETRY_COUNT) { seaf_message ("Concurrent branch update, retry.\n"); /* Sleep random time between 100 and 1000 millisecs. */ USLEEP (g_random_int_range(1, 11) * 100 * 1000); goto retry; } else { seaf_warning ("Stop retrying.\n"); ret = -1; goto out; } } out: seaf_commit_unref (current_head); seaf_commit_unref (merged_commit); seaf_repo_unref (repo); return ret; }
static void * update_repo (void *vprocessor) { CcnetProcessor *processor = vprocessor; USE_PRIV; char *repo_id, *branch_name, *new_head; SeafRepo *repo = NULL; SeafBranch *branch = NULL; SeafCommit *commit = NULL; char old_commit_id[41]; repo_id = priv->repo_id; branch_name = priv->branch_name; new_head = priv->new_head; /* Since this is the last step of upload procedure, commit should exist. */ commit = seaf_commit_manager_get_commit (seaf->commit_mgr, new_head); if (!commit) { priv->rsp_code = g_strdup (SC_BAD_COMMIT); priv->rsp_msg = g_strdup (SS_BAD_COMMIT); goto out; } repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id); if (!repo) { /* repo is deleted on server */ priv->rsp_code = g_strdup (SC_BAD_REPO); priv->rsp_msg = g_strdup (SC_BAD_REPO); goto out; } if (seaf_quota_manager_check_quota (seaf->quota_mgr, repo_id) < 0) { priv->rsp_code = g_strdup(SC_QUOTA_FULL); priv->rsp_msg = g_strdup(SS_QUOTA_FULL); goto out; } branch = seaf_branch_manager_get_branch (seaf->branch_mgr, repo_id, branch_name); if (!branch) { priv->rsp_code = g_strdup (SC_BAD_BRANCH); priv->rsp_msg = g_strdup (SS_BAD_BRANCH); goto out; } /* If branch exists, check fast forward. */ if (strcmp (new_head, branch->commit_id) != 0 && !is_fast_forward (new_head, branch->commit_id)) { g_warning ("Upload is not fast forward. Refusing.\n"); seaf_repo_unref (repo); seaf_commit_unref (commit); seaf_branch_unref (branch); priv->rsp_code = g_strdup (SC_NOT_FF); priv->rsp_msg = g_strdup (SS_NOT_FF); return vprocessor; } /* Update branch. In case of concurrent update, we must ensure atomicity. */ memcpy (old_commit_id, branch->commit_id, 41); seaf_branch_set_commit (branch, commit->commit_id); if (seaf_branch_manager_test_and_update_branch (seaf->branch_mgr, branch, old_commit_id) < 0) { g_warning ("Upload is not fast forward, concurrent update.\n"); priv->rsp_code = g_strdup (SC_NOT_FF); priv->rsp_msg = g_strdup (SS_NOT_FF); goto out; } out: if (repo) seaf_repo_unref (repo); if (commit) seaf_commit_unref (commit); if (branch) seaf_branch_unref (branch); if (!priv->rsp_code) { priv->rsp_code = g_strdup (SC_OK); priv->rsp_msg = g_strdup (SS_OK); } return vprocessor; }