int merge_trees(struct merge_options *o, struct tree *head, struct tree *merge, struct tree *common, struct tree **result) { int code, clean; if (o->subtree_merge) { merge = shift_tree_object(head, merge); common = shift_tree_object(head, common); } if (sha_eq(common->object.sha1, merge->object.sha1)) { output(o, 0, "Already uptodate!"); *result = head; return 1; } code = git_merge_trees(o->call_depth, common, head, merge); if (code != 0) die("merging of trees %s and %s failed", sha1_to_hex(head->object.sha1), sha1_to_hex(merge->object.sha1)); if (unmerged_cache()) { struct string_list *entries, *re_head, *re_merge; int i; string_list_clear(&o->current_file_set, 1); string_list_clear(&o->current_directory_set, 1); get_files_dirs(o, head); get_files_dirs(o, merge); entries = get_unmerged(); re_head = get_renames(o, head, common, head, merge, entries); re_merge = get_renames(o, merge, common, head, merge, entries); clean = process_renames(o, re_head, re_merge); for (i = 0; i < entries->nr; i++) { const char *path = entries->items[i].string; struct stage_data *e = entries->items[i].util; if (!e->processed && !process_entry(o, path, e)) clean = 0; } string_list_clear(re_merge, 0); string_list_clear(re_head, 0); string_list_clear(entries, 1); } else clean = 1; if (o->call_depth) *result = write_tree_from_memory(o); return clean; }
static int rebase_next_inmemory( git_rebase_operation **out, git_rebase *rebase) { git_commit *current_commit = NULL, *parent_commit = NULL; git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; git_rebase_operation *operation; git_index *index = NULL; unsigned int parent_count; int error; *out = NULL; operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_commit_tree(¤t_tree, current_commit)) < 0) goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0) goto done; } if ((error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) goto done; if (!rebase->index) { rebase->index = index; index = NULL; } else { if ((error = git_index_read_index(rebase->index, index)) < 0) goto done; } *out = operation; done: git_commit_free(current_commit); git_commit_free(parent_commit); git_tree_free(current_tree); git_tree_free(head_tree); git_tree_free(parent_tree); git_index_free(index); return error; }
int git_cherry_pick_commit( git_index **out, git_repository *repo, git_commit *cherry_pick_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_opts) { git_commit *parent_commit = NULL; git_tree *parent_tree = NULL, *our_tree = NULL, *cherry_pick_tree = NULL; int parent = 0, error = 0; assert(out && repo && cherry_pick_commit && our_commit); if (git_commit_parentcount(cherry_pick_commit) > 1) { if (!mainline) return cherry_pick_seterr(cherry_pick_commit, "Mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return cherry_pick_seterr(cherry_pick_commit, "Mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(cherry_pick_commit); } if (parent && ((error = git_commit_parent(&parent_commit, cherry_pick_commit, (parent - 1))) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) goto done; if ((error = git_commit_tree(&cherry_pick_tree, cherry_pick_commit)) < 0 || (error = git_commit_tree(&our_tree, our_commit)) < 0) goto done; error = git_merge_trees(out, repo, parent_tree, our_tree, cherry_pick_tree, merge_opts); done: git_tree_free(parent_tree); git_tree_free(our_tree); git_tree_free(cherry_pick_tree); git_commit_free(parent_commit); return error; }
/* * tree.merge(other_tree[, ancestor_tree[, options]]) -> Rugged::Index * tree.merge(other_tree[, options]) -> Rugged::Index * * Merges two trees and returns the a Rugged::Index object that reflects * the result of the merge. * * The following options can be passed in the +options+ Hash: * * :renames :: * If true, looking for renames will be enabled (`--find-renames`). * * :rename_threshold :: * An integer specifying the minimum similarity of a file to be * seen as an eligible rename source (default 50). * * :target_limit :: * An integer specifying the maximum byte size of a file before a it will * be treated as binary. The default value is 512MB. * * :favor :: * Specifies how and if conflicts are auto-resolved by favoring a specific * file output. Can be one of `:normal`, `:ours`, `:theirs` or `:union`. * */ static VALUE rb_git_tree_merge(int argc, VALUE *argv, VALUE self) { VALUE rb_other_tree, rb_ancestor_tree, rb_options; VALUE rb_repo = rugged_owner(self); git_tree *tree, *other_tree, *ancestor_tree; git_repository *repo; git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; int error; if (rb_scan_args(argc, argv, "12", &rb_other_tree, &rb_ancestor_tree, &rb_options) == 2) { if (TYPE(rb_ancestor_tree) == T_HASH) { rb_options = rb_ancestor_tree; rb_ancestor_tree = Qnil; } } if (!NIL_P(rb_options)) { Check_Type(rb_options, T_HASH); rugged_parse_merge_options(&opts, rb_options); } if (!rb_obj_is_kind_of(rb_other_tree, rb_cRuggedTree)) rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance"); else if (!NIL_P(rb_ancestor_tree) && !rb_obj_is_kind_of(rb_ancestor_tree, rb_cRuggedTree)) rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance"); Data_Get_Struct(self, git_tree, tree); Data_Get_Struct(rb_repo, git_repository, repo); Data_Get_Struct(rb_other_tree, git_tree, other_tree); if (!NIL_P(rb_ancestor_tree)) Data_Get_Struct(rb_ancestor_tree, git_tree, ancestor_tree); else ancestor_tree = NULL; error = git_merge_trees(&index, repo, ancestor_tree, tree, other_tree, &opts); if (error == GIT_EMERGECONFLICT) return Qnil; rugged_exception_check(error); return rugged_index_new(rb_cRuggedIndex, rb_repo, index); }
int merge_trees_from_branches( git_index **index, git_repository *repo, const char *ours_name, const char *theirs_name, git_merge_tree_opts *opts) { git_commit *our_commit, *their_commit, *ancestor_commit = NULL; git_tree *our_tree, *their_tree, *ancestor_tree = NULL; git_oid our_oid, their_oid, ancestor_oid; git_buf branch_buf = GIT_BUF_INIT; int error; git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); git_buf_clear(&branch_buf); git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name); cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)); if (error != GIT_ENOTFOUND) { cl_git_pass(error); cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)); cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit)); } cl_git_pass(git_commit_tree(&our_tree, our_commit)); cl_git_pass(git_commit_tree(&their_tree, their_commit)); cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts)); git_buf_free(&branch_buf); git_tree_free(our_tree); git_tree_free(their_tree); git_tree_free(ancestor_tree); git_commit_free(our_commit); git_commit_free(their_commit); git_commit_free(ancestor_commit); return 0; }
static int rebase_next_merge( git_rebase_operation **out, git_rebase *rebase) { git_buf path = GIT_BUF_INIT; git_commit *current_commit = NULL, *parent_commit = NULL; git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; git_rebase_operation *operation; git_checkout_options checkout_opts; char current_idstr[GIT_OID_HEXSZ]; unsigned int parent_count; int error; *out = NULL; operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_commit_tree(¤t_tree, current_commit)) < 0 || (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0) goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0) goto done; } git_oid_fmt(current_idstr, &operation->id); normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) goto done; *out = operation; done: git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_tree_free(current_tree); git_tree_free(head_tree); git_tree_free(parent_tree); git_commit_free(parent_commit); git_commit_free(current_commit); git_buf_free(&path); return error; }
static int try_to_git_merge(git_repository *repo, git_reference *local, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id) { git_tree *local_tree, *remote_tree, *base_tree; git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; git_merge_options merge_options; if (verbose) { char outlocal[41], outremote[41]; outlocal[40] = outremote[40] = 0; git_oid_fmt(outlocal, local_id); git_oid_fmt(outremote, remote_id); fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote); } git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); #ifdef USE_LIBGIT23_API merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; #else merge_options.flags = GIT_MERGE_TREE_FIND_RENAMES; #endif merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; merge_options.rename_threshold = 100; if (git_commit_lookup(&local_commit, repo, local_id)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); if (git_commit_tree(&local_tree, local_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed local tree lookup (%s)"), giterr_last()->message); if (git_commit_lookup(&remote_commit, repo, remote_id)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message); if (git_commit_tree(&remote_tree, remote_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed remote tree lookup (%s)"), giterr_last()->message); if (git_commit_lookup(&base_commit, repo, base)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message); if (git_commit_tree(&base_tree, base_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message); if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); if (git_index_has_conflicts(merged_index)) { int error; const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL; git_index_conflict_iterator *iter = NULL; error = git_index_conflict_iterator_new(&iter, merged_index); while (git_index_conflict_next(&ancestor, &ours, &theirs, iter) != GIT_ITEROVER) { /* Mark this conflict as resolved */ fprintf(stderr, "conflict in %s / %s / %s -- ", ours ? ours->path : "-", theirs ? theirs->path : "-", ancestor ? ancestor->path : "-"); if ((!ours && theirs && ancestor) || (ours && !theirs && ancestor)) { // the file was removed on one side or the other - just remove it fprintf(stderr, "looks like a delete on one side; removing the file from the index\n"); error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY); } else { error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path); } if (error) { fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message); } } git_index_conflict_cleanup(merged_index); git_index_conflict_iterator_free(iter); report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed")); } git_oid merge_oid, commit_oid; git_tree *merged_tree; git_signature *author; git_commit *commit; if (git_index_write_tree_to(&merge_oid, merged_index, repo)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message); if (git_tree_lookup(&merged_tree, repo, &merge_oid)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message); if (git_signature_default(&author, repo) < 0) return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message); if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message); if (git_commit_lookup(&commit, repo, &commit_oid)) return report_error(translate("gettextFromC", "Error: could not lookup the merge commit I just created (%s)"), giterr_last()->message); if (git_branch_is_head(local) && !git_repository_is_bare(repo)) { git_object *parent; git_reference_peel(&parent, local, GIT_OBJ_COMMIT); if (update_git_checkout(repo, parent, merged_tree)) { report_error("Warning: checked out branch is inconsistent with git data"); } } if (git_reference_set_target(&local, local, &commit_oid, "Subsurface merge event")) return report_error("Error: failed to update branch (%s)", giterr_last()->message); set_git_id(&commit_oid); git_signature_free(author); return 0; }
static int try_to_git_merge(git_repository *repo, git_reference **local_p, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id) { UNUSED(remote); git_tree *local_tree, *remote_tree, *base_tree; git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; git_merge_options merge_options; if (verbose) { char outlocal[41], outremote[41]; outlocal[40] = outremote[40] = 0; git_oid_fmt(outlocal, local_id); git_oid_fmt(outremote, remote_id); fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote); } git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION); #if !LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR > 23 merge_options.flags = GIT_MERGE_FIND_RENAMES; #else merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES; #endif merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION; merge_options.rename_threshold = 100; if (git_commit_lookup(&local_commit, repo, local_id)) { fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message); goto diverged_error; } if (git_commit_tree(&local_tree, local_commit)) { fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message); goto diverged_error; } if (git_commit_lookup(&remote_commit, repo, remote_id)) { fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message); goto diverged_error; } if (git_commit_tree(&remote_tree, remote_commit)) { fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message); goto diverged_error; } if (git_commit_lookup(&base_commit, repo, base)) { fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message); goto diverged_error; } if (git_commit_tree(&base_tree, base_commit)) { fprintf(stderr, "Remote storage and local data diverged. Error: failed base tree lookup (%s)", giterr_last()->message); goto diverged_error; } if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) { fprintf(stderr, "Remote storage and local data diverged. Error: merge failed (%s)", giterr_last()->message); // this is the one where I want to report more detail to the user - can't quite explain why return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message); } if (git_index_has_conflicts(merged_index)) { int error; const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL; git_index_conflict_iterator *iter = NULL; error = git_index_conflict_iterator_new(&iter, merged_index); while (git_index_conflict_next(&ancestor, &ours, &theirs, iter) != GIT_ITEROVER) { /* Mark this conflict as resolved */ fprintf(stderr, "conflict in %s / %s / %s -- ", ours ? ours->path : "-", theirs ? theirs->path : "-", ancestor ? ancestor->path : "-"); if ((!ours && theirs && ancestor) || (ours && !theirs && ancestor)) { // the file was removed on one side or the other - just remove it fprintf(stderr, "looks like a delete on one side; removing the file from the index\n"); error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY); } else if (ancestor) { error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path); } if (error) { fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message); } } git_index_conflict_cleanup(merged_index); git_index_conflict_iterator_free(iter); report_error(translate("gettextFromC", "Remote storage and local data diverged. Cannot combine local and remote changes")); } git_oid merge_oid, commit_oid; git_tree *merged_tree; git_signature *author; git_commit *commit; if (git_index_write_tree_to(&merge_oid, merged_index, repo)) goto write_error; if (git_tree_lookup(&merged_tree, repo, &merge_oid)) goto write_error; if (git_signature_default(&author, repo) < 0) if (git_signature_now(&author, "Subsurface", "noemail@given") < 0) goto write_error; if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit)) goto write_error; if (git_commit_lookup(&commit, repo, &commit_oid)) goto write_error; if (git_branch_is_head(*local_p) && !git_repository_is_bare(repo)) { git_object *parent; git_reference_peel(&parent, *local_p, GIT_OBJ_COMMIT); if (update_git_checkout(repo, parent, merged_tree)) { goto write_error; } } if (git_reference_set_target(local_p, *local_p, &commit_oid, "Subsurface merge event")) goto write_error; set_git_id(&commit_oid); git_signature_free(author); if (verbose) fprintf(stderr, "Successfully merged repositories"); return 0; diverged_error: return report_error(translate("gettextFromC", "Remote storage and local data diverged")); write_error: return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the data failed (%s)"), giterr_last()->message); }