/* * Merge the commits h1 and h2, return merged tree root id * and a flag indicating the cleanness of the merge. * Return 0 if merge is done (no matter clean or not); -1 otherwise. */ int merge_recursive(struct merge_options *o, const char *h1_root, const char *h2_root, const char *ca_root, int *clean, char **root_id) { SeafDir *head, *remote, *common; int code, ret = 0; struct unpack_trees_options opts; char *error = NULL; *clean = 1; head = seaf_fs_manager_get_seafdir (seaf->fs_mgr, h1_root); remote = seaf_fs_manager_get_seafdir (seaf->fs_mgr, h2_root); common = seaf_fs_manager_get_seafdir (seaf->fs_mgr, ca_root); if (!head || !remote || !common) { g_warning ("Invalid commits!\n"); return -1; } /* Get merged index. */ code = seafile_merge_trees(o, &opts, common, head, remote, &error); if (code != 0) { ret = -1; goto out; } /* If only collect blocks, return success. */ if (o->collect_blocks_only) { process_unmerged_entries (o, head, remote); goto out; } /* Update worktree. */ /* On windows, we have to Check if any files need to be updated * are locked by other program (e.g. Office). If no file is locked, * update worktree; otherwise just quit. * * Note that if we're recovering merge on startup, we need to update * worktree no matter files are locked or not, since we cannot retry * this operation. This will produce more confusing results, but * it doesn't hurt data integrity. */ #ifdef WIN32 if (o->recover_merge || o->force_merge || !files_locked_on_windows (o->index, o->worktree)) { update_worktree (&opts, o->recover_merge, o->remote_head, o->branch2, NULL); *clean = process_unmerged_entries (o, head, remote); } else { /* Don't update anything. */ g_debug ("[merge] files are locked, quit merge now.\n"); ret = -1; goto out; } #else update_worktree (&opts, o->recover_merge, o->remote_head, o->branch2, NULL); *clean = process_unmerged_entries (o, head, remote); #endif if (*clean) *root_id = write_tree_from_memory(o); out: seaf_dir_free (head); seaf_dir_free (remote); seaf_dir_free (common); return ret; }
static int fast_forward_checkout (SeafRepo *repo, SeafCommit *head, CloneTask *task) { SeafRepoManager *mgr = repo->manager; char index_path[SEAF_PATH_MAX]; struct tree_desc trees[2]; struct unpack_trees_options topts; struct index_state istate; int ret = 0; if (strcmp (head->root_id, task->root_id) == 0) return 0; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { seaf_warning ("Failed to load index.\n"); return -1; } repo->index_corrupted = FALSE; fill_tree_descriptor (&trees[0], task->root_id); fill_tree_descriptor (&trees[1], head->root_id); memset(&topts, 0, sizeof(topts)); topts.base = task->worktree; topts.head_idx = -1; topts.src_index = &istate; topts.update = 1; topts.merge = 1; topts.fn = twoway_merge; if (repo->encrypted) { topts.crypt = seafile_crypt_new (repo->enc_version, repo->enc_key, repo->enc_iv); } if (unpack_trees (2, trees, &topts) < 0) { seaf_warning ("Failed to merge commit %s with work tree.\n", head->commit_id); ret = -1; goto out; } if (update_worktree (&topts, FALSE, head->commit_id, head->creator_name, NULL) < 0) { seaf_warning ("Failed to update worktree.\n"); ret = -1; goto out; } discard_index (&istate); istate = topts.result; if (update_index (&istate, index_path) < 0) { seaf_warning ("Failed to update index.\n"); } out: tree_desc_free (&trees[0]); tree_desc_free (&trees[1]); g_free (topts.crypt); discard_index (&istate); return ret; }
static const char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; const char *namespaced_name, *ret; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; /* only refs/... are allowed */ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { rp_error("refusing to create funny ref '%s' remotely", name); return "funny refname"; } strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name); namespaced_name = strbuf_detach(&namespaced_name_buf, NULL); if (is_ref_checked_out(namespaced_name)) { switch (deny_current_branch) { case DENY_IGNORE: break; case DENY_WARN: rp_warning("updating the current branch"); break; case DENY_REFUSE: case DENY_UNCONFIGURED: rp_error("refusing to update checked out branch: %s", name); if (deny_current_branch == DENY_UNCONFIGURED) refuse_unconfigured_deny(); return "branch is currently checked out"; case DENY_UPDATE_INSTEAD: ret = update_worktree(new_sha1); if (ret) return ret; break; } } if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) { error("unpack should have generated %s, " "but I can't find it!", sha1_to_hex(new_sha1)); return "bad pack"; } if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { if (deny_deletes && starts_with(name, "refs/heads/")) { rp_error("denying ref deletion for %s", name); return "deletion prohibited"; } if (head_name && !strcmp(namespaced_name, head_name)) { switch (deny_delete_current) { case DENY_IGNORE: break; case DENY_WARN: rp_warning("deleting the current branch"); break; case DENY_REFUSE: case DENY_UNCONFIGURED: case DENY_UPDATE_INSTEAD: if (deny_delete_current == DENY_UNCONFIGURED) refuse_unconfigured_deny_delete_current(); rp_error("refusing to delete the current branch: %s", name); return "deletion of the current branch prohibited"; default: return "Invalid denyDeleteCurrent setting"; } } } if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; old_object = parse_object(old_sha1); new_object = parse_object(new_sha1); if (!old_object || !new_object || old_object->type != OBJ_COMMIT || new_object->type != OBJ_COMMIT) { error("bad sha1 objects for %s", name); return "bad ref"; } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; if (!in_merge_bases(old_commit, new_commit)) { rp_error("denying non-fast-forward %s" " (you should pull first)", name); return "non-fast-forward"; } } if (run_update_hook(cmd)) { rp_error("hook declined to update %s", name); return "hook declined"; } if (is_null_sha1(new_sha1)) { struct strbuf err = STRBUF_INIT; if (!parse_object(old_sha1)) { old_sha1 = NULL; if (ref_exists(name)) { rp_warning("Allowing deletion of corrupt ref."); } else { rp_warning("Deleting a non-existent ref."); cmd->did_not_exist = 1; } } if (ref_transaction_delete(transaction, namespaced_name, old_sha1, 0, "push", &err)) { rp_error("%s", err.buf); strbuf_release(&err); return "failed to delete"; } strbuf_release(&err); return NULL; /* good */ } else { struct strbuf err = STRBUF_INIT; if (shallow_update && si->shallow_ref[cmd->index] && update_shallow_ref(cmd, si)) return "shallow error"; if (ref_transaction_update(transaction, namespaced_name, new_sha1, old_sha1, 0, "push", &err)) { rp_error("%s", err.buf); strbuf_release(&err); return "failed to update ref"; } strbuf_release(&err); return NULL; /* good */ } }