##// END OF EJS Templates
merge: move findcopies after workingmanifest
Matt Mackall -
r3161:84561ea8 default
parent child Browse files
Show More
@@ -1,420 +1,419 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 merge3(repo, fn, my, other, p1, p2):
13 def merge3(repo, fn, my, other, p1, p2):
14 """perform a 3-way merge in the working directory"""
14 """perform a 3-way merge in the working directory"""
15
15
16 def temp(prefix, node):
16 def temp(prefix, node):
17 pre = "%s~%s." % (os.path.basename(fn), prefix)
17 pre = "%s~%s." % (os.path.basename(fn), prefix)
18 (fd, name) = tempfile.mkstemp(prefix=pre)
18 (fd, name) = tempfile.mkstemp(prefix=pre)
19 f = os.fdopen(fd, "wb")
19 f = os.fdopen(fd, "wb")
20 repo.wwrite(fn, fl.read(node), f)
20 repo.wwrite(fn, fl.read(node), f)
21 f.close()
21 f.close()
22 return name
22 return name
23
23
24 fl = repo.file(fn)
24 fl = repo.file(fn)
25 base = fl.ancestor(my, other)
25 base = fl.ancestor(my, other)
26 a = repo.wjoin(fn)
26 a = repo.wjoin(fn)
27 b = temp("base", base)
27 b = temp("base", base)
28 c = temp("other", other)
28 c = temp("other", other)
29
29
30 repo.ui.note(_("resolving %s\n") % fn)
30 repo.ui.note(_("resolving %s\n") % fn)
31 repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
31 repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
32 (fn, short(my), short(other), short(base)))
32 (fn, short(my), short(other), short(base)))
33
33
34 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
34 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
35 or "hgmerge")
35 or "hgmerge")
36 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
36 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
37 environ={'HG_FILE': fn,
37 environ={'HG_FILE': fn,
38 'HG_MY_NODE': p1,
38 'HG_MY_NODE': p1,
39 'HG_OTHER_NODE': p2,
39 'HG_OTHER_NODE': p2,
40 'HG_FILE_MY_NODE': hex(my),
40 'HG_FILE_MY_NODE': hex(my),
41 'HG_FILE_OTHER_NODE': hex(other),
41 'HG_FILE_OTHER_NODE': hex(other),
42 'HG_FILE_BASE_NODE': hex(base)})
42 'HG_FILE_BASE_NODE': hex(base)})
43 if r:
43 if r:
44 repo.ui.warn(_("merging %s failed!\n") % fn)
44 repo.ui.warn(_("merging %s failed!\n") % fn)
45
45
46 os.unlink(b)
46 os.unlink(b)
47 os.unlink(c)
47 os.unlink(c)
48 return r
48 return r
49
49
50 def checkunknown(repo, m2, status):
50 def checkunknown(repo, m2, status):
51 """
51 """
52 check for collisions between unknown files and files in m2
52 check for collisions between unknown files and files in m2
53 """
53 """
54 modified, added, removed, deleted, unknown = status[:5]
54 modified, added, removed, deleted, unknown = status[:5]
55 for f in unknown:
55 for f in unknown:
56 if f in m2:
56 if f in m2:
57 if repo.file(f).cmp(m2[f], repo.wread(f)):
57 if repo.file(f).cmp(m2[f], repo.wread(f)):
58 raise util.Abort(_("'%s' already exists in the working"
58 raise util.Abort(_("'%s' already exists in the working"
59 " dir and differs from remote") % f)
59 " dir and differs from remote") % f)
60
60
61 def workingmanifest(repo, man, status):
61 def workingmanifest(repo, man, status):
62 """
62 """
63 Update manifest to correspond to the working directory
63 Update manifest to correspond to the working directory
64 """
64 """
65
65
66 copied = repo.dirstate.copies()
66 copied = repo.dirstate.copies()
67 modified, added, removed, deleted, unknown = status[:5]
67 modified, added, removed, deleted, unknown = status[:5]
68 for i,l in (("a", added), ("m", modified), ("u", unknown)):
68 for i,l in (("a", added), ("m", modified), ("u", unknown)):
69 for f in l:
69 for f in l:
70 man[f] = man.get(copied.get(f, f), nullid) + i
70 man[f] = man.get(copied.get(f, f), nullid) + i
71 man.set(f, util.is_exec(repo.wjoin(f), man.execf(f)))
71 man.set(f, util.is_exec(repo.wjoin(f), man.execf(f)))
72
72
73 for f in deleted + removed:
73 for f in deleted + removed:
74 del man[f]
74 del man[f]
75
75
76 return man
76 return man
77
77
78 def forgetremoved(m2, status):
78 def forgetremoved(m2, status):
79 """
79 """
80 Forget removed files
80 Forget removed files
81
81
82 If we're jumping between revisions (as opposed to merging), and if
82 If we're jumping between revisions (as opposed to merging), and if
83 neither the working directory nor the target rev has the file,
83 neither the working directory nor the target rev has the file,
84 then we need to remove it from the dirstate, to prevent the
84 then we need to remove it from the dirstate, to prevent the
85 dirstate from listing the file when it is no longer in the
85 dirstate from listing the file when it is no longer in the
86 manifest.
86 manifest.
87 """
87 """
88
88
89 modified, added, removed, deleted, unknown = status[:5]
89 modified, added, removed, deleted, unknown = status[:5]
90 action = []
90 action = []
91
91
92 for f in deleted + removed:
92 for f in deleted + 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 # avoid silly behavior for update from empty dir
136 # avoid silly behavior for update from empty dir
137 if not m1:
137 if not m1:
138 return {}
138 return {}
139
139
140 dcopies = repo.dirstate.copies()
140 dcopies = repo.dirstate.copies()
141 copy = {}
141 copy = {}
142 match = {}
142 match = {}
143 u1 = nonoverlap(m1, m2)
143 u1 = nonoverlap(m1, m2)
144 u2 = nonoverlap(m2, m1)
144 u2 = nonoverlap(m2, m1)
145 ctx = util.cachefunc(lambda f,n: repo.filectx(f, fileid=n[:20]))
145 ctx = util.cachefunc(lambda f,n: repo.filectx(f, fileid=n[:20]))
146
146
147 def checkpair(c, f2, man):
147 def checkpair(c, f2, man):
148 ''' check if an apparent pair actually matches '''
148 ''' check if an apparent pair actually matches '''
149 c2 = ctx(f2, man[f2])
149 c2 = ctx(f2, man[f2])
150 ca = c.ancestor(c2)
150 ca = c.ancestor(c2)
151 if ca:
151 if ca:
152 copy[c.path()] = f2
152 copy[c.path()] = f2
153 copy[f2] = c.path()
153 copy[f2] = c.path()
154
154
155 for f in u1:
155 for f in u1:
156 c = ctx(dcopies.get(f, f), m1[f])
156 c = ctx(dcopies.get(f, f), m1[f])
157 for of in findold(c, limit):
157 for of in findold(c, limit):
158 if of in m2:
158 if of in m2:
159 checkpair(c, of, m2)
159 checkpair(c, of, m2)
160 else:
160 else:
161 match.setdefault(of, []).append(f)
161 match.setdefault(of, []).append(f)
162
162
163 for f in u2:
163 for f in u2:
164 c = ctx(f, m2[f])
164 c = ctx(f, m2[f])
165 for of in findold(c, limit):
165 for of in findold(c, limit):
166 if of in m1:
166 if of in m1:
167 checkpair(c, of, m1)
167 checkpair(c, of, m1)
168 elif of in match:
168 elif of in match:
169 for mf in match[of]:
169 for mf in match[of]:
170 checkpair(c, mf, m1)
170 checkpair(c, mf, m1)
171
171
172 return copy
172 return copy
173
173
174 def manifestmerge(ui, m1, m2, ma, overwrite, backwards, partial):
174 def manifestmerge(ui, m1, m2, ma, overwrite, backwards, partial):
175 """
175 """
176 Merge manifest m1 with m2 using ancestor ma and generate merge action list
176 Merge manifest m1 with m2 using ancestor ma and generate merge action list
177 """
177 """
178
178
179 def fmerge(f):
179 def fmerge(f):
180 """merge executable flags"""
180 """merge executable flags"""
181 a, b, c = ma.execf(f), m1.execf(f), m2.execf(f)
181 a, b, c = ma.execf(f), m1.execf(f), m2.execf(f)
182 return ((a^b) | (a^c)) ^ a
182 return ((a^b) | (a^c)) ^ a
183
183
184 action = []
184 action = []
185
185
186 def act(msg, f, m, *args):
186 def act(msg, f, m, *args):
187 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
187 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
188 action.append((f, m) + args)
188 action.append((f, m) + args)
189
189
190 # Filter manifests
190 # Filter manifests
191 if partial:
191 if partial:
192 for f in m1.keys():
192 for f in m1.keys():
193 if not partial(f): del m1[f]
193 if not partial(f): del m1[f]
194 for f in m2.keys():
194 for f in m2.keys():
195 if not partial(f): del m2[f]
195 if not partial(f): del m2[f]
196
196
197 # Compare manifests
197 # Compare manifests
198 for f, n in m1.iteritems():
198 for f, n in m1.iteritems():
199 if f in m2:
199 if f in m2:
200 # are files different?
200 # are files different?
201 if n != m2[f]:
201 if n != m2[f]:
202 a = ma.get(f, nullid)
202 a = ma.get(f, nullid)
203 # are both different from the ancestor?
203 # are both different from the ancestor?
204 if not overwrite and n != a and m2[f] != a:
204 if not overwrite and n != a and m2[f] != a:
205 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
205 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
206 # are we clobbering?
206 # are we clobbering?
207 # is remote's version newer?
207 # is remote's version newer?
208 # or are we going back in time and clean?
208 # or are we going back in time and clean?
209 elif overwrite or m2[f] != a or (backwards and not n[20:]):
209 elif overwrite or m2[f] != a or (backwards and not n[20:]):
210 act("remote is newer", f, "g", m2.execf(f), m2[f])
210 act("remote is newer", f, "g", m2.execf(f), m2[f])
211 # local is newer, not overwrite, check mode bits
211 # local is newer, not overwrite, check mode bits
212 elif fmerge(f) != m1.execf(f):
212 elif fmerge(f) != m1.execf(f):
213 act("update permissions", f, "e", m2.execf(f))
213 act("update permissions", f, "e", m2.execf(f))
214 # contents same, check mode bits
214 # contents same, check mode bits
215 elif m1.execf(f) != m2.execf(f):
215 elif m1.execf(f) != m2.execf(f):
216 if overwrite or fmerge(f) != m1.execf(f):
216 if overwrite or fmerge(f) != m1.execf(f):
217 act("update permissions", f, "e", m2.execf(f))
217 act("update permissions", f, "e", m2.execf(f))
218 del m2[f]
218 del m2[f]
219 elif f in ma:
219 elif f in ma:
220 if n != ma[f] and not overwrite:
220 if n != ma[f] and not overwrite:
221 if ui.prompt(
221 if ui.prompt(
222 (_(" local changed %s which remote deleted\n") % f) +
222 (_(" local changed %s which remote deleted\n") % f) +
223 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
223 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
224 act("prompt delete", f, "r")
224 act("prompt delete", f, "r")
225 else:
225 else:
226 act("other deleted", f, "r")
226 act("other deleted", f, "r")
227 else:
227 else:
228 # file is created on branch or in working directory
228 # file is created on branch or in working directory
229 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
229 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
230 act("remote deleted", f, "r")
230 act("remote deleted", f, "r")
231
231
232 for f, n in m2.iteritems():
232 for f, n in m2.iteritems():
233 if f in ma:
233 if f in ma:
234 if overwrite or backwards:
234 if overwrite or backwards:
235 act("recreating", f, "g", m2.execf(f), n)
235 act("recreating", f, "g", m2.execf(f), n)
236 elif n != ma[f]:
236 elif n != ma[f]:
237 if ui.prompt(
237 if ui.prompt(
238 (_("remote changed %s which local deleted\n") % f) +
238 (_("remote changed %s which local deleted\n") % f) +
239 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
239 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
240 act("prompt recreating", f, "g", m2.execf(f), n)
240 act("prompt recreating", f, "g", m2.execf(f), n)
241 else:
241 else:
242 act("remote created", f, "g", m2.execf(f), n)
242 act("remote created", f, "g", m2.execf(f), n)
243
243
244 return action
244 return action
245
245
246 def applyupdates(repo, action, xp1, xp2):
246 def applyupdates(repo, action, xp1, xp2):
247 updated, merged, removed, unresolved = 0, 0, 0, 0
247 updated, merged, removed, unresolved = 0, 0, 0, 0
248 action.sort()
248 action.sort()
249 for a in action:
249 for a in action:
250 f, m = a[:2]
250 f, m = a[:2]
251 if f[0] == "/":
251 if f[0] == "/":
252 continue
252 continue
253 if m == "r": # remove
253 if m == "r": # remove
254 repo.ui.note(_("removing %s\n") % f)
254 repo.ui.note(_("removing %s\n") % f)
255 util.audit_path(f)
255 util.audit_path(f)
256 try:
256 try:
257 util.unlink(repo.wjoin(f))
257 util.unlink(repo.wjoin(f))
258 except OSError, inst:
258 except OSError, inst:
259 if inst.errno != errno.ENOENT:
259 if inst.errno != errno.ENOENT:
260 repo.ui.warn(_("update failed to remove %s: %s!\n") %
260 repo.ui.warn(_("update failed to remove %s: %s!\n") %
261 (f, inst.strerror))
261 (f, inst.strerror))
262 removed +=1
262 removed +=1
263 elif m == "m": # merge
263 elif m == "m": # merge
264 flag, my, other = a[2:]
264 flag, my, other = a[2:]
265 repo.ui.status(_("merging %s\n") % f)
265 repo.ui.status(_("merging %s\n") % f)
266 if merge3(repo, f, my, other, xp1, xp2):
266 if merge3(repo, f, my, other, xp1, xp2):
267 unresolved += 1
267 unresolved += 1
268 util.set_exec(repo.wjoin(f), flag)
268 util.set_exec(repo.wjoin(f), flag)
269 merged += 1
269 merged += 1
270 elif m == "g": # get
270 elif m == "g": # get
271 flag, node = a[2:]
271 flag, node = a[2:]
272 repo.ui.note(_("getting %s\n") % f)
272 repo.ui.note(_("getting %s\n") % f)
273 t = repo.file(f).read(node)
273 t = repo.file(f).read(node)
274 repo.wwrite(f, t)
274 repo.wwrite(f, t)
275 util.set_exec(repo.wjoin(f), flag)
275 util.set_exec(repo.wjoin(f), flag)
276 updated += 1
276 updated += 1
277 elif m == "e": # exec
277 elif m == "e": # exec
278 flag = a[2:]
278 flag = a[2:]
279 util.set_exec(repo.wjoin(f), flag)
279 util.set_exec(repo.wjoin(f), flag)
280
280
281 return updated, merged, removed, unresolved
281 return updated, merged, removed, unresolved
282
282
283 def recordupdates(repo, action, branchmerge):
283 def recordupdates(repo, action, branchmerge):
284 for a in action:
284 for a in action:
285 f, m = a[:2]
285 f, m = a[:2]
286 if m == "r": # remove
286 if m == "r": # remove
287 if branchmerge:
287 if branchmerge:
288 repo.dirstate.update([f], 'r')
288 repo.dirstate.update([f], 'r')
289 else:
289 else:
290 repo.dirstate.forget([f])
290 repo.dirstate.forget([f])
291 elif m == "f": # forget
291 elif m == "f": # forget
292 repo.dirstate.forget([f])
292 repo.dirstate.forget([f])
293 elif m == "g": # get
293 elif m == "g": # get
294 if branchmerge:
294 if branchmerge:
295 repo.dirstate.update([f], 'n', st_mtime=-1)
295 repo.dirstate.update([f], 'n', st_mtime=-1)
296 else:
296 else:
297 repo.dirstate.update([f], 'n')
297 repo.dirstate.update([f], 'n')
298 elif m == "m": # merge
298 elif m == "m": # merge
299 flag, my, other = a[2:]
299 flag, my, other = a[2:]
300 if branchmerge:
300 if branchmerge:
301 # We've done a branch merge, mark this file as merged
301 # We've done a branch merge, mark this file as merged
302 # so that we properly record the merger later
302 # so that we properly record the merger later
303 repo.dirstate.update([f], 'm')
303 repo.dirstate.update([f], 'm')
304 else:
304 else:
305 # We've update-merged a locally modified file, so
305 # We've update-merged a locally modified file, so
306 # we set the dirstate to emulate a normal checkout
306 # we set the dirstate to emulate a normal checkout
307 # of that file some time in the past. Thus our
307 # of that file some time in the past. Thus our
308 # merge will appear as a normal local file
308 # merge will appear as a normal local file
309 # modification.
309 # modification.
310 fl = repo.file(f)
310 fl = repo.file(f)
311 f_len = fl.size(fl.rev(other))
311 f_len = fl.size(fl.rev(other))
312 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
312 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
313
313
314 def update(repo, node, branchmerge=False, force=False, partial=None,
314 def update(repo, node, branchmerge=False, force=False, partial=None,
315 wlock=None, show_stats=True, remind=True):
315 wlock=None, show_stats=True, remind=True):
316
316
317 overwrite = force and not branchmerge
317 overwrite = force and not branchmerge
318 forcemerge = force and branchmerge
318 forcemerge = force and branchmerge
319
319
320 if not wlock:
320 if not wlock:
321 wlock = repo.wlock()
321 wlock = repo.wlock()
322
322
323 ### check phase
323 ### check phase
324
324
325 pl = repo.dirstate.parents()
325 pl = repo.dirstate.parents()
326 if not overwrite and pl[1] != nullid:
326 if not overwrite and pl[1] != nullid:
327 raise util.Abort(_("outstanding uncommitted merges"))
327 raise util.Abort(_("outstanding uncommitted merges"))
328
328
329 p1, p2 = pl[0], node
329 p1, p2 = pl[0], node
330 pa = repo.changelog.ancestor(p1, p2)
330 pa = repo.changelog.ancestor(p1, p2)
331
331
332 # are we going backwards?
332 # are we going backwards?
333 backwards = (pa == p2)
333 backwards = (pa == p2)
334
334
335 # is there a linear path from p1 to p2?
335 # is there a linear path from p1 to p2?
336 if pa == p1 or pa == p2:
336 if pa == p1 or pa == p2:
337 if branchmerge:
337 if branchmerge:
338 raise util.Abort(_("there is nothing to merge, just use "
338 raise util.Abort(_("there is nothing to merge, just use "
339 "'hg update' or look at 'hg heads'"))
339 "'hg update' or look at 'hg heads'"))
340 elif not (overwrite or branchmerge):
340 elif not (overwrite or branchmerge):
341 raise util.Abort(_("update spans branches, use 'hg merge' "
341 raise util.Abort(_("update spans branches, use 'hg merge' "
342 "or 'hg update -C' to lose changes"))
342 "or 'hg update -C' to lose changes"))
343
343
344 status = repo.status()
344 status = repo.status()
345 modified, added, removed, deleted, unknown = status[:5]
345 modified, added, removed, deleted, unknown = status[:5]
346 if branchmerge and not forcemerge:
346 if branchmerge and not forcemerge:
347 if modified or added or removed:
347 if modified or added or removed:
348 raise util.Abort(_("outstanding uncommitted changes"))
348 raise util.Abort(_("outstanding uncommitted changes"))
349
349
350 m1 = repo.changectx(p1).manifest().copy()
350 m1 = repo.changectx(p1).manifest().copy()
351 m2 = repo.changectx(p2).manifest().copy()
351 m2 = repo.changectx(p2).manifest().copy()
352 ma = repo.changectx(pa).manifest()
352 ma = repo.changectx(pa).manifest()
353
353
354 # resolve the manifest to determine which files
354 # resolve the manifest to determine which files
355 # we care about merging
355 # we care about merging
356 repo.ui.note(_("resolving manifests\n"))
356 repo.ui.note(_("resolving manifests\n"))
357 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
357 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
358 (overwrite, branchmerge, bool(partial)))
358 (overwrite, branchmerge, bool(partial)))
359 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
359 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
360 (short(p1), short(p2), short(pa)))
360 (short(p1), short(p2), short(pa)))
361
361
362 action = []
362 action = []
363
364 copy = {}
363 copy = {}
365 if not (backwards or overwrite):
366 copy = findcopies(repo, m1, m2, repo.changelog.rev(pa))
367
364
368 m1 = workingmanifest(repo, m1, status)
365 m1 = workingmanifest(repo, m1, status)
369
366
370 if not force:
367 if not force:
371 checkunknown(repo, m2, status)
368 checkunknown(repo, m2, status)
372 if not branchmerge:
369 if not branchmerge:
373 action += forgetremoved(m2, status)
370 action += forgetremoved(m2, status)
371 if not (backwards or overwrite):
372 copy = findcopies(repo, m1, m2, repo.changelog.rev(pa))
374
373
375 action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards, partial)
374 action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards, partial)
376 del m1, m2, ma
375 del m1, m2, ma
377
376
378 ### apply phase
377 ### apply phase
379
378
380 if not branchmerge:
379 if not branchmerge:
381 # we don't need to do any magic, just jump to the new rev
380 # we don't need to do any magic, just jump to the new rev
382 p1, p2 = p2, nullid
381 p1, p2 = p2, nullid
383
382
384 xp1, xp2 = hex(p1), hex(p2)
383 xp1, xp2 = hex(p1), hex(p2)
385 if p2 == nullid: xp2 = ''
384 if p2 == nullid: xp2 = ''
386
385
387 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
386 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
388
387
389 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
388 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
390
389
391 # update dirstate
390 # update dirstate
392 if not partial:
391 if not partial:
393 repo.dirstate.setparents(p1, p2)
392 repo.dirstate.setparents(p1, p2)
394 recordupdates(repo, action, branchmerge)
393 recordupdates(repo, action, branchmerge)
395
394
396 if show_stats:
395 if show_stats:
397 stats = ((updated, _("updated")),
396 stats = ((updated, _("updated")),
398 (merged - unresolved, _("merged")),
397 (merged - unresolved, _("merged")),
399 (removed, _("removed")),
398 (removed, _("removed")),
400 (unresolved, _("unresolved")))
399 (unresolved, _("unresolved")))
401 note = ", ".join([_("%d files %s") % s for s in stats])
400 note = ", ".join([_("%d files %s") % s for s in stats])
402 repo.ui.status("%s\n" % note)
401 repo.ui.status("%s\n" % note)
403 if not partial:
402 if not partial:
404 if branchmerge:
403 if branchmerge:
405 if unresolved:
404 if unresolved:
406 repo.ui.status(_("There are unresolved merges,"
405 repo.ui.status(_("There are unresolved merges,"
407 " you can redo the full merge using:\n"
406 " you can redo the full merge using:\n"
408 " hg update -C %s\n"
407 " hg update -C %s\n"
409 " hg merge %s\n"
408 " hg merge %s\n"
410 % (repo.changelog.rev(p1),
409 % (repo.changelog.rev(p1),
411 repo.changelog.rev(p2))))
410 repo.changelog.rev(p2))))
412 elif remind:
411 elif remind:
413 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
412 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
414 elif unresolved:
413 elif unresolved:
415 repo.ui.status(_("There are unresolved merges with"
414 repo.ui.status(_("There are unresolved merges with"
416 " locally modified files.\n"))
415 " locally modified files.\n"))
417
416
418 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
417 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
419 return unresolved
418 return unresolved
420
419
General Comments 0
You need to be logged in to leave comments. Login now