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