##// END OF EJS Templates
filemerge: add support for partial conflict resolution by external tool...
Martin von Zweigbergk -
r49838:f3aafd78 default
parent child Browse files
Show More
@@ -0,0 +1,209 b''
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
@@ -1570,6 +1570,37 b' coreconfigitem('
1570 default=False,
1570 default=False,
1571 )
1571 )
1572 coreconfigitem(
1572 coreconfigitem(
1573 b'partial-merge-tools',
1574 b'.*',
1575 default=None,
1576 generic=True,
1577 experimental=True,
1578 )
1579 coreconfigitem(
1580 b'partial-merge-tools',
1581 br'.*\.patterns',
1582 default=dynamicdefault,
1583 generic=True,
1584 priority=-1,
1585 experimental=True,
1586 )
1587 coreconfigitem(
1588 b'partial-merge-tools',
1589 br'.*\.executable$',
1590 default=dynamicdefault,
1591 generic=True,
1592 priority=-1,
1593 experimental=True,
1594 )
1595 coreconfigitem(
1596 b'partial-merge-tools',
1597 br'.*\.order',
1598 default=0,
1599 generic=True,
1600 priority=-1,
1601 experimental=True,
1602 )
1603 coreconfigitem(
1573 b'merge-tools',
1604 b'merge-tools',
1574 b'.*',
1605 b'.*',
1575 default=None,
1606 default=None,
@@ -1051,6 +1051,7 b' def filemerge(repo, wctx, mynode, orig, '
1051 markerstyle = internalmarkerstyle
1051 markerstyle = internalmarkerstyle
1052
1052
1053 if mergetype == fullmerge:
1053 if mergetype == fullmerge:
1054 _run_partial_resolution_tools(repo, local, other, base)
1054 # conflict markers generated by premerge will use 'detailed'
1055 # conflict markers generated by premerge will use 'detailed'
1055 # settings if either ui.mergemarkers or the tool's mergemarkers
1056 # settings if either ui.mergemarkers or the tool's mergemarkers
1056 # setting is 'detailed'. This way tools can have basic labels in
1057 # setting is 'detailed'. This way tools can have basic labels in
@@ -1115,6 +1116,62 b' def filemerge(repo, wctx, mynode, orig, '
1115 backup.remove()
1116 backup.remove()
1116
1117
1117
1118
1119 def _run_partial_resolution_tools(repo, local, other, base):
1120 """Runs partial-resolution tools on the three inputs and updates them."""
1121 ui = repo.ui
1122 # Tuples of (order, name, executable path)
1123 tools = []
1124 seen = set()
1125 section = b"partial-merge-tools"
1126 for k, v in ui.configitems(section):
1127 name = k.split(b'.')[0]
1128 if name in seen:
1129 continue
1130 patterns = ui.configlist(section, b'%s.patterns' % name, [])
1131 is_match = True
1132 if patterns:
1133 m = match.match(repo.root, b'', patterns)
1134 is_match = m(local.fctx.path())
1135 if is_match:
1136 order = ui.configint(section, b'%s.order' % name, 0)
1137 executable = ui.config(section, b'%s.executable' % name, name)
1138 tools.append((order, name, executable))
1139
1140 if not tools:
1141 return
1142 # Sort in configured order (first in tuple)
1143 tools.sort()
1144
1145 files = [
1146 (b"local", local.fctx.path(), local.text()),
1147 (b"base", base.fctx.path(), base.text()),
1148 (b"other", other.fctx.path(), other.text()),
1149 ]
1150
1151 with _maketempfiles(files) as temppaths:
1152 localpath, basepath, otherpath = temppaths
1153
1154 for order, name, executable in tools:
1155 cmd = procutil.shellquote(executable)
1156 # TODO: Allow the user to configure the command line using
1157 # $local, $base, $other.
1158 cmd = b'%s %s %s %s' % (cmd, localpath, basepath, otherpath)
1159 r = ui.system(cmd, cwd=repo.root, blockedtag=b'partial-mergetool')
1160 if r:
1161 raise error.StateError(
1162 b'partial merge tool %s exited with code %d' % (name, r)
1163 )
1164 local_text = util.readfile(localpath)
1165 other_text = util.readfile(otherpath)
1166 if local_text == other_text:
1167 # No need to run other tools if all conflicts have been resolved
1168 break
1169
1170 local.set_text(local_text)
1171 base.set_text(util.readfile(basepath))
1172 other.set_text(other_text)
1173
1174
1118 def _haltmerge():
1175 def _haltmerge():
1119 msg = _(b'merge halted after failed merge (see hg resolve)')
1176 msg = _(b'merge halted after failed merge (see hg resolve)')
1120 raise error.InterventionRequired(msg)
1177 raise error.InterventionRequired(msg)
@@ -490,6 +490,9 b' class MergeInput:'
490 self._text = self.fctx.decodeddata()
490 self._text = self.fctx.decodeddata()
491 return self._text
491 return self._text
492
492
493 def set_text(self, text):
494 self._text = text
495
493
496
494 def simplemerge(
497 def simplemerge(
495 local,
498 local,
General Comments 0
You need to be logged in to leave comments. Login now