##// END OF EJS Templates
merge: copy fixes and tests...
Matt Mackall -
r3252:ae85272b default
parent child Browse files
Show More
@@ -0,0 +1,102 b''
1 #!/bin/sh
2
3 mkdir -p t
4 cd t
5
6 cat <<'EOF' > merge
7 #!/bin/sh
8 echo merge > $1
9 EOF
10 chmod +x merge
11
12 # perform a test merge with possible renaming
13 #
14 # args:
15 # $1 = action in local branch
16 # $2 = action in remote branch
17 # $3 = action in working dir
18 # $4 = expected result
19 tm()
20 {
21 mkdir t
22 cd t
23 hg init
24 echo "[merge]" >> .hg/hgrc
25 echo "followcopies = 1" >> .hg/hgrc
26
27 # base
28 echo base > a
29 echo base > rev # used to force commits
30 hg add a rev
31 hg ci -m "base" -d "0 0"
32
33 # remote
34 echo remote > rev
35 if [ "$2" != "" ] ; then $2 ; fi
36 hg ci -m "remote" -d "0 0"
37
38 # local
39 hg co -q 0
40 echo local > rev
41 if [ "$1" != "" ] ; then $1 ; fi
42 hg ci -m "local" -d "0 0"
43
44 # working dir
45 echo local > rev
46 if [ "$3" != "" ] ; then $3 ; fi
47
48 # merge
49 echo "--------------"
50 echo "test L:$1 R:$2 W:$3 - $4"
51 echo "--------------"
52 env HGMERGE=../merge hg merge -y --debug --traceback
53
54 echo "--------------"
55 hg status -camC -X rev
56
57 hg ci -m "merge" -d "0 0"
58
59 echo "--------------"
60 echo
61
62 cd ..
63 rm -rf t
64 }
65
66 up() {
67 cp rev $1
68 hg add $1 2> /dev/null
69 if [ "$2" != "" ] ; then
70 cp rev $2
71 hg add $2 2> /dev/null
72 fi
73 }
74
75 uc() { up $1; hg cp $1 $2; } # update + copy
76 um() { up $1; hg mv $1 $2; }
77 nc() { hg cp $1 $2; } # just copy
78 nm() { hg mv $1 $2; } # just move
79
80 tm "up a " "nc a b" " " "1 get local a to b"
81 tm "nc a b" "up a " " " "2 get rem change to a and b"
82 tm "up a " "nm a b" " " "3 get local a change to b, remove a"
83 tm "nm a b" "up a " " " "4 get remote change to b"
84 tm " " "nc a b" " " "5 get b"
85 tm "nc a b" " " " " "6 nothing"
86 tm " " "nm a b" " " "7 get b"
87 tm "nm a b" " " " " "8 nothing"
88 tm "um a b" "um a b" " " "9 do merge with ancestor in a"
89 #tm "um a c" "um x c" " " "10 do merge with no ancestor"
90 tm "nm a b" "nm a c" " " "11 get c, keep b"
91 tm "nc a b" "up b " " " "12 merge b no ancestor"
92 tm "up b " "nm a b" " " "13 merge b no ancestor"
93 tm "nc a b" "up a b" " " "14 merge b no ancestor"
94 tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
95 tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
96 tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
97 tm "nm a b" "up a b" " " "18 merge b no ancestor"
98 tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
99 tm "up a " "um a b" " " "20 merge a and b to b, remove a"
100 tm "um a b" "up a " " " "21 merge a and b to b"
101 #tm "nm a b" "um x a" " " "22 get a, keep b"
102 tm "nm a b" "up a c" " " "23 get c, keep b"
@@ -0,0 +1,457 b''
1 --------------
2 test L:up a R:nc a b W: - 1 get local a to b
3 --------------
4 resolving manifests
5 overwrite None branchmerge True partial False
6 ancestor e300d1c794ec local 735846fee2d7 remote 924404dff337
7 rev: versions differ -> m
8 a: remote copied -> c
9 merging a and b to b
10 resolving a
11 my a@e300d1c794ec other b@735846fee2d7 ancestor a@924404dff337
12 copying a to b
13 merging rev
14 resolving rev
15 my rev@e300d1c794ec other rev@735846fee2d7 ancestor rev@924404dff337
16 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
17 (branch merge, don't forget to commit)
18 --------------
19 M b
20 a
21 C a
22 --------------
23
24 --------------
25 test L:nc a b R:up a W: - 2 get rem change to a and b
26 --------------
27 resolving manifests
28 overwrite None branchmerge True partial False
29 ancestor ac809aeed39a local f4db7e329e71 remote 924404dff337
30 a: remote is newer -> g
31 b: local copied -> c
32 rev: versions differ -> m
33 getting a
34 merging b and a to b
35 resolving b
36 my b@ac809aeed39a other a@f4db7e329e71 ancestor a@924404dff337
37 merging rev
38 resolving rev
39 my rev@ac809aeed39a other rev@f4db7e329e71 ancestor rev@924404dff337
40 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
41 (branch merge, don't forget to commit)
42 --------------
43 M a
44 M b
45 --------------
46
47 --------------
48 test L:up a R:nm a b W: - 3 get local a change to b, remove a
49 --------------
50 resolving manifests
51 overwrite None branchmerge True partial False
52 ancestor e300d1c794ec local e03727d2d66b remote 924404dff337
53 a: remote moved -> c
54 rev: versions differ -> m
55 merging a and b to b
56 resolving a
57 my a@e300d1c794ec other b@e03727d2d66b ancestor a@924404dff337
58 copying a to b
59 removing a
60 merging rev
61 resolving rev
62 my rev@e300d1c794ec other rev@e03727d2d66b ancestor rev@924404dff337
63 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
64 (branch merge, don't forget to commit)
65 --------------
66 M b
67 a
68 --------------
69
70 --------------
71 test L:nm a b R:up a W: - 4 get remote change to b
72 --------------
73 resolving manifests
74 overwrite None branchmerge True partial False
75 ancestor ecf3cb2a4219 local f4db7e329e71 remote 924404dff337
76 b: local moved -> c
77 rev: versions differ -> m
78 merging b and a to b
79 resolving b
80 my b@ecf3cb2a4219 other a@f4db7e329e71 ancestor a@924404dff337
81 merging rev
82 resolving rev
83 my rev@ecf3cb2a4219 other rev@f4db7e329e71 ancestor rev@924404dff337
84 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
85 (branch merge, don't forget to commit)
86 --------------
87 M b
88 --------------
89
90 --------------
91 test L: R:nc a b W: - 5 get b
92 --------------
93 resolving manifests
94 overwrite None branchmerge True partial False
95 ancestor 94b33a1b7f2d local 735846fee2d7 remote 924404dff337
96 rev: versions differ -> m
97 a: remote copied -> c
98 merging a and b to b
99 resolving a
100 my a@924404dff337 other b@735846fee2d7 ancestor a@924404dff337
101 copying a to b
102 merging rev
103 resolving rev
104 my rev@94b33a1b7f2d other rev@735846fee2d7 ancestor rev@924404dff337
105 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
106 (branch merge, don't forget to commit)
107 --------------
108 M a
109 M b
110 a
111 --------------
112
113 --------------
114 test L:nc a b R: W: - 6 nothing
115 --------------
116 resolving manifests
117 overwrite None branchmerge True partial False
118 ancestor ac809aeed39a local 97c705ade336 remote 924404dff337
119 b: local copied -> c
120 rev: versions differ -> m
121 merging b and a to b
122 resolving b
123 my b@ac809aeed39a other a@924404dff337 ancestor a@924404dff337
124 merging rev
125 resolving rev
126 my rev@ac809aeed39a other rev@97c705ade336 ancestor rev@924404dff337
127 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
128 (branch merge, don't forget to commit)
129 --------------
130 M b
131 C a
132 --------------
133
134 --------------
135 test L: R:nm a b W: - 7 get b
136 --------------
137 resolving manifests
138 overwrite None branchmerge True partial False
139 ancestor 94b33a1b7f2d local e03727d2d66b remote 924404dff337
140 a: remote moved -> c
141 rev: versions differ -> m
142 merging a and b to b
143 resolving a
144 my a@924404dff337 other b@e03727d2d66b ancestor a@924404dff337
145 copying a to b
146 removing a
147 merging rev
148 resolving rev
149 my rev@94b33a1b7f2d other rev@e03727d2d66b ancestor rev@924404dff337
150 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
151 (branch merge, don't forget to commit)
152 --------------
153 M b
154 a
155 --------------
156
157 --------------
158 test L:nm a b R: W: - 8 nothing
159 --------------
160 resolving manifests
161 overwrite None branchmerge True partial False
162 ancestor ecf3cb2a4219 local 97c705ade336 remote 924404dff337
163 b: local moved -> c
164 rev: versions differ -> m
165 merging b and a to b
166 resolving b
167 my b@ecf3cb2a4219 other a@924404dff337 ancestor a@924404dff337
168 merging rev
169 resolving rev
170 my rev@ecf3cb2a4219 other rev@97c705ade336 ancestor rev@924404dff337
171 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
172 (branch merge, don't forget to commit)
173 --------------
174 M b
175 --------------
176
177 --------------
178 test L:um a b R:um a b W: - 9 do merge with ancestor in a
179 --------------
180 resolving manifests
181 overwrite None branchmerge True partial False
182 ancestor ec03c2ca8642 local 79cc6877a3b7 remote 924404dff337
183 b: versions differ -> m
184 rev: versions differ -> m
185 merging b
186 resolving b
187 my b@ec03c2ca8642 other b@79cc6877a3b7 ancestor a@924404dff337
188 merging rev
189 resolving rev
190 my rev@ec03c2ca8642 other rev@79cc6877a3b7 ancestor rev@924404dff337
191 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
192 (branch merge, don't forget to commit)
193 --------------
194 M b
195 --------------
196
197 --------------
198 test L:nm a b R:nm a c W: - 11 get c, keep b
199 --------------
200 resolving manifests
201 overwrite None branchmerge True partial False
202 ancestor ecf3cb2a4219 local e6abcc1a30c2 remote 924404dff337
203 rev: versions differ -> m
204 c: remote created -> g
205 getting c
206 merging rev
207 resolving rev
208 my rev@ecf3cb2a4219 other rev@e6abcc1a30c2 ancestor rev@924404dff337
209 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
210 (branch merge, don't forget to commit)
211 --------------
212 M c
213 C b
214 --------------
215
216 --------------
217 test L:nc a b R:up b W: - 12 merge b no ancestor
218 --------------
219 resolving manifests
220 overwrite None branchmerge True partial False
221 ancestor ac809aeed39a local af30c7647fc7 remote 924404dff337
222 b: versions differ -> m
223 rev: versions differ -> m
224 merging b
225 resolving b
226 my b@ac809aeed39a other b@af30c7647fc7 ancestor b@000000000000
227 merging rev
228 resolving rev
229 my rev@ac809aeed39a other rev@af30c7647fc7 ancestor rev@924404dff337
230 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
231 (branch merge, don't forget to commit)
232 --------------
233 M b
234 C a
235 --------------
236
237 --------------
238 test L:up b R:nm a b W: - 13 merge b no ancestor
239 --------------
240 resolving manifests
241 overwrite None branchmerge True partial False
242 ancestor 59318016310c local e03727d2d66b remote 924404dff337
243 a: other deleted -> r
244 b: versions differ -> m
245 rev: versions differ -> m
246 removing a
247 merging b
248 resolving b
249 my b@59318016310c other b@e03727d2d66b ancestor b@000000000000
250 merging rev
251 resolving rev
252 my rev@59318016310c other rev@e03727d2d66b ancestor rev@924404dff337
253 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
254 (branch merge, don't forget to commit)
255 --------------
256 M b
257 --------------
258
259 --------------
260 test L:nc a b R:up a b W: - 14 merge b no ancestor
261 --------------
262 resolving manifests
263 overwrite None branchmerge True partial False
264 ancestor ac809aeed39a local 8dbce441892a remote 924404dff337
265 a: remote is newer -> g
266 b: versions differ -> m
267 rev: versions differ -> m
268 getting a
269 merging b
270 resolving b
271 my b@ac809aeed39a other b@8dbce441892a ancestor b@000000000000
272 merging rev
273 resolving rev
274 my rev@ac809aeed39a other rev@8dbce441892a ancestor rev@924404dff337
275 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
276 (branch merge, don't forget to commit)
277 --------------
278 M a
279 M b
280 --------------
281
282 --------------
283 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
284 --------------
285 resolving manifests
286 overwrite None branchmerge True partial False
287 ancestor 59318016310c local e03727d2d66b remote 924404dff337
288 a: other deleted -> r
289 b: versions differ -> m
290 rev: versions differ -> m
291 removing a
292 merging b
293 resolving b
294 my b@59318016310c other b@e03727d2d66b ancestor b@000000000000
295 merging rev
296 resolving rev
297 my rev@59318016310c other rev@e03727d2d66b ancestor rev@924404dff337
298 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
299 (branch merge, don't forget to commit)
300 --------------
301 M b
302 --------------
303
304 --------------
305 test L:nc a b R:up a b W: - 16 merge a, merge b no ancestor
306 --------------
307 resolving manifests
308 overwrite None branchmerge True partial False
309 ancestor ac809aeed39a local 8dbce441892a remote 924404dff337
310 a: remote is newer -> g
311 b: versions differ -> m
312 rev: versions differ -> m
313 getting a
314 merging b
315 resolving b
316 my b@ac809aeed39a other b@8dbce441892a ancestor b@000000000000
317 merging rev
318 resolving rev
319 my rev@ac809aeed39a other rev@8dbce441892a ancestor rev@924404dff337
320 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
321 (branch merge, don't forget to commit)
322 --------------
323 M a
324 M b
325 --------------
326
327 --------------
328 test L:up a b R:nc a b W: - 17 merge a, merge b no ancestor
329 --------------
330 resolving manifests
331 overwrite None branchmerge True partial False
332 ancestor 0b76e65c8289 local 735846fee2d7 remote 924404dff337
333 b: versions differ -> m
334 rev: versions differ -> m
335 merging b
336 resolving b
337 my b@0b76e65c8289 other b@735846fee2d7 ancestor b@000000000000
338 merging rev
339 resolving rev
340 my rev@0b76e65c8289 other rev@735846fee2d7 ancestor rev@924404dff337
341 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
342 (branch merge, don't forget to commit)
343 --------------
344 M b
345 C a
346 --------------
347
348 --------------
349 test L:nm a b R:up a b W: - 18 merge b no ancestor
350 --------------
351 resolving manifests
352 overwrite None branchmerge True partial False
353 ancestor ecf3cb2a4219 local 8dbce441892a remote 924404dff337
354 b: versions differ -> m
355 rev: versions differ -> m
356 a: prompt recreating -> g
357 getting a
358 merging b
359 resolving b
360 my b@ecf3cb2a4219 other b@8dbce441892a ancestor b@000000000000
361 merging rev
362 resolving rev
363 my rev@ecf3cb2a4219 other rev@8dbce441892a ancestor rev@924404dff337
364 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
365 (branch merge, don't forget to commit)
366 --------------
367 M a
368 M b
369 --------------
370
371 --------------
372 test L:up a b R:nm a b W: - 19 merge b no ancestor, remove a
373 --------------
374 resolving manifests
375 overwrite None branchmerge True partial False
376 ancestor 0b76e65c8289 local e03727d2d66b remote 924404dff337
377 b: versions differ -> m
378 rev: versions differ -> m
379 merging b
380 resolving b
381 my b@0b76e65c8289 other b@e03727d2d66b ancestor b@000000000000
382 merging rev
383 resolving rev
384 my rev@0b76e65c8289 other rev@e03727d2d66b ancestor rev@924404dff337
385 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
386 (branch merge, don't forget to commit)
387 --------------
388 M b
389 C a
390 --------------
391
392 --------------
393 test L:up a R:um a b W: - 20 merge a and b to b, remove a
394 --------------
395 resolving manifests
396 overwrite None branchmerge True partial False
397 ancestor e300d1c794ec local 79cc6877a3b7 remote 924404dff337
398 a: remote moved -> c
399 rev: versions differ -> m
400 merging a and b to b
401 resolving a
402 my a@e300d1c794ec other b@79cc6877a3b7 ancestor a@924404dff337
403 copying a to b
404 removing a
405 merging rev
406 resolving rev
407 my rev@e300d1c794ec other rev@79cc6877a3b7 ancestor rev@924404dff337
408 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
409 (branch merge, don't forget to commit)
410 --------------
411 M b
412 a
413 --------------
414
415 --------------
416 test L:um a b R:up a W: - 21 merge a and b to b
417 --------------
418 resolving manifests
419 overwrite None branchmerge True partial False
420 ancestor ec03c2ca8642 local f4db7e329e71 remote 924404dff337
421 b: local moved -> c
422 rev: versions differ -> m
423 merging b and a to b
424 resolving b
425 my b@ec03c2ca8642 other a@f4db7e329e71 ancestor a@924404dff337
426 merging rev
427 resolving rev
428 my rev@ec03c2ca8642 other rev@f4db7e329e71 ancestor rev@924404dff337
429 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
430 (branch merge, don't forget to commit)
431 --------------
432 M b
433 --------------
434
435 --------------
436 test L:nm a b R:up a c W: - 23 get c, keep b
437 --------------
438 resolving manifests
439 overwrite None branchmerge True partial False
440 ancestor ecf3cb2a4219 local 2b958612230f remote 924404dff337
441 b: local moved -> c
442 rev: versions differ -> m
443 c: remote created -> g
444 merging b and a to b
445 resolving b
446 my b@ecf3cb2a4219 other a@2b958612230f ancestor a@924404dff337
447 getting c
448 merging rev
449 resolving rev
450 my rev@ecf3cb2a4219 other rev@2b958612230f ancestor rev@924404dff337
451 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
452 (branch merge, don't forget to commit)
453 --------------
454 M b
455 M c
456 --------------
457
@@ -1,462 +1,463 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "errno util os tempfile")
11 demandload(globals(), "errno util os tempfile")
12
12
13 def filemerge(repo, fw, fo, fd, my, other, p1, p2, move):
13 def filemerge(repo, fw, fo, fd, my, other, p1, p2, move):
14 """perform a 3-way merge in the working directory
14 """perform a 3-way merge in the working directory
15
15
16 fw = filename in the working directory and first parent
16 fw = filename in the working directory and first parent
17 fo = filename in other parent
17 fo = filename in other parent
18 fd = destination filename
18 fd = destination filename
19 my = fileid in first parent
19 my = fileid in first parent
20 other = fileid in second parent
20 other = fileid in second parent
21 p1, p2 = hex changeset ids for merge command
21 p1, p2 = hex changeset ids for merge command
22 move = whether to move or copy the file to the destination
22 move = whether to move or copy the file to the destination
23
23
24 TODO:
24 TODO:
25 if fw is copied in the working directory, we get confused
25 if fw is copied in the working directory, we get confused
26 implement move and fd
26 implement move and fd
27 """
27 """
28
28
29 def temp(prefix, ctx):
29 def temp(prefix, ctx):
30 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
30 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
31 (fd, name) = tempfile.mkstemp(prefix=pre)
31 (fd, name) = tempfile.mkstemp(prefix=pre)
32 f = os.fdopen(fd, "wb")
32 f = os.fdopen(fd, "wb")
33 repo.wwrite(ctx.path(), ctx.data(), f)
33 repo.wwrite(ctx.path(), ctx.data(), f)
34 f.close()
34 f.close()
35 return name
35 return name
36
36
37 fcm = repo.filectx(fw, fileid=my)
37 fcm = repo.filectx(fw, fileid=my)
38 fco = repo.filectx(fo, fileid=other)
38 fco = repo.filectx(fo, fileid=other)
39 fca = fcm.ancestor(fco)
39 fca = fcm.ancestor(fco)
40 if not fca:
40 if not fca:
41 fca = repo.filectx(fw, fileid=-1)
41 fca = repo.filectx(fw, fileid=-1)
42 a = repo.wjoin(fw)
42 a = repo.wjoin(fw)
43 b = temp("base", fca)
43 b = temp("base", fca)
44 c = temp("other", fco)
44 c = temp("other", fco)
45
45
46 repo.ui.note(_("resolving %s\n") % fw)
46 repo.ui.note(_("resolving %s\n") % fw)
47 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
47 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
48
48
49 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
49 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
50 or "hgmerge")
50 or "hgmerge")
51 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
51 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
52 environ={'HG_FILE': fw,
52 environ={'HG_FILE': fw,
53 'HG_MY_NODE': p1,
53 'HG_MY_NODE': p1,
54 'HG_OTHER_NODE': p2})
54 'HG_OTHER_NODE': p2})
55 if r:
55 if r:
56 repo.ui.warn(_("merging %s failed!\n") % fw)
56 repo.ui.warn(_("merging %s failed!\n") % fw)
57 else:
57 else:
58 if fd != fw:
58 if fd != fw:
59 repo.ui.debug(_("copying %s to %s\n") % (fw, fd))
59 repo.ui.debug(_("copying %s to %s\n") % (fw, fd))
60 repo.wwrite(fd, repo.wread(fw))
60 repo.wwrite(fd, repo.wread(fw))
61 if move:
61 if move:
62 repo.ui.debug(_("removing %s\n") % fw)
62 repo.ui.debug(_("removing %s\n") % fw)
63 os.unlink(a)
63 os.unlink(a)
64
64
65 os.unlink(b)
65 os.unlink(b)
66 os.unlink(c)
66 os.unlink(c)
67 return r
67 return r
68
68
69 def checkunknown(repo, m2, wctx):
69 def checkunknown(repo, m2, wctx):
70 """
70 """
71 check for collisions between unknown files and files in m2
71 check for collisions between unknown files and files in m2
72 """
72 """
73 for f in wctx.unknown():
73 for f in wctx.unknown():
74 if f in m2:
74 if f in m2:
75 if repo.file(f).cmp(m2[f], repo.wread(f)):
75 if repo.file(f).cmp(m2[f], repo.wread(f)):
76 raise util.Abort(_("'%s' already exists in the working"
76 raise util.Abort(_("'%s' already exists in the working"
77 " dir and differs from remote") % f)
77 " dir and differs from remote") % f)
78
78
79 def forgetremoved(m2, wctx):
79 def forgetremoved(m2, wctx):
80 """
80 """
81 Forget removed files
81 Forget removed files
82
82
83 If we're jumping between revisions (as opposed to merging), and if
83 If we're jumping between revisions (as opposed to merging), and if
84 neither the working directory nor the target rev has the file,
84 neither the working directory nor the target rev has the file,
85 then we need to remove it from the dirstate, to prevent the
85 then we need to remove it from the dirstate, to prevent the
86 dirstate from listing the file when it is no longer in the
86 dirstate from listing the file when it is no longer in the
87 manifest.
87 manifest.
88 """
88 """
89
89
90 action = []
90 action = []
91
91
92 for f in wctx.deleted() + wctx.removed():
92 for f in wctx.deleted() + wctx.removed():
93 if f not in m2:
93 if f not in m2:
94 action.append((f, "f"))
94 action.append((f, "f"))
95
95
96 return action
96 return action
97
97
98 def nonoverlap(d1, d2):
98 def nonoverlap(d1, d2):
99 """
99 """
100 Return list of elements in d1 not in d2
100 Return list of elements in d1 not in d2
101 """
101 """
102
102
103 l = []
103 l = []
104 for d in d1:
104 for d in d1:
105 if d not in d2:
105 if d not in d2:
106 l.append(d)
106 l.append(d)
107
107
108 l.sort()
108 l.sort()
109 return l
109 return l
110
110
111 def findold(fctx, limit):
111 def findold(fctx, limit):
112 """
112 """
113 find files that path was copied from, back to linkrev limit
113 find files that path was copied from, back to linkrev limit
114 """
114 """
115
115
116 old = {}
116 old = {}
117 orig = fctx.path()
117 orig = fctx.path()
118 visit = [fctx]
118 visit = [fctx]
119 while visit:
119 while visit:
120 fc = visit.pop()
120 fc = visit.pop()
121 if fc.rev() < limit:
121 if fc.rev() < limit:
122 continue
122 continue
123 if fc.path() != orig and fc.path() not in old:
123 if fc.path() != orig and fc.path() not in old:
124 old[fc.path()] = 1
124 old[fc.path()] = 1
125 visit += fc.parents()
125 visit += fc.parents()
126
126
127 old = old.keys()
127 old = old.keys()
128 old.sort()
128 old.sort()
129 return old
129 return old
130
130
131 def findcopies(repo, m1, m2, limit):
131 def findcopies(repo, m1, m2, limit):
132 """
132 """
133 Find moves and copies between m1 and m2 back to limit linkrev
133 Find moves and copies between m1 and m2 back to limit linkrev
134 """
134 """
135
135
136 if not repo.ui.config("merge", "followcopies"):
136 if not repo.ui.config("merge", "followcopies"):
137 return {}
137 return {}
138
138
139 # avoid silly behavior for update from empty dir
139 # avoid silly behavior for update from empty dir
140 if not m1:
140 if not m1:
141 return {}
141 return {}
142
142
143 dcopies = repo.dirstate.copies()
143 dcopies = repo.dirstate.copies()
144 copy = {}
144 copy = {}
145 match = {}
145 match = {}
146 u1 = nonoverlap(m1, m2)
146 u1 = nonoverlap(m1, m2)
147 u2 = nonoverlap(m2, m1)
147 u2 = nonoverlap(m2, m1)
148 ctx = util.cachefunc(lambda f,n: repo.filectx(f, fileid=n[:20]))
148 ctx = util.cachefunc(lambda f,n: repo.filectx(f, fileid=n[:20]))
149
149
150 def checkpair(c, f2, man):
150 def checkpair(c, f2, man):
151 ''' check if an apparent pair actually matches '''
151 ''' check if an apparent pair actually matches '''
152 c2 = ctx(f2, man[f2])
152 c2 = ctx(f2, man[f2])
153 ca = c.ancestor(c2)
153 ca = c.ancestor(c2)
154 if ca and ca.path() == c.path() or ca.path() == c2.path():
154 if ca and ca.path() == c.path() or ca.path() == c2.path():
155 copy[c.path()] = f2
155 copy[c.path()] = f2
156 copy[f2] = c.path()
156 copy[f2] = c.path()
157
157
158 for f in u1:
158 for f in u1:
159 c = ctx(dcopies.get(f, f), m1[f])
159 c = ctx(dcopies.get(f, f), m1[f])
160 for of in findold(c, limit):
160 for of in findold(c, limit):
161 if of in m2:
161 if of in m2:
162 checkpair(c, of, m2)
162 checkpair(c, of, m2)
163 else:
163 else:
164 match.setdefault(of, []).append(f)
164 match.setdefault(of, []).append(f)
165
165
166 for f in u2:
166 for f in u2:
167 c = ctx(f, m2[f])
167 c = ctx(f, m2[f])
168 for of in findold(c, limit):
168 for of in findold(c, limit):
169 if of in m1:
169 if of in m1:
170 checkpair(c, of, m1)
170 checkpair(c, of, m1)
171 elif of in match:
171 elif of in match:
172 for mf in match[of]:
172 for mf in match[of]:
173 checkpair(c, mf, m1)
173 checkpair(c, mf, m1)
174
174
175 return copy
175 return copy
176
176
177 def manifestmerge(ui, m1, m2, ma, copy, overwrite, backwards, partial):
177 def manifestmerge(ui, m1, m2, ma, copy, overwrite, backwards, partial):
178 """
178 """
179 Merge manifest m1 with m2 using ancestor ma and generate merge action list
179 Merge manifest m1 with m2 using ancestor ma and generate merge action list
180 """
180 """
181
181
182 def fmerge(f, f2=None, fa=None):
182 def fmerge(f, f2=None, fa=None):
183 """merge executable flags"""
183 """merge executable flags"""
184 if not f2:
184 if not f2:
185 f2 = f
185 f2 = f
186 fa = f
186 fa = f
187 a, b, c = ma.execf(fa), m1.execf(f), m2.execf(f2)
187 a, b, c = ma.execf(fa), m1.execf(f), m2.execf(f2)
188 return ((a^b) | (a^c)) ^ a
188 return ((a^b) | (a^c)) ^ a
189
189
190 action = []
190 action = []
191
191
192 def act(msg, f, m, *args):
192 def act(msg, f, m, *args):
193 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
193 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
194 action.append((f, m) + args)
194 action.append((f, m) + args)
195
195
196 # Compare manifests
196 # Compare manifests
197 for f, n in m1.iteritems():
197 for f, n in m1.iteritems():
198 if partial and not partial(f):
198 if partial and not partial(f):
199 continue
199 continue
200 if f in m2:
200 if f in m2:
201 # are files different?
201 # are files different?
202 if n != m2[f]:
202 if n != m2[f]:
203 a = ma.get(f, nullid)
203 a = ma.get(f, nullid)
204 # are both different from the ancestor?
204 # are both different from the ancestor?
205 if not overwrite and n != a and m2[f] != a:
205 if not overwrite and n != a and m2[f] != a:
206 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
206 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
207 # are we clobbering?
207 # are we clobbering?
208 # is remote's version newer?
208 # is remote's version newer?
209 # or are we going back in time and clean?
209 # or are we going back in time and clean?
210 elif overwrite or m2[f] != a or (backwards and not n[20:]):
210 elif overwrite or m2[f] != a or (backwards and not n[20:]):
211 act("remote is newer", f, "g", m2.execf(f), m2[f])
211 act("remote is newer", f, "g", m2.execf(f), m2[f])
212 # local is newer, not overwrite, check mode bits
212 # local is newer, not overwrite, check mode bits
213 elif fmerge(f) != m1.execf(f):
213 elif fmerge(f) != m1.execf(f):
214 act("update permissions", f, "e", m2.execf(f))
214 act("update permissions", f, "e", m2.execf(f))
215 # contents same, check mode bits
215 # contents same, check mode bits
216 elif m1.execf(f) != m2.execf(f):
216 elif m1.execf(f) != m2.execf(f):
217 if overwrite or fmerge(f) != m1.execf(f):
217 if overwrite or fmerge(f) != m1.execf(f):
218 act("update permissions", f, "e", m2.execf(f))
218 act("update permissions", f, "e", m2.execf(f))
219 elif f in copy:
219 elif f in copy:
220 f2 = copy[f]
220 f2 = copy[f]
221 if f in ma: # case 3,20 A/B/A
221 if f in ma: # case 3,20 A/B/A
222 act("remote moved",
222 act("remote moved",
223 f, "c", f2, f2, m1[f], m2[f2], fmerge(f, f2, f), True)
223 f, "c", f2, f2, m1[f], m2[f2], fmerge(f, f2, f), True)
224 else:
224 else:
225 if f2 in m1: # case 2 A,B/B/B
225 if f2 in m1: # case 2 A,B/B/B
226 act("local copied",
226 act("local copied",
227 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False)
227 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False)
228 else: # case 4,21 A/B/B
228 else: # case 4,21 A/B/B
229 act("local moved",
229 act("local moved",
230 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False)
230 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False)
231 elif f in ma:
231 elif f in ma:
232 if n != ma[f] and not overwrite:
232 if n != ma[f] and not overwrite:
233 if ui.prompt(
233 if ui.prompt(
234 (_(" local changed %s which remote deleted\n") % f) +
234 (_(" local changed %s which remote deleted\n") % f) +
235 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
235 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
236 act("prompt delete", f, "r")
236 act("prompt delete", f, "r")
237 else:
237 else:
238 act("other deleted", f, "r")
238 act("other deleted", f, "r")
239 else:
239 else:
240 # file is created on branch or in working directory
240 # file is created on branch or in working directory
241 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
241 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
242 act("remote deleted", f, "r")
242 act("remote deleted", f, "r")
243
243
244 for f, n in m2.iteritems():
244 for f, n in m2.iteritems():
245 if partial and not partial(f):
245 if partial and not partial(f):
246 continue
246 continue
247 if f in m1:
247 if f in m1:
248 continue
248 continue
249 if f in copy:
249 if f in copy:
250 f2 = copy[f]
250 f2 = copy[f]
251 if f2 in ma or f2 in m1: # already seen
251 if f2 not in m2: # already seen
252 continue
252 continue
253 # rename case 1, A/A,B/A
253 # rename case 1, A/A,B/A
254 act("remote copied",
254 act("remote copied",
255 f, "c", f2, f, m1[f2], m2[f], fmerge(f2, f, f2), False)
255 f2, "c", f, f, m1[f2], m2[f], fmerge(f2, f, f2), False)
256 elif f in ma:
256 elif f in ma:
257 if overwrite or backwards:
257 if overwrite or backwards:
258 act("recreating", f, "g", m2.execf(f), n)
258 act("recreating", f, "g", m2.execf(f), n)
259 elif n != ma[f]:
259 elif n != ma[f]:
260 if ui.prompt(
260 if ui.prompt(
261 (_("remote changed %s which local deleted\n") % f) +
261 (_("remote changed %s which local deleted\n") % f) +
262 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
262 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
263 act("prompt recreating", f, "g", m2.execf(f), n)
263 act("prompt recreating", f, "g", m2.execf(f), n)
264 else:
264 else:
265 act("remote created", f, "g", m2.execf(f), n)
265 act("remote created", f, "g", m2.execf(f), n)
266
266
267 return action
267 return action
268
268
269 def applyupdates(repo, action, xp1, xp2):
269 def applyupdates(repo, action, xp1, xp2):
270 updated, merged, removed, unresolved = 0, 0, 0, 0
270 updated, merged, removed, unresolved = 0, 0, 0, 0
271 action.sort()
271 action.sort()
272 for a in action:
272 for a in action:
273 f, m = a[:2]
273 f, m = a[:2]
274 if f[0] == "/":
274 if f[0] == "/":
275 continue
275 continue
276 if m == "r": # remove
276 if m == "r": # remove
277 repo.ui.note(_("removing %s\n") % f)
277 repo.ui.note(_("removing %s\n") % f)
278 util.audit_path(f)
278 util.audit_path(f)
279 try:
279 try:
280 util.unlink(repo.wjoin(f))
280 util.unlink(repo.wjoin(f))
281 except OSError, inst:
281 except OSError, inst:
282 if inst.errno != errno.ENOENT:
282 if inst.errno != errno.ENOENT:
283 repo.ui.warn(_("update failed to remove %s: %s!\n") %
283 repo.ui.warn(_("update failed to remove %s: %s!\n") %
284 (f, inst.strerror))
284 (f, inst.strerror))
285 removed +=1
285 removed +=1
286 elif m == "c": # copy
286 elif m == "c": # copy
287 f2, fd, my, other, flag, move = a[2:]
287 f2, fd, my, other, flag, move = a[2:]
288 repo.ui.status(_("merging %s and %s to %s\n") % (f, f2, fd))
288 if filemerge(repo, f, f2, fd, my, other, xp1, xp2, move):
289 if filemerge(repo, f, f2, fd, my, other, xp1, xp2, move):
289 unresolved += 1
290 unresolved += 1
290 util.set_exec(repo.wjoin(fd), flag)
291 util.set_exec(repo.wjoin(fd), flag)
291 merged += 1
292 merged += 1
292 elif m == "m": # merge
293 elif m == "m": # merge
293 flag, my, other = a[2:]
294 flag, my, other = a[2:]
294 repo.ui.status(_("merging %s\n") % f)
295 repo.ui.status(_("merging %s\n") % f)
295 if filemerge(repo, f, f, f, my, other, xp1, xp2, False):
296 if filemerge(repo, f, f, f, my, other, xp1, xp2, False):
296 unresolved += 1
297 unresolved += 1
297 util.set_exec(repo.wjoin(f), flag)
298 util.set_exec(repo.wjoin(f), flag)
298 merged += 1
299 merged += 1
299 elif m == "g": # get
300 elif m == "g": # get
300 flag, node = a[2:]
301 flag, node = a[2:]
301 repo.ui.note(_("getting %s\n") % f)
302 repo.ui.note(_("getting %s\n") % f)
302 t = repo.file(f).read(node)
303 t = repo.file(f).read(node)
303 repo.wwrite(f, t)
304 repo.wwrite(f, t)
304 util.set_exec(repo.wjoin(f), flag)
305 util.set_exec(repo.wjoin(f), flag)
305 updated += 1
306 updated += 1
306 elif m == "e": # exec
307 elif m == "e": # exec
307 flag = a[2:]
308 flag = a[2:]
308 util.set_exec(repo.wjoin(f), flag)
309 util.set_exec(repo.wjoin(f), flag)
309
310
310 return updated, merged, removed, unresolved
311 return updated, merged, removed, unresolved
311
312
312 def recordupdates(repo, action, branchmerge):
313 def recordupdates(repo, action, branchmerge):
313 for a in action:
314 for a in action:
314 f, m = a[:2]
315 f, m = a[:2]
315 if m == "r": # remove
316 if m == "r": # remove
316 if branchmerge:
317 if branchmerge:
317 repo.dirstate.update([f], 'r')
318 repo.dirstate.update([f], 'r')
318 else:
319 else:
319 repo.dirstate.forget([f])
320 repo.dirstate.forget([f])
320 elif m == "f": # forget
321 elif m == "f": # forget
321 repo.dirstate.forget([f])
322 repo.dirstate.forget([f])
322 elif m == "g": # get
323 elif m == "g": # get
323 if branchmerge:
324 if branchmerge:
324 repo.dirstate.update([f], 'n', st_mtime=-1)
325 repo.dirstate.update([f], 'n', st_mtime=-1)
325 else:
326 else:
326 repo.dirstate.update([f], 'n')
327 repo.dirstate.update([f], 'n')
327 elif m == "m": # merge
328 elif m == "m": # merge
328 flag, my, other = a[2:]
329 flag, my, other = a[2:]
329 if branchmerge:
330 if branchmerge:
330 # We've done a branch merge, mark this file as merged
331 # We've done a branch merge, mark this file as merged
331 # so that we properly record the merger later
332 # so that we properly record the merger later
332 repo.dirstate.update([f], 'm')
333 repo.dirstate.update([f], 'm')
333 else:
334 else:
334 # We've update-merged a locally modified file, so
335 # We've update-merged a locally modified file, so
335 # we set the dirstate to emulate a normal checkout
336 # we set the dirstate to emulate a normal checkout
336 # of that file some time in the past. Thus our
337 # of that file some time in the past. Thus our
337 # merge will appear as a normal local file
338 # merge will appear as a normal local file
338 # modification.
339 # modification.
339 fl = repo.file(f)
340 fl = repo.file(f)
340 f_len = fl.size(fl.rev(other))
341 f_len = fl.size(fl.rev(other))
341 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
342 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
342 elif m == "c": # copy
343 elif m == "c": # copy
343 f2, fd, my, other, flag, move = a[2:]
344 f2, fd, my, other, flag, move = a[2:]
344 if branchmerge:
345 if branchmerge:
345 # We've done a branch merge, mark this file as merged
346 # We've done a branch merge, mark this file as merged
346 # so that we properly record the merger later
347 # so that we properly record the merger later
347 repo.dirstate.update([fd], 'm')
348 repo.dirstate.update([fd], 'm')
348 else:
349 else:
349 # We've update-merged a locally modified file, so
350 # We've update-merged a locally modified file, so
350 # we set the dirstate to emulate a normal checkout
351 # we set the dirstate to emulate a normal checkout
351 # of that file some time in the past. Thus our
352 # of that file some time in the past. Thus our
352 # merge will appear as a normal local file
353 # merge will appear as a normal local file
353 # modification.
354 # modification.
354 fl = repo.file(f)
355 fl = repo.file(f)
355 f_len = fl.size(fl.rev(other))
356 f_len = fl.size(fl.rev(other))
356 repo.dirstate.update([fd], 'n', st_size=f_len, st_mtime=-1)
357 repo.dirstate.update([fd], 'n', st_size=f_len, st_mtime=-1)
357 if move:
358 if move:
358 repo.dirstate.update([f], 'r')
359 repo.dirstate.update([f], 'r')
359 if f != fd:
360 if f != fd:
360 repo.dirstate.copy(f, fd)
361 repo.dirstate.copy(f, fd)
361
362
362 def update(repo, node, branchmerge=False, force=False, partial=None,
363 def update(repo, node, branchmerge=False, force=False, partial=None,
363 wlock=None, show_stats=True, remind=True):
364 wlock=None, show_stats=True, remind=True):
364
365
365 overwrite = force and not branchmerge
366 overwrite = force and not branchmerge
366 forcemerge = force and branchmerge
367 forcemerge = force and branchmerge
367
368
368 if not wlock:
369 if not wlock:
369 wlock = repo.wlock()
370 wlock = repo.wlock()
370
371
371 ### check phase
372 ### check phase
372
373
373 wc = repo.workingctx()
374 wc = repo.workingctx()
374 pl = wc.parents()
375 pl = wc.parents()
375 if not overwrite and len(pl) > 1:
376 if not overwrite and len(pl) > 1:
376 raise util.Abort(_("outstanding uncommitted merges"))
377 raise util.Abort(_("outstanding uncommitted merges"))
377
378
378 p1, p2 = pl[0], repo.changectx(node)
379 p1, p2 = pl[0], repo.changectx(node)
379 pa = p1.ancestor(p2)
380 pa = p1.ancestor(p2)
380
381
381 # are we going backwards?
382 # are we going backwards?
382 backwards = (pa == p2)
383 backwards = (pa == p2)
383
384
384 # is there a linear path from p1 to p2?
385 # is there a linear path from p1 to p2?
385 if pa == p1 or pa == p2:
386 if pa == p1 or pa == p2:
386 if branchmerge:
387 if branchmerge:
387 raise util.Abort(_("there is nothing to merge, just use "
388 raise util.Abort(_("there is nothing to merge, just use "
388 "'hg update' or look at 'hg heads'"))
389 "'hg update' or look at 'hg heads'"))
389 elif not (overwrite or branchmerge):
390 elif not (overwrite or branchmerge):
390 raise util.Abort(_("update spans branches, use 'hg merge' "
391 raise util.Abort(_("update spans branches, use 'hg merge' "
391 "or 'hg update -C' to lose changes"))
392 "or 'hg update -C' to lose changes"))
392
393
393 if branchmerge and not forcemerge:
394 if branchmerge and not forcemerge:
394 if wc.modified() or wc.added() or wc.removed():
395 if wc.modified() or wc.added() or wc.removed():
395 raise util.Abort(_("outstanding uncommitted changes"))
396 raise util.Abort(_("outstanding uncommitted changes"))
396
397
397 m1 = wc.manifest()
398 m1 = wc.manifest()
398 m2 = p2.manifest()
399 m2 = p2.manifest()
399 ma = pa.manifest()
400 ma = pa.manifest()
400
401
401 # resolve the manifest to determine which files
402 # resolve the manifest to determine which files
402 # we care about merging
403 # we care about merging
403 repo.ui.note(_("resolving manifests\n"))
404 repo.ui.note(_("resolving manifests\n"))
404 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
405 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
405 (overwrite, branchmerge, bool(partial)))
406 (overwrite, branchmerge, bool(partial)))
406 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (p1, p2, pa))
407 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (p1, p2, pa))
407
408
408 action = []
409 action = []
409 copy = {}
410 copy = {}
410
411
411 if not force:
412 if not force:
412 checkunknown(repo, m2, wc)
413 checkunknown(repo, m2, wc)
413 if not branchmerge:
414 if not branchmerge:
414 action += forgetremoved(m2, wc)
415 action += forgetremoved(m2, wc)
415 if not (backwards or overwrite):
416 if not (backwards or overwrite):
416 copy = findcopies(repo, m1, m2, pa.rev())
417 copy = findcopies(repo, m1, m2, pa.rev())
417
418
418 action += manifestmerge(repo.ui, m1, m2, ma, copy,
419 action += manifestmerge(repo.ui, m1, m2, ma, copy,
419 overwrite, backwards, partial)
420 overwrite, backwards, partial)
420
421
421 ### apply phase
422 ### apply phase
422
423
423 if not branchmerge:
424 if not branchmerge:
424 # we don't need to do any magic, just jump to the new rev
425 # we don't need to do any magic, just jump to the new rev
425 p1, p2 = p2, repo.changectx(nullid)
426 p1, p2 = p2, repo.changectx(nullid)
426
427
427 xp1, xp2 = str(p1), str(p2)
428 xp1, xp2 = str(p1), str(p2)
428 if not p2: xp2 = ''
429 if not p2: xp2 = ''
429
430
430 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
431 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
431
432
432 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
433 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
433
434
434 # update dirstate
435 # update dirstate
435 if not partial:
436 if not partial:
436 recordupdates(repo, action, branchmerge)
437 recordupdates(repo, action, branchmerge)
437 repo.dirstate.setparents(p1.node(), p2.node())
438 repo.dirstate.setparents(p1.node(), p2.node())
438
439
439 if show_stats:
440 if show_stats:
440 stats = ((updated, _("updated")),
441 stats = ((updated, _("updated")),
441 (merged - unresolved, _("merged")),
442 (merged - unresolved, _("merged")),
442 (removed, _("removed")),
443 (removed, _("removed")),
443 (unresolved, _("unresolved")))
444 (unresolved, _("unresolved")))
444 note = ", ".join([_("%d files %s") % s for s in stats])
445 note = ", ".join([_("%d files %s") % s for s in stats])
445 repo.ui.status("%s\n" % note)
446 repo.ui.status("%s\n" % note)
446 if not partial:
447 if not partial:
447 if branchmerge:
448 if branchmerge:
448 if unresolved:
449 if unresolved:
449 repo.ui.status(_("There are unresolved merges,"
450 repo.ui.status(_("There are unresolved merges,"
450 " you can redo the full merge using:\n"
451 " you can redo the full merge using:\n"
451 " hg update -C %s\n"
452 " hg update -C %s\n"
452 " hg merge %s\n"
453 " hg merge %s\n"
453 % (p1.rev(), p2.rev())))
454 % (p1.rev(), p2.rev())))
454 elif remind:
455 elif remind:
455 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
456 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
456 elif unresolved:
457 elif unresolved:
457 repo.ui.status(_("There are unresolved merges with"
458 repo.ui.status(_("There are unresolved merges with"
458 " locally modified files.\n"))
459 " locally modified files.\n"))
459
460
460 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
461 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
461 return unresolved
462 return unresolved
462
463
@@ -1,21 +1,22 b''
1 checkout
1 checkout
2 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
2 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
3 merge
3 merge
4 resolving manifests
4 resolving manifests
5 overwrite None branchmerge True partial False
5 overwrite None branchmerge True partial False
6 ancestor f26ec4fc3fa3 local 8e765a822af2 remote af1939970a1c
6 ancestor f26ec4fc3fa3 local 8e765a822af2 remote af1939970a1c
7 a: remote moved -> c
7 a: remote moved -> c
8 b2: remote created -> g
8 b2: remote created -> g
9 merging a and b to b
9 resolving a
10 resolving a
10 my a@f26ec4fc3fa3 other b@8e765a822af2 ancestor a@af1939970a1c
11 my a@f26ec4fc3fa3 other b@8e765a822af2 ancestor a@af1939970a1c
11 copying a to b
12 copying a to b
12 removing a
13 removing a
13 getting b2
14 getting b2
14 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
15 (branch merge, don't forget to commit)
16 (branch merge, don't forget to commit)
16 M b
17 M b
17 a
18 a
18 M b2
19 M b2
19 R a
20 R a
20 C c2
21 C c2
21 blahblah
22 blahblah
General Comments 0
You need to be logged in to leave comments. Login now