##// END OF EJS Templates
merge.mergestate: factor out code to validate v1/v2 records...
Siddharth Agarwal -
r26500:5bd7c4c0 default
parent child Browse files
Show More
@@ -1,1185 +1,1191 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import shutil
12 import shutil
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from . import (
22 from . import (
23 copies,
23 copies,
24 filemerge,
24 filemerge,
25 obsolete,
25 obsolete,
26 subrepo,
26 subrepo,
27 util,
27 util,
28 worker,
28 worker,
29 )
29 )
30
30
31 _pack = struct.pack
31 _pack = struct.pack
32 _unpack = struct.unpack
32 _unpack = struct.unpack
33
33
34 def _droponode(data):
34 def _droponode(data):
35 # used for compatibility for v1
35 # used for compatibility for v1
36 bits = data.split('\0')
36 bits = data.split('\0')
37 bits = bits[:-2] + bits[-1:]
37 bits = bits[:-2] + bits[-1:]
38 return '\0'.join(bits)
38 return '\0'.join(bits)
39
39
40 class mergestate(object):
40 class mergestate(object):
41 '''track 3-way merge state of individual files
41 '''track 3-way merge state of individual files
42
42
43 it is stored on disk when needed. Two file are used, one with an old
43 it is stored on disk when needed. Two file are used, one with an old
44 format, one with a new format. Both contains similar data, but the new
44 format, one with a new format. Both contains similar data, but the new
45 format can store new kinds of field.
45 format can store new kinds of field.
46
46
47 Current new format is a list of arbitrary record of the form:
47 Current new format is a list of arbitrary record of the form:
48
48
49 [type][length][content]
49 [type][length][content]
50
50
51 Type is a single character, length is a 4 bytes integer, content is an
51 Type is a single character, length is a 4 bytes integer, content is an
52 arbitrary suites of bytes of length `length`.
52 arbitrary suites of bytes of length `length`.
53
53
54 Type should be a letter. Capital letter are mandatory record, Mercurial
54 Type should be a letter. Capital letter are mandatory record, Mercurial
55 should abort if they are unknown. lower case record can be safely ignored.
55 should abort if they are unknown. lower case record can be safely ignored.
56
56
57 Currently known record:
57 Currently known record:
58
58
59 L: the node of the "local" part of the merge (hexified version)
59 L: the node of the "local" part of the merge (hexified version)
60 O: the node of the "other" part of the merge (hexified version)
60 O: the node of the "other" part of the merge (hexified version)
61 F: a file to be merged entry
61 F: a file to be merged entry
62 '''
62 '''
63 statepathv1 = 'merge/state'
63 statepathv1 = 'merge/state'
64 statepathv2 = 'merge/state2'
64 statepathv2 = 'merge/state2'
65
65
66 def __init__(self, repo):
66 def __init__(self, repo):
67 self._repo = repo
67 self._repo = repo
68 self._dirty = False
68 self._dirty = False
69 self._read()
69 self._read()
70
70
71 def reset(self, node=None, other=None):
71 def reset(self, node=None, other=None):
72 self._state = {}
72 self._state = {}
73 self._local = None
73 self._local = None
74 self._other = None
74 self._other = None
75 if node:
75 if node:
76 self._local = node
76 self._local = node
77 self._other = other
77 self._other = other
78 shutil.rmtree(self._repo.join('merge'), True)
78 shutil.rmtree(self._repo.join('merge'), True)
79 self._dirty = False
79 self._dirty = False
80
80
81 def _read(self):
81 def _read(self):
82 """Analyse each record content to restore a serialized state from disk
82 """Analyse each record content to restore a serialized state from disk
83
83
84 This function process "record" entry produced by the de-serialization
84 This function process "record" entry produced by the de-serialization
85 of on disk file.
85 of on disk file.
86 """
86 """
87 self._state = {}
87 self._state = {}
88 self._local = None
88 self._local = None
89 self._other = None
89 self._other = None
90 records = self._readrecords()
90 records = self._readrecords()
91 for rtype, record in records:
91 for rtype, record in records:
92 if rtype == 'L':
92 if rtype == 'L':
93 self._local = bin(record)
93 self._local = bin(record)
94 elif rtype == 'O':
94 elif rtype == 'O':
95 self._other = bin(record)
95 self._other = bin(record)
96 elif rtype == 'F':
96 elif rtype == 'F':
97 bits = record.split('\0')
97 bits = record.split('\0')
98 self._state[bits[0]] = bits[1:]
98 self._state[bits[0]] = bits[1:]
99 elif not rtype.islower():
99 elif not rtype.islower():
100 raise util.Abort(_('unsupported merge state record: %s')
100 raise util.Abort(_('unsupported merge state record: %s')
101 % rtype)
101 % rtype)
102 self._dirty = False
102 self._dirty = False
103
103
104 def _readrecords(self):
104 def _readrecords(self):
105 """Read merge state from disk and return a list of record (TYPE, data)
105 """Read merge state from disk and return a list of record (TYPE, data)
106
106
107 We read data from both v1 and v2 files and decide which one to use.
107 We read data from both v1 and v2 files and decide which one to use.
108
108
109 V1 has been used by version prior to 2.9.1 and contains less data than
109 V1 has been used by version prior to 2.9.1 and contains less data than
110 v2. We read both versions and check if no data in v2 contradicts
110 v2. We read both versions and check if no data in v2 contradicts
111 v1. If there is not contradiction we can safely assume that both v1
111 v1. If there is not contradiction we can safely assume that both v1
112 and v2 were written at the same time and use the extract data in v2. If
112 and v2 were written at the same time and use the extract data in v2. If
113 there is contradiction we ignore v2 content as we assume an old version
113 there is contradiction we ignore v2 content as we assume an old version
114 of Mercurial has overwritten the mergestate file and left an old v2
114 of Mercurial has overwritten the mergestate file and left an old v2
115 file around.
115 file around.
116
116
117 returns list of record [(TYPE, data), ...]"""
117 returns list of record [(TYPE, data), ...]"""
118 v1records = self._readrecordsv1()
118 v1records = self._readrecordsv1()
119 v2records = self._readrecordsv2()
119 v2records = self._readrecordsv2()
120 if self._v1v2match(v1records, v2records):
121 return v2records
122 else:
123 # v1 file is newer than v2 file, use it
124 # we have to infer the "other" changeset of the merge
125 # we cannot do better than that with v1 of the format
126 mctx = self._repo[None].parents()[-1]
127 v1records.append(('O', mctx.hex()))
128 # add place holder "other" file node information
129 # nobody is using it yet so we do no need to fetch the data
130 # if mctx was wrong `mctx[bits[-2]]` may fails.
131 for idx, r in enumerate(v1records):
132 if r[0] == 'F':
133 bits = r[1].split('\0')
134 bits.insert(-2, '')
135 v1records[idx] = (r[0], '\0'.join(bits))
136 return v1records
137
138 def _v1v2match(self, v1records, v2records):
120 oldv2 = set() # old format version of v2 record
139 oldv2 = set() # old format version of v2 record
121 for rec in v2records:
140 for rec in v2records:
122 if rec[0] == 'L':
141 if rec[0] == 'L':
123 oldv2.add(rec)
142 oldv2.add(rec)
124 elif rec[0] == 'F':
143 elif rec[0] == 'F':
125 # drop the onode data (not contained in v1)
144 # drop the onode data (not contained in v1)
126 oldv2.add(('F', _droponode(rec[1])))
145 oldv2.add(('F', _droponode(rec[1])))
127 for rec in v1records:
146 for rec in v1records:
128 if rec not in oldv2:
147 if rec not in oldv2:
129 # v1 file is newer than v2 file, use it
148 return False
130 # we have to infer the "other" changeset of the merge
131 # we cannot do better than that with v1 of the format
132 mctx = self._repo[None].parents()[-1]
133 v1records.append(('O', mctx.hex()))
134 # add place holder "other" file node information
135 # nobody is using it yet so we do no need to fetch the data
136 # if mctx was wrong `mctx[bits[-2]]` may fails.
137 for idx, r in enumerate(v1records):
138 if r[0] == 'F':
139 bits = r[1].split('\0')
140 bits.insert(-2, '')
141 v1records[idx] = (r[0], '\0'.join(bits))
142 return v1records
143 else:
149 else:
144 return v2records
150 return True
145
151
146 def _readrecordsv1(self):
152 def _readrecordsv1(self):
147 """read on disk merge state for version 1 file
153 """read on disk merge state for version 1 file
148
154
149 returns list of record [(TYPE, data), ...]
155 returns list of record [(TYPE, data), ...]
150
156
151 Note: the "F" data from this file are one entry short
157 Note: the "F" data from this file are one entry short
152 (no "other file node" entry)
158 (no "other file node" entry)
153 """
159 """
154 records = []
160 records = []
155 try:
161 try:
156 f = self._repo.vfs(self.statepathv1)
162 f = self._repo.vfs(self.statepathv1)
157 for i, l in enumerate(f):
163 for i, l in enumerate(f):
158 if i == 0:
164 if i == 0:
159 records.append(('L', l[:-1]))
165 records.append(('L', l[:-1]))
160 else:
166 else:
161 records.append(('F', l[:-1]))
167 records.append(('F', l[:-1]))
162 f.close()
168 f.close()
163 except IOError as err:
169 except IOError as err:
164 if err.errno != errno.ENOENT:
170 if err.errno != errno.ENOENT:
165 raise
171 raise
166 return records
172 return records
167
173
168 def _readrecordsv2(self):
174 def _readrecordsv2(self):
169 """read on disk merge state for version 2 file
175 """read on disk merge state for version 2 file
170
176
171 returns list of record [(TYPE, data), ...]
177 returns list of record [(TYPE, data), ...]
172 """
178 """
173 records = []
179 records = []
174 try:
180 try:
175 f = self._repo.vfs(self.statepathv2)
181 f = self._repo.vfs(self.statepathv2)
176 data = f.read()
182 data = f.read()
177 off = 0
183 off = 0
178 end = len(data)
184 end = len(data)
179 while off < end:
185 while off < end:
180 rtype = data[off]
186 rtype = data[off]
181 off += 1
187 off += 1
182 length = _unpack('>I', data[off:(off + 4)])[0]
188 length = _unpack('>I', data[off:(off + 4)])[0]
183 off += 4
189 off += 4
184 record = data[off:(off + length)]
190 record = data[off:(off + length)]
185 off += length
191 off += length
186 records.append((rtype, record))
192 records.append((rtype, record))
187 f.close()
193 f.close()
188 except IOError as err:
194 except IOError as err:
189 if err.errno != errno.ENOENT:
195 if err.errno != errno.ENOENT:
190 raise
196 raise
191 return records
197 return records
192
198
193 def active(self):
199 def active(self):
194 """Whether mergestate is active.
200 """Whether mergestate is active.
195
201
196 Returns True if there appears to be mergestate. This is a rough proxy
202 Returns True if there appears to be mergestate. This is a rough proxy
197 for "is a merge in progress."
203 for "is a merge in progress."
198 """
204 """
199 # Check local variables before looking at filesystem for performance
205 # Check local variables before looking at filesystem for performance
200 # reasons.
206 # reasons.
201 return bool(self._local) or bool(self._state) or \
207 return bool(self._local) or bool(self._state) or \
202 self._repo.vfs.exists(self.statepathv1) or \
208 self._repo.vfs.exists(self.statepathv1) or \
203 self._repo.vfs.exists(self.statepathv2)
209 self._repo.vfs.exists(self.statepathv2)
204
210
205 def commit(self):
211 def commit(self):
206 """Write current state on disk (if necessary)"""
212 """Write current state on disk (if necessary)"""
207 if self._dirty:
213 if self._dirty:
208 records = []
214 records = []
209 records.append(('L', hex(self._local)))
215 records.append(('L', hex(self._local)))
210 records.append(('O', hex(self._other)))
216 records.append(('O', hex(self._other)))
211 for d, v in self._state.iteritems():
217 for d, v in self._state.iteritems():
212 records.append(('F', '\0'.join([d] + v)))
218 records.append(('F', '\0'.join([d] + v)))
213 self._writerecords(records)
219 self._writerecords(records)
214 self._dirty = False
220 self._dirty = False
215
221
216 def _writerecords(self, records):
222 def _writerecords(self, records):
217 """Write current state on disk (both v1 and v2)"""
223 """Write current state on disk (both v1 and v2)"""
218 self._writerecordsv1(records)
224 self._writerecordsv1(records)
219 self._writerecordsv2(records)
225 self._writerecordsv2(records)
220
226
221 def _writerecordsv1(self, records):
227 def _writerecordsv1(self, records):
222 """Write current state on disk in a version 1 file"""
228 """Write current state on disk in a version 1 file"""
223 f = self._repo.vfs(self.statepathv1, 'w')
229 f = self._repo.vfs(self.statepathv1, 'w')
224 irecords = iter(records)
230 irecords = iter(records)
225 lrecords = irecords.next()
231 lrecords = irecords.next()
226 assert lrecords[0] == 'L'
232 assert lrecords[0] == 'L'
227 f.write(hex(self._local) + '\n')
233 f.write(hex(self._local) + '\n')
228 for rtype, data in irecords:
234 for rtype, data in irecords:
229 if rtype == 'F':
235 if rtype == 'F':
230 f.write('%s\n' % _droponode(data))
236 f.write('%s\n' % _droponode(data))
231 f.close()
237 f.close()
232
238
233 def _writerecordsv2(self, records):
239 def _writerecordsv2(self, records):
234 """Write current state on disk in a version 2 file"""
240 """Write current state on disk in a version 2 file"""
235 f = self._repo.vfs(self.statepathv2, 'w')
241 f = self._repo.vfs(self.statepathv2, 'w')
236 for key, data in records:
242 for key, data in records:
237 assert len(key) == 1
243 assert len(key) == 1
238 format = '>sI%is' % len(data)
244 format = '>sI%is' % len(data)
239 f.write(_pack(format, key, len(data), data))
245 f.write(_pack(format, key, len(data), data))
240 f.close()
246 f.close()
241
247
242 def add(self, fcl, fco, fca, fd):
248 def add(self, fcl, fco, fca, fd):
243 """add a new (potentially?) conflicting file the merge state
249 """add a new (potentially?) conflicting file the merge state
244 fcl: file context for local,
250 fcl: file context for local,
245 fco: file context for remote,
251 fco: file context for remote,
246 fca: file context for ancestors,
252 fca: file context for ancestors,
247 fd: file path of the resulting merge.
253 fd: file path of the resulting merge.
248
254
249 note: also write the local version to the `.hg/merge` directory.
255 note: also write the local version to the `.hg/merge` directory.
250 """
256 """
251 hash = util.sha1(fcl.path()).hexdigest()
257 hash = util.sha1(fcl.path()).hexdigest()
252 self._repo.vfs.write('merge/' + hash, fcl.data())
258 self._repo.vfs.write('merge/' + hash, fcl.data())
253 self._state[fd] = ['u', hash, fcl.path(),
259 self._state[fd] = ['u', hash, fcl.path(),
254 fca.path(), hex(fca.filenode()),
260 fca.path(), hex(fca.filenode()),
255 fco.path(), hex(fco.filenode()),
261 fco.path(), hex(fco.filenode()),
256 fcl.flags()]
262 fcl.flags()]
257 self._dirty = True
263 self._dirty = True
258
264
259 def __contains__(self, dfile):
265 def __contains__(self, dfile):
260 return dfile in self._state
266 return dfile in self._state
261
267
262 def __getitem__(self, dfile):
268 def __getitem__(self, dfile):
263 return self._state[dfile][0]
269 return self._state[dfile][0]
264
270
265 def __iter__(self):
271 def __iter__(self):
266 return iter(sorted(self._state))
272 return iter(sorted(self._state))
267
273
268 def files(self):
274 def files(self):
269 return self._state.keys()
275 return self._state.keys()
270
276
271 def mark(self, dfile, state):
277 def mark(self, dfile, state):
272 self._state[dfile][0] = state
278 self._state[dfile][0] = state
273 self._dirty = True
279 self._dirty = True
274
280
275 def unresolved(self):
281 def unresolved(self):
276 """Obtain the paths of unresolved files."""
282 """Obtain the paths of unresolved files."""
277
283
278 for f, entry in self._state.items():
284 for f, entry in self._state.items():
279 if entry[0] == 'u':
285 if entry[0] == 'u':
280 yield f
286 yield f
281
287
282 def resolve(self, dfile, wctx, labels=None):
288 def resolve(self, dfile, wctx, labels=None):
283 """rerun merge process for file path `dfile`"""
289 """rerun merge process for file path `dfile`"""
284 if self[dfile] == 'r':
290 if self[dfile] == 'r':
285 return 0
291 return 0
286 stateentry = self._state[dfile]
292 stateentry = self._state[dfile]
287 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
293 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
288 octx = self._repo[self._other]
294 octx = self._repo[self._other]
289 fcd = wctx[dfile]
295 fcd = wctx[dfile]
290 fco = octx[ofile]
296 fco = octx[ofile]
291 fca = self._repo.filectx(afile, fileid=anode)
297 fca = self._repo.filectx(afile, fileid=anode)
292 # "premerge" x flags
298 # "premerge" x flags
293 flo = fco.flags()
299 flo = fco.flags()
294 fla = fca.flags()
300 fla = fca.flags()
295 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
301 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
296 if fca.node() == nullid:
302 if fca.node() == nullid:
297 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
303 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
298 afile)
304 afile)
299 elif flags == fla:
305 elif flags == fla:
300 flags = flo
306 flags = flo
301 # restore local
307 # restore local
302 f = self._repo.vfs('merge/' + hash)
308 f = self._repo.vfs('merge/' + hash)
303 self._repo.wwrite(dfile, f.read(), flags)
309 self._repo.wwrite(dfile, f.read(), flags)
304 f.close()
310 f.close()
305 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
311 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
306 labels=labels)
312 labels=labels)
307 if r is None:
313 if r is None:
308 # no real conflict
314 # no real conflict
309 del self._state[dfile]
315 del self._state[dfile]
310 self._dirty = True
316 self._dirty = True
311 elif not r:
317 elif not r:
312 self.mark(dfile, 'r')
318 self.mark(dfile, 'r')
313 return r
319 return r
314
320
315 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
321 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
316 if f2 is None:
322 if f2 is None:
317 f2 = f
323 f2 = f
318 return (os.path.isfile(repo.wjoin(f))
324 return (os.path.isfile(repo.wjoin(f))
319 and repo.wvfs.audit.check(f)
325 and repo.wvfs.audit.check(f)
320 and repo.dirstate.normalize(f) not in repo.dirstate
326 and repo.dirstate.normalize(f) not in repo.dirstate
321 and mctx[f2].cmp(wctx[f]))
327 and mctx[f2].cmp(wctx[f]))
322
328
323 def _checkunknownfiles(repo, wctx, mctx, force, actions):
329 def _checkunknownfiles(repo, wctx, mctx, force, actions):
324 """
330 """
325 Considers any actions that care about the presence of conflicting unknown
331 Considers any actions that care about the presence of conflicting unknown
326 files. For some actions, the result is to abort; for others, it is to
332 files. For some actions, the result is to abort; for others, it is to
327 choose a different action.
333 choose a different action.
328 """
334 """
329 aborts = []
335 aborts = []
330 if not force:
336 if not force:
331 for f, (m, args, msg) in actions.iteritems():
337 for f, (m, args, msg) in actions.iteritems():
332 if m in ('c', 'dc'):
338 if m in ('c', 'dc'):
333 if _checkunknownfile(repo, wctx, mctx, f):
339 if _checkunknownfile(repo, wctx, mctx, f):
334 aborts.append(f)
340 aborts.append(f)
335 elif m == 'dg':
341 elif m == 'dg':
336 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
342 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
337 aborts.append(f)
343 aborts.append(f)
338
344
339 for f in sorted(aborts):
345 for f in sorted(aborts):
340 repo.ui.warn(_("%s: untracked file differs\n") % f)
346 repo.ui.warn(_("%s: untracked file differs\n") % f)
341 if aborts:
347 if aborts:
342 raise util.Abort(_("untracked files in working directory differ "
348 raise util.Abort(_("untracked files in working directory differ "
343 "from files in requested revision"))
349 "from files in requested revision"))
344
350
345 for f, (m, args, msg) in actions.iteritems():
351 for f, (m, args, msg) in actions.iteritems():
346 if m == 'c':
352 if m == 'c':
347 actions[f] = ('g', args, msg)
353 actions[f] = ('g', args, msg)
348 elif m == 'cm':
354 elif m == 'cm':
349 fl2, anc = args
355 fl2, anc = args
350 different = _checkunknownfile(repo, wctx, mctx, f)
356 different = _checkunknownfile(repo, wctx, mctx, f)
351 if different:
357 if different:
352 actions[f] = ('m', (f, f, None, False, anc),
358 actions[f] = ('m', (f, f, None, False, anc),
353 "remote differs from untracked local")
359 "remote differs from untracked local")
354 else:
360 else:
355 actions[f] = ('g', (fl2,), "remote created")
361 actions[f] = ('g', (fl2,), "remote created")
356
362
357 def _forgetremoved(wctx, mctx, branchmerge):
363 def _forgetremoved(wctx, mctx, branchmerge):
358 """
364 """
359 Forget removed files
365 Forget removed files
360
366
361 If we're jumping between revisions (as opposed to merging), and if
367 If we're jumping between revisions (as opposed to merging), and if
362 neither the working directory nor the target rev has the file,
368 neither the working directory nor the target rev has the file,
363 then we need to remove it from the dirstate, to prevent the
369 then we need to remove it from the dirstate, to prevent the
364 dirstate from listing the file when it is no longer in the
370 dirstate from listing the file when it is no longer in the
365 manifest.
371 manifest.
366
372
367 If we're merging, and the other revision has removed a file
373 If we're merging, and the other revision has removed a file
368 that is not present in the working directory, we need to mark it
374 that is not present in the working directory, we need to mark it
369 as removed.
375 as removed.
370 """
376 """
371
377
372 actions = {}
378 actions = {}
373 m = 'f'
379 m = 'f'
374 if branchmerge:
380 if branchmerge:
375 m = 'r'
381 m = 'r'
376 for f in wctx.deleted():
382 for f in wctx.deleted():
377 if f not in mctx:
383 if f not in mctx:
378 actions[f] = m, None, "forget deleted"
384 actions[f] = m, None, "forget deleted"
379
385
380 if not branchmerge:
386 if not branchmerge:
381 for f in wctx.removed():
387 for f in wctx.removed():
382 if f not in mctx:
388 if f not in mctx:
383 actions[f] = 'f', None, "forget removed"
389 actions[f] = 'f', None, "forget removed"
384
390
385 return actions
391 return actions
386
392
387 def _checkcollision(repo, wmf, actions):
393 def _checkcollision(repo, wmf, actions):
388 # build provisional merged manifest up
394 # build provisional merged manifest up
389 pmmf = set(wmf)
395 pmmf = set(wmf)
390
396
391 if actions:
397 if actions:
392 # k, dr, e and rd are no-op
398 # k, dr, e and rd are no-op
393 for m in 'a', 'f', 'g', 'cd', 'dc':
399 for m in 'a', 'f', 'g', 'cd', 'dc':
394 for f, args, msg in actions[m]:
400 for f, args, msg in actions[m]:
395 pmmf.add(f)
401 pmmf.add(f)
396 for f, args, msg in actions['r']:
402 for f, args, msg in actions['r']:
397 pmmf.discard(f)
403 pmmf.discard(f)
398 for f, args, msg in actions['dm']:
404 for f, args, msg in actions['dm']:
399 f2, flags = args
405 f2, flags = args
400 pmmf.discard(f2)
406 pmmf.discard(f2)
401 pmmf.add(f)
407 pmmf.add(f)
402 for f, args, msg in actions['dg']:
408 for f, args, msg in actions['dg']:
403 pmmf.add(f)
409 pmmf.add(f)
404 for f, args, msg in actions['m']:
410 for f, args, msg in actions['m']:
405 f1, f2, fa, move, anc = args
411 f1, f2, fa, move, anc = args
406 if move:
412 if move:
407 pmmf.discard(f1)
413 pmmf.discard(f1)
408 pmmf.add(f)
414 pmmf.add(f)
409
415
410 # check case-folding collision in provisional merged manifest
416 # check case-folding collision in provisional merged manifest
411 foldmap = {}
417 foldmap = {}
412 for f in sorted(pmmf):
418 for f in sorted(pmmf):
413 fold = util.normcase(f)
419 fold = util.normcase(f)
414 if fold in foldmap:
420 if fold in foldmap:
415 raise util.Abort(_("case-folding collision between %s and %s")
421 raise util.Abort(_("case-folding collision between %s and %s")
416 % (f, foldmap[fold]))
422 % (f, foldmap[fold]))
417 foldmap[fold] = f
423 foldmap[fold] = f
418
424
419 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
425 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
420 acceptremote, followcopies):
426 acceptremote, followcopies):
421 """
427 """
422 Merge p1 and p2 with ancestor pa and generate merge action list
428 Merge p1 and p2 with ancestor pa and generate merge action list
423
429
424 branchmerge and force are as passed in to update
430 branchmerge and force are as passed in to update
425 partial = function to filter file lists
431 partial = function to filter file lists
426 acceptremote = accept the incoming changes without prompting
432 acceptremote = accept the incoming changes without prompting
427 """
433 """
428
434
429 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
435 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
430
436
431 # manifests fetched in order are going to be faster, so prime the caches
437 # manifests fetched in order are going to be faster, so prime the caches
432 [x.manifest() for x in
438 [x.manifest() for x in
433 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
439 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
434
440
435 if followcopies:
441 if followcopies:
436 ret = copies.mergecopies(repo, wctx, p2, pa)
442 ret = copies.mergecopies(repo, wctx, p2, pa)
437 copy, movewithdir, diverge, renamedelete = ret
443 copy, movewithdir, diverge, renamedelete = ret
438
444
439 repo.ui.note(_("resolving manifests\n"))
445 repo.ui.note(_("resolving manifests\n"))
440 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
446 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
441 % (bool(branchmerge), bool(force), bool(partial)))
447 % (bool(branchmerge), bool(force), bool(partial)))
442 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
448 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
443
449
444 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
450 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
445 copied = set(copy.values())
451 copied = set(copy.values())
446 copied.update(movewithdir.values())
452 copied.update(movewithdir.values())
447
453
448 if '.hgsubstate' in m1:
454 if '.hgsubstate' in m1:
449 # check whether sub state is modified
455 # check whether sub state is modified
450 for s in sorted(wctx.substate):
456 for s in sorted(wctx.substate):
451 if wctx.sub(s).dirty():
457 if wctx.sub(s).dirty():
452 m1['.hgsubstate'] += '+'
458 m1['.hgsubstate'] += '+'
453 break
459 break
454
460
455 # Compare manifests
461 # Compare manifests
456 diff = m1.diff(m2)
462 diff = m1.diff(m2)
457
463
458 actions = {}
464 actions = {}
459 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
465 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
460 if partial and not partial(f):
466 if partial and not partial(f):
461 continue
467 continue
462 if n1 and n2: # file exists on both local and remote side
468 if n1 and n2: # file exists on both local and remote side
463 if f not in ma:
469 if f not in ma:
464 fa = copy.get(f, None)
470 fa = copy.get(f, None)
465 if fa is not None:
471 if fa is not None:
466 actions[f] = ('m', (f, f, fa, False, pa.node()),
472 actions[f] = ('m', (f, f, fa, False, pa.node()),
467 "both renamed from " + fa)
473 "both renamed from " + fa)
468 else:
474 else:
469 actions[f] = ('m', (f, f, None, False, pa.node()),
475 actions[f] = ('m', (f, f, None, False, pa.node()),
470 "both created")
476 "both created")
471 else:
477 else:
472 a = ma[f]
478 a = ma[f]
473 fla = ma.flags(f)
479 fla = ma.flags(f)
474 nol = 'l' not in fl1 + fl2 + fla
480 nol = 'l' not in fl1 + fl2 + fla
475 if n2 == a and fl2 == fla:
481 if n2 == a and fl2 == fla:
476 actions[f] = ('k' , (), "remote unchanged")
482 actions[f] = ('k' , (), "remote unchanged")
477 elif n1 == a and fl1 == fla: # local unchanged - use remote
483 elif n1 == a and fl1 == fla: # local unchanged - use remote
478 if n1 == n2: # optimization: keep local content
484 if n1 == n2: # optimization: keep local content
479 actions[f] = ('e', (fl2,), "update permissions")
485 actions[f] = ('e', (fl2,), "update permissions")
480 else:
486 else:
481 actions[f] = ('g', (fl2,), "remote is newer")
487 actions[f] = ('g', (fl2,), "remote is newer")
482 elif nol and n2 == a: # remote only changed 'x'
488 elif nol and n2 == a: # remote only changed 'x'
483 actions[f] = ('e', (fl2,), "update permissions")
489 actions[f] = ('e', (fl2,), "update permissions")
484 elif nol and n1 == a: # local only changed 'x'
490 elif nol and n1 == a: # local only changed 'x'
485 actions[f] = ('g', (fl1,), "remote is newer")
491 actions[f] = ('g', (fl1,), "remote is newer")
486 else: # both changed something
492 else: # both changed something
487 actions[f] = ('m', (f, f, f, False, pa.node()),
493 actions[f] = ('m', (f, f, f, False, pa.node()),
488 "versions differ")
494 "versions differ")
489 elif n1: # file exists only on local side
495 elif n1: # file exists only on local side
490 if f in copied:
496 if f in copied:
491 pass # we'll deal with it on m2 side
497 pass # we'll deal with it on m2 side
492 elif f in movewithdir: # directory rename, move local
498 elif f in movewithdir: # directory rename, move local
493 f2 = movewithdir[f]
499 f2 = movewithdir[f]
494 if f2 in m2:
500 if f2 in m2:
495 actions[f2] = ('m', (f, f2, None, True, pa.node()),
501 actions[f2] = ('m', (f, f2, None, True, pa.node()),
496 "remote directory rename, both created")
502 "remote directory rename, both created")
497 else:
503 else:
498 actions[f2] = ('dm', (f, fl1),
504 actions[f2] = ('dm', (f, fl1),
499 "remote directory rename - move from " + f)
505 "remote directory rename - move from " + f)
500 elif f in copy:
506 elif f in copy:
501 f2 = copy[f]
507 f2 = copy[f]
502 actions[f] = ('m', (f, f2, f2, False, pa.node()),
508 actions[f] = ('m', (f, f2, f2, False, pa.node()),
503 "local copied/moved from " + f2)
509 "local copied/moved from " + f2)
504 elif f in ma: # clean, a different, no remote
510 elif f in ma: # clean, a different, no remote
505 if n1 != ma[f]:
511 if n1 != ma[f]:
506 if acceptremote:
512 if acceptremote:
507 actions[f] = ('r', None, "remote delete")
513 actions[f] = ('r', None, "remote delete")
508 else:
514 else:
509 actions[f] = ('cd', None, "prompt changed/deleted")
515 actions[f] = ('cd', None, "prompt changed/deleted")
510 elif n1[20:] == 'a':
516 elif n1[20:] == 'a':
511 # This extra 'a' is added by working copy manifest to mark
517 # This extra 'a' is added by working copy manifest to mark
512 # the file as locally added. We should forget it instead of
518 # the file as locally added. We should forget it instead of
513 # deleting it.
519 # deleting it.
514 actions[f] = ('f', None, "remote deleted")
520 actions[f] = ('f', None, "remote deleted")
515 else:
521 else:
516 actions[f] = ('r', None, "other deleted")
522 actions[f] = ('r', None, "other deleted")
517 elif n2: # file exists only on remote side
523 elif n2: # file exists only on remote side
518 if f in copied:
524 if f in copied:
519 pass # we'll deal with it on m1 side
525 pass # we'll deal with it on m1 side
520 elif f in movewithdir:
526 elif f in movewithdir:
521 f2 = movewithdir[f]
527 f2 = movewithdir[f]
522 if f2 in m1:
528 if f2 in m1:
523 actions[f2] = ('m', (f2, f, None, False, pa.node()),
529 actions[f2] = ('m', (f2, f, None, False, pa.node()),
524 "local directory rename, both created")
530 "local directory rename, both created")
525 else:
531 else:
526 actions[f2] = ('dg', (f, fl2),
532 actions[f2] = ('dg', (f, fl2),
527 "local directory rename - get from " + f)
533 "local directory rename - get from " + f)
528 elif f in copy:
534 elif f in copy:
529 f2 = copy[f]
535 f2 = copy[f]
530 if f2 in m2:
536 if f2 in m2:
531 actions[f] = ('m', (f2, f, f2, False, pa.node()),
537 actions[f] = ('m', (f2, f, f2, False, pa.node()),
532 "remote copied from " + f2)
538 "remote copied from " + f2)
533 else:
539 else:
534 actions[f] = ('m', (f2, f, f2, True, pa.node()),
540 actions[f] = ('m', (f2, f, f2, True, pa.node()),
535 "remote moved from " + f2)
541 "remote moved from " + f2)
536 elif f not in ma:
542 elif f not in ma:
537 # local unknown, remote created: the logic is described by the
543 # local unknown, remote created: the logic is described by the
538 # following table:
544 # following table:
539 #
545 #
540 # force branchmerge different | action
546 # force branchmerge different | action
541 # n * * | create
547 # n * * | create
542 # y n * | create
548 # y n * | create
543 # y y n | create
549 # y y n | create
544 # y y y | merge
550 # y y y | merge
545 #
551 #
546 # Checking whether the files are different is expensive, so we
552 # Checking whether the files are different is expensive, so we
547 # don't do that when we can avoid it.
553 # don't do that when we can avoid it.
548 if not force:
554 if not force:
549 actions[f] = ('c', (fl2,), "remote created")
555 actions[f] = ('c', (fl2,), "remote created")
550 elif not branchmerge:
556 elif not branchmerge:
551 actions[f] = ('c', (fl2,), "remote created")
557 actions[f] = ('c', (fl2,), "remote created")
552 else:
558 else:
553 actions[f] = ('cm', (fl2, pa.node()),
559 actions[f] = ('cm', (fl2, pa.node()),
554 "remote created, get or merge")
560 "remote created, get or merge")
555 elif n2 != ma[f]:
561 elif n2 != ma[f]:
556 if acceptremote:
562 if acceptremote:
557 actions[f] = ('c', (fl2,), "remote recreating")
563 actions[f] = ('c', (fl2,), "remote recreating")
558 else:
564 else:
559 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
565 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
560
566
561 return actions, diverge, renamedelete
567 return actions, diverge, renamedelete
562
568
563 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
569 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
564 """Resolves false conflicts where the nodeid changed but the content
570 """Resolves false conflicts where the nodeid changed but the content
565 remained the same."""
571 remained the same."""
566
572
567 for f, (m, args, msg) in actions.items():
573 for f, (m, args, msg) in actions.items():
568 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
574 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
569 # local did change but ended up with same content
575 # local did change but ended up with same content
570 actions[f] = 'r', None, "prompt same"
576 actions[f] = 'r', None, "prompt same"
571 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
577 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
572 # remote did change but ended up with same content
578 # remote did change but ended up with same content
573 del actions[f] # don't get = keep local deleted
579 del actions[f] # don't get = keep local deleted
574
580
575 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
581 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
576 acceptremote, followcopies):
582 acceptremote, followcopies):
577 "Calculate the actions needed to merge mctx into wctx using ancestors"
583 "Calculate the actions needed to merge mctx into wctx using ancestors"
578
584
579 if len(ancestors) == 1: # default
585 if len(ancestors) == 1: # default
580 actions, diverge, renamedelete = manifestmerge(
586 actions, diverge, renamedelete = manifestmerge(
581 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
587 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
582 acceptremote, followcopies)
588 acceptremote, followcopies)
583 _checkunknownfiles(repo, wctx, mctx, force, actions)
589 _checkunknownfiles(repo, wctx, mctx, force, actions)
584
590
585 else: # only when merge.preferancestor=* - the default
591 else: # only when merge.preferancestor=* - the default
586 repo.ui.note(
592 repo.ui.note(
587 _("note: merging %s and %s using bids from ancestors %s\n") %
593 _("note: merging %s and %s using bids from ancestors %s\n") %
588 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
594 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
589
595
590 # Call for bids
596 # Call for bids
591 fbids = {} # mapping filename to bids (action method to list af actions)
597 fbids = {} # mapping filename to bids (action method to list af actions)
592 diverge, renamedelete = None, None
598 diverge, renamedelete = None, None
593 for ancestor in ancestors:
599 for ancestor in ancestors:
594 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
600 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
595 actions, diverge1, renamedelete1 = manifestmerge(
601 actions, diverge1, renamedelete1 = manifestmerge(
596 repo, wctx, mctx, ancestor, branchmerge, force, partial,
602 repo, wctx, mctx, ancestor, branchmerge, force, partial,
597 acceptremote, followcopies)
603 acceptremote, followcopies)
598 _checkunknownfiles(repo, wctx, mctx, force, actions)
604 _checkunknownfiles(repo, wctx, mctx, force, actions)
599
605
600 # Track the shortest set of warning on the theory that bid
606 # Track the shortest set of warning on the theory that bid
601 # merge will correctly incorporate more information
607 # merge will correctly incorporate more information
602 if diverge is None or len(diverge1) < len(diverge):
608 if diverge is None or len(diverge1) < len(diverge):
603 diverge = diverge1
609 diverge = diverge1
604 if renamedelete is None or len(renamedelete) < len(renamedelete1):
610 if renamedelete is None or len(renamedelete) < len(renamedelete1):
605 renamedelete = renamedelete1
611 renamedelete = renamedelete1
606
612
607 for f, a in sorted(actions.iteritems()):
613 for f, a in sorted(actions.iteritems()):
608 m, args, msg = a
614 m, args, msg = a
609 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
615 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
610 if f in fbids:
616 if f in fbids:
611 d = fbids[f]
617 d = fbids[f]
612 if m in d:
618 if m in d:
613 d[m].append(a)
619 d[m].append(a)
614 else:
620 else:
615 d[m] = [a]
621 d[m] = [a]
616 else:
622 else:
617 fbids[f] = {m: [a]}
623 fbids[f] = {m: [a]}
618
624
619 # Pick the best bid for each file
625 # Pick the best bid for each file
620 repo.ui.note(_('\nauction for merging merge bids\n'))
626 repo.ui.note(_('\nauction for merging merge bids\n'))
621 actions = {}
627 actions = {}
622 for f, bids in sorted(fbids.items()):
628 for f, bids in sorted(fbids.items()):
623 # bids is a mapping from action method to list af actions
629 # bids is a mapping from action method to list af actions
624 # Consensus?
630 # Consensus?
625 if len(bids) == 1: # all bids are the same kind of method
631 if len(bids) == 1: # all bids are the same kind of method
626 m, l = bids.items()[0]
632 m, l = bids.items()[0]
627 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
633 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
628 repo.ui.note(" %s: consensus for %s\n" % (f, m))
634 repo.ui.note(" %s: consensus for %s\n" % (f, m))
629 actions[f] = l[0]
635 actions[f] = l[0]
630 continue
636 continue
631 # If keep is an option, just do it.
637 # If keep is an option, just do it.
632 if 'k' in bids:
638 if 'k' in bids:
633 repo.ui.note(" %s: picking 'keep' action\n" % f)
639 repo.ui.note(" %s: picking 'keep' action\n" % f)
634 actions[f] = bids['k'][0]
640 actions[f] = bids['k'][0]
635 continue
641 continue
636 # If there are gets and they all agree [how could they not?], do it.
642 # If there are gets and they all agree [how could they not?], do it.
637 if 'g' in bids:
643 if 'g' in bids:
638 ga0 = bids['g'][0]
644 ga0 = bids['g'][0]
639 if all(a == ga0 for a in bids['g'][1:]):
645 if all(a == ga0 for a in bids['g'][1:]):
640 repo.ui.note(" %s: picking 'get' action\n" % f)
646 repo.ui.note(" %s: picking 'get' action\n" % f)
641 actions[f] = ga0
647 actions[f] = ga0
642 continue
648 continue
643 # TODO: Consider other simple actions such as mode changes
649 # TODO: Consider other simple actions such as mode changes
644 # Handle inefficient democrazy.
650 # Handle inefficient democrazy.
645 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
651 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
646 for m, l in sorted(bids.items()):
652 for m, l in sorted(bids.items()):
647 for _f, args, msg in l:
653 for _f, args, msg in l:
648 repo.ui.note(' %s -> %s\n' % (msg, m))
654 repo.ui.note(' %s -> %s\n' % (msg, m))
649 # Pick random action. TODO: Instead, prompt user when resolving
655 # Pick random action. TODO: Instead, prompt user when resolving
650 m, l = bids.items()[0]
656 m, l = bids.items()[0]
651 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
657 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
652 (f, m))
658 (f, m))
653 actions[f] = l[0]
659 actions[f] = l[0]
654 continue
660 continue
655 repo.ui.note(_('end of auction\n\n'))
661 repo.ui.note(_('end of auction\n\n'))
656
662
657 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
663 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
658
664
659 if wctx.rev() is None:
665 if wctx.rev() is None:
660 fractions = _forgetremoved(wctx, mctx, branchmerge)
666 fractions = _forgetremoved(wctx, mctx, branchmerge)
661 actions.update(fractions)
667 actions.update(fractions)
662
668
663 return actions, diverge, renamedelete
669 return actions, diverge, renamedelete
664
670
665 def batchremove(repo, actions):
671 def batchremove(repo, actions):
666 """apply removes to the working directory
672 """apply removes to the working directory
667
673
668 yields tuples for progress updates
674 yields tuples for progress updates
669 """
675 """
670 verbose = repo.ui.verbose
676 verbose = repo.ui.verbose
671 unlink = util.unlinkpath
677 unlink = util.unlinkpath
672 wjoin = repo.wjoin
678 wjoin = repo.wjoin
673 audit = repo.wvfs.audit
679 audit = repo.wvfs.audit
674 i = 0
680 i = 0
675 for f, args, msg in actions:
681 for f, args, msg in actions:
676 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
682 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
677 if verbose:
683 if verbose:
678 repo.ui.note(_("removing %s\n") % f)
684 repo.ui.note(_("removing %s\n") % f)
679 audit(f)
685 audit(f)
680 try:
686 try:
681 unlink(wjoin(f), ignoremissing=True)
687 unlink(wjoin(f), ignoremissing=True)
682 except OSError as inst:
688 except OSError as inst:
683 repo.ui.warn(_("update failed to remove %s: %s!\n") %
689 repo.ui.warn(_("update failed to remove %s: %s!\n") %
684 (f, inst.strerror))
690 (f, inst.strerror))
685 if i == 100:
691 if i == 100:
686 yield i, f
692 yield i, f
687 i = 0
693 i = 0
688 i += 1
694 i += 1
689 if i > 0:
695 if i > 0:
690 yield i, f
696 yield i, f
691
697
692 def batchget(repo, mctx, actions):
698 def batchget(repo, mctx, actions):
693 """apply gets to the working directory
699 """apply gets to the working directory
694
700
695 mctx is the context to get from
701 mctx is the context to get from
696
702
697 yields tuples for progress updates
703 yields tuples for progress updates
698 """
704 """
699 verbose = repo.ui.verbose
705 verbose = repo.ui.verbose
700 fctx = mctx.filectx
706 fctx = mctx.filectx
701 wwrite = repo.wwrite
707 wwrite = repo.wwrite
702 i = 0
708 i = 0
703 for f, args, msg in actions:
709 for f, args, msg in actions:
704 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
710 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
705 if verbose:
711 if verbose:
706 repo.ui.note(_("getting %s\n") % f)
712 repo.ui.note(_("getting %s\n") % f)
707 wwrite(f, fctx(f).data(), args[0])
713 wwrite(f, fctx(f).data(), args[0])
708 if i == 100:
714 if i == 100:
709 yield i, f
715 yield i, f
710 i = 0
716 i = 0
711 i += 1
717 i += 1
712 if i > 0:
718 if i > 0:
713 yield i, f
719 yield i, f
714
720
715 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
721 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
716 """apply the merge action list to the working directory
722 """apply the merge action list to the working directory
717
723
718 wctx is the working copy context
724 wctx is the working copy context
719 mctx is the context to be merged into the working copy
725 mctx is the context to be merged into the working copy
720
726
721 Return a tuple of counts (updated, merged, removed, unresolved) that
727 Return a tuple of counts (updated, merged, removed, unresolved) that
722 describes how many files were affected by the update.
728 describes how many files were affected by the update.
723 """
729 """
724
730
725 updated, merged, removed, unresolved = 0, 0, 0, 0
731 updated, merged, removed, unresolved = 0, 0, 0, 0
726 ms = mergestate(repo)
732 ms = mergestate(repo)
727 ms.reset(wctx.p1().node(), mctx.node())
733 ms.reset(wctx.p1().node(), mctx.node())
728 moves = []
734 moves = []
729 for m, l in actions.items():
735 for m, l in actions.items():
730 l.sort()
736 l.sort()
731
737
732 # prescan for merges
738 # prescan for merges
733 for f, args, msg in actions['m']:
739 for f, args, msg in actions['m']:
734 f1, f2, fa, move, anc = args
740 f1, f2, fa, move, anc = args
735 if f == '.hgsubstate': # merged internally
741 if f == '.hgsubstate': # merged internally
736 continue
742 continue
737 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
743 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
738 fcl = wctx[f1]
744 fcl = wctx[f1]
739 fco = mctx[f2]
745 fco = mctx[f2]
740 actx = repo[anc]
746 actx = repo[anc]
741 if fa in actx:
747 if fa in actx:
742 fca = actx[fa]
748 fca = actx[fa]
743 else:
749 else:
744 fca = repo.filectx(f1, fileid=nullrev)
750 fca = repo.filectx(f1, fileid=nullrev)
745 ms.add(fcl, fco, fca, f)
751 ms.add(fcl, fco, fca, f)
746 if f1 != f and move:
752 if f1 != f and move:
747 moves.append(f1)
753 moves.append(f1)
748
754
749 audit = repo.wvfs.audit
755 audit = repo.wvfs.audit
750 _updating = _('updating')
756 _updating = _('updating')
751 _files = _('files')
757 _files = _('files')
752 progress = repo.ui.progress
758 progress = repo.ui.progress
753
759
754 # remove renamed files after safely stored
760 # remove renamed files after safely stored
755 for f in moves:
761 for f in moves:
756 if os.path.lexists(repo.wjoin(f)):
762 if os.path.lexists(repo.wjoin(f)):
757 repo.ui.debug("removing %s\n" % f)
763 repo.ui.debug("removing %s\n" % f)
758 audit(f)
764 audit(f)
759 util.unlinkpath(repo.wjoin(f))
765 util.unlinkpath(repo.wjoin(f))
760
766
761 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
767 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
762
768
763 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
769 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
764 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
770 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
765
771
766 # remove in parallel (must come first)
772 # remove in parallel (must come first)
767 z = 0
773 z = 0
768 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
774 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
769 for i, item in prog:
775 for i, item in prog:
770 z += i
776 z += i
771 progress(_updating, z, item=item, total=numupdates, unit=_files)
777 progress(_updating, z, item=item, total=numupdates, unit=_files)
772 removed = len(actions['r'])
778 removed = len(actions['r'])
773
779
774 # get in parallel
780 # get in parallel
775 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
781 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
776 for i, item in prog:
782 for i, item in prog:
777 z += i
783 z += i
778 progress(_updating, z, item=item, total=numupdates, unit=_files)
784 progress(_updating, z, item=item, total=numupdates, unit=_files)
779 updated = len(actions['g'])
785 updated = len(actions['g'])
780
786
781 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
787 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
782 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
788 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
783
789
784 # forget (manifest only, just log it) (must come first)
790 # forget (manifest only, just log it) (must come first)
785 for f, args, msg in actions['f']:
791 for f, args, msg in actions['f']:
786 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
792 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
787 z += 1
793 z += 1
788 progress(_updating, z, item=f, total=numupdates, unit=_files)
794 progress(_updating, z, item=f, total=numupdates, unit=_files)
789
795
790 # re-add (manifest only, just log it)
796 # re-add (manifest only, just log it)
791 for f, args, msg in actions['a']:
797 for f, args, msg in actions['a']:
792 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
798 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
793 z += 1
799 z += 1
794 progress(_updating, z, item=f, total=numupdates, unit=_files)
800 progress(_updating, z, item=f, total=numupdates, unit=_files)
795
801
796 # keep (noop, just log it)
802 # keep (noop, just log it)
797 for f, args, msg in actions['k']:
803 for f, args, msg in actions['k']:
798 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
804 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
799 # no progress
805 # no progress
800
806
801 # directory rename, move local
807 # directory rename, move local
802 for f, args, msg in actions['dm']:
808 for f, args, msg in actions['dm']:
803 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
809 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
804 z += 1
810 z += 1
805 progress(_updating, z, item=f, total=numupdates, unit=_files)
811 progress(_updating, z, item=f, total=numupdates, unit=_files)
806 f0, flags = args
812 f0, flags = args
807 repo.ui.note(_("moving %s to %s\n") % (f0, f))
813 repo.ui.note(_("moving %s to %s\n") % (f0, f))
808 audit(f)
814 audit(f)
809 repo.wwrite(f, wctx.filectx(f0).data(), flags)
815 repo.wwrite(f, wctx.filectx(f0).data(), flags)
810 util.unlinkpath(repo.wjoin(f0))
816 util.unlinkpath(repo.wjoin(f0))
811 updated += 1
817 updated += 1
812
818
813 # local directory rename, get
819 # local directory rename, get
814 for f, args, msg in actions['dg']:
820 for f, args, msg in actions['dg']:
815 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
821 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
816 z += 1
822 z += 1
817 progress(_updating, z, item=f, total=numupdates, unit=_files)
823 progress(_updating, z, item=f, total=numupdates, unit=_files)
818 f0, flags = args
824 f0, flags = args
819 repo.ui.note(_("getting %s to %s\n") % (f0, f))
825 repo.ui.note(_("getting %s to %s\n") % (f0, f))
820 repo.wwrite(f, mctx.filectx(f0).data(), flags)
826 repo.wwrite(f, mctx.filectx(f0).data(), flags)
821 updated += 1
827 updated += 1
822
828
823 # exec
829 # exec
824 for f, args, msg in actions['e']:
830 for f, args, msg in actions['e']:
825 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
831 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
826 z += 1
832 z += 1
827 progress(_updating, z, item=f, total=numupdates, unit=_files)
833 progress(_updating, z, item=f, total=numupdates, unit=_files)
828 flags, = args
834 flags, = args
829 audit(f)
835 audit(f)
830 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
836 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
831 updated += 1
837 updated += 1
832
838
833 # merge
839 # merge
834 for f, args, msg in actions['m']:
840 for f, args, msg in actions['m']:
835 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
841 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
836 z += 1
842 z += 1
837 progress(_updating, z, item=f, total=numupdates, unit=_files)
843 progress(_updating, z, item=f, total=numupdates, unit=_files)
838 if f == '.hgsubstate': # subrepo states need updating
844 if f == '.hgsubstate': # subrepo states need updating
839 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
845 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
840 overwrite)
846 overwrite)
841 continue
847 continue
842 audit(f)
848 audit(f)
843 r = ms.resolve(f, wctx, labels=labels)
849 r = ms.resolve(f, wctx, labels=labels)
844 if r is not None and r > 0:
850 if r is not None and r > 0:
845 unresolved += 1
851 unresolved += 1
846 else:
852 else:
847 if r is None:
853 if r is None:
848 updated += 1
854 updated += 1
849 else:
855 else:
850 merged += 1
856 merged += 1
851
857
852 ms.commit()
858 ms.commit()
853 progress(_updating, None, total=numupdates, unit=_files)
859 progress(_updating, None, total=numupdates, unit=_files)
854
860
855 return updated, merged, removed, unresolved
861 return updated, merged, removed, unresolved
856
862
857 def recordupdates(repo, actions, branchmerge):
863 def recordupdates(repo, actions, branchmerge):
858 "record merge actions to the dirstate"
864 "record merge actions to the dirstate"
859 # remove (must come first)
865 # remove (must come first)
860 for f, args, msg in actions['r']:
866 for f, args, msg in actions['r']:
861 if branchmerge:
867 if branchmerge:
862 repo.dirstate.remove(f)
868 repo.dirstate.remove(f)
863 else:
869 else:
864 repo.dirstate.drop(f)
870 repo.dirstate.drop(f)
865
871
866 # forget (must come first)
872 # forget (must come first)
867 for f, args, msg in actions['f']:
873 for f, args, msg in actions['f']:
868 repo.dirstate.drop(f)
874 repo.dirstate.drop(f)
869
875
870 # re-add
876 # re-add
871 for f, args, msg in actions['a']:
877 for f, args, msg in actions['a']:
872 if not branchmerge:
878 if not branchmerge:
873 repo.dirstate.add(f)
879 repo.dirstate.add(f)
874
880
875 # exec change
881 # exec change
876 for f, args, msg in actions['e']:
882 for f, args, msg in actions['e']:
877 repo.dirstate.normallookup(f)
883 repo.dirstate.normallookup(f)
878
884
879 # keep
885 # keep
880 for f, args, msg in actions['k']:
886 for f, args, msg in actions['k']:
881 pass
887 pass
882
888
883 # get
889 # get
884 for f, args, msg in actions['g']:
890 for f, args, msg in actions['g']:
885 if branchmerge:
891 if branchmerge:
886 repo.dirstate.otherparent(f)
892 repo.dirstate.otherparent(f)
887 else:
893 else:
888 repo.dirstate.normal(f)
894 repo.dirstate.normal(f)
889
895
890 # merge
896 # merge
891 for f, args, msg in actions['m']:
897 for f, args, msg in actions['m']:
892 f1, f2, fa, move, anc = args
898 f1, f2, fa, move, anc = args
893 if branchmerge:
899 if branchmerge:
894 # We've done a branch merge, mark this file as merged
900 # We've done a branch merge, mark this file as merged
895 # so that we properly record the merger later
901 # so that we properly record the merger later
896 repo.dirstate.merge(f)
902 repo.dirstate.merge(f)
897 if f1 != f2: # copy/rename
903 if f1 != f2: # copy/rename
898 if move:
904 if move:
899 repo.dirstate.remove(f1)
905 repo.dirstate.remove(f1)
900 if f1 != f:
906 if f1 != f:
901 repo.dirstate.copy(f1, f)
907 repo.dirstate.copy(f1, f)
902 else:
908 else:
903 repo.dirstate.copy(f2, f)
909 repo.dirstate.copy(f2, f)
904 else:
910 else:
905 # We've update-merged a locally modified file, so
911 # We've update-merged a locally modified file, so
906 # we set the dirstate to emulate a normal checkout
912 # we set the dirstate to emulate a normal checkout
907 # of that file some time in the past. Thus our
913 # of that file some time in the past. Thus our
908 # merge will appear as a normal local file
914 # merge will appear as a normal local file
909 # modification.
915 # modification.
910 if f2 == f: # file not locally copied/moved
916 if f2 == f: # file not locally copied/moved
911 repo.dirstate.normallookup(f)
917 repo.dirstate.normallookup(f)
912 if move:
918 if move:
913 repo.dirstate.drop(f1)
919 repo.dirstate.drop(f1)
914
920
915 # directory rename, move local
921 # directory rename, move local
916 for f, args, msg in actions['dm']:
922 for f, args, msg in actions['dm']:
917 f0, flag = args
923 f0, flag = args
918 if branchmerge:
924 if branchmerge:
919 repo.dirstate.add(f)
925 repo.dirstate.add(f)
920 repo.dirstate.remove(f0)
926 repo.dirstate.remove(f0)
921 repo.dirstate.copy(f0, f)
927 repo.dirstate.copy(f0, f)
922 else:
928 else:
923 repo.dirstate.normal(f)
929 repo.dirstate.normal(f)
924 repo.dirstate.drop(f0)
930 repo.dirstate.drop(f0)
925
931
926 # directory rename, get
932 # directory rename, get
927 for f, args, msg in actions['dg']:
933 for f, args, msg in actions['dg']:
928 f0, flag = args
934 f0, flag = args
929 if branchmerge:
935 if branchmerge:
930 repo.dirstate.add(f)
936 repo.dirstate.add(f)
931 repo.dirstate.copy(f0, f)
937 repo.dirstate.copy(f0, f)
932 else:
938 else:
933 repo.dirstate.normal(f)
939 repo.dirstate.normal(f)
934
940
935 def update(repo, node, branchmerge, force, partial, ancestor=None,
941 def update(repo, node, branchmerge, force, partial, ancestor=None,
936 mergeancestor=False, labels=None):
942 mergeancestor=False, labels=None):
937 """
943 """
938 Perform a merge between the working directory and the given node
944 Perform a merge between the working directory and the given node
939
945
940 node = the node to update to, or None if unspecified
946 node = the node to update to, or None if unspecified
941 branchmerge = whether to merge between branches
947 branchmerge = whether to merge between branches
942 force = whether to force branch merging or file overwriting
948 force = whether to force branch merging or file overwriting
943 partial = a function to filter file lists (dirstate not updated)
949 partial = a function to filter file lists (dirstate not updated)
944 mergeancestor = whether it is merging with an ancestor. If true,
950 mergeancestor = whether it is merging with an ancestor. If true,
945 we should accept the incoming changes for any prompts that occur.
951 we should accept the incoming changes for any prompts that occur.
946 If false, merging with an ancestor (fast-forward) is only allowed
952 If false, merging with an ancestor (fast-forward) is only allowed
947 between different named branches. This flag is used by rebase extension
953 between different named branches. This flag is used by rebase extension
948 as a temporary fix and should be avoided in general.
954 as a temporary fix and should be avoided in general.
949
955
950 The table below shows all the behaviors of the update command
956 The table below shows all the behaviors of the update command
951 given the -c and -C or no options, whether the working directory
957 given the -c and -C or no options, whether the working directory
952 is dirty, whether a revision is specified, and the relationship of
958 is dirty, whether a revision is specified, and the relationship of
953 the parent rev to the target rev (linear, on the same named
959 the parent rev to the target rev (linear, on the same named
954 branch, or on another named branch).
960 branch, or on another named branch).
955
961
956 This logic is tested by test-update-branches.t.
962 This logic is tested by test-update-branches.t.
957
963
958 -c -C dirty rev | linear same cross
964 -c -C dirty rev | linear same cross
959 n n n n | ok (1) x
965 n n n n | ok (1) x
960 n n n y | ok ok ok
966 n n n y | ok ok ok
961 n n y n | merge (2) (2)
967 n n y n | merge (2) (2)
962 n n y y | merge (3) (3)
968 n n y y | merge (3) (3)
963 n y * * | --- discard ---
969 n y * * | --- discard ---
964 y n y * | --- (4) ---
970 y n y * | --- (4) ---
965 y n n * | --- ok ---
971 y n n * | --- ok ---
966 y y * * | --- (5) ---
972 y y * * | --- (5) ---
967
973
968 x = can't happen
974 x = can't happen
969 * = don't-care
975 * = don't-care
970 1 = abort: not a linear update (merge or update --check to force update)
976 1 = abort: not a linear update (merge or update --check to force update)
971 2 = abort: uncommitted changes (commit and merge, or update --clean to
977 2 = abort: uncommitted changes (commit and merge, or update --clean to
972 discard changes)
978 discard changes)
973 3 = abort: uncommitted changes (commit or update --clean to discard changes)
979 3 = abort: uncommitted changes (commit or update --clean to discard changes)
974 4 = abort: uncommitted changes (checked in commands.py)
980 4 = abort: uncommitted changes (checked in commands.py)
975 5 = incompatible options (checked in commands.py)
981 5 = incompatible options (checked in commands.py)
976
982
977 Return the same tuple as applyupdates().
983 Return the same tuple as applyupdates().
978 """
984 """
979
985
980 onode = node
986 onode = node
981 wlock = repo.wlock()
987 wlock = repo.wlock()
982 try:
988 try:
983 wc = repo[None]
989 wc = repo[None]
984 pl = wc.parents()
990 pl = wc.parents()
985 p1 = pl[0]
991 p1 = pl[0]
986 pas = [None]
992 pas = [None]
987 if ancestor is not None:
993 if ancestor is not None:
988 pas = [repo[ancestor]]
994 pas = [repo[ancestor]]
989
995
990 if node is None:
996 if node is None:
991 nodes = list(repo.set('_updatedefaultdest()'))
997 nodes = list(repo.set('_updatedefaultdest()'))
992 if nodes:
998 if nodes:
993 node = nodes[0].node()
999 node = nodes[0].node()
994 if p1.obsolete() and not p1.children():
1000 if p1.obsolete() and not p1.children():
995 pas = [p1]
1001 pas = [p1]
996
1002
997 overwrite = force and not branchmerge
1003 overwrite = force and not branchmerge
998
1004
999 p2 = repo[node]
1005 p2 = repo[node]
1000 if pas[0] is None:
1006 if pas[0] is None:
1001 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1007 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1002 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1008 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1003 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1009 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1004 else:
1010 else:
1005 pas = [p1.ancestor(p2, warn=branchmerge)]
1011 pas = [p1.ancestor(p2, warn=branchmerge)]
1006
1012
1007 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1013 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1008
1014
1009 ### check phase
1015 ### check phase
1010 if not overwrite and len(pl) > 1:
1016 if not overwrite and len(pl) > 1:
1011 raise util.Abort(_("outstanding uncommitted merge"))
1017 raise util.Abort(_("outstanding uncommitted merge"))
1012 if branchmerge:
1018 if branchmerge:
1013 if pas == [p2]:
1019 if pas == [p2]:
1014 raise util.Abort(_("merging with a working directory ancestor"
1020 raise util.Abort(_("merging with a working directory ancestor"
1015 " has no effect"))
1021 " has no effect"))
1016 elif pas == [p1]:
1022 elif pas == [p1]:
1017 if not mergeancestor and p1.branch() == p2.branch():
1023 if not mergeancestor and p1.branch() == p2.branch():
1018 raise util.Abort(_("nothing to merge"),
1024 raise util.Abort(_("nothing to merge"),
1019 hint=_("use 'hg update' "
1025 hint=_("use 'hg update' "
1020 "or check 'hg heads'"))
1026 "or check 'hg heads'"))
1021 if not force and (wc.files() or wc.deleted()):
1027 if not force and (wc.files() or wc.deleted()):
1022 raise util.Abort(_("uncommitted changes"),
1028 raise util.Abort(_("uncommitted changes"),
1023 hint=_("use 'hg status' to list changes"))
1029 hint=_("use 'hg status' to list changes"))
1024 for s in sorted(wc.substate):
1030 for s in sorted(wc.substate):
1025 wc.sub(s).bailifchanged()
1031 wc.sub(s).bailifchanged()
1026
1032
1027 elif not overwrite:
1033 elif not overwrite:
1028 if p1 == p2: # no-op update
1034 if p1 == p2: # no-op update
1029 # call the hooks and exit early
1035 # call the hooks and exit early
1030 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1036 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1031 repo.hook('update', parent1=xp2, parent2='', error=0)
1037 repo.hook('update', parent1=xp2, parent2='', error=0)
1032 return 0, 0, 0, 0
1038 return 0, 0, 0, 0
1033
1039
1034 if pas not in ([p1], [p2]): # nonlinear
1040 if pas not in ([p1], [p2]): # nonlinear
1035 dirty = wc.dirty(missing=True)
1041 dirty = wc.dirty(missing=True)
1036 if dirty or onode is None:
1042 if dirty or onode is None:
1037 # Branching is a bit strange to ensure we do the minimal
1043 # Branching is a bit strange to ensure we do the minimal
1038 # amount of call to obsolete.background.
1044 # amount of call to obsolete.background.
1039 foreground = obsolete.foreground(repo, [p1.node()])
1045 foreground = obsolete.foreground(repo, [p1.node()])
1040 # note: the <node> variable contains a random identifier
1046 # note: the <node> variable contains a random identifier
1041 if repo[node].node() in foreground:
1047 if repo[node].node() in foreground:
1042 pas = [p1] # allow updating to successors
1048 pas = [p1] # allow updating to successors
1043 elif dirty:
1049 elif dirty:
1044 msg = _("uncommitted changes")
1050 msg = _("uncommitted changes")
1045 if onode is None:
1051 if onode is None:
1046 hint = _("commit and merge, or update --clean to"
1052 hint = _("commit and merge, or update --clean to"
1047 " discard changes")
1053 " discard changes")
1048 else:
1054 else:
1049 hint = _("commit or update --clean to discard"
1055 hint = _("commit or update --clean to discard"
1050 " changes")
1056 " changes")
1051 raise util.Abort(msg, hint=hint)
1057 raise util.Abort(msg, hint=hint)
1052 else: # node is none
1058 else: # node is none
1053 msg = _("not a linear update")
1059 msg = _("not a linear update")
1054 hint = _("merge or update --check to force update")
1060 hint = _("merge or update --check to force update")
1055 raise util.Abort(msg, hint=hint)
1061 raise util.Abort(msg, hint=hint)
1056 else:
1062 else:
1057 # Allow jumping branches if clean and specific rev given
1063 # Allow jumping branches if clean and specific rev given
1058 pas = [p1]
1064 pas = [p1]
1059
1065
1060 # deprecated config: merge.followcopies
1066 # deprecated config: merge.followcopies
1061 followcopies = False
1067 followcopies = False
1062 if overwrite:
1068 if overwrite:
1063 pas = [wc]
1069 pas = [wc]
1064 elif pas == [p2]: # backwards
1070 elif pas == [p2]: # backwards
1065 pas = [wc.p1()]
1071 pas = [wc.p1()]
1066 elif not branchmerge and not wc.dirty(missing=True):
1072 elif not branchmerge and not wc.dirty(missing=True):
1067 pass
1073 pass
1068 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1074 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1069 followcopies = True
1075 followcopies = True
1070
1076
1071 ### calculate phase
1077 ### calculate phase
1072 actionbyfile, diverge, renamedelete = calculateupdates(
1078 actionbyfile, diverge, renamedelete = calculateupdates(
1073 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1079 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1074 followcopies)
1080 followcopies)
1075 # Convert to dictionary-of-lists format
1081 # Convert to dictionary-of-lists format
1076 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1082 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1077 for f, (m, args, msg) in actionbyfile.iteritems():
1083 for f, (m, args, msg) in actionbyfile.iteritems():
1078 if m not in actions:
1084 if m not in actions:
1079 actions[m] = []
1085 actions[m] = []
1080 actions[m].append((f, args, msg))
1086 actions[m].append((f, args, msg))
1081
1087
1082 if not util.checkcase(repo.path):
1088 if not util.checkcase(repo.path):
1083 # check collision between files only in p2 for clean update
1089 # check collision between files only in p2 for clean update
1084 if (not branchmerge and
1090 if (not branchmerge and
1085 (force or not wc.dirty(missing=True, branch=False))):
1091 (force or not wc.dirty(missing=True, branch=False))):
1086 _checkcollision(repo, p2.manifest(), None)
1092 _checkcollision(repo, p2.manifest(), None)
1087 else:
1093 else:
1088 _checkcollision(repo, wc.manifest(), actions)
1094 _checkcollision(repo, wc.manifest(), actions)
1089
1095
1090 # Prompt and create actions. TODO: Move this towards resolve phase.
1096 # Prompt and create actions. TODO: Move this towards resolve phase.
1091 for f, args, msg in sorted(actions['cd']):
1097 for f, args, msg in sorted(actions['cd']):
1092 if repo.ui.promptchoice(
1098 if repo.ui.promptchoice(
1093 _("local changed %s which remote deleted\n"
1099 _("local changed %s which remote deleted\n"
1094 "use (c)hanged version or (d)elete?"
1100 "use (c)hanged version or (d)elete?"
1095 "$$ &Changed $$ &Delete") % f, 0):
1101 "$$ &Changed $$ &Delete") % f, 0):
1096 actions['r'].append((f, None, "prompt delete"))
1102 actions['r'].append((f, None, "prompt delete"))
1097 else:
1103 else:
1098 actions['a'].append((f, None, "prompt keep"))
1104 actions['a'].append((f, None, "prompt keep"))
1099 del actions['cd'][:]
1105 del actions['cd'][:]
1100
1106
1101 for f, args, msg in sorted(actions['dc']):
1107 for f, args, msg in sorted(actions['dc']):
1102 flags, = args
1108 flags, = args
1103 if repo.ui.promptchoice(
1109 if repo.ui.promptchoice(
1104 _("remote changed %s which local deleted\n"
1110 _("remote changed %s which local deleted\n"
1105 "use (c)hanged version or leave (d)eleted?"
1111 "use (c)hanged version or leave (d)eleted?"
1106 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1112 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1107 actions['g'].append((f, (flags,), "prompt recreating"))
1113 actions['g'].append((f, (flags,), "prompt recreating"))
1108 del actions['dc'][:]
1114 del actions['dc'][:]
1109
1115
1110 ### apply phase
1116 ### apply phase
1111 if not branchmerge: # just jump to the new rev
1117 if not branchmerge: # just jump to the new rev
1112 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1118 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1113 if not partial:
1119 if not partial:
1114 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1120 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1115 # note that we're in the middle of an update
1121 # note that we're in the middle of an update
1116 repo.vfs.write('updatestate', p2.hex())
1122 repo.vfs.write('updatestate', p2.hex())
1117
1123
1118 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1124 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1119
1125
1120 # divergent renames
1126 # divergent renames
1121 for f, fl in sorted(diverge.iteritems()):
1127 for f, fl in sorted(diverge.iteritems()):
1122 repo.ui.warn(_("note: possible conflict - %s was renamed "
1128 repo.ui.warn(_("note: possible conflict - %s was renamed "
1123 "multiple times to:\n") % f)
1129 "multiple times to:\n") % f)
1124 for nf in fl:
1130 for nf in fl:
1125 repo.ui.warn(" %s\n" % nf)
1131 repo.ui.warn(" %s\n" % nf)
1126
1132
1127 # rename and delete
1133 # rename and delete
1128 for f, fl in sorted(renamedelete.iteritems()):
1134 for f, fl in sorted(renamedelete.iteritems()):
1129 repo.ui.warn(_("note: possible conflict - %s was deleted "
1135 repo.ui.warn(_("note: possible conflict - %s was deleted "
1130 "and renamed to:\n") % f)
1136 "and renamed to:\n") % f)
1131 for nf in fl:
1137 for nf in fl:
1132 repo.ui.warn(" %s\n" % nf)
1138 repo.ui.warn(" %s\n" % nf)
1133
1139
1134 if not partial:
1140 if not partial:
1135 repo.dirstate.beginparentchange()
1141 repo.dirstate.beginparentchange()
1136 repo.setparents(fp1, fp2)
1142 repo.setparents(fp1, fp2)
1137 recordupdates(repo, actions, branchmerge)
1143 recordupdates(repo, actions, branchmerge)
1138 # update completed, clear state
1144 # update completed, clear state
1139 util.unlink(repo.join('updatestate'))
1145 util.unlink(repo.join('updatestate'))
1140
1146
1141 if not branchmerge:
1147 if not branchmerge:
1142 repo.dirstate.setbranch(p2.branch())
1148 repo.dirstate.setbranch(p2.branch())
1143 repo.dirstate.endparentchange()
1149 repo.dirstate.endparentchange()
1144 finally:
1150 finally:
1145 wlock.release()
1151 wlock.release()
1146
1152
1147 if not partial:
1153 if not partial:
1148 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1154 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1149 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1155 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1150 repo._afterlock(updatehook)
1156 repo._afterlock(updatehook)
1151 return stats
1157 return stats
1152
1158
1153 def graft(repo, ctx, pctx, labels):
1159 def graft(repo, ctx, pctx, labels):
1154 """Do a graft-like merge.
1160 """Do a graft-like merge.
1155
1161
1156 This is a merge where the merge ancestor is chosen such that one
1162 This is a merge where the merge ancestor is chosen such that one
1157 or more changesets are grafted onto the current changeset. In
1163 or more changesets are grafted onto the current changeset. In
1158 addition to the merge, this fixes up the dirstate to include only
1164 addition to the merge, this fixes up the dirstate to include only
1159 a single parent and tries to duplicate any renames/copies
1165 a single parent and tries to duplicate any renames/copies
1160 appropriately.
1166 appropriately.
1161
1167
1162 ctx - changeset to rebase
1168 ctx - changeset to rebase
1163 pctx - merge base, usually ctx.p1()
1169 pctx - merge base, usually ctx.p1()
1164 labels - merge labels eg ['local', 'graft']
1170 labels - merge labels eg ['local', 'graft']
1165
1171
1166 """
1172 """
1167 # If we're grafting a descendant onto an ancestor, be sure to pass
1173 # If we're grafting a descendant onto an ancestor, be sure to pass
1168 # mergeancestor=True to update. This does two things: 1) allows the merge if
1174 # mergeancestor=True to update. This does two things: 1) allows the merge if
1169 # the destination is the same as the parent of the ctx (so we can use graft
1175 # the destination is the same as the parent of the ctx (so we can use graft
1170 # to copy commits), and 2) informs update that the incoming changes are
1176 # to copy commits), and 2) informs update that the incoming changes are
1171 # newer than the destination so it doesn't prompt about "remote changed foo
1177 # newer than the destination so it doesn't prompt about "remote changed foo
1172 # which local deleted".
1178 # which local deleted".
1173 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1179 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1174
1180
1175 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1181 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1176 mergeancestor=mergeancestor, labels=labels)
1182 mergeancestor=mergeancestor, labels=labels)
1177
1183
1178 # drop the second merge parent
1184 # drop the second merge parent
1179 repo.dirstate.beginparentchange()
1185 repo.dirstate.beginparentchange()
1180 repo.setparents(repo['.'].node(), nullid)
1186 repo.setparents(repo['.'].node(), nullid)
1181 repo.dirstate.write()
1187 repo.dirstate.write()
1182 # fix up dirstate for copies and renames
1188 # fix up dirstate for copies and renames
1183 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1189 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1184 repo.dirstate.endparentchange()
1190 repo.dirstate.endparentchange()
1185 return stats
1191 return stats
General Comments 0
You need to be logged in to leave comments. Login now