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