comparison tests/test-merge-partial-tool.t @ 48981:f3aafd785e65

filemerge: add support for partial conflict resolution by external tool A common class of merge conflicts is in imports/#includes/etc. It's relatively easy to write a tool that can resolve these conflicts, perhaps by naively just unioning the statements and leaving any cleanup to other tools to do later [1]. Such specialized tools cannot generally resolve all conflicts in a file, of course. Let's therefore call them "partial merge tools". Note that the internal simplemerge algorithm is such a partial merge tool - one that only resolves trivial "conflicts" where one side is unchanged or both sides change in the same way. One can also imagine having smarter language-aware partial tools that merge the AST. It may be useful for such tools to interactively let the user resolve any conflicts it can't resolve itself. However, having the option of implementing it as a partial merge tool means that the developer doesn't *need* to create a UI for it. Instead, the user can resolve any remaining conflicts with their regular merge tool (e.g. `:merge3` or `meld). We don't currently have a way to let the user define such partial merge tools. That's what this patch addresses. It lets the user configure partial merge tools to run. Each tool can be configured to run only on files matching certain patterns (e.g. "*.py"). The tool takes three inputs (local, base, other) and resolves conflicts by updating these in place. For example, let's say the inputs are these: base: ``` import sys def main(): print('Hello') ``` local: ``` import os import sys def main(): print('Hi') ``` other: ``` import re import sys def main(): print('Howdy') ``` A partial merge tool could now resolve the conflicting imports by replacing the import statements in *all* files by the following snippet, while leaving the remainder of the files unchanged. ``` import os import re import sys ``` As a result, simplemerge and any regular merge tool that runs after the partial merge tool(s) will consider the imports to be non-conflicting and will only present the conflict in `main()` to the user. Differential Revision: https://phab.mercurial-scm.org/D12356
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 18 Jan 2022 13:05:21 -0800
parents
children 9dfbea54b680
comparison
equal deleted inserted replaced
48978:c80544aa4971 48981:f3aafd785e65
1 Test support for partial-resolution tools
2
3 Create a tool that resolves conflicts after line 5 by simply dropping those
4 lines (even if there are no conflicts there)
5 $ cat >> "$TESTTMP/head.sh" <<'EOF'
6 > #!/bin/sh
7 > for f in "$@"; do
8 > head -5 $f > tmp
9 > mv -f tmp $f
10 > done
11 > EOF
12 $ chmod +x "$TESTTMP/head.sh"
13 ...and another tool that keeps only the last 5 lines instead of the first 5.
14 $ cat >> "$TESTTMP/tail.sh" <<'EOF'
15 > #!/bin/sh
16 > for f in "$@"; do
17 > tail -5 $f > tmp
18 > mv -f tmp $f
19 > done
20 > EOF
21 $ chmod +x "$TESTTMP/tail.sh"
22
23 Set up both tools to run on all patterns (the default), and let the `tail` tool
24 run after the `head` tool, which means it will have no effect (we'll override it
25 to test order later)
26 $ cat >> "$HGRCPATH" <<EOF
27 > [partial-merge-tools]
28 > head.executable=$TESTTMP/head.sh
29 > tail.executable=$TESTTMP/tail.sh
30 > tail.order=1
31 > EOF
32
33 $ make_commit() {
34 > echo "$@" | xargs -n1 > file
35 > hg add file 2> /dev/null
36 > hg ci -m "$*"
37 > }
38
39
40 Let a partial-resolution tool resolve some conflicts and leave other conflicts
41 for the regular merge tool (:merge3 here)
42
43 $ hg init repo
44 $ cd repo
45 $ make_commit a b c d e f
46 $ make_commit a b2 c d e f2
47 $ hg up 0
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 $ make_commit a b3 c d e f3
50 created new head
51 $ hg merge 1 -t :merge3
52 merging file
53 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
54 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
55 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
56 [1]
57 $ cat file
58 a
59 <<<<<<< working copy: e11a49d4b620 - test: a b3 c d e f3
60 b3
61 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
62 b
63 =======
64 b2
65 >>>>>>> merge rev: fbc096a40cc5 - test: a b2 c d e f2
66 c
67 d
68 e
69
70
71 With premerge=keep, the partial-resolution tools runs before and doesn't see
72 the conflict markers
73
74 $ hg up -C 2
75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ cat >> .hg/hgrc <<EOF
77 > [merge-tools]
78 > my-local.executable = cat
79 > my-local.args = $local
80 > my-local.premerge = keep-merge3
81 > EOF
82 $ hg merge 1 -t my-local
83 merging file
84 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
85 (branch merge, don't forget to commit)
86 $ cat file
87 a
88 <<<<<<< working copy: e11a49d4b620 - test: a b3 c d e f3
89 b3
90 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
91 b
92 =======
93 b2
94 >>>>>>> merge rev: fbc096a40cc5 - test: a b2 c d e f2
95 c
96 d
97 e
98
99
100 When a partial-resolution tool resolves all conflicts, the resolution should
101 be recorded and the regular merge tool should not be invoked for the file.
102
103 $ hg up -C 0
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ make_commit a b c d e f2
106 created new head
107 $ hg up 0
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 $ make_commit a b c d e f3
110 created new head
111 $ hg merge 3 -t false
112 merging file
113 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
114 (branch merge, don't forget to commit)
115 $ cat file
116 a
117 b
118 c
119 d
120 e
121
122
123 Only tools whose patterns match are run. We make `head` not match here, so
124 only `tail` should run
125
126 $ hg up -C 4
127 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 $ hg merge 3 -t :merge3 --config partial-merge-tools.head.patterns=other
129 merging file
130 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
131 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
132 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
133 [1]
134 $ cat file
135 b
136 c
137 d
138 e
139 <<<<<<< working copy: d57edaa6e21a - test: a b c d e f3
140 f3
141 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
142 f
143 =======
144 f2
145 >>>>>>> merge rev: 8c217da987be - test: a b c d e f2
146
147
148 If there are several matching tools, they are run in requested order. We move
149 `head` after `tail` in order here so it has no effect (the conflict in "f" thus
150 remains).
151
152 $ hg up -C 4
153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 $ hg merge 3 -t :merge3 --config partial-merge-tools.head.order=2
155 merging file
156 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
157 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
158 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
159 [1]
160 $ cat file
161 b
162 c
163 d
164 e
165 <<<<<<< working copy: d57edaa6e21a - test: a b c d e f3
166 f3
167 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
168 f
169 =======
170 f2
171 >>>>>>> merge rev: 8c217da987be - test: a b c d e f2
172
173
174 When using "nomerge" tools (e.g. `:other`), the partial-resolution tools
175 should not be run.
176
177 $ hg up -C 4
178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 $ hg merge 3 -t :other
180 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
181 (branch merge, don't forget to commit)
182 $ cat file
183 a
184 b
185 c
186 d
187 e
188 f2
189
190
191 If a partial-resolution tool resolved some conflict and simplemerge can
192 merge the rest, then the regular merge tool should not be used. Here we merge
193 "a b c d e3 f3" with "a b2 c d e f2". The `head` tool resolves the conflict in
194 "f" and the internal simplemerge merges the remaining changes in "b" and "e".
195
196 $ hg up -C 0
197 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 $ make_commit a b c d e3 f3
199 created new head
200 $ hg merge 1 -t false
201 merging file
202 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
203 (branch merge, don't forget to commit)
204 $ cat file
205 a
206 b2
207 c
208 d
209 e3