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