##// END OF EJS Templates
merge: move partial filtering out of manifest merge
Matt Mackall -
r3162:a9e75b37 default
parent child Browse files
Show More
@@ -1,419 +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 filtermanifest(man, partial):
175 if partial:
176 for k in man.keys():
177 if not partial(k): del man[k]
178
179 def manifestmerge(ui, m1, m2, ma, overwrite, backwards):
175 """
180 """
176 Merge manifest m1 with m2 using ancestor ma and generate merge action list
181 Merge manifest m1 with m2 using ancestor ma and generate merge action list
177 """
182 """
178
183
179 def fmerge(f):
184 def fmerge(f):
180 """merge executable flags"""
185 """merge executable flags"""
181 a, b, c = ma.execf(f), m1.execf(f), m2.execf(f)
186 a, b, c = ma.execf(f), m1.execf(f), m2.execf(f)
182 return ((a^b) | (a^c)) ^ a
187 return ((a^b) | (a^c)) ^ a
183
188
184 action = []
189 action = []
185
190
186 def act(msg, f, m, *args):
191 def act(msg, f, m, *args):
187 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
192 ui.debug(" %s: %s -> %s\n" % (f, msg, m))
188 action.append((f, m) + args)
193 action.append((f, m) + args)
189
194
190 # Filter manifests
191 if partial:
192 for f in m1.keys():
193 if not partial(f): del m1[f]
194 for f in m2.keys():
195 if not partial(f): del m2[f]
196
197 # Compare manifests
195 # Compare manifests
198 for f, n in m1.iteritems():
196 for f, n in m1.iteritems():
199 if f in m2:
197 if f in m2:
200 # are files different?
198 # are files different?
201 if n != m2[f]:
199 if n != m2[f]:
202 a = ma.get(f, nullid)
200 a = ma.get(f, nullid)
203 # are both different from the ancestor?
201 # are both different from the ancestor?
204 if not overwrite and n != a and m2[f] != a:
202 if not overwrite and n != a and m2[f] != a:
205 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
203 act("versions differ", f, "m", fmerge(f), n[:20], m2[f])
206 # are we clobbering?
204 # are we clobbering?
207 # is remote's version newer?
205 # is remote's version newer?
208 # or are we going back in time and clean?
206 # or are we going back in time and clean?
209 elif overwrite or m2[f] != a or (backwards and not n[20:]):
207 elif overwrite or m2[f] != a or (backwards and not n[20:]):
210 act("remote is newer", f, "g", m2.execf(f), m2[f])
208 act("remote is newer", f, "g", m2.execf(f), m2[f])
211 # local is newer, not overwrite, check mode bits
209 # local is newer, not overwrite, check mode bits
212 elif fmerge(f) != m1.execf(f):
210 elif fmerge(f) != m1.execf(f):
213 act("update permissions", f, "e", m2.execf(f))
211 act("update permissions", f, "e", m2.execf(f))
214 # contents same, check mode bits
212 # contents same, check mode bits
215 elif m1.execf(f) != m2.execf(f):
213 elif m1.execf(f) != m2.execf(f):
216 if overwrite or fmerge(f) != m1.execf(f):
214 if overwrite or fmerge(f) != m1.execf(f):
217 act("update permissions", f, "e", m2.execf(f))
215 act("update permissions", f, "e", m2.execf(f))
218 del m2[f]
216 del m2[f]
219 elif f in ma:
217 elif f in ma:
220 if n != ma[f] and not overwrite:
218 if n != ma[f] and not overwrite:
221 if ui.prompt(
219 if ui.prompt(
222 (_(" local changed %s which remote deleted\n") % f) +
220 (_(" local changed %s which remote deleted\n") % f) +
223 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
221 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"):
224 act("prompt delete", f, "r")
222 act("prompt delete", f, "r")
225 else:
223 else:
226 act("other deleted", f, "r")
224 act("other deleted", f, "r")
227 else:
225 else:
228 # file is created on branch or in working directory
226 # file is created on branch or in working directory
229 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
227 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
230 act("remote deleted", f, "r")
228 act("remote deleted", f, "r")
231
229
232 for f, n in m2.iteritems():
230 for f, n in m2.iteritems():
233 if f in ma:
231 if f in ma:
234 if overwrite or backwards:
232 if overwrite or backwards:
235 act("recreating", f, "g", m2.execf(f), n)
233 act("recreating", f, "g", m2.execf(f), n)
236 elif n != ma[f]:
234 elif n != ma[f]:
237 if ui.prompt(
235 if ui.prompt(
238 (_("remote changed %s which local deleted\n") % f) +
236 (_("remote changed %s which local deleted\n") % f) +
239 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
237 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"):
240 act("prompt recreating", f, "g", m2.execf(f), n)
238 act("prompt recreating", f, "g", m2.execf(f), n)
241 else:
239 else:
242 act("remote created", f, "g", m2.execf(f), n)
240 act("remote created", f, "g", m2.execf(f), n)
243
241
244 return action
242 return action
245
243
246 def applyupdates(repo, action, xp1, xp2):
244 def applyupdates(repo, action, xp1, xp2):
247 updated, merged, removed, unresolved = 0, 0, 0, 0
245 updated, merged, removed, unresolved = 0, 0, 0, 0
248 action.sort()
246 action.sort()
249 for a in action:
247 for a in action:
250 f, m = a[:2]
248 f, m = a[:2]
251 if f[0] == "/":
249 if f[0] == "/":
252 continue
250 continue
253 if m == "r": # remove
251 if m == "r": # remove
254 repo.ui.note(_("removing %s\n") % f)
252 repo.ui.note(_("removing %s\n") % f)
255 util.audit_path(f)
253 util.audit_path(f)
256 try:
254 try:
257 util.unlink(repo.wjoin(f))
255 util.unlink(repo.wjoin(f))
258 except OSError, inst:
256 except OSError, inst:
259 if inst.errno != errno.ENOENT:
257 if inst.errno != errno.ENOENT:
260 repo.ui.warn(_("update failed to remove %s: %s!\n") %
258 repo.ui.warn(_("update failed to remove %s: %s!\n") %
261 (f, inst.strerror))
259 (f, inst.strerror))
262 removed +=1
260 removed +=1
263 elif m == "m": # merge
261 elif m == "m": # merge
264 flag, my, other = a[2:]
262 flag, my, other = a[2:]
265 repo.ui.status(_("merging %s\n") % f)
263 repo.ui.status(_("merging %s\n") % f)
266 if merge3(repo, f, my, other, xp1, xp2):
264 if merge3(repo, f, my, other, xp1, xp2):
267 unresolved += 1
265 unresolved += 1
268 util.set_exec(repo.wjoin(f), flag)
266 util.set_exec(repo.wjoin(f), flag)
269 merged += 1
267 merged += 1
270 elif m == "g": # get
268 elif m == "g": # get
271 flag, node = a[2:]
269 flag, node = a[2:]
272 repo.ui.note(_("getting %s\n") % f)
270 repo.ui.note(_("getting %s\n") % f)
273 t = repo.file(f).read(node)
271 t = repo.file(f).read(node)
274 repo.wwrite(f, t)
272 repo.wwrite(f, t)
275 util.set_exec(repo.wjoin(f), flag)
273 util.set_exec(repo.wjoin(f), flag)
276 updated += 1
274 updated += 1
277 elif m == "e": # exec
275 elif m == "e": # exec
278 flag = a[2:]
276 flag = a[2:]
279 util.set_exec(repo.wjoin(f), flag)
277 util.set_exec(repo.wjoin(f), flag)
280
278
281 return updated, merged, removed, unresolved
279 return updated, merged, removed, unresolved
282
280
283 def recordupdates(repo, action, branchmerge):
281 def recordupdates(repo, action, branchmerge):
284 for a in action:
282 for a in action:
285 f, m = a[:2]
283 f, m = a[:2]
286 if m == "r": # remove
284 if m == "r": # remove
287 if branchmerge:
285 if branchmerge:
288 repo.dirstate.update([f], 'r')
286 repo.dirstate.update([f], 'r')
289 else:
287 else:
290 repo.dirstate.forget([f])
288 repo.dirstate.forget([f])
291 elif m == "f": # forget
289 elif m == "f": # forget
292 repo.dirstate.forget([f])
290 repo.dirstate.forget([f])
293 elif m == "g": # get
291 elif m == "g": # get
294 if branchmerge:
292 if branchmerge:
295 repo.dirstate.update([f], 'n', st_mtime=-1)
293 repo.dirstate.update([f], 'n', st_mtime=-1)
296 else:
294 else:
297 repo.dirstate.update([f], 'n')
295 repo.dirstate.update([f], 'n')
298 elif m == "m": # merge
296 elif m == "m": # merge
299 flag, my, other = a[2:]
297 flag, my, other = a[2:]
300 if branchmerge:
298 if branchmerge:
301 # We've done a branch merge, mark this file as merged
299 # We've done a branch merge, mark this file as merged
302 # so that we properly record the merger later
300 # so that we properly record the merger later
303 repo.dirstate.update([f], 'm')
301 repo.dirstate.update([f], 'm')
304 else:
302 else:
305 # We've update-merged a locally modified file, so
303 # We've update-merged a locally modified file, so
306 # we set the dirstate to emulate a normal checkout
304 # we set the dirstate to emulate a normal checkout
307 # of that file some time in the past. Thus our
305 # of that file some time in the past. Thus our
308 # merge will appear as a normal local file
306 # merge will appear as a normal local file
309 # modification.
307 # modification.
310 fl = repo.file(f)
308 fl = repo.file(f)
311 f_len = fl.size(fl.rev(other))
309 f_len = fl.size(fl.rev(other))
312 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
310 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
313
311
314 def update(repo, node, branchmerge=False, force=False, partial=None,
312 def update(repo, node, branchmerge=False, force=False, partial=None,
315 wlock=None, show_stats=True, remind=True):
313 wlock=None, show_stats=True, remind=True):
316
314
317 overwrite = force and not branchmerge
315 overwrite = force and not branchmerge
318 forcemerge = force and branchmerge
316 forcemerge = force and branchmerge
319
317
320 if not wlock:
318 if not wlock:
321 wlock = repo.wlock()
319 wlock = repo.wlock()
322
320
323 ### check phase
321 ### check phase
324
322
325 pl = repo.dirstate.parents()
323 pl = repo.dirstate.parents()
326 if not overwrite and pl[1] != nullid:
324 if not overwrite and pl[1] != nullid:
327 raise util.Abort(_("outstanding uncommitted merges"))
325 raise util.Abort(_("outstanding uncommitted merges"))
328
326
329 p1, p2 = pl[0], node
327 p1, p2 = pl[0], node
330 pa = repo.changelog.ancestor(p1, p2)
328 pa = repo.changelog.ancestor(p1, p2)
331
329
332 # are we going backwards?
330 # are we going backwards?
333 backwards = (pa == p2)
331 backwards = (pa == p2)
334
332
335 # is there a linear path from p1 to p2?
333 # is there a linear path from p1 to p2?
336 if pa == p1 or pa == p2:
334 if pa == p1 or pa == p2:
337 if branchmerge:
335 if branchmerge:
338 raise util.Abort(_("there is nothing to merge, just use "
336 raise util.Abort(_("there is nothing to merge, just use "
339 "'hg update' or look at 'hg heads'"))
337 "'hg update' or look at 'hg heads'"))
340 elif not (overwrite or branchmerge):
338 elif not (overwrite or branchmerge):
341 raise util.Abort(_("update spans branches, use 'hg merge' "
339 raise util.Abort(_("update spans branches, use 'hg merge' "
342 "or 'hg update -C' to lose changes"))
340 "or 'hg update -C' to lose changes"))
343
341
344 status = repo.status()
342 status = repo.status()
345 modified, added, removed, deleted, unknown = status[:5]
343 modified, added, removed, deleted, unknown = status[:5]
346 if branchmerge and not forcemerge:
344 if branchmerge and not forcemerge:
347 if modified or added or removed:
345 if modified or added or removed:
348 raise util.Abort(_("outstanding uncommitted changes"))
346 raise util.Abort(_("outstanding uncommitted changes"))
349
347
350 m1 = repo.changectx(p1).manifest().copy()
348 m1 = repo.changectx(p1).manifest().copy()
351 m2 = repo.changectx(p2).manifest().copy()
349 m2 = repo.changectx(p2).manifest().copy()
352 ma = repo.changectx(pa).manifest()
350 ma = repo.changectx(pa).manifest()
353
351
354 # resolve the manifest to determine which files
352 # resolve the manifest to determine which files
355 # we care about merging
353 # we care about merging
356 repo.ui.note(_("resolving manifests\n"))
354 repo.ui.note(_("resolving manifests\n"))
357 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
355 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s\n") %
358 (overwrite, branchmerge, bool(partial)))
356 (overwrite, branchmerge, bool(partial)))
359 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
357 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
360 (short(p1), short(p2), short(pa)))
358 (short(p1), short(p2), short(pa)))
361
359
362 action = []
360 action = []
363 copy = {}
361 copy = {}
364
362
365 m1 = workingmanifest(repo, m1, status)
363 m1 = workingmanifest(repo, m1, status)
364 filtermanifest(m1, partial)
365 filtermanifest(m2, partial)
366
366
367 if not force:
367 if not force:
368 checkunknown(repo, m2, status)
368 checkunknown(repo, m2, status)
369 if not branchmerge:
369 if not branchmerge:
370 action += forgetremoved(m2, status)
370 action += forgetremoved(m2, status)
371 if not (backwards or overwrite):
371 if not (backwards or overwrite):
372 copy = findcopies(repo, m1, m2, repo.changelog.rev(pa))
372 copy = findcopies(repo, m1, m2, repo.changelog.rev(pa))
373
373
374 action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards, partial)
374 action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards)
375 del m1, m2, ma
375 del m1, m2, ma
376
376
377 ### apply phase
377 ### apply phase
378
378
379 if not branchmerge:
379 if not branchmerge:
380 # 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
381 p1, p2 = p2, nullid
381 p1, p2 = p2, nullid
382
382
383 xp1, xp2 = hex(p1), hex(p2)
383 xp1, xp2 = hex(p1), hex(p2)
384 if p2 == nullid: xp2 = ''
384 if p2 == nullid: xp2 = ''
385
385
386 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
386 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
387
387
388 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
388 updated, merged, removed, unresolved = applyupdates(repo, action, xp1, xp2)
389
389
390 # update dirstate
390 # update dirstate
391 if not partial:
391 if not partial:
392 repo.dirstate.setparents(p1, p2)
392 repo.dirstate.setparents(p1, p2)
393 recordupdates(repo, action, branchmerge)
393 recordupdates(repo, action, branchmerge)
394
394
395 if show_stats:
395 if show_stats:
396 stats = ((updated, _("updated")),
396 stats = ((updated, _("updated")),
397 (merged - unresolved, _("merged")),
397 (merged - unresolved, _("merged")),
398 (removed, _("removed")),
398 (removed, _("removed")),
399 (unresolved, _("unresolved")))
399 (unresolved, _("unresolved")))
400 note = ", ".join([_("%d files %s") % s for s in stats])
400 note = ", ".join([_("%d files %s") % s for s in stats])
401 repo.ui.status("%s\n" % note)
401 repo.ui.status("%s\n" % note)
402 if not partial:
402 if not partial:
403 if branchmerge:
403 if branchmerge:
404 if unresolved:
404 if unresolved:
405 repo.ui.status(_("There are unresolved merges,"
405 repo.ui.status(_("There are unresolved merges,"
406 " you can redo the full merge using:\n"
406 " you can redo the full merge using:\n"
407 " hg update -C %s\n"
407 " hg update -C %s\n"
408 " hg merge %s\n"
408 " hg merge %s\n"
409 % (repo.changelog.rev(p1),
409 % (repo.changelog.rev(p1),
410 repo.changelog.rev(p2))))
410 repo.changelog.rev(p2))))
411 elif remind:
411 elif remind:
412 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
412 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
413 elif unresolved:
413 elif unresolved:
414 repo.ui.status(_("There are unresolved merges with"
414 repo.ui.status(_("There are unresolved merges with"
415 " locally modified files.\n"))
415 " locally modified files.\n"))
416
416
417 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
417 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved)
418 return unresolved
418 return unresolved
419
419
General Comments 0
You need to be logged in to leave comments. Login now