##// END OF EJS Templates
merge.applyupdates: don't return early if merge driver's conclude failed...
Siddharth Agarwal -
r26975:6618dfd3 default
parent child Browse files
Show More
@@ -1,1350 +1,1350 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
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 """run premerge process for dfile
398 """run premerge process for dfile
399
399
400 Returns whether the merge is complete, and the exit code."""
400 Returns whether the merge is complete, and the exit code."""
401 return self._resolve(True, dfile, wctx, labels=labels)
401 return self._resolve(True, dfile, wctx, labels=labels)
402
402
403 def resolve(self, dfile, wctx, labels=None):
403 def resolve(self, dfile, wctx, labels=None):
404 """run merge process (assuming premerge was run) for dfile
404 """run merge process (assuming premerge was run) for dfile
405
405
406 Returns the exit code of the merge."""
406 Returns the exit code of the merge."""
407 return self._resolve(False, dfile, wctx, labels=labels)[1]
407 return self._resolve(False, dfile, wctx, labels=labels)[1]
408
408
409 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
409 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
410 if f2 is None:
410 if f2 is None:
411 f2 = f
411 f2 = f
412 return (os.path.isfile(repo.wjoin(f))
412 return (os.path.isfile(repo.wjoin(f))
413 and repo.wvfs.audit.check(f)
413 and repo.wvfs.audit.check(f)
414 and repo.dirstate.normalize(f) not in repo.dirstate
414 and repo.dirstate.normalize(f) not in repo.dirstate
415 and mctx[f2].cmp(wctx[f]))
415 and mctx[f2].cmp(wctx[f]))
416
416
417 def _checkunknownfiles(repo, wctx, mctx, force, actions):
417 def _checkunknownfiles(repo, wctx, mctx, force, actions):
418 """
418 """
419 Considers any actions that care about the presence of conflicting unknown
419 Considers any actions that care about the presence of conflicting unknown
420 files. For some actions, the result is to abort; for others, it is to
420 files. For some actions, the result is to abort; for others, it is to
421 choose a different action.
421 choose a different action.
422 """
422 """
423 aborts = []
423 aborts = []
424 if not force:
424 if not force:
425 for f, (m, args, msg) in actions.iteritems():
425 for f, (m, args, msg) in actions.iteritems():
426 if m in ('c', 'dc'):
426 if m in ('c', 'dc'):
427 if _checkunknownfile(repo, wctx, mctx, f):
427 if _checkunknownfile(repo, wctx, mctx, f):
428 aborts.append(f)
428 aborts.append(f)
429 elif m == 'dg':
429 elif m == 'dg':
430 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
430 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
431 aborts.append(f)
431 aborts.append(f)
432
432
433 for f in sorted(aborts):
433 for f in sorted(aborts):
434 repo.ui.warn(_("%s: untracked file differs\n") % f)
434 repo.ui.warn(_("%s: untracked file differs\n") % f)
435 if aborts:
435 if aborts:
436 raise error.Abort(_("untracked files in working directory differ "
436 raise error.Abort(_("untracked files in working directory differ "
437 "from files in requested revision"))
437 "from files in requested revision"))
438
438
439 for f, (m, args, msg) in actions.iteritems():
439 for f, (m, args, msg) in actions.iteritems():
440 if m == 'c':
440 if m == 'c':
441 actions[f] = ('g', args, msg)
441 actions[f] = ('g', args, msg)
442 elif m == 'cm':
442 elif m == 'cm':
443 fl2, anc = args
443 fl2, anc = args
444 different = _checkunknownfile(repo, wctx, mctx, f)
444 different = _checkunknownfile(repo, wctx, mctx, f)
445 if different:
445 if different:
446 actions[f] = ('m', (f, f, None, False, anc),
446 actions[f] = ('m', (f, f, None, False, anc),
447 "remote differs from untracked local")
447 "remote differs from untracked local")
448 else:
448 else:
449 actions[f] = ('g', (fl2,), "remote created")
449 actions[f] = ('g', (fl2,), "remote created")
450
450
451 def _forgetremoved(wctx, mctx, branchmerge):
451 def _forgetremoved(wctx, mctx, branchmerge):
452 """
452 """
453 Forget removed files
453 Forget removed files
454
454
455 If we're jumping between revisions (as opposed to merging), and if
455 If we're jumping between revisions (as opposed to merging), and if
456 neither the working directory nor the target rev has the file,
456 neither the working directory nor the target rev has the file,
457 then we need to remove it from the dirstate, to prevent the
457 then we need to remove it from the dirstate, to prevent the
458 dirstate from listing the file when it is no longer in the
458 dirstate from listing the file when it is no longer in the
459 manifest.
459 manifest.
460
460
461 If we're merging, and the other revision has removed a file
461 If we're merging, and the other revision has removed a file
462 that is not present in the working directory, we need to mark it
462 that is not present in the working directory, we need to mark it
463 as removed.
463 as removed.
464 """
464 """
465
465
466 actions = {}
466 actions = {}
467 m = 'f'
467 m = 'f'
468 if branchmerge:
468 if branchmerge:
469 m = 'r'
469 m = 'r'
470 for f in wctx.deleted():
470 for f in wctx.deleted():
471 if f not in mctx:
471 if f not in mctx:
472 actions[f] = m, None, "forget deleted"
472 actions[f] = m, None, "forget deleted"
473
473
474 if not branchmerge:
474 if not branchmerge:
475 for f in wctx.removed():
475 for f in wctx.removed():
476 if f not in mctx:
476 if f not in mctx:
477 actions[f] = 'f', None, "forget removed"
477 actions[f] = 'f', None, "forget removed"
478
478
479 return actions
479 return actions
480
480
481 def _checkcollision(repo, wmf, actions):
481 def _checkcollision(repo, wmf, actions):
482 # build provisional merged manifest up
482 # build provisional merged manifest up
483 pmmf = set(wmf)
483 pmmf = set(wmf)
484
484
485 if actions:
485 if actions:
486 # k, dr, e and rd are no-op
486 # k, dr, e and rd are no-op
487 for m in 'a', 'f', 'g', 'cd', 'dc':
487 for m in 'a', 'f', 'g', 'cd', 'dc':
488 for f, args, msg in actions[m]:
488 for f, args, msg in actions[m]:
489 pmmf.add(f)
489 pmmf.add(f)
490 for f, args, msg in actions['r']:
490 for f, args, msg in actions['r']:
491 pmmf.discard(f)
491 pmmf.discard(f)
492 for f, args, msg in actions['dm']:
492 for f, args, msg in actions['dm']:
493 f2, flags = args
493 f2, flags = args
494 pmmf.discard(f2)
494 pmmf.discard(f2)
495 pmmf.add(f)
495 pmmf.add(f)
496 for f, args, msg in actions['dg']:
496 for f, args, msg in actions['dg']:
497 pmmf.add(f)
497 pmmf.add(f)
498 for f, args, msg in actions['m']:
498 for f, args, msg in actions['m']:
499 f1, f2, fa, move, anc = args
499 f1, f2, fa, move, anc = args
500 if move:
500 if move:
501 pmmf.discard(f1)
501 pmmf.discard(f1)
502 pmmf.add(f)
502 pmmf.add(f)
503
503
504 # check case-folding collision in provisional merged manifest
504 # check case-folding collision in provisional merged manifest
505 foldmap = {}
505 foldmap = {}
506 for f in sorted(pmmf):
506 for f in sorted(pmmf):
507 fold = util.normcase(f)
507 fold = util.normcase(f)
508 if fold in foldmap:
508 if fold in foldmap:
509 raise error.Abort(_("case-folding collision between %s and %s")
509 raise error.Abort(_("case-folding collision between %s and %s")
510 % (f, foldmap[fold]))
510 % (f, foldmap[fold]))
511 foldmap[fold] = f
511 foldmap[fold] = f
512
512
513 # check case-folding of directories
513 # check case-folding of directories
514 foldprefix = unfoldprefix = lastfull = ''
514 foldprefix = unfoldprefix = lastfull = ''
515 for fold, f in sorted(foldmap.items()):
515 for fold, f in sorted(foldmap.items()):
516 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
516 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
517 # the folded prefix matches but actual casing is different
517 # the folded prefix matches but actual casing is different
518 raise error.Abort(_("case-folding collision between "
518 raise error.Abort(_("case-folding collision between "
519 "%s and directory of %s") % (lastfull, f))
519 "%s and directory of %s") % (lastfull, f))
520 foldprefix = fold + '/'
520 foldprefix = fold + '/'
521 unfoldprefix = f + '/'
521 unfoldprefix = f + '/'
522 lastfull = f
522 lastfull = f
523
523
524 def driverpreprocess(repo, ms, wctx, labels=None):
524 def driverpreprocess(repo, ms, wctx, labels=None):
525 """run the preprocess step of the merge driver, if any
525 """run the preprocess step of the merge driver, if any
526
526
527 This is currently not implemented -- it's an extension point."""
527 This is currently not implemented -- it's an extension point."""
528 return True
528 return True
529
529
530 def driverconclude(repo, ms, wctx, labels=None):
530 def driverconclude(repo, ms, wctx, labels=None):
531 """run the conclude step of the merge driver, if any
531 """run the conclude step of the merge driver, if any
532
532
533 This is currently not implemented -- it's an extension point."""
533 This is currently not implemented -- it's an extension point."""
534 return True
534 return True
535
535
536 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
536 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
537 acceptremote, followcopies):
537 acceptremote, followcopies):
538 """
538 """
539 Merge p1 and p2 with ancestor pa and generate merge action list
539 Merge p1 and p2 with ancestor pa and generate merge action list
540
540
541 branchmerge and force are as passed in to update
541 branchmerge and force are as passed in to update
542 partial = function to filter file lists
542 partial = function to filter file lists
543 acceptremote = accept the incoming changes without prompting
543 acceptremote = accept the incoming changes without prompting
544 """
544 """
545
545
546 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
546 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
547
547
548 # manifests fetched in order are going to be faster, so prime the caches
548 # manifests fetched in order are going to be faster, so prime the caches
549 [x.manifest() for x in
549 [x.manifest() for x in
550 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
550 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
551
551
552 if followcopies:
552 if followcopies:
553 ret = copies.mergecopies(repo, wctx, p2, pa)
553 ret = copies.mergecopies(repo, wctx, p2, pa)
554 copy, movewithdir, diverge, renamedelete = ret
554 copy, movewithdir, diverge, renamedelete = ret
555
555
556 repo.ui.note(_("resolving manifests\n"))
556 repo.ui.note(_("resolving manifests\n"))
557 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
557 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
558 % (bool(branchmerge), bool(force), bool(partial)))
558 % (bool(branchmerge), bool(force), bool(partial)))
559 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
559 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
560
560
561 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
561 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
562 copied = set(copy.values())
562 copied = set(copy.values())
563 copied.update(movewithdir.values())
563 copied.update(movewithdir.values())
564
564
565 if '.hgsubstate' in m1:
565 if '.hgsubstate' in m1:
566 # check whether sub state is modified
566 # check whether sub state is modified
567 for s in sorted(wctx.substate):
567 for s in sorted(wctx.substate):
568 if wctx.sub(s).dirty():
568 if wctx.sub(s).dirty():
569 m1['.hgsubstate'] += '+'
569 m1['.hgsubstate'] += '+'
570 break
570 break
571
571
572 # Compare manifests
572 # Compare manifests
573 diff = m1.diff(m2)
573 diff = m1.diff(m2)
574
574
575 actions = {}
575 actions = {}
576 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
576 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
577 if partial and not partial(f):
577 if partial and not partial(f):
578 continue
578 continue
579 if n1 and n2: # file exists on both local and remote side
579 if n1 and n2: # file exists on both local and remote side
580 if f not in ma:
580 if f not in ma:
581 fa = copy.get(f, None)
581 fa = copy.get(f, None)
582 if fa is not None:
582 if fa is not None:
583 actions[f] = ('m', (f, f, fa, False, pa.node()),
583 actions[f] = ('m', (f, f, fa, False, pa.node()),
584 "both renamed from " + fa)
584 "both renamed from " + fa)
585 else:
585 else:
586 actions[f] = ('m', (f, f, None, False, pa.node()),
586 actions[f] = ('m', (f, f, None, False, pa.node()),
587 "both created")
587 "both created")
588 else:
588 else:
589 a = ma[f]
589 a = ma[f]
590 fla = ma.flags(f)
590 fla = ma.flags(f)
591 nol = 'l' not in fl1 + fl2 + fla
591 nol = 'l' not in fl1 + fl2 + fla
592 if n2 == a and fl2 == fla:
592 if n2 == a and fl2 == fla:
593 actions[f] = ('k' , (), "remote unchanged")
593 actions[f] = ('k' , (), "remote unchanged")
594 elif n1 == a and fl1 == fla: # local unchanged - use remote
594 elif n1 == a and fl1 == fla: # local unchanged - use remote
595 if n1 == n2: # optimization: keep local content
595 if n1 == n2: # optimization: keep local content
596 actions[f] = ('e', (fl2,), "update permissions")
596 actions[f] = ('e', (fl2,), "update permissions")
597 else:
597 else:
598 actions[f] = ('g', (fl2,), "remote is newer")
598 actions[f] = ('g', (fl2,), "remote is newer")
599 elif nol and n2 == a: # remote only changed 'x'
599 elif nol and n2 == a: # remote only changed 'x'
600 actions[f] = ('e', (fl2,), "update permissions")
600 actions[f] = ('e', (fl2,), "update permissions")
601 elif nol and n1 == a: # local only changed 'x'
601 elif nol and n1 == a: # local only changed 'x'
602 actions[f] = ('g', (fl1,), "remote is newer")
602 actions[f] = ('g', (fl1,), "remote is newer")
603 else: # both changed something
603 else: # both changed something
604 actions[f] = ('m', (f, f, f, False, pa.node()),
604 actions[f] = ('m', (f, f, f, False, pa.node()),
605 "versions differ")
605 "versions differ")
606 elif n1: # file exists only on local side
606 elif n1: # file exists only on local side
607 if f in copied:
607 if f in copied:
608 pass # we'll deal with it on m2 side
608 pass # we'll deal with it on m2 side
609 elif f in movewithdir: # directory rename, move local
609 elif f in movewithdir: # directory rename, move local
610 f2 = movewithdir[f]
610 f2 = movewithdir[f]
611 if f2 in m2:
611 if f2 in m2:
612 actions[f2] = ('m', (f, f2, None, True, pa.node()),
612 actions[f2] = ('m', (f, f2, None, True, pa.node()),
613 "remote directory rename, both created")
613 "remote directory rename, both created")
614 else:
614 else:
615 actions[f2] = ('dm', (f, fl1),
615 actions[f2] = ('dm', (f, fl1),
616 "remote directory rename - move from " + f)
616 "remote directory rename - move from " + f)
617 elif f in copy:
617 elif f in copy:
618 f2 = copy[f]
618 f2 = copy[f]
619 actions[f] = ('m', (f, f2, f2, False, pa.node()),
619 actions[f] = ('m', (f, f2, f2, False, pa.node()),
620 "local copied/moved from " + f2)
620 "local copied/moved from " + f2)
621 elif f in ma: # clean, a different, no remote
621 elif f in ma: # clean, a different, no remote
622 if n1 != ma[f]:
622 if n1 != ma[f]:
623 if acceptremote:
623 if acceptremote:
624 actions[f] = ('r', None, "remote delete")
624 actions[f] = ('r', None, "remote delete")
625 else:
625 else:
626 actions[f] = ('cd', (f, None, f, False, pa.node()),
626 actions[f] = ('cd', (f, None, f, False, pa.node()),
627 "prompt changed/deleted")
627 "prompt changed/deleted")
628 elif n1[20:] == 'a':
628 elif n1[20:] == 'a':
629 # This extra 'a' is added by working copy manifest to mark
629 # This extra 'a' is added by working copy manifest to mark
630 # the file as locally added. We should forget it instead of
630 # the file as locally added. We should forget it instead of
631 # deleting it.
631 # deleting it.
632 actions[f] = ('f', None, "remote deleted")
632 actions[f] = ('f', None, "remote deleted")
633 else:
633 else:
634 actions[f] = ('r', None, "other deleted")
634 actions[f] = ('r', None, "other deleted")
635 elif n2: # file exists only on remote side
635 elif n2: # file exists only on remote side
636 if f in copied:
636 if f in copied:
637 pass # we'll deal with it on m1 side
637 pass # we'll deal with it on m1 side
638 elif f in movewithdir:
638 elif f in movewithdir:
639 f2 = movewithdir[f]
639 f2 = movewithdir[f]
640 if f2 in m1:
640 if f2 in m1:
641 actions[f2] = ('m', (f2, f, None, False, pa.node()),
641 actions[f2] = ('m', (f2, f, None, False, pa.node()),
642 "local directory rename, both created")
642 "local directory rename, both created")
643 else:
643 else:
644 actions[f2] = ('dg', (f, fl2),
644 actions[f2] = ('dg', (f, fl2),
645 "local directory rename - get from " + f)
645 "local directory rename - get from " + f)
646 elif f in copy:
646 elif f in copy:
647 f2 = copy[f]
647 f2 = copy[f]
648 if f2 in m2:
648 if f2 in m2:
649 actions[f] = ('m', (f2, f, f2, False, pa.node()),
649 actions[f] = ('m', (f2, f, f2, False, pa.node()),
650 "remote copied from " + f2)
650 "remote copied from " + f2)
651 else:
651 else:
652 actions[f] = ('m', (f2, f, f2, True, pa.node()),
652 actions[f] = ('m', (f2, f, f2, True, pa.node()),
653 "remote moved from " + f2)
653 "remote moved from " + f2)
654 elif f not in ma:
654 elif f not in ma:
655 # local unknown, remote created: the logic is described by the
655 # local unknown, remote created: the logic is described by the
656 # following table:
656 # following table:
657 #
657 #
658 # force branchmerge different | action
658 # force branchmerge different | action
659 # n * * | create
659 # n * * | create
660 # y n * | create
660 # y n * | create
661 # y y n | create
661 # y y n | create
662 # y y y | merge
662 # y y y | merge
663 #
663 #
664 # Checking whether the files are different is expensive, so we
664 # Checking whether the files are different is expensive, so we
665 # don't do that when we can avoid it.
665 # don't do that when we can avoid it.
666 if not force:
666 if not force:
667 actions[f] = ('c', (fl2,), "remote created")
667 actions[f] = ('c', (fl2,), "remote created")
668 elif not branchmerge:
668 elif not branchmerge:
669 actions[f] = ('c', (fl2,), "remote created")
669 actions[f] = ('c', (fl2,), "remote created")
670 else:
670 else:
671 actions[f] = ('cm', (fl2, pa.node()),
671 actions[f] = ('cm', (fl2, pa.node()),
672 "remote created, get or merge")
672 "remote created, get or merge")
673 elif n2 != ma[f]:
673 elif n2 != ma[f]:
674 if acceptremote:
674 if acceptremote:
675 actions[f] = ('c', (fl2,), "remote recreating")
675 actions[f] = ('c', (fl2,), "remote recreating")
676 else:
676 else:
677 actions[f] = ('dc', (None, f, f, False, pa.node()),
677 actions[f] = ('dc', (None, f, f, False, pa.node()),
678 "prompt deleted/changed")
678 "prompt deleted/changed")
679
679
680 return actions, diverge, renamedelete
680 return actions, diverge, renamedelete
681
681
682 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
682 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
683 """Resolves false conflicts where the nodeid changed but the content
683 """Resolves false conflicts where the nodeid changed but the content
684 remained the same."""
684 remained the same."""
685
685
686 for f, (m, args, msg) in actions.items():
686 for f, (m, args, msg) in actions.items():
687 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
687 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
688 # local did change but ended up with same content
688 # local did change but ended up with same content
689 actions[f] = 'r', None, "prompt same"
689 actions[f] = 'r', None, "prompt same"
690 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
690 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
691 # remote did change but ended up with same content
691 # remote did change but ended up with same content
692 del actions[f] # don't get = keep local deleted
692 del actions[f] # don't get = keep local deleted
693
693
694 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
694 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
695 acceptremote, followcopies):
695 acceptremote, followcopies):
696 "Calculate the actions needed to merge mctx into wctx using ancestors"
696 "Calculate the actions needed to merge mctx into wctx using ancestors"
697
697
698 if len(ancestors) == 1: # default
698 if len(ancestors) == 1: # default
699 actions, diverge, renamedelete = manifestmerge(
699 actions, diverge, renamedelete = manifestmerge(
700 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
700 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
701 acceptremote, followcopies)
701 acceptremote, followcopies)
702 _checkunknownfiles(repo, wctx, mctx, force, actions)
702 _checkunknownfiles(repo, wctx, mctx, force, actions)
703
703
704 else: # only when merge.preferancestor=* - the default
704 else: # only when merge.preferancestor=* - the default
705 repo.ui.note(
705 repo.ui.note(
706 _("note: merging %s and %s using bids from ancestors %s\n") %
706 _("note: merging %s and %s using bids from ancestors %s\n") %
707 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
707 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
708
708
709 # Call for bids
709 # Call for bids
710 fbids = {} # mapping filename to bids (action method to list af actions)
710 fbids = {} # mapping filename to bids (action method to list af actions)
711 diverge, renamedelete = None, None
711 diverge, renamedelete = None, None
712 for ancestor in ancestors:
712 for ancestor in ancestors:
713 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
713 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
714 actions, diverge1, renamedelete1 = manifestmerge(
714 actions, diverge1, renamedelete1 = manifestmerge(
715 repo, wctx, mctx, ancestor, branchmerge, force, partial,
715 repo, wctx, mctx, ancestor, branchmerge, force, partial,
716 acceptremote, followcopies)
716 acceptremote, followcopies)
717 _checkunknownfiles(repo, wctx, mctx, force, actions)
717 _checkunknownfiles(repo, wctx, mctx, force, actions)
718
718
719 # Track the shortest set of warning on the theory that bid
719 # Track the shortest set of warning on the theory that bid
720 # merge will correctly incorporate more information
720 # merge will correctly incorporate more information
721 if diverge is None or len(diverge1) < len(diverge):
721 if diverge is None or len(diverge1) < len(diverge):
722 diverge = diverge1
722 diverge = diverge1
723 if renamedelete is None or len(renamedelete) < len(renamedelete1):
723 if renamedelete is None or len(renamedelete) < len(renamedelete1):
724 renamedelete = renamedelete1
724 renamedelete = renamedelete1
725
725
726 for f, a in sorted(actions.iteritems()):
726 for f, a in sorted(actions.iteritems()):
727 m, args, msg = a
727 m, args, msg = a
728 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
728 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
729 if f in fbids:
729 if f in fbids:
730 d = fbids[f]
730 d = fbids[f]
731 if m in d:
731 if m in d:
732 d[m].append(a)
732 d[m].append(a)
733 else:
733 else:
734 d[m] = [a]
734 d[m] = [a]
735 else:
735 else:
736 fbids[f] = {m: [a]}
736 fbids[f] = {m: [a]}
737
737
738 # Pick the best bid for each file
738 # Pick the best bid for each file
739 repo.ui.note(_('\nauction for merging merge bids\n'))
739 repo.ui.note(_('\nauction for merging merge bids\n'))
740 actions = {}
740 actions = {}
741 for f, bids in sorted(fbids.items()):
741 for f, bids in sorted(fbids.items()):
742 # bids is a mapping from action method to list af actions
742 # bids is a mapping from action method to list af actions
743 # Consensus?
743 # Consensus?
744 if len(bids) == 1: # all bids are the same kind of method
744 if len(bids) == 1: # all bids are the same kind of method
745 m, l = bids.items()[0]
745 m, l = bids.items()[0]
746 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
746 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
747 repo.ui.note(" %s: consensus for %s\n" % (f, m))
747 repo.ui.note(" %s: consensus for %s\n" % (f, m))
748 actions[f] = l[0]
748 actions[f] = l[0]
749 continue
749 continue
750 # If keep is an option, just do it.
750 # If keep is an option, just do it.
751 if 'k' in bids:
751 if 'k' in bids:
752 repo.ui.note(" %s: picking 'keep' action\n" % f)
752 repo.ui.note(" %s: picking 'keep' action\n" % f)
753 actions[f] = bids['k'][0]
753 actions[f] = bids['k'][0]
754 continue
754 continue
755 # If there are gets and they all agree [how could they not?], do it.
755 # If there are gets and they all agree [how could they not?], do it.
756 if 'g' in bids:
756 if 'g' in bids:
757 ga0 = bids['g'][0]
757 ga0 = bids['g'][0]
758 if all(a == ga0 for a in bids['g'][1:]):
758 if all(a == ga0 for a in bids['g'][1:]):
759 repo.ui.note(" %s: picking 'get' action\n" % f)
759 repo.ui.note(" %s: picking 'get' action\n" % f)
760 actions[f] = ga0
760 actions[f] = ga0
761 continue
761 continue
762 # TODO: Consider other simple actions such as mode changes
762 # TODO: Consider other simple actions such as mode changes
763 # Handle inefficient democrazy.
763 # Handle inefficient democrazy.
764 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
764 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
765 for m, l in sorted(bids.items()):
765 for m, l in sorted(bids.items()):
766 for _f, args, msg in l:
766 for _f, args, msg in l:
767 repo.ui.note(' %s -> %s\n' % (msg, m))
767 repo.ui.note(' %s -> %s\n' % (msg, m))
768 # Pick random action. TODO: Instead, prompt user when resolving
768 # Pick random action. TODO: Instead, prompt user when resolving
769 m, l = bids.items()[0]
769 m, l = bids.items()[0]
770 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
770 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
771 (f, m))
771 (f, m))
772 actions[f] = l[0]
772 actions[f] = l[0]
773 continue
773 continue
774 repo.ui.note(_('end of auction\n\n'))
774 repo.ui.note(_('end of auction\n\n'))
775
775
776 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
776 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
777
777
778 if wctx.rev() is None:
778 if wctx.rev() is None:
779 fractions = _forgetremoved(wctx, mctx, branchmerge)
779 fractions = _forgetremoved(wctx, mctx, branchmerge)
780 actions.update(fractions)
780 actions.update(fractions)
781
781
782 return actions, diverge, renamedelete
782 return actions, diverge, renamedelete
783
783
784 def batchremove(repo, actions):
784 def batchremove(repo, actions):
785 """apply removes to the working directory
785 """apply removes to the working directory
786
786
787 yields tuples for progress updates
787 yields tuples for progress updates
788 """
788 """
789 verbose = repo.ui.verbose
789 verbose = repo.ui.verbose
790 unlink = util.unlinkpath
790 unlink = util.unlinkpath
791 wjoin = repo.wjoin
791 wjoin = repo.wjoin
792 audit = repo.wvfs.audit
792 audit = repo.wvfs.audit
793 i = 0
793 i = 0
794 for f, args, msg in actions:
794 for f, args, msg in actions:
795 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
795 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
796 if verbose:
796 if verbose:
797 repo.ui.note(_("removing %s\n") % f)
797 repo.ui.note(_("removing %s\n") % f)
798 audit(f)
798 audit(f)
799 try:
799 try:
800 unlink(wjoin(f), ignoremissing=True)
800 unlink(wjoin(f), ignoremissing=True)
801 except OSError as inst:
801 except OSError as inst:
802 repo.ui.warn(_("update failed to remove %s: %s!\n") %
802 repo.ui.warn(_("update failed to remove %s: %s!\n") %
803 (f, inst.strerror))
803 (f, inst.strerror))
804 if i == 100:
804 if i == 100:
805 yield i, f
805 yield i, f
806 i = 0
806 i = 0
807 i += 1
807 i += 1
808 if i > 0:
808 if i > 0:
809 yield i, f
809 yield i, f
810
810
811 def batchget(repo, mctx, actions):
811 def batchget(repo, mctx, actions):
812 """apply gets to the working directory
812 """apply gets to the working directory
813
813
814 mctx is the context to get from
814 mctx is the context to get from
815
815
816 yields tuples for progress updates
816 yields tuples for progress updates
817 """
817 """
818 verbose = repo.ui.verbose
818 verbose = repo.ui.verbose
819 fctx = mctx.filectx
819 fctx = mctx.filectx
820 wwrite = repo.wwrite
820 wwrite = repo.wwrite
821 i = 0
821 i = 0
822 for f, args, msg in actions:
822 for f, args, msg in actions:
823 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
823 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
824 if verbose:
824 if verbose:
825 repo.ui.note(_("getting %s\n") % f)
825 repo.ui.note(_("getting %s\n") % f)
826 wwrite(f, fctx(f).data(), args[0])
826 wwrite(f, fctx(f).data(), args[0])
827 if i == 100:
827 if i == 100:
828 yield i, f
828 yield i, f
829 i = 0
829 i = 0
830 i += 1
830 i += 1
831 if i > 0:
831 if i > 0:
832 yield i, f
832 yield i, f
833
833
834 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
834 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
835 """apply the merge action list to the working directory
835 """apply the merge action list to the working directory
836
836
837 wctx is the working copy context
837 wctx is the working copy context
838 mctx is the context to be merged into the working copy
838 mctx is the context to be merged into the working copy
839
839
840 Return a tuple of counts (updated, merged, removed, unresolved) that
840 Return a tuple of counts (updated, merged, removed, unresolved) that
841 describes how many files were affected by the update.
841 describes how many files were affected by the update.
842 """
842 """
843
843
844 updated, merged, removed, unresolved = 0, 0, 0, 0
844 updated, merged, removed, unresolved = 0, 0, 0, 0
845 ms = mergestate(repo)
845 ms = mergestate(repo)
846 ms.reset(wctx.p1().node(), mctx.node())
846 ms.reset(wctx.p1().node(), mctx.node())
847 moves = []
847 moves = []
848 for m, l in actions.items():
848 for m, l in actions.items():
849 l.sort()
849 l.sort()
850
850
851 # prescan for merges
851 # prescan for merges
852 for f, args, msg in actions['m']:
852 for f, args, msg in actions['m']:
853 f1, f2, fa, move, anc = args
853 f1, f2, fa, move, anc = args
854 if f == '.hgsubstate': # merged internally
854 if f == '.hgsubstate': # merged internally
855 continue
855 continue
856 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
856 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
857 fcl = wctx[f1]
857 fcl = wctx[f1]
858 fco = mctx[f2]
858 fco = mctx[f2]
859 actx = repo[anc]
859 actx = repo[anc]
860 if fa in actx:
860 if fa in actx:
861 fca = actx[fa]
861 fca = actx[fa]
862 else:
862 else:
863 fca = repo.filectx(f1, fileid=nullrev)
863 fca = repo.filectx(f1, fileid=nullrev)
864 ms.add(fcl, fco, fca, f)
864 ms.add(fcl, fco, fca, f)
865 if f1 != f and move:
865 if f1 != f and move:
866 moves.append(f1)
866 moves.append(f1)
867
867
868 audit = repo.wvfs.audit
868 audit = repo.wvfs.audit
869 _updating = _('updating')
869 _updating = _('updating')
870 _files = _('files')
870 _files = _('files')
871 progress = repo.ui.progress
871 progress = repo.ui.progress
872
872
873 # remove renamed files after safely stored
873 # remove renamed files after safely stored
874 for f in moves:
874 for f in moves:
875 if os.path.lexists(repo.wjoin(f)):
875 if os.path.lexists(repo.wjoin(f)):
876 repo.ui.debug("removing %s\n" % f)
876 repo.ui.debug("removing %s\n" % f)
877 audit(f)
877 audit(f)
878 util.unlinkpath(repo.wjoin(f))
878 util.unlinkpath(repo.wjoin(f))
879
879
880 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
880 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
881
881
882 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
882 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
883 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
883 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
884
884
885 # remove in parallel (must come first)
885 # remove in parallel (must come first)
886 z = 0
886 z = 0
887 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
887 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
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 removed = len(actions['r'])
891 removed = len(actions['r'])
892
892
893 # get in parallel
893 # get in parallel
894 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
894 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
895 for i, item in prog:
895 for i, item in prog:
896 z += i
896 z += i
897 progress(_updating, z, item=item, total=numupdates, unit=_files)
897 progress(_updating, z, item=item, total=numupdates, unit=_files)
898 updated = len(actions['g'])
898 updated = len(actions['g'])
899
899
900 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
900 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
901 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
901 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
902
902
903 # forget (manifest only, just log it) (must come first)
903 # forget (manifest only, just log it) (must come first)
904 for f, args, msg in actions['f']:
904 for f, args, msg in actions['f']:
905 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
905 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
906 z += 1
906 z += 1
907 progress(_updating, z, item=f, total=numupdates, unit=_files)
907 progress(_updating, z, item=f, total=numupdates, unit=_files)
908
908
909 # re-add (manifest only, just log it)
909 # re-add (manifest only, just log it)
910 for f, args, msg in actions['a']:
910 for f, args, msg in actions['a']:
911 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
911 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
912 z += 1
912 z += 1
913 progress(_updating, z, item=f, total=numupdates, unit=_files)
913 progress(_updating, z, item=f, total=numupdates, unit=_files)
914
914
915 # keep (noop, just log it)
915 # keep (noop, just log it)
916 for f, args, msg in actions['k']:
916 for f, args, msg in actions['k']:
917 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
917 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
918 # no progress
918 # no progress
919
919
920 # directory rename, move local
920 # directory rename, move local
921 for f, args, msg in actions['dm']:
921 for f, args, msg in actions['dm']:
922 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
922 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
923 z += 1
923 z += 1
924 progress(_updating, z, item=f, total=numupdates, unit=_files)
924 progress(_updating, z, item=f, total=numupdates, unit=_files)
925 f0, flags = args
925 f0, flags = args
926 repo.ui.note(_("moving %s to %s\n") % (f0, f))
926 repo.ui.note(_("moving %s to %s\n") % (f0, f))
927 audit(f)
927 audit(f)
928 repo.wwrite(f, wctx.filectx(f0).data(), flags)
928 repo.wwrite(f, wctx.filectx(f0).data(), flags)
929 util.unlinkpath(repo.wjoin(f0))
929 util.unlinkpath(repo.wjoin(f0))
930 updated += 1
930 updated += 1
931
931
932 # local directory rename, get
932 # local directory rename, get
933 for f, args, msg in actions['dg']:
933 for f, args, msg in actions['dg']:
934 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
934 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
935 z += 1
935 z += 1
936 progress(_updating, z, item=f, total=numupdates, unit=_files)
936 progress(_updating, z, item=f, total=numupdates, unit=_files)
937 f0, flags = args
937 f0, flags = args
938 repo.ui.note(_("getting %s to %s\n") % (f0, f))
938 repo.ui.note(_("getting %s to %s\n") % (f0, f))
939 repo.wwrite(f, mctx.filectx(f0).data(), flags)
939 repo.wwrite(f, mctx.filectx(f0).data(), flags)
940 updated += 1
940 updated += 1
941
941
942 # exec
942 # exec
943 for f, args, msg in actions['e']:
943 for f, args, msg in actions['e']:
944 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
944 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
945 z += 1
945 z += 1
946 progress(_updating, z, item=f, total=numupdates, unit=_files)
946 progress(_updating, z, item=f, total=numupdates, unit=_files)
947 flags, = args
947 flags, = args
948 audit(f)
948 audit(f)
949 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
949 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
950 updated += 1
950 updated += 1
951
951
952 mergeactions = actions['m']
952 mergeactions = actions['m']
953 # the ordering is important here -- ms.mergedriver will raise if the merge
953 # the ordering is important here -- ms.mergedriver will raise if the merge
954 # driver has changed, and we want to be able to bypass it when overwrite is
954 # driver has changed, and we want to be able to bypass it when overwrite is
955 # True
955 # True
956 usemergedriver = not overwrite and mergeactions and ms.mergedriver
956 usemergedriver = not overwrite and mergeactions and ms.mergedriver
957
957
958 if usemergedriver:
958 if usemergedriver:
959 ms.commit()
959 ms.commit()
960 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
960 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
961 # the driver might leave some files unresolved
961 # the driver might leave some files unresolved
962 unresolvedf = set(ms.unresolved())
962 unresolvedf = set(ms.unresolved())
963 if not proceed:
963 if not proceed:
964 # XXX setting unresolved to at least 1 is a hack to make sure we
964 # XXX setting unresolved to at least 1 is a hack to make sure we
965 # error out
965 # error out
966 return updated, merged, removed, max(len(unresolvedf), 1)
966 return updated, merged, removed, max(len(unresolvedf), 1)
967 newactions = []
967 newactions = []
968 for f, args, msg in mergeactions:
968 for f, args, msg in mergeactions:
969 if f in unresolvedf:
969 if f in unresolvedf:
970 newactions.append((f, args, msg))
970 newactions.append((f, args, msg))
971 mergeactions = newactions
971 mergeactions = newactions
972
972
973 # premerge
973 # premerge
974 tocomplete = []
974 tocomplete = []
975 for f, args, msg in mergeactions:
975 for f, args, msg in mergeactions:
976 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
976 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
977 z += 1
977 z += 1
978 progress(_updating, z, item=f, total=numupdates, unit=_files)
978 progress(_updating, z, item=f, total=numupdates, unit=_files)
979 if f == '.hgsubstate': # subrepo states need updating
979 if f == '.hgsubstate': # subrepo states need updating
980 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
980 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
981 overwrite)
981 overwrite)
982 continue
982 continue
983 audit(f)
983 audit(f)
984 complete, r = ms.preresolve(f, wctx, labels=labels)
984 complete, r = ms.preresolve(f, wctx, labels=labels)
985 if complete:
985 if complete:
986 if r is not None and r > 0:
986 if r is not None and r > 0:
987 unresolved += 1
987 unresolved += 1
988 else:
988 else:
989 if r is None:
989 if r is None:
990 updated += 1
990 updated += 1
991 else:
991 else:
992 merged += 1
992 merged += 1
993 else:
993 else:
994 numupdates += 1
994 numupdates += 1
995 tocomplete.append((f, args, msg))
995 tocomplete.append((f, args, msg))
996
996
997 # merge
997 # merge
998 for f, args, msg in tocomplete:
998 for f, args, msg in tocomplete:
999 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
999 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1000 z += 1
1000 z += 1
1001 progress(_updating, z, item=f, total=numupdates, unit=_files)
1001 progress(_updating, z, item=f, total=numupdates, unit=_files)
1002 r = ms.resolve(f, wctx, labels=labels)
1002 r = ms.resolve(f, wctx, labels=labels)
1003 if r is not None and r > 0:
1003 if r is not None and r > 0:
1004 unresolved += 1
1004 unresolved += 1
1005 else:
1005 else:
1006 if r is None:
1006 if r is None:
1007 updated += 1
1007 updated += 1
1008 else:
1008 else:
1009 merged += 1
1009 merged += 1
1010
1010
1011 ms.commit()
1011 ms.commit()
1012
1012
1013 if usemergedriver and not unresolved and ms.mdstate() != 's':
1013 if usemergedriver and not unresolved and ms.mdstate() != 's':
1014 if not driverconclude(repo, ms, wctx, labels=labels):
1014 if not driverconclude(repo, ms, wctx, labels=labels):
1015 # XXX setting unresolved to at least 1 is a hack to make sure we
1015 # XXX setting unresolved to at least 1 is a hack to make sure we
1016 # error out
1016 # error out
1017 return updated, merged, removed, max(unresolved, 1)
1017 unresolved = max(unresolved, 1)
1018
1018
1019 ms.commit()
1019 ms.commit()
1020
1020
1021 progress(_updating, None, total=numupdates, unit=_files)
1021 progress(_updating, None, total=numupdates, unit=_files)
1022
1022
1023 return updated, merged, removed, unresolved
1023 return updated, merged, removed, unresolved
1024
1024
1025 def recordupdates(repo, actions, branchmerge):
1025 def recordupdates(repo, actions, branchmerge):
1026 "record merge actions to the dirstate"
1026 "record merge actions to the dirstate"
1027 # remove (must come first)
1027 # remove (must come first)
1028 for f, args, msg in actions['r']:
1028 for f, args, msg in actions['r']:
1029 if branchmerge:
1029 if branchmerge:
1030 repo.dirstate.remove(f)
1030 repo.dirstate.remove(f)
1031 else:
1031 else:
1032 repo.dirstate.drop(f)
1032 repo.dirstate.drop(f)
1033
1033
1034 # forget (must come first)
1034 # forget (must come first)
1035 for f, args, msg in actions['f']:
1035 for f, args, msg in actions['f']:
1036 repo.dirstate.drop(f)
1036 repo.dirstate.drop(f)
1037
1037
1038 # re-add
1038 # re-add
1039 for f, args, msg in actions['a']:
1039 for f, args, msg in actions['a']:
1040 if not branchmerge:
1040 if not branchmerge:
1041 repo.dirstate.add(f)
1041 repo.dirstate.add(f)
1042
1042
1043 # exec change
1043 # exec change
1044 for f, args, msg in actions['e']:
1044 for f, args, msg in actions['e']:
1045 repo.dirstate.normallookup(f)
1045 repo.dirstate.normallookup(f)
1046
1046
1047 # keep
1047 # keep
1048 for f, args, msg in actions['k']:
1048 for f, args, msg in actions['k']:
1049 pass
1049 pass
1050
1050
1051 # get
1051 # get
1052 for f, args, msg in actions['g']:
1052 for f, args, msg in actions['g']:
1053 if branchmerge:
1053 if branchmerge:
1054 repo.dirstate.otherparent(f)
1054 repo.dirstate.otherparent(f)
1055 else:
1055 else:
1056 repo.dirstate.normal(f)
1056 repo.dirstate.normal(f)
1057
1057
1058 # merge
1058 # merge
1059 for f, args, msg in actions['m']:
1059 for f, args, msg in actions['m']:
1060 f1, f2, fa, move, anc = args
1060 f1, f2, fa, move, anc = args
1061 if branchmerge:
1061 if branchmerge:
1062 # We've done a branch merge, mark this file as merged
1062 # We've done a branch merge, mark this file as merged
1063 # so that we properly record the merger later
1063 # so that we properly record the merger later
1064 repo.dirstate.merge(f)
1064 repo.dirstate.merge(f)
1065 if f1 != f2: # copy/rename
1065 if f1 != f2: # copy/rename
1066 if move:
1066 if move:
1067 repo.dirstate.remove(f1)
1067 repo.dirstate.remove(f1)
1068 if f1 != f:
1068 if f1 != f:
1069 repo.dirstate.copy(f1, f)
1069 repo.dirstate.copy(f1, f)
1070 else:
1070 else:
1071 repo.dirstate.copy(f2, f)
1071 repo.dirstate.copy(f2, f)
1072 else:
1072 else:
1073 # We've update-merged a locally modified file, so
1073 # We've update-merged a locally modified file, so
1074 # we set the dirstate to emulate a normal checkout
1074 # we set the dirstate to emulate a normal checkout
1075 # of that file some time in the past. Thus our
1075 # of that file some time in the past. Thus our
1076 # merge will appear as a normal local file
1076 # merge will appear as a normal local file
1077 # modification.
1077 # modification.
1078 if f2 == f: # file not locally copied/moved
1078 if f2 == f: # file not locally copied/moved
1079 repo.dirstate.normallookup(f)
1079 repo.dirstate.normallookup(f)
1080 if move:
1080 if move:
1081 repo.dirstate.drop(f1)
1081 repo.dirstate.drop(f1)
1082
1082
1083 # directory rename, move local
1083 # directory rename, move local
1084 for f, args, msg in actions['dm']:
1084 for f, args, msg in actions['dm']:
1085 f0, flag = args
1085 f0, flag = args
1086 if branchmerge:
1086 if branchmerge:
1087 repo.dirstate.add(f)
1087 repo.dirstate.add(f)
1088 repo.dirstate.remove(f0)
1088 repo.dirstate.remove(f0)
1089 repo.dirstate.copy(f0, f)
1089 repo.dirstate.copy(f0, f)
1090 else:
1090 else:
1091 repo.dirstate.normal(f)
1091 repo.dirstate.normal(f)
1092 repo.dirstate.drop(f0)
1092 repo.dirstate.drop(f0)
1093
1093
1094 # directory rename, get
1094 # directory rename, get
1095 for f, args, msg in actions['dg']:
1095 for f, args, msg in actions['dg']:
1096 f0, flag = args
1096 f0, flag = args
1097 if branchmerge:
1097 if branchmerge:
1098 repo.dirstate.add(f)
1098 repo.dirstate.add(f)
1099 repo.dirstate.copy(f0, f)
1099 repo.dirstate.copy(f0, f)
1100 else:
1100 else:
1101 repo.dirstate.normal(f)
1101 repo.dirstate.normal(f)
1102
1102
1103 def update(repo, node, branchmerge, force, partial, ancestor=None,
1103 def update(repo, node, branchmerge, force, partial, ancestor=None,
1104 mergeancestor=False, labels=None):
1104 mergeancestor=False, labels=None):
1105 """
1105 """
1106 Perform a merge between the working directory and the given node
1106 Perform a merge between the working directory and the given node
1107
1107
1108 node = the node to update to, or None if unspecified
1108 node = the node to update to, or None if unspecified
1109 branchmerge = whether to merge between branches
1109 branchmerge = whether to merge between branches
1110 force = whether to force branch merging or file overwriting
1110 force = whether to force branch merging or file overwriting
1111 partial = a function to filter file lists (dirstate not updated)
1111 partial = a function to filter file lists (dirstate not updated)
1112 mergeancestor = whether it is merging with an ancestor. If true,
1112 mergeancestor = whether it is merging with an ancestor. If true,
1113 we should accept the incoming changes for any prompts that occur.
1113 we should accept the incoming changes for any prompts that occur.
1114 If false, merging with an ancestor (fast-forward) is only allowed
1114 If false, merging with an ancestor (fast-forward) is only allowed
1115 between different named branches. This flag is used by rebase extension
1115 between different named branches. This flag is used by rebase extension
1116 as a temporary fix and should be avoided in general.
1116 as a temporary fix and should be avoided in general.
1117
1117
1118 The table below shows all the behaviors of the update command
1118 The table below shows all the behaviors of the update command
1119 given the -c and -C or no options, whether the working directory
1119 given the -c and -C or no options, whether the working directory
1120 is dirty, whether a revision is specified, and the relationship of
1120 is dirty, whether a revision is specified, and the relationship of
1121 the parent rev to the target rev (linear, on the same named
1121 the parent rev to the target rev (linear, on the same named
1122 branch, or on another named branch).
1122 branch, or on another named branch).
1123
1123
1124 This logic is tested by test-update-branches.t.
1124 This logic is tested by test-update-branches.t.
1125
1125
1126 -c -C dirty rev | linear same cross
1126 -c -C dirty rev | linear same cross
1127 n n n n | ok (1) x
1127 n n n n | ok (1) x
1128 n n n y | ok ok ok
1128 n n n y | ok ok ok
1129 n n y n | merge (2) (2)
1129 n n y n | merge (2) (2)
1130 n n y y | merge (3) (3)
1130 n n y y | merge (3) (3)
1131 n y * * | discard discard discard
1131 n y * * | discard discard discard
1132 y n y * | (4) (4) (4)
1132 y n y * | (4) (4) (4)
1133 y n n * | ok ok ok
1133 y n n * | ok ok ok
1134 y y * * | (5) (5) (5)
1134 y y * * | (5) (5) (5)
1135
1135
1136 x = can't happen
1136 x = can't happen
1137 * = don't-care
1137 * = don't-care
1138 1 = abort: not a linear update (merge or update --check to force update)
1138 1 = abort: not a linear update (merge or update --check to force update)
1139 2 = abort: uncommitted changes (commit and merge, or update --clean to
1139 2 = abort: uncommitted changes (commit and merge, or update --clean to
1140 discard changes)
1140 discard changes)
1141 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1141 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1142 4 = abort: uncommitted changes (checked in commands.py)
1142 4 = abort: uncommitted changes (checked in commands.py)
1143 5 = incompatible options (checked in commands.py)
1143 5 = incompatible options (checked in commands.py)
1144
1144
1145 Return the same tuple as applyupdates().
1145 Return the same tuple as applyupdates().
1146 """
1146 """
1147
1147
1148 onode = node
1148 onode = node
1149 wlock = repo.wlock()
1149 wlock = repo.wlock()
1150 try:
1150 try:
1151 wc = repo[None]
1151 wc = repo[None]
1152 pl = wc.parents()
1152 pl = wc.parents()
1153 p1 = pl[0]
1153 p1 = pl[0]
1154 pas = [None]
1154 pas = [None]
1155 if ancestor is not None:
1155 if ancestor is not None:
1156 pas = [repo[ancestor]]
1156 pas = [repo[ancestor]]
1157
1157
1158 if node is None:
1158 if node is None:
1159 if (repo.ui.configbool('devel', 'all-warnings')
1159 if (repo.ui.configbool('devel', 'all-warnings')
1160 or repo.ui.configbool('devel', 'oldapi')):
1160 or repo.ui.configbool('devel', 'oldapi')):
1161 repo.ui.develwarn('update with no target')
1161 repo.ui.develwarn('update with no target')
1162 rev, _mark, _act = destutil.destupdate(repo)
1162 rev, _mark, _act = destutil.destupdate(repo)
1163 node = repo[rev].node()
1163 node = repo[rev].node()
1164
1164
1165 overwrite = force and not branchmerge
1165 overwrite = force and not branchmerge
1166
1166
1167 p2 = repo[node]
1167 p2 = repo[node]
1168 if pas[0] is None:
1168 if pas[0] is None:
1169 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1169 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1170 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1170 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1171 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1171 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1172 else:
1172 else:
1173 pas = [p1.ancestor(p2, warn=branchmerge)]
1173 pas = [p1.ancestor(p2, warn=branchmerge)]
1174
1174
1175 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1175 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1176
1176
1177 ### check phase
1177 ### check phase
1178 if not overwrite and len(pl) > 1:
1178 if not overwrite and len(pl) > 1:
1179 raise error.Abort(_("outstanding uncommitted merge"))
1179 raise error.Abort(_("outstanding uncommitted merge"))
1180 if branchmerge:
1180 if branchmerge:
1181 if pas == [p2]:
1181 if pas == [p2]:
1182 raise error.Abort(_("merging with a working directory ancestor"
1182 raise error.Abort(_("merging with a working directory ancestor"
1183 " has no effect"))
1183 " has no effect"))
1184 elif pas == [p1]:
1184 elif pas == [p1]:
1185 if not mergeancestor and p1.branch() == p2.branch():
1185 if not mergeancestor and p1.branch() == p2.branch():
1186 raise error.Abort(_("nothing to merge"),
1186 raise error.Abort(_("nothing to merge"),
1187 hint=_("use 'hg update' "
1187 hint=_("use 'hg update' "
1188 "or check 'hg heads'"))
1188 "or check 'hg heads'"))
1189 if not force and (wc.files() or wc.deleted()):
1189 if not force and (wc.files() or wc.deleted()):
1190 raise error.Abort(_("uncommitted changes"),
1190 raise error.Abort(_("uncommitted changes"),
1191 hint=_("use 'hg status' to list changes"))
1191 hint=_("use 'hg status' to list changes"))
1192 for s in sorted(wc.substate):
1192 for s in sorted(wc.substate):
1193 wc.sub(s).bailifchanged()
1193 wc.sub(s).bailifchanged()
1194
1194
1195 elif not overwrite:
1195 elif not overwrite:
1196 if p1 == p2: # no-op update
1196 if p1 == p2: # no-op update
1197 # call the hooks and exit early
1197 # call the hooks and exit early
1198 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1198 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1199 repo.hook('update', parent1=xp2, parent2='', error=0)
1199 repo.hook('update', parent1=xp2, parent2='', error=0)
1200 return 0, 0, 0, 0
1200 return 0, 0, 0, 0
1201
1201
1202 if pas not in ([p1], [p2]): # nonlinear
1202 if pas not in ([p1], [p2]): # nonlinear
1203 dirty = wc.dirty(missing=True)
1203 dirty = wc.dirty(missing=True)
1204 if dirty or onode is None:
1204 if dirty or onode is None:
1205 # Branching is a bit strange to ensure we do the minimal
1205 # Branching is a bit strange to ensure we do the minimal
1206 # amount of call to obsolete.background.
1206 # amount of call to obsolete.background.
1207 foreground = obsolete.foreground(repo, [p1.node()])
1207 foreground = obsolete.foreground(repo, [p1.node()])
1208 # note: the <node> variable contains a random identifier
1208 # note: the <node> variable contains a random identifier
1209 if repo[node].node() in foreground:
1209 if repo[node].node() in foreground:
1210 pas = [p1] # allow updating to successors
1210 pas = [p1] # allow updating to successors
1211 elif dirty:
1211 elif dirty:
1212 msg = _("uncommitted changes")
1212 msg = _("uncommitted changes")
1213 if onode is None:
1213 if onode is None:
1214 hint = _("commit and merge, or update --clean to"
1214 hint = _("commit and merge, or update --clean to"
1215 " discard changes")
1215 " discard changes")
1216 else:
1216 else:
1217 hint = _("commit or update --clean to discard"
1217 hint = _("commit or update --clean to discard"
1218 " changes")
1218 " changes")
1219 raise error.Abort(msg, hint=hint)
1219 raise error.Abort(msg, hint=hint)
1220 else: # node is none
1220 else: # node is none
1221 msg = _("not a linear update")
1221 msg = _("not a linear update")
1222 hint = _("merge or update --check to force update")
1222 hint = _("merge or update --check to force update")
1223 raise error.Abort(msg, hint=hint)
1223 raise error.Abort(msg, hint=hint)
1224 else:
1224 else:
1225 # Allow jumping branches if clean and specific rev given
1225 # Allow jumping branches if clean and specific rev given
1226 pas = [p1]
1226 pas = [p1]
1227
1227
1228 # deprecated config: merge.followcopies
1228 # deprecated config: merge.followcopies
1229 followcopies = False
1229 followcopies = False
1230 if overwrite:
1230 if overwrite:
1231 pas = [wc]
1231 pas = [wc]
1232 elif pas == [p2]: # backwards
1232 elif pas == [p2]: # backwards
1233 pas = [wc.p1()]
1233 pas = [wc.p1()]
1234 elif not branchmerge and not wc.dirty(missing=True):
1234 elif not branchmerge and not wc.dirty(missing=True):
1235 pass
1235 pass
1236 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1236 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1237 followcopies = True
1237 followcopies = True
1238
1238
1239 ### calculate phase
1239 ### calculate phase
1240 actionbyfile, diverge, renamedelete = calculateupdates(
1240 actionbyfile, diverge, renamedelete = calculateupdates(
1241 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1241 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1242 followcopies)
1242 followcopies)
1243 # Convert to dictionary-of-lists format
1243 # Convert to dictionary-of-lists format
1244 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1244 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1245 for f, (m, args, msg) in actionbyfile.iteritems():
1245 for f, (m, args, msg) in actionbyfile.iteritems():
1246 if m not in actions:
1246 if m not in actions:
1247 actions[m] = []
1247 actions[m] = []
1248 actions[m].append((f, args, msg))
1248 actions[m].append((f, args, msg))
1249
1249
1250 if not util.checkcase(repo.path):
1250 if not util.checkcase(repo.path):
1251 # check collision between files only in p2 for clean update
1251 # check collision between files only in p2 for clean update
1252 if (not branchmerge and
1252 if (not branchmerge and
1253 (force or not wc.dirty(missing=True, branch=False))):
1253 (force or not wc.dirty(missing=True, branch=False))):
1254 _checkcollision(repo, p2.manifest(), None)
1254 _checkcollision(repo, p2.manifest(), None)
1255 else:
1255 else:
1256 _checkcollision(repo, wc.manifest(), actions)
1256 _checkcollision(repo, wc.manifest(), actions)
1257
1257
1258 # Prompt and create actions. TODO: Move this towards resolve phase.
1258 # Prompt and create actions. TODO: Move this towards resolve phase.
1259 for f, args, msg in sorted(actions['cd']):
1259 for f, args, msg in sorted(actions['cd']):
1260 if repo.ui.promptchoice(
1260 if repo.ui.promptchoice(
1261 _("local changed %s which remote deleted\n"
1261 _("local changed %s which remote deleted\n"
1262 "use (c)hanged version or (d)elete?"
1262 "use (c)hanged version or (d)elete?"
1263 "$$ &Changed $$ &Delete") % f, 0):
1263 "$$ &Changed $$ &Delete") % f, 0):
1264 actions['r'].append((f, None, "prompt delete"))
1264 actions['r'].append((f, None, "prompt delete"))
1265 else:
1265 else:
1266 actions['a'].append((f, None, "prompt keep"))
1266 actions['a'].append((f, None, "prompt keep"))
1267
1267
1268 for f, args, msg in sorted(actions['dc']):
1268 for f, args, msg in sorted(actions['dc']):
1269 f1, f2, fa, move, anc = args
1269 f1, f2, fa, move, anc = args
1270 flags = p2[f2].flags()
1270 flags = p2[f2].flags()
1271 if repo.ui.promptchoice(
1271 if repo.ui.promptchoice(
1272 _("remote changed %s which local deleted\n"
1272 _("remote changed %s which local deleted\n"
1273 "use (c)hanged version or leave (d)eleted?"
1273 "use (c)hanged version or leave (d)eleted?"
1274 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1274 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1275 actions['g'].append((f, (flags,), "prompt recreating"))
1275 actions['g'].append((f, (flags,), "prompt recreating"))
1276
1276
1277 # divergent renames
1277 # divergent renames
1278 for f, fl in sorted(diverge.iteritems()):
1278 for f, fl in sorted(diverge.iteritems()):
1279 repo.ui.warn(_("note: possible conflict - %s was renamed "
1279 repo.ui.warn(_("note: possible conflict - %s was renamed "
1280 "multiple times to:\n") % f)
1280 "multiple times to:\n") % f)
1281 for nf in fl:
1281 for nf in fl:
1282 repo.ui.warn(" %s\n" % nf)
1282 repo.ui.warn(" %s\n" % nf)
1283
1283
1284 # rename and delete
1284 # rename and delete
1285 for f, fl in sorted(renamedelete.iteritems()):
1285 for f, fl in sorted(renamedelete.iteritems()):
1286 repo.ui.warn(_("note: possible conflict - %s was deleted "
1286 repo.ui.warn(_("note: possible conflict - %s was deleted "
1287 "and renamed to:\n") % f)
1287 "and renamed to:\n") % f)
1288 for nf in fl:
1288 for nf in fl:
1289 repo.ui.warn(" %s\n" % nf)
1289 repo.ui.warn(" %s\n" % nf)
1290
1290
1291 ### apply phase
1291 ### apply phase
1292 if not branchmerge: # just jump to the new rev
1292 if not branchmerge: # just jump to the new rev
1293 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1293 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1294 if not partial:
1294 if not partial:
1295 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1295 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1296 # note that we're in the middle of an update
1296 # note that we're in the middle of an update
1297 repo.vfs.write('updatestate', p2.hex())
1297 repo.vfs.write('updatestate', p2.hex())
1298
1298
1299 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1299 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1300
1300
1301 if not partial:
1301 if not partial:
1302 repo.dirstate.beginparentchange()
1302 repo.dirstate.beginparentchange()
1303 repo.setparents(fp1, fp2)
1303 repo.setparents(fp1, fp2)
1304 recordupdates(repo, actions, branchmerge)
1304 recordupdates(repo, actions, branchmerge)
1305 # update completed, clear state
1305 # update completed, clear state
1306 util.unlink(repo.join('updatestate'))
1306 util.unlink(repo.join('updatestate'))
1307
1307
1308 if not branchmerge:
1308 if not branchmerge:
1309 repo.dirstate.setbranch(p2.branch())
1309 repo.dirstate.setbranch(p2.branch())
1310 repo.dirstate.endparentchange()
1310 repo.dirstate.endparentchange()
1311 finally:
1311 finally:
1312 wlock.release()
1312 wlock.release()
1313
1313
1314 if not partial:
1314 if not partial:
1315 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1315 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1316 return stats
1316 return stats
1317
1317
1318 def graft(repo, ctx, pctx, labels):
1318 def graft(repo, ctx, pctx, labels):
1319 """Do a graft-like merge.
1319 """Do a graft-like merge.
1320
1320
1321 This is a merge where the merge ancestor is chosen such that one
1321 This is a merge where the merge ancestor is chosen such that one
1322 or more changesets are grafted onto the current changeset. In
1322 or more changesets are grafted onto the current changeset. In
1323 addition to the merge, this fixes up the dirstate to include only
1323 addition to the merge, this fixes up the dirstate to include only
1324 a single parent and tries to duplicate any renames/copies
1324 a single parent and tries to duplicate any renames/copies
1325 appropriately.
1325 appropriately.
1326
1326
1327 ctx - changeset to rebase
1327 ctx - changeset to rebase
1328 pctx - merge base, usually ctx.p1()
1328 pctx - merge base, usually ctx.p1()
1329 labels - merge labels eg ['local', 'graft']
1329 labels - merge labels eg ['local', 'graft']
1330
1330
1331 """
1331 """
1332 # If we're grafting a descendant onto an ancestor, be sure to pass
1332 # If we're grafting a descendant onto an ancestor, be sure to pass
1333 # mergeancestor=True to update. This does two things: 1) allows the merge if
1333 # mergeancestor=True to update. This does two things: 1) allows the merge if
1334 # the destination is the same as the parent of the ctx (so we can use graft
1334 # the destination is the same as the parent of the ctx (so we can use graft
1335 # to copy commits), and 2) informs update that the incoming changes are
1335 # to copy commits), and 2) informs update that the incoming changes are
1336 # newer than the destination so it doesn't prompt about "remote changed foo
1336 # newer than the destination so it doesn't prompt about "remote changed foo
1337 # which local deleted".
1337 # which local deleted".
1338 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1338 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1339
1339
1340 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1340 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1341 mergeancestor=mergeancestor, labels=labels)
1341 mergeancestor=mergeancestor, labels=labels)
1342
1342
1343 # drop the second merge parent
1343 # drop the second merge parent
1344 repo.dirstate.beginparentchange()
1344 repo.dirstate.beginparentchange()
1345 repo.setparents(repo['.'].node(), nullid)
1345 repo.setparents(repo['.'].node(), nullid)
1346 repo.dirstate.write(repo.currenttransaction())
1346 repo.dirstate.write(repo.currenttransaction())
1347 # fix up dirstate for copies and renames
1347 # fix up dirstate for copies and renames
1348 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1348 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1349 repo.dirstate.endparentchange()
1349 repo.dirstate.endparentchange()
1350 return stats
1350 return stats
General Comments 0
You need to be logged in to leave comments. Login now