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