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