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