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