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