##// END OF EJS Templates
Merge: refactor err and failedmerge -> unresolved
Matt Mackall -
r2813:56f99f5a default
parent child Browse files
Show More
@@ -1,347 +1,344
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(), "util os tempfile")
11 demandload(globals(), "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 update(repo, node, branchmerge=False, force=False, partial=None,
50 def update(repo, node, branchmerge=False, force=False, partial=None,
51 forcemerge=False, wlock=None, show_stats=True, remind=True):
51 forcemerge=False, wlock=None, show_stats=True, remind=True):
52
52
53 if not wlock:
53 if not wlock:
54 wlock = repo.wlock()
54 wlock = repo.wlock()
55
55
56 pl = repo.dirstate.parents()
56 pl = repo.dirstate.parents()
57 if not force and pl[1] != nullid:
57 if not force and pl[1] != nullid:
58 raise util.Abort(_("outstanding uncommitted merges"))
58 raise util.Abort(_("outstanding uncommitted merges"))
59
59
60 err = False
61
62 p1, p2 = pl[0], node
60 p1, p2 = pl[0], node
63 pa = repo.changelog.ancestor(p1, p2)
61 pa = repo.changelog.ancestor(p1, p2)
64 m1n = repo.changelog.read(p1)[0]
62 m1n = repo.changelog.read(p1)[0]
65 m2n = repo.changelog.read(p2)[0]
63 m2n = repo.changelog.read(p2)[0]
66 man = repo.manifest.ancestor(m1n, m2n)
64 man = repo.manifest.ancestor(m1n, m2n)
67 m1 = repo.manifest.read(m1n)
65 m1 = repo.manifest.read(m1n)
68 mf1 = repo.manifest.readflags(m1n)
66 mf1 = repo.manifest.readflags(m1n)
69 m2 = repo.manifest.read(m2n).copy()
67 m2 = repo.manifest.read(m2n).copy()
70 mf2 = repo.manifest.readflags(m2n)
68 mf2 = repo.manifest.readflags(m2n)
71 ma = repo.manifest.read(man)
69 ma = repo.manifest.read(man)
72 mfa = repo.manifest.readflags(man)
70 mfa = repo.manifest.readflags(man)
73
71
74 modified, added, removed, deleted, unknown = repo.changes()
72 modified, added, removed, deleted, unknown = repo.changes()
75
73
76 # is this a jump, or a merge? i.e. is there a linear path
74 # is this a jump, or a merge? i.e. is there a linear path
77 # from p1 to p2?
75 # from p1 to p2?
78 linear_path = (pa == p1 or pa == p2)
76 linear_path = (pa == p1 or pa == p2)
79
77
80 if branchmerge and linear_path:
78 if branchmerge and linear_path:
81 raise util.Abort(_("there is nothing to merge, just use "
79 raise util.Abort(_("there is nothing to merge, just use "
82 "'hg update' or look at 'hg heads'"))
80 "'hg update' or look at 'hg heads'"))
83 if branchmerge and not forcemerge:
81 if branchmerge and not forcemerge:
84 if modified or added or removed:
82 if modified or added or removed:
85 raise util.Abort(_("outstanding uncommitted changes"))
83 raise util.Abort(_("outstanding uncommitted changes"))
86
84
87 if not forcemerge and not force:
85 if not forcemerge and not force:
88 for f in unknown:
86 for f in unknown:
89 if f in m2:
87 if f in m2:
90 t1 = repo.wread(f)
88 t1 = repo.wread(f)
91 t2 = repo.file(f).read(m2[f])
89 t2 = repo.file(f).read(m2[f])
92 if cmp(t1, t2) != 0:
90 if cmp(t1, t2) != 0:
93 raise util.Abort(_("'%s' already exists in the working"
91 raise util.Abort(_("'%s' already exists in the working"
94 " dir and differs from remote") % f)
92 " dir and differs from remote") % f)
95
93
96 # resolve the manifest to determine which files
94 # resolve the manifest to determine which files
97 # we care about merging
95 # we care about merging
98 repo.ui.note(_("resolving manifests\n"))
96 repo.ui.note(_("resolving manifests\n"))
99 repo.ui.debug(_(" force %s branchmerge %s partial %s linear %s\n") %
97 repo.ui.debug(_(" force %s branchmerge %s partial %s linear %s\n") %
100 (force, branchmerge, partial and True or False, linear_path))
98 (force, branchmerge, partial and True or False, linear_path))
101 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
99 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
102 (short(man), short(m1n), short(m2n)))
100 (short(man), short(m1n), short(m2n)))
103
101
104 merge = {}
102 merge = {}
105 get = {}
103 get = {}
106 remove = []
104 remove = []
107
105
108 # construct a working dir manifest
106 # construct a working dir manifest
109 mw = m1.copy()
107 mw = m1.copy()
110 mfw = mf1.copy()
108 mfw = mf1.copy()
111 umap = dict.fromkeys(unknown)
109 umap = dict.fromkeys(unknown)
112
110
113 for f in added + modified + unknown:
111 for f in added + modified + unknown:
114 mw[f] = ""
112 mw[f] = ""
115 mfw[f] = util.is_exec(repo.wjoin(f), mfw.get(f, False))
113 mfw[f] = util.is_exec(repo.wjoin(f), mfw.get(f, False))
116
114
117 for f in deleted + removed:
115 for f in deleted + removed:
118 if f in mw:
116 if f in mw:
119 del mw[f]
117 del mw[f]
120
118
121 # If we're jumping between revisions (as opposed to merging),
119 # If we're jumping between revisions (as opposed to merging),
122 # and if neither the working directory nor the target rev has
120 # and if neither the working directory nor the target rev has
123 # the file, then we need to remove it from the dirstate, to
121 # the file, then we need to remove it from the dirstate, to
124 # prevent the dirstate from listing the file when it is no
122 # prevent the dirstate from listing the file when it is no
125 # longer in the manifest.
123 # longer in the manifest.
126 if not partial and linear_path and f not in m2:
124 if not partial and linear_path and f not in m2:
127 repo.dirstate.forget((f,))
125 repo.dirstate.forget((f,))
128
126
129 # Compare manifests
127 # Compare manifests
130 for f, n in mw.iteritems():
128 for f, n in mw.iteritems():
131 if partial and not partial(f):
129 if partial and not partial(f):
132 continue
130 continue
133 if f in m2:
131 if f in m2:
134 s = 0
132 s = 0
135
133
136 # is the wfile new since m1, and match m2?
134 # is the wfile new since m1, and match m2?
137 if f not in m1:
135 if f not in m1:
138 t1 = repo.wread(f)
136 t1 = repo.wread(f)
139 t2 = repo.file(f).read(m2[f])
137 t2 = repo.file(f).read(m2[f])
140 if cmp(t1, t2) == 0:
138 if cmp(t1, t2) == 0:
141 n = m2[f]
139 n = m2[f]
142 del t1, t2
140 del t1, t2
143
141
144 # are files different?
142 # are files different?
145 if n != m2[f]:
143 if n != m2[f]:
146 a = ma.get(f, nullid)
144 a = ma.get(f, nullid)
147 # are both different from the ancestor?
145 # are both different from the ancestor?
148 if n != a and m2[f] != a:
146 if n != a and m2[f] != a:
149 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
147 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
150 # merge executable bits
148 # merge executable bits
151 # "if we changed or they changed, change in merge"
149 # "if we changed or they changed, change in merge"
152 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
150 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
153 mode = ((a^b) | (a^c)) ^ a
151 mode = ((a^b) | (a^c)) ^ a
154 merge[f] = (m1.get(f, nullid), m2[f], mode)
152 merge[f] = (m1.get(f, nullid), m2[f], mode)
155 s = 1
153 s = 1
156 # are we clobbering?
154 # are we clobbering?
157 # is remote's version newer?
155 # is remote's version newer?
158 # or are we going back in time?
156 # or are we going back in time?
159 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
157 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
160 repo.ui.debug(_(" remote %s is newer, get\n") % f)
158 repo.ui.debug(_(" remote %s is newer, get\n") % f)
161 get[f] = m2[f]
159 get[f] = m2[f]
162 s = 1
160 s = 1
163 elif f in umap or f in added:
161 elif f in umap or f in added:
164 # this unknown file is the same as the checkout
162 # this unknown file is the same as the checkout
165 # we need to reset the dirstate if the file was added
163 # we need to reset the dirstate if the file was added
166 get[f] = m2[f]
164 get[f] = m2[f]
167
165
168 if not s and mfw[f] != mf2[f]:
166 if not s and mfw[f] != mf2[f]:
169 if force:
167 if force:
170 repo.ui.debug(_(" updating permissions for %s\n") % f)
168 repo.ui.debug(_(" updating permissions for %s\n") % f)
171 util.set_exec(repo.wjoin(f), mf2[f])
169 util.set_exec(repo.wjoin(f), mf2[f])
172 else:
170 else:
173 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
171 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
174 mode = ((a^b) | (a^c)) ^ a
172 mode = ((a^b) | (a^c)) ^ a
175 if mode != b:
173 if mode != b:
176 repo.ui.debug(_(" updating permissions for %s\n")
174 repo.ui.debug(_(" updating permissions for %s\n")
177 % f)
175 % f)
178 util.set_exec(repo.wjoin(f), mode)
176 util.set_exec(repo.wjoin(f), mode)
179 del m2[f]
177 del m2[f]
180 elif f in ma:
178 elif f in ma:
181 if n != ma[f]:
179 if n != ma[f]:
182 r = _("d")
180 r = _("d")
183 if not force and (linear_path or branchmerge):
181 if not force and (linear_path or branchmerge):
184 r = repo.ui.prompt(
182 r = repo.ui.prompt(
185 (_(" local changed %s which remote deleted\n") % f) +
183 (_(" local changed %s which remote deleted\n") % f) +
186 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
184 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
187 if r == _("d"):
185 if r == _("d"):
188 remove.append(f)
186 remove.append(f)
189 else:
187 else:
190 repo.ui.debug(_("other deleted %s\n") % f)
188 repo.ui.debug(_("other deleted %s\n") % f)
191 remove.append(f) # other deleted it
189 remove.append(f) # other deleted it
192 else:
190 else:
193 # file is created on branch or in working directory
191 # file is created on branch or in working directory
194 if force and f not in umap:
192 if force and f not in umap:
195 repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
193 repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
196 remove.append(f)
194 remove.append(f)
197 elif n == m1.get(f, nullid): # same as parent
195 elif n == m1.get(f, nullid): # same as parent
198 if p2 == pa: # going backwards?
196 if p2 == pa: # going backwards?
199 repo.ui.debug(_("remote deleted %s\n") % f)
197 repo.ui.debug(_("remote deleted %s\n") % f)
200 remove.append(f)
198 remove.append(f)
201 else:
199 else:
202 repo.ui.debug(_("local modified %s, keeping\n") % f)
200 repo.ui.debug(_("local modified %s, keeping\n") % f)
203 else:
201 else:
204 repo.ui.debug(_("working dir created %s, keeping\n") % f)
202 repo.ui.debug(_("working dir created %s, keeping\n") % f)
205
203
206 for f, n in m2.iteritems():
204 for f, n in m2.iteritems():
207 if partial and not partial(f):
205 if partial and not partial(f):
208 continue
206 continue
209 if f[0] == "/":
207 if f[0] == "/":
210 continue
208 continue
211 if f in ma and n != ma[f]:
209 if f in ma and n != ma[f]:
212 r = _("k")
210 r = _("k")
213 if not force and (linear_path or branchmerge):
211 if not force and (linear_path or branchmerge):
214 r = repo.ui.prompt(
212 r = repo.ui.prompt(
215 (_("remote changed %s which local deleted\n") % f) +
213 (_("remote changed %s which local deleted\n") % f) +
216 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
214 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
217 if r == _("k"):
215 if r == _("k"):
218 get[f] = n
216 get[f] = n
219 elif f not in ma:
217 elif f not in ma:
220 repo.ui.debug(_("remote created %s\n") % f)
218 repo.ui.debug(_("remote created %s\n") % f)
221 get[f] = n
219 get[f] = n
222 else:
220 else:
223 if force or p2 == pa: # going backwards?
221 if force or p2 == pa: # going backwards?
224 repo.ui.debug(_("local deleted %s, recreating\n") % f)
222 repo.ui.debug(_("local deleted %s, recreating\n") % f)
225 get[f] = n
223 get[f] = n
226 else:
224 else:
227 repo.ui.debug(_("local deleted %s\n") % f)
225 repo.ui.debug(_("local deleted %s\n") % f)
228
226
229 del mw, m1, m2, ma
227 del mw, m1, m2, ma
230
228
231 if force:
229 if force:
232 for f in merge:
230 for f in merge:
233 get[f] = merge[f][1]
231 get[f] = merge[f][1]
234 merge = {}
232 merge = {}
235
233
236 if linear_path or force:
234 if linear_path or force:
237 # we don't need to do any magic, just jump to the new rev
235 # we don't need to do any magic, just jump to the new rev
238 p1, p2 = p2, nullid
236 p1, p2 = p2, nullid
239 else:
237 else:
240 if not branchmerge:
238 if not branchmerge:
241 repo.ui.status(_("this update spans a branch"
239 repo.ui.status(_("this update spans a branch"
242 " affecting the following files:\n"))
240 " affecting the following files:\n"))
243 fl = merge.keys() + get.keys()
241 fl = merge.keys() + get.keys()
244 fl.sort()
242 fl.sort()
245 for f in fl:
243 for f in fl:
246 cf = ""
244 cf = ""
247 if f in merge:
245 if f in merge:
248 cf = _(" (resolve)")
246 cf = _(" (resolve)")
249 repo.ui.status(" %s%s\n" % (f, cf))
247 repo.ui.status(" %s%s\n" % (f, cf))
250 repo.ui.warn(_("aborting update spanning branches!\n"))
248 repo.ui.warn(_("aborting update spanning branches!\n"))
251 repo.ui.status(_("(use 'hg merge' to merge across branches"
249 repo.ui.status(_("(use 'hg merge' to merge across branches"
252 " or 'hg update -C' to lose changes)\n"))
250 " or 'hg update -C' to lose changes)\n"))
253 return 1
251 return 1
254
252
255 xp1 = hex(p1)
253 xp1 = hex(p1)
256 xp2 = hex(p2)
254 xp2 = hex(p2)
257 if p2 == nullid: xxp2 = ''
255 if p2 == nullid: xxp2 = ''
258 else: xxp2 = xp2
256 else: xxp2 = xp2
259
257
260 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
258 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
261
259
262 # get the files we don't need to change
260 # get the files we don't need to change
263 files = get.keys()
261 files = get.keys()
264 files.sort()
262 files.sort()
265 for f in files:
263 for f in files:
266 if f[0] == "/":
264 if f[0] == "/":
267 continue
265 continue
268 repo.ui.note(_("getting %s\n") % f)
266 repo.ui.note(_("getting %s\n") % f)
269 t = repo.file(f).read(get[f])
267 t = repo.file(f).read(get[f])
270 repo.wwrite(f, t)
268 repo.wwrite(f, t)
271 util.set_exec(repo.wjoin(f), mf2[f])
269 util.set_exec(repo.wjoin(f), mf2[f])
272 if not partial:
270 if not partial:
273 if branchmerge:
271 if branchmerge:
274 repo.dirstate.update([f], 'n', st_mtime=-1)
272 repo.dirstate.update([f], 'n', st_mtime=-1)
275 else:
273 else:
276 repo.dirstate.update([f], 'n')
274 repo.dirstate.update([f], 'n')
277
275
278 # merge the tricky bits
276 # merge the tricky bits
279 failedmerge = []
277 unresolved = []
280 files = merge.keys()
278 files = merge.keys()
281 files.sort()
279 files.sort()
282 for f in files:
280 for f in files:
283 repo.ui.status(_("merging %s\n") % f)
281 repo.ui.status(_("merging %s\n") % f)
284 my, other, flag = merge[f]
282 my, other, flag = merge[f]
285 ret = merge3(repo, f, my, other, xp1, xp2)
283 ret = merge3(repo, f, my, other, xp1, xp2)
286 if ret:
284 if ret:
287 err = True
285 unresolved.append(f)
288 failedmerge.append(f)
289 util.set_exec(repo.wjoin(f), flag)
286 util.set_exec(repo.wjoin(f), flag)
290 if not partial:
287 if not partial:
291 if branchmerge:
288 if branchmerge:
292 # We've done a branch merge, mark this file as merged
289 # We've done a branch merge, mark this file as merged
293 # so that we properly record the merger later
290 # so that we properly record the merger later
294 repo.dirstate.update([f], 'm')
291 repo.dirstate.update([f], 'm')
295 else:
292 else:
296 # We've update-merged a locally modified file, so
293 # We've update-merged a locally modified file, so
297 # we set the dirstate to emulate a normal checkout
294 # we set the dirstate to emulate a normal checkout
298 # of that file some time in the past. Thus our
295 # of that file some time in the past. Thus our
299 # merge will appear as a normal local file
296 # merge will appear as a normal local file
300 # modification.
297 # modification.
301 f_len = len(repo.file(f).read(other))
298 f_len = len(repo.file(f).read(other))
302 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
299 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
303
300
304 remove.sort()
301 remove.sort()
305 for f in remove:
302 for f in remove:
306 repo.ui.note(_("removing %s\n") % f)
303 repo.ui.note(_("removing %s\n") % f)
307 util.audit_path(f)
304 util.audit_path(f)
308 try:
305 try:
309 util.unlink(repo.wjoin(f))
306 util.unlink(repo.wjoin(f))
310 except OSError, inst:
307 except OSError, inst:
311 if inst.errno != errno.ENOENT:
308 if inst.errno != errno.ENOENT:
312 repo.ui.warn(_("update failed to remove %s: %s!\n") %
309 repo.ui.warn(_("update failed to remove %s: %s!\n") %
313 (f, inst.strerror))
310 (f, inst.strerror))
314 if not partial:
311 if not partial:
315 if branchmerge:
312 if branchmerge:
316 repo.dirstate.update(remove, 'r')
313 repo.dirstate.update(remove, 'r')
317 else:
314 else:
318 repo.dirstate.forget(remove)
315 repo.dirstate.forget(remove)
319
316
320 if not partial:
317 if not partial:
321 repo.dirstate.setparents(p1, p2)
318 repo.dirstate.setparents(p1, p2)
322
319
323 if show_stats:
320 if show_stats:
324 stats = ((len(get), _("updated")),
321 stats = ((len(get), _("updated")),
325 (len(merge) - len(failedmerge), _("merged")),
322 (len(merge) - len(unresolved), _("merged")),
326 (len(remove), _("removed")),
323 (len(remove), _("removed")),
327 (len(failedmerge), _("unresolved")))
324 (len(unresolved), _("unresolved")))
328 note = ", ".join([_("%d files %s") % s for s in stats])
325 note = ", ".join([_("%d files %s") % s for s in stats])
329 repo.ui.status("%s\n" % note)
326 repo.ui.status("%s\n" % note)
330 if not partial:
327 if not partial:
331 if branchmerge:
328 if branchmerge:
332 if failedmerge:
329 if unresolved:
333 repo.ui.status(_("There are unresolved merges,"
330 repo.ui.status(_("There are unresolved merges,"
334 " you can redo the full merge using:\n"
331 " you can redo the full merge using:\n"
335 " hg update -C %s\n"
332 " hg update -C %s\n"
336 " hg merge %s\n"
333 " hg merge %s\n"
337 % (repo.changelog.rev(p1),
334 % (repo.changelog.rev(p1),
338 repo.changelog.rev(p2))))
335 repo.changelog.rev(p2))))
339 elif remind:
336 elif remind:
340 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
337 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
341 elif failedmerge:
338 elif unresolved:
342 repo.ui.status(_("There are unresolved merges with"
339 repo.ui.status(_("There are unresolved merges with"
343 " locally modified files.\n"))
340 " locally modified files.\n"))
344
341
345 repo.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
342 repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved))
346 return err
343 return len(unresolved)
347
344
General Comments 0
You need to be logged in to leave comments. Login now