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