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