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