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