##// END OF EJS Templates
merge: change the merge state serialisation to use a record based logic...
Pierre-Yves David -
r20589:31993cd2 stable
parent child Browse files
Show More
@@ -1,821 +1,843 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 from mercurial import obsolete
10 from mercurial import obsolete
11 import error, util, filemerge, copies, subrepo, worker, dicthelpers
11 import error, util, filemerge, copies, subrepo, worker, dicthelpers
12 import errno, os, shutil
12 import errno, os, shutil
13
13
14 class mergestate(object):
14 class mergestate(object):
15 '''track 3-way merge state of individual files'''
15 '''track 3-way merge state of individual files'''
16 statepath = "merge/state"
16 statepath = "merge/state"
17 def __init__(self, repo):
17 def __init__(self, repo):
18 self._repo = repo
18 self._repo = repo
19 self._dirty = False
19 self._dirty = False
20 self._read()
20 self._read()
21 def reset(self, node=None):
21 def reset(self, node=None):
22 self._state = {}
22 self._state = {}
23 if node:
23 if node:
24 self._local = node
24 self._local = node
25 shutil.rmtree(self._repo.join("merge"), True)
25 shutil.rmtree(self._repo.join("merge"), True)
26 self._dirty = False
26 self._dirty = False
27 def _read(self):
27 def _read(self):
28 self._state = {}
28 self._state = {}
29 records = self._readrecords()
30 for rtype, record in records:
31 if rtype == 'L':
32 self._local = bin(record)
33 elif rtype == "F":
34 bits = record.split("\0")
35 self._state[bits[0]] = bits[1:]
36 elif not rtype.islower():
37 raise util.Abort(_('unsupported merge state record:'
38 % rtype))
39 self._dirty = False
40 def _readrecords(self):
41 records = []
29 try:
42 try:
30 f = self._repo.opener(self.statepath)
43 f = self._repo.opener(self.statepath)
31 for i, l in enumerate(f):
44 for i, l in enumerate(f):
32 if i == 0:
45 if i == 0:
33 self._local = bin(l[:-1])
46 records.append(('L', l[:-1]))
34 else:
47 else:
35 bits = l[:-1].split("\0")
48 records.append(('F', l[:-1]))
36 self._state[bits[0]] = bits[1:]
37 f.close()
49 f.close()
38 except IOError, err:
50 except IOError, err:
39 if err.errno != errno.ENOENT:
51 if err.errno != errno.ENOENT:
40 raise
52 raise
41 self._dirty = False
53 return records
42 def commit(self):
54 def commit(self):
43 if self._dirty:
55 if self._dirty:
44 f = self._repo.opener(self.statepath, "w")
56 records = []
45 f.write(hex(self._local) + "\n")
57 records.append(("L", hex(self._local)))
46 for d, v in self._state.iteritems():
58 for d, v in self._state.iteritems():
47 f.write("\0".join([d] + v) + "\n")
59 records.append(("F", "\0".join([d] + v)))
48 f.close()
60 self._writerecords(records)
49 self._dirty = False
61 self._dirty = False
62 def _writerecords(self, records):
63 f = self._repo.opener(self.statepath, "w")
64 irecords = iter(records)
65 lrecords = irecords.next()
66 assert lrecords[0] == 'L'
67 f.write(hex(self._local) + "\n")
68 for rtype, data in irecords:
69 if rtype == "F":
70 f.write("%s\n" % data)
71 f.close()
50 def add(self, fcl, fco, fca, fd):
72 def add(self, fcl, fco, fca, fd):
51 hash = util.sha1(fcl.path()).hexdigest()
73 hash = util.sha1(fcl.path()).hexdigest()
52 self._repo.opener.write("merge/" + hash, fcl.data())
74 self._repo.opener.write("merge/" + hash, fcl.data())
53 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
75 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
54 hex(fca.filenode()), fco.path(), fcl.flags()]
76 hex(fca.filenode()), fco.path(), fcl.flags()]
55 self._dirty = True
77 self._dirty = True
56 def __contains__(self, dfile):
78 def __contains__(self, dfile):
57 return dfile in self._state
79 return dfile in self._state
58 def __getitem__(self, dfile):
80 def __getitem__(self, dfile):
59 return self._state[dfile][0]
81 return self._state[dfile][0]
60 def __iter__(self):
82 def __iter__(self):
61 l = self._state.keys()
83 l = self._state.keys()
62 l.sort()
84 l.sort()
63 for f in l:
85 for f in l:
64 yield f
86 yield f
65 def files(self):
87 def files(self):
66 return self._state.keys()
88 return self._state.keys()
67 def mark(self, dfile, state):
89 def mark(self, dfile, state):
68 self._state[dfile][0] = state
90 self._state[dfile][0] = state
69 self._dirty = True
91 self._dirty = True
70 def resolve(self, dfile, wctx, octx):
92 def resolve(self, dfile, wctx, octx):
71 if self[dfile] == 'r':
93 if self[dfile] == 'r':
72 return 0
94 return 0
73 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
95 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
74 fcd = wctx[dfile]
96 fcd = wctx[dfile]
75 fco = octx[ofile]
97 fco = octx[ofile]
76 fca = self._repo.filectx(afile, fileid=anode)
98 fca = self._repo.filectx(afile, fileid=anode)
77 # "premerge" x flags
99 # "premerge" x flags
78 flo = fco.flags()
100 flo = fco.flags()
79 fla = fca.flags()
101 fla = fca.flags()
80 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
102 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
81 if fca.node() == nullid:
103 if fca.node() == nullid:
82 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
104 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
83 afile)
105 afile)
84 elif flags == fla:
106 elif flags == fla:
85 flags = flo
107 flags = flo
86 # restore local
108 # restore local
87 f = self._repo.opener("merge/" + hash)
109 f = self._repo.opener("merge/" + hash)
88 self._repo.wwrite(dfile, f.read(), flags)
110 self._repo.wwrite(dfile, f.read(), flags)
89 f.close()
111 f.close()
90 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
112 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
91 if r is None:
113 if r is None:
92 # no real conflict
114 # no real conflict
93 del self._state[dfile]
115 del self._state[dfile]
94 elif not r:
116 elif not r:
95 self.mark(dfile, 'r')
117 self.mark(dfile, 'r')
96 return r
118 return r
97
119
98 def _checkunknownfile(repo, wctx, mctx, f):
120 def _checkunknownfile(repo, wctx, mctx, f):
99 return (not repo.dirstate._ignore(f)
121 return (not repo.dirstate._ignore(f)
100 and os.path.isfile(repo.wjoin(f))
122 and os.path.isfile(repo.wjoin(f))
101 and repo.wopener.audit.check(f)
123 and repo.wopener.audit.check(f)
102 and repo.dirstate.normalize(f) not in repo.dirstate
124 and repo.dirstate.normalize(f) not in repo.dirstate
103 and mctx[f].cmp(wctx[f]))
125 and mctx[f].cmp(wctx[f]))
104
126
105 def _checkunknown(repo, wctx, mctx):
127 def _checkunknown(repo, wctx, mctx):
106 "check for collisions between unknown files and files in mctx"
128 "check for collisions between unknown files and files in mctx"
107
129
108 error = False
130 error = False
109 for f in mctx:
131 for f in mctx:
110 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
132 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
111 error = True
133 error = True
112 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
134 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
113 if error:
135 if error:
114 raise util.Abort(_("untracked files in working directory differ "
136 raise util.Abort(_("untracked files in working directory differ "
115 "from files in requested revision"))
137 "from files in requested revision"))
116
138
117 def _forgetremoved(wctx, mctx, branchmerge):
139 def _forgetremoved(wctx, mctx, branchmerge):
118 """
140 """
119 Forget removed files
141 Forget removed files
120
142
121 If we're jumping between revisions (as opposed to merging), and if
143 If we're jumping between revisions (as opposed to merging), and if
122 neither the working directory nor the target rev has the file,
144 neither the working directory nor the target rev has the file,
123 then we need to remove it from the dirstate, to prevent the
145 then we need to remove it from the dirstate, to prevent the
124 dirstate from listing the file when it is no longer in the
146 dirstate from listing the file when it is no longer in the
125 manifest.
147 manifest.
126
148
127 If we're merging, and the other revision has removed a file
149 If we're merging, and the other revision has removed a file
128 that is not present in the working directory, we need to mark it
150 that is not present in the working directory, we need to mark it
129 as removed.
151 as removed.
130 """
152 """
131
153
132 actions = []
154 actions = []
133 state = branchmerge and 'r' or 'f'
155 state = branchmerge and 'r' or 'f'
134 for f in wctx.deleted():
156 for f in wctx.deleted():
135 if f not in mctx:
157 if f not in mctx:
136 actions.append((f, state, None, "forget deleted"))
158 actions.append((f, state, None, "forget deleted"))
137
159
138 if not branchmerge:
160 if not branchmerge:
139 for f in wctx.removed():
161 for f in wctx.removed():
140 if f not in mctx:
162 if f not in mctx:
141 actions.append((f, "f", None, "forget removed"))
163 actions.append((f, "f", None, "forget removed"))
142
164
143 return actions
165 return actions
144
166
145 def _checkcollision(repo, wmf, actions, prompts):
167 def _checkcollision(repo, wmf, actions, prompts):
146 # build provisional merged manifest up
168 # build provisional merged manifest up
147 pmmf = set(wmf)
169 pmmf = set(wmf)
148
170
149 def addop(f, args):
171 def addop(f, args):
150 pmmf.add(f)
172 pmmf.add(f)
151 def removeop(f, args):
173 def removeop(f, args):
152 pmmf.discard(f)
174 pmmf.discard(f)
153 def nop(f, args):
175 def nop(f, args):
154 pass
176 pass
155
177
156 def renameop(f, args):
178 def renameop(f, args):
157 f2, fd, flags = args
179 f2, fd, flags = args
158 if f:
180 if f:
159 pmmf.discard(f)
181 pmmf.discard(f)
160 pmmf.add(fd)
182 pmmf.add(fd)
161 def mergeop(f, args):
183 def mergeop(f, args):
162 f2, fd, move = args
184 f2, fd, move = args
163 if move:
185 if move:
164 pmmf.discard(f)
186 pmmf.discard(f)
165 pmmf.add(fd)
187 pmmf.add(fd)
166
188
167 opmap = {
189 opmap = {
168 "a": addop,
190 "a": addop,
169 "d": renameop,
191 "d": renameop,
170 "dr": nop,
192 "dr": nop,
171 "e": nop,
193 "e": nop,
172 "f": addop, # untracked file should be kept in working directory
194 "f": addop, # untracked file should be kept in working directory
173 "g": addop,
195 "g": addop,
174 "m": mergeop,
196 "m": mergeop,
175 "r": removeop,
197 "r": removeop,
176 "rd": nop,
198 "rd": nop,
177 }
199 }
178 for f, m, args, msg in actions:
200 for f, m, args, msg in actions:
179 op = opmap.get(m)
201 op = opmap.get(m)
180 assert op, m
202 assert op, m
181 op(f, args)
203 op(f, args)
182
204
183 opmap = {
205 opmap = {
184 "cd": addop,
206 "cd": addop,
185 "dc": addop,
207 "dc": addop,
186 }
208 }
187 for f, m in prompts:
209 for f, m in prompts:
188 op = opmap.get(m)
210 op = opmap.get(m)
189 assert op, m
211 assert op, m
190 op(f, None)
212 op(f, None)
191
213
192 # check case-folding collision in provisional merged manifest
214 # check case-folding collision in provisional merged manifest
193 foldmap = {}
215 foldmap = {}
194 for f in sorted(pmmf):
216 for f in sorted(pmmf):
195 fold = util.normcase(f)
217 fold = util.normcase(f)
196 if fold in foldmap:
218 if fold in foldmap:
197 raise util.Abort(_("case-folding collision between %s and %s")
219 raise util.Abort(_("case-folding collision between %s and %s")
198 % (f, foldmap[fold]))
220 % (f, foldmap[fold]))
199 foldmap[fold] = f
221 foldmap[fold] = f
200
222
201 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
223 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
202 acceptremote=False):
224 acceptremote=False):
203 """
225 """
204 Merge p1 and p2 with ancestor pa and generate merge action list
226 Merge p1 and p2 with ancestor pa and generate merge action list
205
227
206 branchmerge and force are as passed in to update
228 branchmerge and force are as passed in to update
207 partial = function to filter file lists
229 partial = function to filter file lists
208 acceptremote = accept the incoming changes without prompting
230 acceptremote = accept the incoming changes without prompting
209 """
231 """
210
232
211 overwrite = force and not branchmerge
233 overwrite = force and not branchmerge
212 actions, copy, movewithdir = [], {}, {}
234 actions, copy, movewithdir = [], {}, {}
213
235
214 followcopies = False
236 followcopies = False
215 if overwrite:
237 if overwrite:
216 pa = wctx
238 pa = wctx
217 elif pa == p2: # backwards
239 elif pa == p2: # backwards
218 pa = wctx.p1()
240 pa = wctx.p1()
219 elif not branchmerge and not wctx.dirty(missing=True):
241 elif not branchmerge and not wctx.dirty(missing=True):
220 pass
242 pass
221 elif pa and repo.ui.configbool("merge", "followcopies", True):
243 elif pa and repo.ui.configbool("merge", "followcopies", True):
222 followcopies = True
244 followcopies = True
223
245
224 # manifests fetched in order are going to be faster, so prime the caches
246 # manifests fetched in order are going to be faster, so prime the caches
225 [x.manifest() for x in
247 [x.manifest() for x in
226 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
248 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
227
249
228 if followcopies:
250 if followcopies:
229 ret = copies.mergecopies(repo, wctx, p2, pa)
251 ret = copies.mergecopies(repo, wctx, p2, pa)
230 copy, movewithdir, diverge, renamedelete = ret
252 copy, movewithdir, diverge, renamedelete = ret
231 for of, fl in diverge.iteritems():
253 for of, fl in diverge.iteritems():
232 actions.append((of, "dr", (fl,), "divergent renames"))
254 actions.append((of, "dr", (fl,), "divergent renames"))
233 for of, fl in renamedelete.iteritems():
255 for of, fl in renamedelete.iteritems():
234 actions.append((of, "rd", (fl,), "rename and delete"))
256 actions.append((of, "rd", (fl,), "rename and delete"))
235
257
236 repo.ui.note(_("resolving manifests\n"))
258 repo.ui.note(_("resolving manifests\n"))
237 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
259 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
238 % (bool(branchmerge), bool(force), bool(partial)))
260 % (bool(branchmerge), bool(force), bool(partial)))
239 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
261 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
240
262
241 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
263 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
242 copied = set(copy.values())
264 copied = set(copy.values())
243 copied.update(movewithdir.values())
265 copied.update(movewithdir.values())
244
266
245 if '.hgsubstate' in m1:
267 if '.hgsubstate' in m1:
246 # check whether sub state is modified
268 # check whether sub state is modified
247 for s in sorted(wctx.substate):
269 for s in sorted(wctx.substate):
248 if wctx.sub(s).dirty():
270 if wctx.sub(s).dirty():
249 m1['.hgsubstate'] += "+"
271 m1['.hgsubstate'] += "+"
250 break
272 break
251
273
252 aborts, prompts = [], []
274 aborts, prompts = [], []
253 # Compare manifests
275 # Compare manifests
254 fdiff = dicthelpers.diff(m1, m2)
276 fdiff = dicthelpers.diff(m1, m2)
255 flagsdiff = m1.flagsdiff(m2)
277 flagsdiff = m1.flagsdiff(m2)
256 diff12 = dicthelpers.join(fdiff, flagsdiff)
278 diff12 = dicthelpers.join(fdiff, flagsdiff)
257
279
258 for f, (n12, fl12) in diff12.iteritems():
280 for f, (n12, fl12) in diff12.iteritems():
259 if n12:
281 if n12:
260 n1, n2 = n12
282 n1, n2 = n12
261 else: # file contents didn't change, but flags did
283 else: # file contents didn't change, but flags did
262 n1 = n2 = m1.get(f, None)
284 n1 = n2 = m1.get(f, None)
263 if n1 is None:
285 if n1 is None:
264 # Since n1 == n2, the file isn't present in m2 either. This
286 # Since n1 == n2, the file isn't present in m2 either. This
265 # means that the file was removed or deleted locally and
287 # means that the file was removed or deleted locally and
266 # removed remotely, but that residual entries remain in flags.
288 # removed remotely, but that residual entries remain in flags.
267 # This can happen in manifests generated by workingctx.
289 # This can happen in manifests generated by workingctx.
268 continue
290 continue
269 if fl12:
291 if fl12:
270 fl1, fl2 = fl12
292 fl1, fl2 = fl12
271 else: # flags didn't change, file contents did
293 else: # flags didn't change, file contents did
272 fl1 = fl2 = m1.flags(f)
294 fl1 = fl2 = m1.flags(f)
273
295
274 if partial and not partial(f):
296 if partial and not partial(f):
275 continue
297 continue
276 if n1 and n2:
298 if n1 and n2:
277 fla = ma.flags(f)
299 fla = ma.flags(f)
278 nol = 'l' not in fl1 + fl2 + fla
300 nol = 'l' not in fl1 + fl2 + fla
279 a = ma.get(f, nullid)
301 a = ma.get(f, nullid)
280 if n2 == a and fl2 == fla:
302 if n2 == a and fl2 == fla:
281 pass # remote unchanged - keep local
303 pass # remote unchanged - keep local
282 elif n1 == a and fl1 == fla: # local unchanged - use remote
304 elif n1 == a and fl1 == fla: # local unchanged - use remote
283 if n1 == n2: # optimization: keep local content
305 if n1 == n2: # optimization: keep local content
284 actions.append((f, "e", (fl2,), "update permissions"))
306 actions.append((f, "e", (fl2,), "update permissions"))
285 else:
307 else:
286 actions.append((f, "g", (fl2,), "remote is newer"))
308 actions.append((f, "g", (fl2,), "remote is newer"))
287 elif nol and n2 == a: # remote only changed 'x'
309 elif nol and n2 == a: # remote only changed 'x'
288 actions.append((f, "e", (fl2,), "update permissions"))
310 actions.append((f, "e", (fl2,), "update permissions"))
289 elif nol and n1 == a: # local only changed 'x'
311 elif nol and n1 == a: # local only changed 'x'
290 actions.append((f, "g", (fl1,), "remote is newer"))
312 actions.append((f, "g", (fl1,), "remote is newer"))
291 else: # both changed something
313 else: # both changed something
292 actions.append((f, "m", (f, f, False), "versions differ"))
314 actions.append((f, "m", (f, f, False), "versions differ"))
293 elif f in copied: # files we'll deal with on m2 side
315 elif f in copied: # files we'll deal with on m2 side
294 pass
316 pass
295 elif n1 and f in movewithdir: # directory rename
317 elif n1 and f in movewithdir: # directory rename
296 f2 = movewithdir[f]
318 f2 = movewithdir[f]
297 actions.append((f, "d", (None, f2, fl1),
319 actions.append((f, "d", (None, f2, fl1),
298 "remote renamed directory to " + f2))
320 "remote renamed directory to " + f2))
299 elif n1 and f in copy:
321 elif n1 and f in copy:
300 f2 = copy[f]
322 f2 = copy[f]
301 actions.append((f, "m", (f2, f, False),
323 actions.append((f, "m", (f2, f, False),
302 "local copied/moved to " + f2))
324 "local copied/moved to " + f2))
303 elif n1 and f in ma: # clean, a different, no remote
325 elif n1 and f in ma: # clean, a different, no remote
304 if n1 != ma[f]:
326 if n1 != ma[f]:
305 prompts.append((f, "cd")) # prompt changed/deleted
327 prompts.append((f, "cd")) # prompt changed/deleted
306 elif n1[20:] == "a": # added, no remote
328 elif n1[20:] == "a": # added, no remote
307 actions.append((f, "f", None, "remote deleted"))
329 actions.append((f, "f", None, "remote deleted"))
308 else:
330 else:
309 actions.append((f, "r", None, "other deleted"))
331 actions.append((f, "r", None, "other deleted"))
310 elif n2 and f in movewithdir:
332 elif n2 and f in movewithdir:
311 f2 = movewithdir[f]
333 f2 = movewithdir[f]
312 actions.append((None, "d", (f, f2, fl2),
334 actions.append((None, "d", (f, f2, fl2),
313 "local renamed directory to " + f2))
335 "local renamed directory to " + f2))
314 elif n2 and f in copy:
336 elif n2 and f in copy:
315 f2 = copy[f]
337 f2 = copy[f]
316 if f2 in m2:
338 if f2 in m2:
317 actions.append((f2, "m", (f, f, False),
339 actions.append((f2, "m", (f, f, False),
318 "remote copied to " + f))
340 "remote copied to " + f))
319 else:
341 else:
320 actions.append((f2, "m", (f, f, True),
342 actions.append((f2, "m", (f, f, True),
321 "remote moved to " + f))
343 "remote moved to " + f))
322 elif n2 and f not in ma:
344 elif n2 and f not in ma:
323 # local unknown, remote created: the logic is described by the
345 # local unknown, remote created: the logic is described by the
324 # following table:
346 # following table:
325 #
347 #
326 # force branchmerge different | action
348 # force branchmerge different | action
327 # n * n | get
349 # n * n | get
328 # n * y | abort
350 # n * y | abort
329 # y n * | get
351 # y n * | get
330 # y y n | get
352 # y y n | get
331 # y y y | merge
353 # y y y | merge
332 #
354 #
333 # Checking whether the files are different is expensive, so we
355 # Checking whether the files are different is expensive, so we
334 # don't do that when we can avoid it.
356 # don't do that when we can avoid it.
335 if force and not branchmerge:
357 if force and not branchmerge:
336 actions.append((f, "g", (fl2,), "remote created"))
358 actions.append((f, "g", (fl2,), "remote created"))
337 else:
359 else:
338 different = _checkunknownfile(repo, wctx, p2, f)
360 different = _checkunknownfile(repo, wctx, p2, f)
339 if force and branchmerge and different:
361 if force and branchmerge and different:
340 actions.append((f, "m", (f, f, False),
362 actions.append((f, "m", (f, f, False),
341 "remote differs from untracked local"))
363 "remote differs from untracked local"))
342 elif not force and different:
364 elif not force and different:
343 aborts.append((f, "ud"))
365 aborts.append((f, "ud"))
344 else:
366 else:
345 actions.append((f, "g", (fl2,), "remote created"))
367 actions.append((f, "g", (fl2,), "remote created"))
346 elif n2 and n2 != ma[f]:
368 elif n2 and n2 != ma[f]:
347 different = _checkunknownfile(repo, wctx, p2, f)
369 different = _checkunknownfile(repo, wctx, p2, f)
348 if not force and different:
370 if not force and different:
349 aborts.append((f, "ud"))
371 aborts.append((f, "ud"))
350 else:
372 else:
351 # if different: old untracked f may be overwritten and lost
373 # if different: old untracked f may be overwritten and lost
352 prompts.append((f, "dc")) # prompt deleted/changed
374 prompts.append((f, "dc")) # prompt deleted/changed
353
375
354 for f, m in sorted(aborts):
376 for f, m in sorted(aborts):
355 if m == "ud":
377 if m == "ud":
356 repo.ui.warn(_("%s: untracked file differs\n") % f)
378 repo.ui.warn(_("%s: untracked file differs\n") % f)
357 else: assert False, m
379 else: assert False, m
358 if aborts:
380 if aborts:
359 raise util.Abort(_("untracked files in working directory differ "
381 raise util.Abort(_("untracked files in working directory differ "
360 "from files in requested revision"))
382 "from files in requested revision"))
361
383
362 if not util.checkcase(repo.path):
384 if not util.checkcase(repo.path):
363 # check collision between files only in p2 for clean update
385 # check collision between files only in p2 for clean update
364 if (not branchmerge and
386 if (not branchmerge and
365 (force or not wctx.dirty(missing=True, branch=False))):
387 (force or not wctx.dirty(missing=True, branch=False))):
366 _checkcollision(repo, m2, [], [])
388 _checkcollision(repo, m2, [], [])
367 else:
389 else:
368 _checkcollision(repo, m1, actions, prompts)
390 _checkcollision(repo, m1, actions, prompts)
369
391
370 for f, m in sorted(prompts):
392 for f, m in sorted(prompts):
371 if m == "cd":
393 if m == "cd":
372 if acceptremote:
394 if acceptremote:
373 actions.append((f, "r", None, "remote delete"))
395 actions.append((f, "r", None, "remote delete"))
374 elif repo.ui.promptchoice(
396 elif repo.ui.promptchoice(
375 _("local changed %s which remote deleted\n"
397 _("local changed %s which remote deleted\n"
376 "use (c)hanged version or (d)elete?"
398 "use (c)hanged version or (d)elete?"
377 "$$ &Changed $$ &Delete") % f, 0):
399 "$$ &Changed $$ &Delete") % f, 0):
378 actions.append((f, "r", None, "prompt delete"))
400 actions.append((f, "r", None, "prompt delete"))
379 else:
401 else:
380 actions.append((f, "a", None, "prompt keep"))
402 actions.append((f, "a", None, "prompt keep"))
381 elif m == "dc":
403 elif m == "dc":
382 if acceptremote:
404 if acceptremote:
383 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
405 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
384 elif repo.ui.promptchoice(
406 elif repo.ui.promptchoice(
385 _("remote changed %s which local deleted\n"
407 _("remote changed %s which local deleted\n"
386 "use (c)hanged version or leave (d)eleted?"
408 "use (c)hanged version or leave (d)eleted?"
387 "$$ &Changed $$ &Deleted") % f, 0) == 0:
409 "$$ &Changed $$ &Deleted") % f, 0) == 0:
388 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
410 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
389 else: assert False, m
411 else: assert False, m
390 return actions
412 return actions
391
413
392 def actionkey(a):
414 def actionkey(a):
393 return a[1] in "rf" and -1 or 0, a
415 return a[1] in "rf" and -1 or 0, a
394
416
395 def getremove(repo, mctx, overwrite, args):
417 def getremove(repo, mctx, overwrite, args):
396 """apply usually-non-interactive updates to the working directory
418 """apply usually-non-interactive updates to the working directory
397
419
398 mctx is the context to be merged into the working copy
420 mctx is the context to be merged into the working copy
399
421
400 yields tuples for progress updates
422 yields tuples for progress updates
401 """
423 """
402 verbose = repo.ui.verbose
424 verbose = repo.ui.verbose
403 unlink = util.unlinkpath
425 unlink = util.unlinkpath
404 wjoin = repo.wjoin
426 wjoin = repo.wjoin
405 fctx = mctx.filectx
427 fctx = mctx.filectx
406 wwrite = repo.wwrite
428 wwrite = repo.wwrite
407 audit = repo.wopener.audit
429 audit = repo.wopener.audit
408 i = 0
430 i = 0
409 for arg in args:
431 for arg in args:
410 f = arg[0]
432 f = arg[0]
411 if arg[1] == 'r':
433 if arg[1] == 'r':
412 if verbose:
434 if verbose:
413 repo.ui.note(_("removing %s\n") % f)
435 repo.ui.note(_("removing %s\n") % f)
414 audit(f)
436 audit(f)
415 try:
437 try:
416 unlink(wjoin(f), ignoremissing=True)
438 unlink(wjoin(f), ignoremissing=True)
417 except OSError, inst:
439 except OSError, inst:
418 repo.ui.warn(_("update failed to remove %s: %s!\n") %
440 repo.ui.warn(_("update failed to remove %s: %s!\n") %
419 (f, inst.strerror))
441 (f, inst.strerror))
420 else:
442 else:
421 if verbose:
443 if verbose:
422 repo.ui.note(_("getting %s\n") % f)
444 repo.ui.note(_("getting %s\n") % f)
423 wwrite(f, fctx(f).data(), arg[2][0])
445 wwrite(f, fctx(f).data(), arg[2][0])
424 if i == 100:
446 if i == 100:
425 yield i, f
447 yield i, f
426 i = 0
448 i = 0
427 i += 1
449 i += 1
428 if i > 0:
450 if i > 0:
429 yield i, f
451 yield i, f
430
452
431 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
453 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
432 """apply the merge action list to the working directory
454 """apply the merge action list to the working directory
433
455
434 wctx is the working copy context
456 wctx is the working copy context
435 mctx is the context to be merged into the working copy
457 mctx is the context to be merged into the working copy
436 actx is the context of the common ancestor
458 actx is the context of the common ancestor
437
459
438 Return a tuple of counts (updated, merged, removed, unresolved) that
460 Return a tuple of counts (updated, merged, removed, unresolved) that
439 describes how many files were affected by the update.
461 describes how many files were affected by the update.
440 """
462 """
441
463
442 updated, merged, removed, unresolved = 0, 0, 0, 0
464 updated, merged, removed, unresolved = 0, 0, 0, 0
443 ms = mergestate(repo)
465 ms = mergestate(repo)
444 ms.reset(wctx.p1().node())
466 ms.reset(wctx.p1().node())
445 moves = []
467 moves = []
446 actions.sort(key=actionkey)
468 actions.sort(key=actionkey)
447
469
448 # prescan for merges
470 # prescan for merges
449 for a in actions:
471 for a in actions:
450 f, m, args, msg = a
472 f, m, args, msg = a
451 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
473 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
452 if m == "m": # merge
474 if m == "m": # merge
453 f2, fd, move = args
475 f2, fd, move = args
454 if fd == '.hgsubstate': # merged internally
476 if fd == '.hgsubstate': # merged internally
455 continue
477 continue
456 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
478 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
457 fcl = wctx[f]
479 fcl = wctx[f]
458 fco = mctx[f2]
480 fco = mctx[f2]
459 if mctx == actx: # backwards, use working dir parent as ancestor
481 if mctx == actx: # backwards, use working dir parent as ancestor
460 if fcl.parents():
482 if fcl.parents():
461 fca = fcl.p1()
483 fca = fcl.p1()
462 else:
484 else:
463 fca = repo.filectx(f, fileid=nullrev)
485 fca = repo.filectx(f, fileid=nullrev)
464 else:
486 else:
465 fca = fcl.ancestor(fco, actx)
487 fca = fcl.ancestor(fco, actx)
466 if not fca:
488 if not fca:
467 fca = repo.filectx(f, fileid=nullrev)
489 fca = repo.filectx(f, fileid=nullrev)
468 ms.add(fcl, fco, fca, fd)
490 ms.add(fcl, fco, fca, fd)
469 if f != fd and move:
491 if f != fd and move:
470 moves.append(f)
492 moves.append(f)
471
493
472 audit = repo.wopener.audit
494 audit = repo.wopener.audit
473
495
474 # remove renamed files after safely stored
496 # remove renamed files after safely stored
475 for f in moves:
497 for f in moves:
476 if os.path.lexists(repo.wjoin(f)):
498 if os.path.lexists(repo.wjoin(f)):
477 repo.ui.debug("removing %s\n" % f)
499 repo.ui.debug("removing %s\n" % f)
478 audit(f)
500 audit(f)
479 util.unlinkpath(repo.wjoin(f))
501 util.unlinkpath(repo.wjoin(f))
480
502
481 numupdates = len(actions)
503 numupdates = len(actions)
482 workeractions = [a for a in actions if a[1] in 'gr']
504 workeractions = [a for a in actions if a[1] in 'gr']
483 updateactions = [a for a in workeractions if a[1] == 'g']
505 updateactions = [a for a in workeractions if a[1] == 'g']
484 updated = len(updateactions)
506 updated = len(updateactions)
485 removeactions = [a for a in workeractions if a[1] == 'r']
507 removeactions = [a for a in workeractions if a[1] == 'r']
486 removed = len(removeactions)
508 removed = len(removeactions)
487 actions = [a for a in actions if a[1] not in 'gr']
509 actions = [a for a in actions if a[1] not in 'gr']
488
510
489 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
511 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
490 if hgsub and hgsub[0] == 'r':
512 if hgsub and hgsub[0] == 'r':
491 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
513 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
492
514
493 z = 0
515 z = 0
494 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
516 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
495 removeactions)
517 removeactions)
496 for i, item in prog:
518 for i, item in prog:
497 z += i
519 z += i
498 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
520 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
499 unit=_('files'))
521 unit=_('files'))
500 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
522 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
501 updateactions)
523 updateactions)
502 for i, item in prog:
524 for i, item in prog:
503 z += i
525 z += i
504 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
526 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
505 unit=_('files'))
527 unit=_('files'))
506
528
507 if hgsub and hgsub[0] == 'g':
529 if hgsub and hgsub[0] == 'g':
508 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
530 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
509
531
510 _updating = _('updating')
532 _updating = _('updating')
511 _files = _('files')
533 _files = _('files')
512 progress = repo.ui.progress
534 progress = repo.ui.progress
513
535
514 for i, a in enumerate(actions):
536 for i, a in enumerate(actions):
515 f, m, args, msg = a
537 f, m, args, msg = a
516 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
538 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
517 if m == "m": # merge
539 if m == "m": # merge
518 f2, fd, move = args
540 f2, fd, move = args
519 if fd == '.hgsubstate': # subrepo states need updating
541 if fd == '.hgsubstate': # subrepo states need updating
520 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
542 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
521 overwrite)
543 overwrite)
522 continue
544 continue
523 audit(fd)
545 audit(fd)
524 r = ms.resolve(fd, wctx, mctx)
546 r = ms.resolve(fd, wctx, mctx)
525 if r is not None and r > 0:
547 if r is not None and r > 0:
526 unresolved += 1
548 unresolved += 1
527 else:
549 else:
528 if r is None:
550 if r is None:
529 updated += 1
551 updated += 1
530 else:
552 else:
531 merged += 1
553 merged += 1
532 elif m == "d": # directory rename
554 elif m == "d": # directory rename
533 f2, fd, flags = args
555 f2, fd, flags = args
534 if f:
556 if f:
535 repo.ui.note(_("moving %s to %s\n") % (f, fd))
557 repo.ui.note(_("moving %s to %s\n") % (f, fd))
536 audit(f)
558 audit(f)
537 repo.wwrite(fd, wctx.filectx(f).data(), flags)
559 repo.wwrite(fd, wctx.filectx(f).data(), flags)
538 util.unlinkpath(repo.wjoin(f))
560 util.unlinkpath(repo.wjoin(f))
539 if f2:
561 if f2:
540 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
562 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
541 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
563 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
542 updated += 1
564 updated += 1
543 elif m == "dr": # divergent renames
565 elif m == "dr": # divergent renames
544 fl, = args
566 fl, = args
545 repo.ui.warn(_("note: possible conflict - %s was renamed "
567 repo.ui.warn(_("note: possible conflict - %s was renamed "
546 "multiple times to:\n") % f)
568 "multiple times to:\n") % f)
547 for nf in fl:
569 for nf in fl:
548 repo.ui.warn(" %s\n" % nf)
570 repo.ui.warn(" %s\n" % nf)
549 elif m == "rd": # rename and delete
571 elif m == "rd": # rename and delete
550 fl, = args
572 fl, = args
551 repo.ui.warn(_("note: possible conflict - %s was deleted "
573 repo.ui.warn(_("note: possible conflict - %s was deleted "
552 "and renamed to:\n") % f)
574 "and renamed to:\n") % f)
553 for nf in fl:
575 for nf in fl:
554 repo.ui.warn(" %s\n" % nf)
576 repo.ui.warn(" %s\n" % nf)
555 elif m == "e": # exec
577 elif m == "e": # exec
556 flags, = args
578 flags, = args
557 audit(f)
579 audit(f)
558 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
580 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
559 updated += 1
581 updated += 1
560 ms.commit()
582 ms.commit()
561 progress(_updating, None, total=numupdates, unit=_files)
583 progress(_updating, None, total=numupdates, unit=_files)
562
584
563 return updated, merged, removed, unresolved
585 return updated, merged, removed, unresolved
564
586
565 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
587 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
566 acceptremote=False):
588 acceptremote=False):
567 "Calculate the actions needed to merge mctx into tctx"
589 "Calculate the actions needed to merge mctx into tctx"
568 actions = []
590 actions = []
569 actions += manifestmerge(repo, tctx, mctx,
591 actions += manifestmerge(repo, tctx, mctx,
570 ancestor,
592 ancestor,
571 branchmerge, force,
593 branchmerge, force,
572 partial, acceptremote)
594 partial, acceptremote)
573 if tctx.rev() is None:
595 if tctx.rev() is None:
574 actions += _forgetremoved(tctx, mctx, branchmerge)
596 actions += _forgetremoved(tctx, mctx, branchmerge)
575 return actions
597 return actions
576
598
577 def recordupdates(repo, actions, branchmerge):
599 def recordupdates(repo, actions, branchmerge):
578 "record merge actions to the dirstate"
600 "record merge actions to the dirstate"
579
601
580 for a in actions:
602 for a in actions:
581 f, m, args, msg = a
603 f, m, args, msg = a
582 if m == "r": # remove
604 if m == "r": # remove
583 if branchmerge:
605 if branchmerge:
584 repo.dirstate.remove(f)
606 repo.dirstate.remove(f)
585 else:
607 else:
586 repo.dirstate.drop(f)
608 repo.dirstate.drop(f)
587 elif m == "a": # re-add
609 elif m == "a": # re-add
588 if not branchmerge:
610 if not branchmerge:
589 repo.dirstate.add(f)
611 repo.dirstate.add(f)
590 elif m == "f": # forget
612 elif m == "f": # forget
591 repo.dirstate.drop(f)
613 repo.dirstate.drop(f)
592 elif m == "e": # exec change
614 elif m == "e": # exec change
593 repo.dirstate.normallookup(f)
615 repo.dirstate.normallookup(f)
594 elif m == "g": # get
616 elif m == "g": # get
595 if branchmerge:
617 if branchmerge:
596 repo.dirstate.otherparent(f)
618 repo.dirstate.otherparent(f)
597 else:
619 else:
598 repo.dirstate.normal(f)
620 repo.dirstate.normal(f)
599 elif m == "m": # merge
621 elif m == "m": # merge
600 f2, fd, move = args
622 f2, fd, move = args
601 if branchmerge:
623 if branchmerge:
602 # We've done a branch merge, mark this file as merged
624 # We've done a branch merge, mark this file as merged
603 # so that we properly record the merger later
625 # so that we properly record the merger later
604 repo.dirstate.merge(fd)
626 repo.dirstate.merge(fd)
605 if f != f2: # copy/rename
627 if f != f2: # copy/rename
606 if move:
628 if move:
607 repo.dirstate.remove(f)
629 repo.dirstate.remove(f)
608 if f != fd:
630 if f != fd:
609 repo.dirstate.copy(f, fd)
631 repo.dirstate.copy(f, fd)
610 else:
632 else:
611 repo.dirstate.copy(f2, fd)
633 repo.dirstate.copy(f2, fd)
612 else:
634 else:
613 # We've update-merged a locally modified file, so
635 # We've update-merged a locally modified file, so
614 # we set the dirstate to emulate a normal checkout
636 # we set the dirstate to emulate a normal checkout
615 # of that file some time in the past. Thus our
637 # of that file some time in the past. Thus our
616 # merge will appear as a normal local file
638 # merge will appear as a normal local file
617 # modification.
639 # modification.
618 if f2 == fd: # file not locally copied/moved
640 if f2 == fd: # file not locally copied/moved
619 repo.dirstate.normallookup(fd)
641 repo.dirstate.normallookup(fd)
620 if move:
642 if move:
621 repo.dirstate.drop(f)
643 repo.dirstate.drop(f)
622 elif m == "d": # directory rename
644 elif m == "d": # directory rename
623 f2, fd, flag = args
645 f2, fd, flag = args
624 if not f2 and f not in repo.dirstate:
646 if not f2 and f not in repo.dirstate:
625 # untracked file moved
647 # untracked file moved
626 continue
648 continue
627 if branchmerge:
649 if branchmerge:
628 repo.dirstate.add(fd)
650 repo.dirstate.add(fd)
629 if f:
651 if f:
630 repo.dirstate.remove(f)
652 repo.dirstate.remove(f)
631 repo.dirstate.copy(f, fd)
653 repo.dirstate.copy(f, fd)
632 if f2:
654 if f2:
633 repo.dirstate.copy(f2, fd)
655 repo.dirstate.copy(f2, fd)
634 else:
656 else:
635 repo.dirstate.normal(fd)
657 repo.dirstate.normal(fd)
636 if f:
658 if f:
637 repo.dirstate.drop(f)
659 repo.dirstate.drop(f)
638
660
639 def update(repo, node, branchmerge, force, partial, ancestor=None,
661 def update(repo, node, branchmerge, force, partial, ancestor=None,
640 mergeancestor=False):
662 mergeancestor=False):
641 """
663 """
642 Perform a merge between the working directory and the given node
664 Perform a merge between the working directory and the given node
643
665
644 node = the node to update to, or None if unspecified
666 node = the node to update to, or None if unspecified
645 branchmerge = whether to merge between branches
667 branchmerge = whether to merge between branches
646 force = whether to force branch merging or file overwriting
668 force = whether to force branch merging or file overwriting
647 partial = a function to filter file lists (dirstate not updated)
669 partial = a function to filter file lists (dirstate not updated)
648 mergeancestor = whether it is merging with an ancestor. If true,
670 mergeancestor = whether it is merging with an ancestor. If true,
649 we should accept the incoming changes for any prompts that occur.
671 we should accept the incoming changes for any prompts that occur.
650 If false, merging with an ancestor (fast-forward) is only allowed
672 If false, merging with an ancestor (fast-forward) is only allowed
651 between different named branches. This flag is used by rebase extension
673 between different named branches. This flag is used by rebase extension
652 as a temporary fix and should be avoided in general.
674 as a temporary fix and should be avoided in general.
653
675
654 The table below shows all the behaviors of the update command
676 The table below shows all the behaviors of the update command
655 given the -c and -C or no options, whether the working directory
677 given the -c and -C or no options, whether the working directory
656 is dirty, whether a revision is specified, and the relationship of
678 is dirty, whether a revision is specified, and the relationship of
657 the parent rev to the target rev (linear, on the same named
679 the parent rev to the target rev (linear, on the same named
658 branch, or on another named branch).
680 branch, or on another named branch).
659
681
660 This logic is tested by test-update-branches.t.
682 This logic is tested by test-update-branches.t.
661
683
662 -c -C dirty rev | linear same cross
684 -c -C dirty rev | linear same cross
663 n n n n | ok (1) x
685 n n n n | ok (1) x
664 n n n y | ok ok ok
686 n n n y | ok ok ok
665 n n y n | merge (2) (2)
687 n n y n | merge (2) (2)
666 n n y y | merge (3) (3)
688 n n y y | merge (3) (3)
667 n y * * | --- discard ---
689 n y * * | --- discard ---
668 y n y * | --- (4) ---
690 y n y * | --- (4) ---
669 y n n * | --- ok ---
691 y n n * | --- ok ---
670 y y * * | --- (5) ---
692 y y * * | --- (5) ---
671
693
672 x = can't happen
694 x = can't happen
673 * = don't-care
695 * = don't-care
674 1 = abort: not a linear update (merge or update --check to force update)
696 1 = abort: not a linear update (merge or update --check to force update)
675 2 = abort: uncommitted changes (commit and merge, or update --clean to
697 2 = abort: uncommitted changes (commit and merge, or update --clean to
676 discard changes)
698 discard changes)
677 3 = abort: uncommitted changes (commit or update --clean to discard changes)
699 3 = abort: uncommitted changes (commit or update --clean to discard changes)
678 4 = abort: uncommitted changes (checked in commands.py)
700 4 = abort: uncommitted changes (checked in commands.py)
679 5 = incompatible options (checked in commands.py)
701 5 = incompatible options (checked in commands.py)
680
702
681 Return the same tuple as applyupdates().
703 Return the same tuple as applyupdates().
682 """
704 """
683
705
684 onode = node
706 onode = node
685 wlock = repo.wlock()
707 wlock = repo.wlock()
686 try:
708 try:
687 wc = repo[None]
709 wc = repo[None]
688 pl = wc.parents()
710 pl = wc.parents()
689 p1 = pl[0]
711 p1 = pl[0]
690 pa = None
712 pa = None
691 if ancestor:
713 if ancestor:
692 pa = repo[ancestor]
714 pa = repo[ancestor]
693
715
694 if node is None:
716 if node is None:
695 # Here is where we should consider bookmarks, divergent bookmarks,
717 # Here is where we should consider bookmarks, divergent bookmarks,
696 # foreground changesets (successors), and tip of current branch;
718 # foreground changesets (successors), and tip of current branch;
697 # but currently we are only checking the branch tips.
719 # but currently we are only checking the branch tips.
698 try:
720 try:
699 node = repo.branchtip(wc.branch())
721 node = repo.branchtip(wc.branch())
700 except error.RepoLookupError:
722 except error.RepoLookupError:
701 if wc.branch() == "default": # no default branch!
723 if wc.branch() == "default": # no default branch!
702 node = repo.lookup("tip") # update to tip
724 node = repo.lookup("tip") # update to tip
703 else:
725 else:
704 raise util.Abort(_("branch %s not found") % wc.branch())
726 raise util.Abort(_("branch %s not found") % wc.branch())
705
727
706 if p1.obsolete() and not p1.children():
728 if p1.obsolete() and not p1.children():
707 # allow updating to successors
729 # allow updating to successors
708 successors = obsolete.successorssets(repo, p1.node())
730 successors = obsolete.successorssets(repo, p1.node())
709
731
710 # behavior of certain cases is as follows,
732 # behavior of certain cases is as follows,
711 #
733 #
712 # divergent changesets: update to highest rev, similar to what
734 # divergent changesets: update to highest rev, similar to what
713 # is currently done when there are more than one head
735 # is currently done when there are more than one head
714 # (i.e. 'tip')
736 # (i.e. 'tip')
715 #
737 #
716 # replaced changesets: same as divergent except we know there
738 # replaced changesets: same as divergent except we know there
717 # is no conflict
739 # is no conflict
718 #
740 #
719 # pruned changeset: no update is done; though, we could
741 # pruned changeset: no update is done; though, we could
720 # consider updating to the first non-obsolete parent,
742 # consider updating to the first non-obsolete parent,
721 # similar to what is current done for 'hg prune'
743 # similar to what is current done for 'hg prune'
722
744
723 if successors:
745 if successors:
724 # flatten the list here handles both divergent (len > 1)
746 # flatten the list here handles both divergent (len > 1)
725 # and the usual case (len = 1)
747 # and the usual case (len = 1)
726 successors = [n for sub in successors for n in sub]
748 successors = [n for sub in successors for n in sub]
727
749
728 # get the max revision for the given successors set,
750 # get the max revision for the given successors set,
729 # i.e. the 'tip' of a set
751 # i.e. the 'tip' of a set
730 node = repo.revs("max(%ln)", successors)[0]
752 node = repo.revs("max(%ln)", successors)[0]
731 pa = p1
753 pa = p1
732
754
733 overwrite = force and not branchmerge
755 overwrite = force and not branchmerge
734
756
735 p2 = repo[node]
757 p2 = repo[node]
736 if pa is None:
758 if pa is None:
737 pa = p1.ancestor(p2)
759 pa = p1.ancestor(p2)
738
760
739 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
761 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
740
762
741 ### check phase
763 ### check phase
742 if not overwrite and len(pl) > 1:
764 if not overwrite and len(pl) > 1:
743 raise util.Abort(_("outstanding uncommitted merges"))
765 raise util.Abort(_("outstanding uncommitted merges"))
744 if branchmerge:
766 if branchmerge:
745 if pa == p2:
767 if pa == p2:
746 raise util.Abort(_("merging with a working directory ancestor"
768 raise util.Abort(_("merging with a working directory ancestor"
747 " has no effect"))
769 " has no effect"))
748 elif pa == p1:
770 elif pa == p1:
749 if not mergeancestor and p1.branch() == p2.branch():
771 if not mergeancestor and p1.branch() == p2.branch():
750 raise util.Abort(_("nothing to merge"),
772 raise util.Abort(_("nothing to merge"),
751 hint=_("use 'hg update' "
773 hint=_("use 'hg update' "
752 "or check 'hg heads'"))
774 "or check 'hg heads'"))
753 if not force and (wc.files() or wc.deleted()):
775 if not force and (wc.files() or wc.deleted()):
754 raise util.Abort(_("uncommitted changes"),
776 raise util.Abort(_("uncommitted changes"),
755 hint=_("use 'hg status' to list changes"))
777 hint=_("use 'hg status' to list changes"))
756 for s in sorted(wc.substate):
778 for s in sorted(wc.substate):
757 if wc.sub(s).dirty():
779 if wc.sub(s).dirty():
758 raise util.Abort(_("uncommitted changes in "
780 raise util.Abort(_("uncommitted changes in "
759 "subrepository '%s'") % s)
781 "subrepository '%s'") % s)
760
782
761 elif not overwrite:
783 elif not overwrite:
762 if p1 == p2: # no-op update
784 if p1 == p2: # no-op update
763 # call the hooks and exit early
785 # call the hooks and exit early
764 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
786 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
765 repo.hook('update', parent1=xp2, parent2='', error=0)
787 repo.hook('update', parent1=xp2, parent2='', error=0)
766 return 0, 0, 0, 0
788 return 0, 0, 0, 0
767
789
768 if pa not in (p1, p2): # nonlinear
790 if pa not in (p1, p2): # nonlinear
769 dirty = wc.dirty(missing=True)
791 dirty = wc.dirty(missing=True)
770 if dirty or onode is None:
792 if dirty or onode is None:
771 # Branching is a bit strange to ensure we do the minimal
793 # Branching is a bit strange to ensure we do the minimal
772 # amount of call to obsolete.background.
794 # amount of call to obsolete.background.
773 foreground = obsolete.foreground(repo, [p1.node()])
795 foreground = obsolete.foreground(repo, [p1.node()])
774 # note: the <node> variable contains a random identifier
796 # note: the <node> variable contains a random identifier
775 if repo[node].node() in foreground:
797 if repo[node].node() in foreground:
776 pa = p1 # allow updating to successors
798 pa = p1 # allow updating to successors
777 elif dirty:
799 elif dirty:
778 msg = _("uncommitted changes")
800 msg = _("uncommitted changes")
779 if onode is None:
801 if onode is None:
780 hint = _("commit and merge, or update --clean to"
802 hint = _("commit and merge, or update --clean to"
781 " discard changes")
803 " discard changes")
782 else:
804 else:
783 hint = _("commit or update --clean to discard"
805 hint = _("commit or update --clean to discard"
784 " changes")
806 " changes")
785 raise util.Abort(msg, hint=hint)
807 raise util.Abort(msg, hint=hint)
786 else: # node is none
808 else: # node is none
787 msg = _("not a linear update")
809 msg = _("not a linear update")
788 hint = _("merge or update --check to force update")
810 hint = _("merge or update --check to force update")
789 raise util.Abort(msg, hint=hint)
811 raise util.Abort(msg, hint=hint)
790 else:
812 else:
791 # Allow jumping branches if clean and specific rev given
813 # Allow jumping branches if clean and specific rev given
792 pa = p1
814 pa = p1
793
815
794 ### calculate phase
816 ### calculate phase
795 actions = calculateupdates(repo, wc, p2, pa,
817 actions = calculateupdates(repo, wc, p2, pa,
796 branchmerge, force, partial, mergeancestor)
818 branchmerge, force, partial, mergeancestor)
797
819
798 ### apply phase
820 ### apply phase
799 if not branchmerge: # just jump to the new rev
821 if not branchmerge: # just jump to the new rev
800 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
822 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
801 if not partial:
823 if not partial:
802 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
824 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
803 # note that we're in the middle of an update
825 # note that we're in the middle of an update
804 repo.vfs.write('updatestate', p2.hex())
826 repo.vfs.write('updatestate', p2.hex())
805
827
806 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
828 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
807
829
808 if not partial:
830 if not partial:
809 repo.setparents(fp1, fp2)
831 repo.setparents(fp1, fp2)
810 recordupdates(repo, actions, branchmerge)
832 recordupdates(repo, actions, branchmerge)
811 # update completed, clear state
833 # update completed, clear state
812 util.unlink(repo.join('updatestate'))
834 util.unlink(repo.join('updatestate'))
813
835
814 if not branchmerge:
836 if not branchmerge:
815 repo.dirstate.setbranch(p2.branch())
837 repo.dirstate.setbranch(p2.branch())
816 finally:
838 finally:
817 wlock.release()
839 wlock.release()
818
840
819 if not partial:
841 if not partial:
820 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
842 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
821 return stats
843 return stats
General Comments 0
You need to be logged in to leave comments. Login now