builtin-am: implement --3way

Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), git-am.sh supported the --3way option, and if set, would attempt to do a 3-way merge if the initial patch application fails. Since 5d86861 (am -3: list the paths that needed 3-way fallback, 2012-03-28), in a 3-way merge git-am.sh would list the paths that needed 3-way fallback, so that the user can review them more carefully to spot mismerges. Re-implement the above in builtin/am.c. Signed-off-by: Paul Tan <pyokagan@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Paul Tan committed Aug 4, 2015 at 21:51 UTC 84f3de28baf6fc6a91b95fad9a4c5468dec3ea8e
1 file changed +150 -4
builtin/am.c
+150 -4
@@ -19,6 +19,8 @@
19 #include "unpack-trees.h"
20 #include "branch.h"
21 #include "sequencer.h"
22 +#include "revision.h"
23 +#include "merge-recursive.h"
24
25 /**
26 * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -83,6 +85,7 @@ struct am_state {
85 int prec;
86
87 /* various operating modes and command line options */
88 + int threeway;
89 int quiet;
90 int signoff;
91 const char *resolvemsg;
@@ -352,6 +355,9 @@ static void am_load(struct am_state *state)
355
356 read_commit_msg(state);
357
358 + read_state_file(&sb, state, "threeway", 1);
359 + state->threeway = !strcmp(sb.buf, "t");
360 +
361 read_state_file(&sb, state, "quiet", 1);
362 state->quiet = !strcmp(sb.buf, "t");
363
@@ -536,6 +542,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
542 die(_("Failed to split patches."));
543 }
544
545 + write_file(am_path(state, "threeway"), 1, state->threeway ? "t" : "f");
546 +
547 write_file(am_path(state, "quiet"), 1, state->quiet ? "t" : "f");
548
549 write_file(am_path(state, "sign"), 1, state->signoff ? "t" : "f");
@@ -766,25 +774,141 @@ finish:
774 }
775
776 /**
769 - * Applies current patch with git-apply. Returns 0 on success, -1 otherwise.
777 + * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
778 + * `index_file` is not NULL, the patch will be applied to that index.
779 */
771 -static int run_apply(const struct am_state *state)
780 +static int run_apply(const struct am_state *state, const char *index_file)
781 {
782 struct child_process cp = CHILD_PROCESS_INIT;
783
784 cp.git_cmd = 1;
785
786 + if (index_file)
787 + argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
788 +
789 + /*
790 + * If we are allowed to fall back on 3-way merge, don't give false
791 + * errors during the initial attempt.
792 + */
793 + if (state->threeway && !index_file) {
794 + cp.no_stdout = 1;
795 + cp.no_stderr = 1;
796 + }
797 +
798 argv_array_push(&cp.args, "apply");
778 - argv_array_push(&cp.args, "--index");
799 +
800 + if (index_file)
801 + argv_array_push(&cp.args, "--cached");
802 + else
803 + argv_array_push(&cp.args, "--index");
804 +
805 argv_array_push(&cp.args, am_path(state, "patch"));
806
807 if (run_command(&cp))
808 return -1;
809
810 /* Reload index as git-apply will have modified it. */
811 + discard_cache();
812 + read_cache_from(index_file ? index_file : get_index_file());
813 +
814 + return 0;
815 +}
816 +
817 +/**
818 + * Builds an index that contains just the blobs needed for a 3way merge.
819 + */
820 +static int build_fake_ancestor(const struct am_state *state, const char *index_file)
821 +{
822 + struct child_process cp = CHILD_PROCESS_INIT;
823 +
824 + cp.git_cmd = 1;
825 + argv_array_push(&cp.args, "apply");
826 + argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
827 + argv_array_push(&cp.args, am_path(state, "patch"));
828 +
829 + if (run_command(&cp))
830 + return -1;
831 +
832 + return 0;
833 +}
834 +
835 +/**
836 + * Attempt a threeway merge, using index_path as the temporary index.
837 + */
838 +static int fall_back_threeway(const struct am_state *state, const char *index_path)
839 +{
840 + unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
841 + our_tree[GIT_SHA1_RAWSZ];
842 + const unsigned char *bases[1] = {orig_tree};
843 + struct merge_options o;
844 + struct commit *result;
845 + char *his_tree_name;
846 +
847 + if (get_sha1("HEAD", our_tree) < 0)
848 + hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
849 +
850 + if (build_fake_ancestor(state, index_path))
851 + return error("could not build fake ancestor");
852 +
853 + discard_cache();
854 + read_cache_from(index_path);
855 +
856 + if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
857 + return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
858 +
859 + say(state, stdout, _("Using index info to reconstruct a base tree..."));
860 +
861 + if (!state->quiet) {
862 + /*
863 + * List paths that needed 3-way fallback, so that the user can
864 + * review them with extra care to spot mismerges.
865 + */
866 + struct rev_info rev_info;
867 + const char *diff_filter_str = "--diff-filter=AM";
868 +
869 + init_revisions(&rev_info, NULL);
870 + rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
871 + diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
872 + add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
873 + diff_setup_done(&rev_info.diffopt);
874 + run_diff_index(&rev_info, 1);
875 + }
876 +
877 + if (run_apply(state, index_path))
878 + return error(_("Did you hand edit your patch?\n"
879 + "It does not apply to blobs recorded in its index."));
880 +
881 + if (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))
882 + return error("could not write tree");
883 +
884 + say(state, stdout, _("Falling back to patching base and 3-way merge..."));
885 +
886 discard_cache();
887 read_cache();
888
889 + /*
890 + * This is not so wrong. Depending on which base we picked, orig_tree
891 + * may be wildly different from ours, but his_tree has the same set of
892 + * wildly different changes in parts the patch did not touch, so
893 + * recursive ends up canceling them, saying that we reverted all those
894 + * changes.
895 + */
896 +
897 + init_merge_options(&o);
898 +
899 + o.branch1 = "HEAD";
900 + his_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
901 + o.branch2 = his_tree_name;
902 +
903 + if (state->quiet)
904 + o.verbosity = 0;
905 +
906 + if (merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result)) {
907 + free(his_tree_name);
908 + return error(_("Failed to merge in the changes."));
909 + }
910 +
911 + free(his_tree_name);
912 return 0;
913 }
914
@@ -872,6 +996,7 @@ static void am_run(struct am_state *state, int resume)
996
997 while (state->cur <= state->last) {
998 const char *mail = am_path(state, msgnum(state));
999 + int apply_status;
1000
1001 if (!file_exists(mail))
1002 goto next;
@@ -889,7 +1014,26 @@ static void am_run(struct am_state *state, int resume)
1014
1015 say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
1016
892 - if (run_apply(state) < 0) {
1017 + apply_status = run_apply(state, NULL);
1018 +
1019 + if (apply_status && state->threeway) {
1020 + struct strbuf sb = STRBUF_INIT;
1021 +
1022 + strbuf_addstr(&sb, am_path(state, "patch-merge-index"));
1023 + apply_status = fall_back_threeway(state, sb.buf);
1024 + strbuf_release(&sb);
1025 +
1026 + /*
1027 + * Applying the patch to an earlier tree and merging
1028 + * the result may have produced the same tree as ours.
1029 + */
1030 + if (!apply_status && !index_has_changes(NULL)) {
1031 + say(state, stdout, _("No changes -- Patch already applied."));
1032 + goto next;
1033 + }
1034 + }
1035 +
1036 + if (apply_status) {
1037 int advice_amworkdir = 1;
1038
1039 printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
@@ -1159,6 +1303,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
1303 };
1304
1305 struct option options[] = {
1306 + OPT_BOOL('3', "3way", &state.threeway,
1307 + N_("allow fall back on 3way merging if needed")),
1308 OPT__QUIET(&state.quiet, N_("be quiet")),
1309 OPT_BOOL('s', "signoff", &state.signoff,
1310 N_("add a Signed-off-by line to the commit message")),