/* * Get the list of new blocks that would be checked out after * we merge with a branch headed by @remote. * * This function should be called before downloading any block * if the repo is set to not preserving history. In this case, * we don't want to download any block that will not be checked * out to the worktree (i.e. data from any historical commits). * * Return 0 if successfully calculate the block list, -1 otherwise. * If there is no new block to download, *@bl will be set to NULL; * otherwise it's set to the block list. */ int merge_get_new_block_list (SeafRepo *repo, SeafCommit *remote, BlockList **bl) { SeafCommit *common = NULL; SeafCommit *head; int ret = 0; head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->head->commit_id); if (!head) { g_warning ("current branch corrupted.\n"); return -1; } common = get_merge_base (head, remote); if (!common) { g_warning ("Cannot find common ancestor\n"); ret = -1; goto free_head; } if (strcmp(common->commit_id, remote->commit_id) == 0) { /* We are already up to date. No new block. */ *bl = NULL; } else if (strcmp(common->commit_id, head->commit_id) == 0) { /* Fast forward. */ ret = get_new_blocks_ff (repo, head, remote, bl); } else { /* Not up-to-date and ff, we need a real merge. */ ret = get_new_blocks_merge (repo, head, remote, common, bl); } seaf_commit_unref (common); free_head: seaf_commit_unref (head); return ret; }
VCCompareResult vc_compare_commits (const char *repo_id, int version, const char *c1, const char *c2) { SeafCommit *commit1, *commit2, *ca; VCCompareResult ret; /* Treat the same as up-to-date. */ if (strcmp (c1, c2) == 0) return VC_UP_TO_DATE; commit1 = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, c1); if (!commit1) return VC_INDEPENDENT; commit2 = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id, version, c2); if (!commit2) { seaf_commit_unref (commit1); return VC_INDEPENDENT; } ca = get_merge_base (commit1, commit2); if (!ca) ret = VC_INDEPENDENT; else if (strcmp(ca->commit_id, commit1->commit_id) == 0) ret = VC_UP_TO_DATE; else if (strcmp(ca->commit_id, commit2->commit_id) == 0) ret = VC_FAST_FORWARD; else ret = VC_INDEPENDENT; if (ca) seaf_commit_unref (ca); seaf_commit_unref (commit1); seaf_commit_unref (commit2); return ret; }
int merge_branches (SeafRepo *repo, SeafBranch *remote_branch, char **error, gboolean *real_merge) { SeafCommit *common = NULL; SeafCommit *head, *remote; int ret = 0; SeafRepoMergeInfo minfo; g_assert (repo && remote_branch && error); *real_merge = FALSE; memset (&minfo, 0, sizeof(minfo)); if (seaf_repo_manager_get_merge_info (repo->manager, repo->id, &minfo) < 0) { g_warning ("Failed to get merge status of repo %s.\n", repo->id); return -1; } head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->head->commit_id); if (!head) { *error = g_strdup("Internal error: current branch corrupted.\n"); return -1; } remote = seaf_commit_manager_get_commit (seaf->commit_mgr, remote_branch->commit_id); if (!remote) { *error = g_strdup("Invalid remote branch.\n"); ret = -1; goto free_head; } /* Are we going to recover from the last interrupted merge? */ if (minfo.in_merge) { /* We don't need to recover 2 cases, since the last merge was actually finished. * - "master" and "local" are the same; * - index is unmerged. * * The first case is a clean merge; the second case is unclean merge. */ if (strcmp (head->commit_id, remote->commit_id) == 0 || seaf_repo_is_index_unmerged (repo)) { seaf_repo_manager_clear_merge (repo->manager, repo->id); goto free_head; } } /* We use the same logic for normal merge and recover. */ /* Set in_merge state. */ seaf_repo_manager_set_merge (repo->manager, repo->id, remote_branch->commit_id); common = get_merge_base (head, remote); if (!common) { g_warning ("Cannot find common ancestor\n"); *error = g_strdup ("Cannot find common ancestor\n"); ret = -1; goto free_remote; } /* printf ("common commit id is %s.\n", common->commit_id); */ if (strcmp(common->commit_id, remote->commit_id) == 0) { /* We are already up to date. */ g_debug ("Already up to date.\n"); } else if (strcmp(common->commit_id, head->commit_id) == 0) { /* Fast forward. */ if (seaf_repo_checkout_commit (repo, remote, minfo.in_merge, error) < 0) { ret = -1; goto out; } seaf_branch_set_commit (repo->head, remote->commit_id); seaf_branch_manager_update_branch (seaf->branch_mgr, repo->head); /* Repo info on the client is in memory. */ g_free (repo->name); repo->name = g_strdup(remote->repo_name); g_free (repo->desc); repo->desc = g_strdup(remote->repo_desc); g_debug ("Fast forward.\n"); } else { /* Not up-to-date and ff, we need a real merge. */ *real_merge = TRUE; ret = do_real_merge (repo, repo->head, head, remote_branch, remote, common, minfo.in_merge, error); } out: /* Clear in_merge state, no matter clean or not. */ seaf_repo_manager_clear_merge (repo->manager, repo->id); seaf_commit_unref (common); free_remote: seaf_commit_unref (remote); free_head: seaf_commit_unref (head); return ret; }