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")),