##// END OF EJS Templates
Fix missing space in one of the new update messages.
Thomas Arendsen Hein -
r6381:0b89315d default
parent child Browse files
Show More
@@ -1,411 +1,411 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, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 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 nullid, nullrev
8 from node import nullid, nullrev
9 from i18n import _
9 from i18n import _
10 import errno, util, os, filemerge, copies
10 import errno, util, os, filemerge, copies
11
11
12 def _checkunknown(wctx, mctx):
12 def _checkunknown(wctx, mctx):
13 "check for collisions between unknown files and files in mctx"
13 "check for collisions between unknown files and files in mctx"
14 for f in wctx.unknown():
14 for f in wctx.unknown():
15 if f in mctx and mctx[f].cmp(wctx[f].data()):
15 if f in mctx and mctx[f].cmp(wctx[f].data()):
16 raise util.Abort(_("untracked file in working directory differs"
16 raise util.Abort(_("untracked file in working directory differs"
17 " from file in requested revision: '%s'") % f)
17 " from file in requested revision: '%s'") % f)
18
18
19 def _checkcollision(mctx):
19 def _checkcollision(mctx):
20 "check for case folding collisions in the destination context"
20 "check for case folding collisions in the destination context"
21 folded = {}
21 folded = {}
22 for fn in mctx:
22 for fn in mctx:
23 fold = fn.lower()
23 fold = fn.lower()
24 if fold in folded:
24 if fold in folded:
25 raise util.Abort(_("case-folding collision between %s and %s")
25 raise util.Abort(_("case-folding collision between %s and %s")
26 % (fn, folded[fold]))
26 % (fn, folded[fold]))
27 folded[fold] = fn
27 folded[fold] = fn
28
28
29 def _forgetremoved(wctx, mctx, branchmerge):
29 def _forgetremoved(wctx, mctx, branchmerge):
30 """
30 """
31 Forget removed files
31 Forget removed files
32
32
33 If we're jumping between revisions (as opposed to merging), and if
33 If we're jumping between revisions (as opposed to merging), and if
34 neither the working directory nor the target rev has the file,
34 neither the working directory nor the target rev has the file,
35 then we need to remove it from the dirstate, to prevent the
35 then we need to remove it from the dirstate, to prevent the
36 dirstate from listing the file when it is no longer in the
36 dirstate from listing the file when it is no longer in the
37 manifest.
37 manifest.
38
38
39 If we're merging, and the other revision has removed a file
39 If we're merging, and the other revision has removed a file
40 that is not present in the working directory, we need to mark it
40 that is not present in the working directory, we need to mark it
41 as removed.
41 as removed.
42 """
42 """
43
43
44 action = []
44 action = []
45 state = branchmerge and 'r' or 'f'
45 state = branchmerge and 'r' or 'f'
46 for f in wctx.deleted():
46 for f in wctx.deleted():
47 if f not in mctx:
47 if f not in mctx:
48 action.append((f, state))
48 action.append((f, state))
49
49
50 if not branchmerge:
50 if not branchmerge:
51 for f in wctx.removed():
51 for f in wctx.removed():
52 if f not in mctx:
52 if f not in mctx:
53 action.append((f, "f"))
53 action.append((f, "f"))
54
54
55 return action
55 return action
56
56
57 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
57 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
58 """
58 """
59 Merge p1 and p2 with ancestor ma and generate merge action list
59 Merge p1 and p2 with ancestor ma and generate merge action list
60
60
61 overwrite = whether we clobber working files
61 overwrite = whether we clobber working files
62 partial = function to filter file lists
62 partial = function to filter file lists
63 """
63 """
64
64
65 repo.ui.note(_("resolving manifests\n"))
65 repo.ui.note(_("resolving manifests\n"))
66 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
66 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
67 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
67 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
68
68
69 m1 = p1.manifest()
69 m1 = p1.manifest()
70 m2 = p2.manifest()
70 m2 = p2.manifest()
71 ma = pa.manifest()
71 ma = pa.manifest()
72 backwards = (pa == p2)
72 backwards = (pa == p2)
73 action = []
73 action = []
74 copy, copied, diverge = {}, {}, {}
74 copy, copied, diverge = {}, {}, {}
75
75
76 def fmerge(f, f2=None, fa=None):
76 def fmerge(f, f2=None, fa=None):
77 """merge flags"""
77 """merge flags"""
78 if not f2:
78 if not f2:
79 f2 = f
79 f2 = f
80 fa = f
80 fa = f
81 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
81 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
82 if m == n: # flags agree
82 if m == n: # flags agree
83 return m # unchanged
83 return m # unchanged
84 if m and n: # flags are set but don't agree
84 if m and n: # flags are set but don't agree
85 if not a: # both differ from parent
85 if not a: # both differ from parent
86 r = repo.ui.prompt(
86 r = repo.ui.prompt(
87 _(" conflicting flags for %s\n"
87 _(" conflicting flags for %s\n"
88 "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
88 "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
89 return r != "n" and r or ''
89 return r != "n" and r or ''
90 if m == a:
90 if m == a:
91 return n # changed from m to n
91 return n # changed from m to n
92 return m # changed from n to m
92 return m # changed from n to m
93 if m and m != a: # changed from a to m
93 if m and m != a: # changed from a to m
94 return m
94 return m
95 if n and n != a: # changed from a to n
95 if n and n != a: # changed from a to n
96 return n
96 return n
97 return '' # flag was cleared
97 return '' # flag was cleared
98
98
99 def act(msg, m, f, *args):
99 def act(msg, m, f, *args):
100 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
100 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
101 action.append((f, m) + args)
101 action.append((f, m) + args)
102
102
103 if pa and not (backwards or overwrite):
103 if pa and not (backwards or overwrite):
104 copy, diverge = copies.copies(repo, p1, p2, pa)
104 copy, diverge = copies.copies(repo, p1, p2, pa)
105 copied = dict.fromkeys(copy.values())
105 copied = dict.fromkeys(copy.values())
106 for of, fl in diverge.items():
106 for of, fl in diverge.items():
107 act("divergent renames", "dr", of, fl)
107 act("divergent renames", "dr", of, fl)
108
108
109 # Compare manifests
109 # Compare manifests
110 for f, n in m1.iteritems():
110 for f, n in m1.iteritems():
111 if partial and not partial(f):
111 if partial and not partial(f):
112 continue
112 continue
113 if f in m2:
113 if f in m2:
114 if overwrite or backwards:
114 if overwrite or backwards:
115 rflags = m2.flags(f)
115 rflags = m2.flags(f)
116 else:
116 else:
117 rflags = fmerge(f)
117 rflags = fmerge(f)
118 # are files different?
118 # are files different?
119 if n != m2[f]:
119 if n != m2[f]:
120 a = ma.get(f, nullid)
120 a = ma.get(f, nullid)
121 # are we clobbering?
121 # are we clobbering?
122 if overwrite:
122 if overwrite:
123 act("clobbering", "g", f, rflags)
123 act("clobbering", "g", f, rflags)
124 # or are we going back in time and clean?
124 # or are we going back in time and clean?
125 elif backwards and not n[20:]:
125 elif backwards and not n[20:]:
126 act("reverting", "g", f, rflags)
126 act("reverting", "g", f, rflags)
127 # are both different from the ancestor?
127 # are both different from the ancestor?
128 elif n != a and m2[f] != a:
128 elif n != a and m2[f] != a:
129 act("versions differ", "m", f, f, f, rflags, False)
129 act("versions differ", "m", f, f, f, rflags, False)
130 # is remote's version newer?
130 # is remote's version newer?
131 elif m2[f] != a:
131 elif m2[f] != a:
132 act("remote is newer", "g", f, rflags)
132 act("remote is newer", "g", f, rflags)
133 # local is newer, not overwrite, check mode bits
133 # local is newer, not overwrite, check mode bits
134 elif m1.flags(f) != rflags:
134 elif m1.flags(f) != rflags:
135 act("update permissions", "e", f, rflags)
135 act("update permissions", "e", f, rflags)
136 # contents same, check mode bits
136 # contents same, check mode bits
137 elif m1.flags(f) != rflags:
137 elif m1.flags(f) != rflags:
138 act("update permissions", "e", f, rflags)
138 act("update permissions", "e", f, rflags)
139 elif f in copied:
139 elif f in copied:
140 continue
140 continue
141 elif f in copy:
141 elif f in copy:
142 f2 = copy[f]
142 f2 = copy[f]
143 if f2 not in m2: # directory rename
143 if f2 not in m2: # directory rename
144 act("remote renamed directory to " + f2, "d",
144 act("remote renamed directory to " + f2, "d",
145 f, None, f2, m1.flags(f))
145 f, None, f2, m1.flags(f))
146 elif f2 in m1: # case 2 A,B/B/B
146 elif f2 in m1: # case 2 A,B/B/B
147 act("local copied to " + f2, "m",
147 act("local copied to " + f2, "m",
148 f, f2, f, fmerge(f, f2, f2), False)
148 f, f2, f, fmerge(f, f2, f2), False)
149 else: # case 4,21 A/B/B
149 else: # case 4,21 A/B/B
150 act("local moved to " + f2, "m",
150 act("local moved to " + f2, "m",
151 f, f2, f, fmerge(f, f2, f2), False)
151 f, f2, f, fmerge(f, f2, f2), False)
152 elif f in ma:
152 elif f in ma:
153 if n != ma[f] and not overwrite:
153 if n != ma[f] and not overwrite:
154 if repo.ui.prompt(
154 if repo.ui.prompt(
155 _(" local changed %s which remote deleted\n"
155 _(" local changed %s which remote deleted\n"
156 "use (c)hanged version or (d)elete?") % f,
156 "use (c)hanged version or (d)elete?") % f,
157 _("[cd]"), _("c")) == _("d"):
157 _("[cd]"), _("c")) == _("d"):
158 act("prompt delete", "r", f)
158 act("prompt delete", "r", f)
159 else:
159 else:
160 act("other deleted", "r", f)
160 act("other deleted", "r", f)
161 else:
161 else:
162 # file is created on branch or in working directory
162 # file is created on branch or in working directory
163 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
163 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
164 act("remote deleted", "r", f)
164 act("remote deleted", "r", f)
165
165
166 for f, n in m2.iteritems():
166 for f, n in m2.iteritems():
167 if partial and not partial(f):
167 if partial and not partial(f):
168 continue
168 continue
169 if f in m1:
169 if f in m1:
170 continue
170 continue
171 if f in copied:
171 if f in copied:
172 continue
172 continue
173 if f in copy:
173 if f in copy:
174 f2 = copy[f]
174 f2 = copy[f]
175 if f2 not in m1: # directory rename
175 if f2 not in m1: # directory rename
176 act("local renamed directory to " + f2, "d",
176 act("local renamed directory to " + f2, "d",
177 None, f, f2, m2.flags(f))
177 None, f, f2, m2.flags(f))
178 elif f2 in m2: # rename case 1, A/A,B/A
178 elif f2 in m2: # rename case 1, A/A,B/A
179 act("remote copied to " + f, "m",
179 act("remote copied to " + f, "m",
180 f2, f, f, fmerge(f2, f, f2), False)
180 f2, f, f, fmerge(f2, f, f2), False)
181 else: # case 3,20 A/B/A
181 else: # case 3,20 A/B/A
182 act("remote moved to " + f, "m",
182 act("remote moved to " + f, "m",
183 f2, f, f, fmerge(f2, f, f2), True)
183 f2, f, f, fmerge(f2, f, f2), True)
184 elif f in ma:
184 elif f in ma:
185 if overwrite or backwards:
185 if overwrite or backwards:
186 act("recreating", "g", f, m2.flags(f))
186 act("recreating", "g", f, m2.flags(f))
187 elif n != ma[f]:
187 elif n != ma[f]:
188 if repo.ui.prompt(
188 if repo.ui.prompt(
189 _("remote changed %s which local deleted\n"
189 _("remote changed %s which local deleted\n"
190 "use (c)hanged version or leave (d)eleted?") % f,
190 "use (c)hanged version or leave (d)eleted?") % f,
191 _("[cd]"), _("c")) == _("c"):
191 _("[cd]"), _("c")) == _("c"):
192 act("prompt recreating", "g", f, m2.flags(f))
192 act("prompt recreating", "g", f, m2.flags(f))
193 else:
193 else:
194 act("remote created", "g", f, m2.flags(f))
194 act("remote created", "g", f, m2.flags(f))
195
195
196 return action
196 return action
197
197
198 def applyupdates(repo, action, wctx, mctx):
198 def applyupdates(repo, action, wctx, mctx):
199 "apply the merge action list to the working directory"
199 "apply the merge action list to the working directory"
200
200
201 updated, merged, removed, unresolved = 0, 0, 0, 0
201 updated, merged, removed, unresolved = 0, 0, 0, 0
202 action.sort()
202 action.sort()
203 # prescan for copy/renames
203 # prescan for copy/renames
204 for a in action:
204 for a in action:
205 f, m = a[:2]
205 f, m = a[:2]
206 if m == 'm': # merge
206 if m == 'm': # merge
207 f2, fd, flags, move = a[2:]
207 f2, fd, flags, move = a[2:]
208 if f != fd:
208 if f != fd:
209 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
209 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
210 repo.wwrite(fd, repo.wread(f), flags)
210 repo.wwrite(fd, repo.wread(f), flags)
211
211
212 audit_path = util.path_auditor(repo.root)
212 audit_path = util.path_auditor(repo.root)
213
213
214 for a in action:
214 for a in action:
215 f, m = a[:2]
215 f, m = a[:2]
216 if f and f[0] == "/":
216 if f and f[0] == "/":
217 continue
217 continue
218 if m == "r": # remove
218 if m == "r": # remove
219 repo.ui.note(_("removing %s\n") % f)
219 repo.ui.note(_("removing %s\n") % f)
220 audit_path(f)
220 audit_path(f)
221 try:
221 try:
222 util.unlink(repo.wjoin(f))
222 util.unlink(repo.wjoin(f))
223 except OSError, inst:
223 except OSError, inst:
224 if inst.errno != errno.ENOENT:
224 if inst.errno != errno.ENOENT:
225 repo.ui.warn(_("update failed to remove %s: %s!\n") %
225 repo.ui.warn(_("update failed to remove %s: %s!\n") %
226 (f, inst.strerror))
226 (f, inst.strerror))
227 removed += 1
227 removed += 1
228 elif m == "m": # merge
228 elif m == "m": # merge
229 f2, fd, flags, move = a[2:]
229 f2, fd, flags, move = a[2:]
230 r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx)
230 r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx)
231 if r > 0:
231 if r > 0:
232 unresolved += 1
232 unresolved += 1
233 else:
233 else:
234 if r is None:
234 if r is None:
235 updated += 1
235 updated += 1
236 else:
236 else:
237 merged += 1
237 merged += 1
238 util.set_flags(repo.wjoin(fd), flags)
238 util.set_flags(repo.wjoin(fd), flags)
239 if f != fd and move and util.lexists(repo.wjoin(f)):
239 if f != fd and move and util.lexists(repo.wjoin(f)):
240 repo.ui.debug(_("removing %s\n") % f)
240 repo.ui.debug(_("removing %s\n") % f)
241 os.unlink(repo.wjoin(f))
241 os.unlink(repo.wjoin(f))
242 elif m == "g": # get
242 elif m == "g": # get
243 flags = a[2]
243 flags = a[2]
244 repo.ui.note(_("getting %s\n") % f)
244 repo.ui.note(_("getting %s\n") % f)
245 t = mctx.filectx(f).data()
245 t = mctx.filectx(f).data()
246 repo.wwrite(f, t, flags)
246 repo.wwrite(f, t, flags)
247 updated += 1
247 updated += 1
248 elif m == "d": # directory rename
248 elif m == "d": # directory rename
249 f2, fd, flags = a[2:]
249 f2, fd, flags = a[2:]
250 if f:
250 if f:
251 repo.ui.note(_("moving %s to %s\n") % (f, fd))
251 repo.ui.note(_("moving %s to %s\n") % (f, fd))
252 t = wctx.filectx(f).data()
252 t = wctx.filectx(f).data()
253 repo.wwrite(fd, t, flags)
253 repo.wwrite(fd, t, flags)
254 util.unlink(repo.wjoin(f))
254 util.unlink(repo.wjoin(f))
255 if f2:
255 if f2:
256 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
256 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
257 t = mctx.filectx(f2).data()
257 t = mctx.filectx(f2).data()
258 repo.wwrite(fd, t, flags)
258 repo.wwrite(fd, t, flags)
259 updated += 1
259 updated += 1
260 elif m == "dr": # divergent renames
260 elif m == "dr": # divergent renames
261 fl = a[2]
261 fl = a[2]
262 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
262 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
263 for nf in fl:
263 for nf in fl:
264 repo.ui.warn(" %s\n" % nf)
264 repo.ui.warn(" %s\n" % nf)
265 elif m == "e": # exec
265 elif m == "e": # exec
266 flags = a[2]
266 flags = a[2]
267 util.set_flags(repo.wjoin(f), flags)
267 util.set_flags(repo.wjoin(f), flags)
268
268
269 return updated, merged, removed, unresolved
269 return updated, merged, removed, unresolved
270
270
271 def recordupdates(repo, action, branchmerge):
271 def recordupdates(repo, action, branchmerge):
272 "record merge actions to the dirstate"
272 "record merge actions to the dirstate"
273
273
274 for a in action:
274 for a in action:
275 f, m = a[:2]
275 f, m = a[:2]
276 if m == "r": # remove
276 if m == "r": # remove
277 if branchmerge:
277 if branchmerge:
278 repo.dirstate.remove(f)
278 repo.dirstate.remove(f)
279 else:
279 else:
280 repo.dirstate.forget(f)
280 repo.dirstate.forget(f)
281 elif m == "f": # forget
281 elif m == "f": # forget
282 repo.dirstate.forget(f)
282 repo.dirstate.forget(f)
283 elif m in "ge": # get or exec change
283 elif m in "ge": # get or exec change
284 if branchmerge:
284 if branchmerge:
285 repo.dirstate.normaldirty(f)
285 repo.dirstate.normaldirty(f)
286 else:
286 else:
287 repo.dirstate.normal(f)
287 repo.dirstate.normal(f)
288 elif m == "m": # merge
288 elif m == "m": # merge
289 f2, fd, flag, move = a[2:]
289 f2, fd, flag, move = a[2:]
290 if branchmerge:
290 if branchmerge:
291 # We've done a branch merge, mark this file as merged
291 # We've done a branch merge, mark this file as merged
292 # so that we properly record the merger later
292 # so that we properly record the merger later
293 repo.dirstate.merge(fd)
293 repo.dirstate.merge(fd)
294 if f != f2: # copy/rename
294 if f != f2: # copy/rename
295 if move:
295 if move:
296 repo.dirstate.remove(f)
296 repo.dirstate.remove(f)
297 if f != fd:
297 if f != fd:
298 repo.dirstate.copy(f, fd)
298 repo.dirstate.copy(f, fd)
299 else:
299 else:
300 repo.dirstate.copy(f2, fd)
300 repo.dirstate.copy(f2, fd)
301 else:
301 else:
302 # We've update-merged a locally modified file, so
302 # We've update-merged a locally modified file, so
303 # we set the dirstate to emulate a normal checkout
303 # we set the dirstate to emulate a normal checkout
304 # of that file some time in the past. Thus our
304 # of that file some time in the past. Thus our
305 # merge will appear as a normal local file
305 # merge will appear as a normal local file
306 # modification.
306 # modification.
307 repo.dirstate.normallookup(fd)
307 repo.dirstate.normallookup(fd)
308 if move:
308 if move:
309 repo.dirstate.forget(f)
309 repo.dirstate.forget(f)
310 elif m == "d": # directory rename
310 elif m == "d": # directory rename
311 f2, fd, flag = a[2:]
311 f2, fd, flag = a[2:]
312 if not f2 and f not in repo.dirstate:
312 if not f2 and f not in repo.dirstate:
313 # untracked file moved
313 # untracked file moved
314 continue
314 continue
315 if branchmerge:
315 if branchmerge:
316 repo.dirstate.add(fd)
316 repo.dirstate.add(fd)
317 if f:
317 if f:
318 repo.dirstate.remove(f)
318 repo.dirstate.remove(f)
319 repo.dirstate.copy(f, fd)
319 repo.dirstate.copy(f, fd)
320 if f2:
320 if f2:
321 repo.dirstate.copy(f2, fd)
321 repo.dirstate.copy(f2, fd)
322 else:
322 else:
323 repo.dirstate.normal(fd)
323 repo.dirstate.normal(fd)
324 if f:
324 if f:
325 repo.dirstate.forget(f)
325 repo.dirstate.forget(f)
326
326
327 def update(repo, node, branchmerge, force, partial):
327 def update(repo, node, branchmerge, force, partial):
328 """
328 """
329 Perform a merge between the working directory and the given node
329 Perform a merge between the working directory and the given node
330
330
331 branchmerge = whether to merge between branches
331 branchmerge = whether to merge between branches
332 force = whether to force branch merging or file overwriting
332 force = whether to force branch merging or file overwriting
333 partial = a function to filter file lists (dirstate not updated)
333 partial = a function to filter file lists (dirstate not updated)
334 """
334 """
335
335
336 wlock = repo.wlock()
336 wlock = repo.wlock()
337 try:
337 try:
338 wc = repo.workingctx()
338 wc = repo.workingctx()
339 if node is None:
339 if node is None:
340 # tip of current branch
340 # tip of current branch
341 try:
341 try:
342 node = repo.branchtags()[wc.branch()]
342 node = repo.branchtags()[wc.branch()]
343 except KeyError:
343 except KeyError:
344 if wc.branch() == "default": # no default branch!
344 if wc.branch() == "default": # no default branch!
345 node = repo.lookup("tip") # update to tip
345 node = repo.lookup("tip") # update to tip
346 else:
346 else:
347 raise util.Abort(_("branch %s not found") % wc.branch())
347 raise util.Abort(_("branch %s not found") % wc.branch())
348 overwrite = force and not branchmerge
348 overwrite = force and not branchmerge
349 pl = wc.parents()
349 pl = wc.parents()
350 p1, p2 = pl[0], repo.changectx(node)
350 p1, p2 = pl[0], repo.changectx(node)
351 pa = p1.ancestor(p2)
351 pa = p1.ancestor(p2)
352 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
352 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
353 fastforward = False
353 fastforward = False
354
354
355 ### check phase
355 ### check phase
356 if not overwrite and len(pl) > 1:
356 if not overwrite and len(pl) > 1:
357 raise util.Abort(_("outstanding uncommitted merges"))
357 raise util.Abort(_("outstanding uncommitted merges"))
358 if branchmerge:
358 if branchmerge:
359 if pa == p2:
359 if pa == p2:
360 raise util.Abort(_("can't merge with ancestor"))
360 raise util.Abort(_("can't merge with ancestor"))
361 elif pa == p1:
361 elif pa == p1:
362 if p1.branch() != p2.branch():
362 if p1.branch() != p2.branch():
363 fastforward = True
363 fastforward = True
364 else:
364 else:
365 raise util.Abort(_("nothing to merge (use 'hg update'"
365 raise util.Abort(_("nothing to merge (use 'hg update'"
366 " or check 'hg heads')"))
366 " or check 'hg heads')"))
367 if not force and (wc.files() or wc.deleted()):
367 if not force and (wc.files() or wc.deleted()):
368 raise util.Abort(_("outstanding uncommitted changes"))
368 raise util.Abort(_("outstanding uncommitted changes"))
369 elif not overwrite:
369 elif not overwrite:
370 if pa == p1 or pa == p2: # linear
370 if pa == p1 or pa == p2: # linear
371 pass # all good
371 pass # all good
372 elif p1.branch() == p2.branch():
372 elif p1.branch() == p2.branch():
373 if wc.files() or wc.deleted():
373 if wc.files() or wc.deleted():
374 raise util.Abort(_("crosses branches (use 'hg merge' or "
374 raise util.Abort(_("crosses branches (use 'hg merge' or "
375 "'hg update -C' to discard changes)"))
375 "'hg update -C' to discard changes)"))
376 raise util.Abort(_("crosses branches (use 'hg merge'"
376 raise util.Abort(_("crosses branches (use 'hg merge' "
377 "or 'hg update -C')"))
377 "or 'hg update -C')"))
378 elif wc.files() or wc.deleted():
378 elif wc.files() or wc.deleted():
379 raise util.Abort(_("crosses named branches (use "
379 raise util.Abort(_("crosses named branches (use "
380 "'hg update -C' to discard changes)"))
380 "'hg update -C' to discard changes)"))
381 else:
381 else:
382 # Allow jumping branches if there are no changes
382 # Allow jumping branches if there are no changes
383 overwrite = True
383 overwrite = True
384
384
385 ### calculate phase
385 ### calculate phase
386 action = []
386 action = []
387 if not force:
387 if not force:
388 _checkunknown(wc, p2)
388 _checkunknown(wc, p2)
389 if not util.checkfolding(repo.path):
389 if not util.checkfolding(repo.path):
390 _checkcollision(p2)
390 _checkcollision(p2)
391 action += _forgetremoved(wc, p2, branchmerge)
391 action += _forgetremoved(wc, p2, branchmerge)
392 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
392 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
393
393
394 ### apply phase
394 ### apply phase
395 if not branchmerge: # just jump to the new rev
395 if not branchmerge: # just jump to the new rev
396 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
396 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
397 if not partial:
397 if not partial:
398 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
398 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
399
399
400 stats = applyupdates(repo, action, wc, p2)
400 stats = applyupdates(repo, action, wc, p2)
401
401
402 if not partial:
402 if not partial:
403 recordupdates(repo, action, branchmerge)
403 recordupdates(repo, action, branchmerge)
404 repo.dirstate.setparents(fp1, fp2)
404 repo.dirstate.setparents(fp1, fp2)
405 if not branchmerge and not fastforward:
405 if not branchmerge and not fastforward:
406 repo.dirstate.setbranch(p2.branch())
406 repo.dirstate.setbranch(p2.branch())
407 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
407 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
408
408
409 return stats
409 return stats
410 finally:
410 finally:
411 del wlock
411 del wlock
@@ -1,7 +1,7 b''
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 removing b
2 removing b
3 created new head
3 created new head
4 % should abort
4 % should abort
5 abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
5 abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
6 % should succeed
6 % should succeed
7 abort: crosses branches (use 'hg merge'or 'hg update -C')
7 abort: crosses branches (use 'hg merge' or 'hg update -C')
General Comments 0
You need to be logged in to leave comments. Login now