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