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