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