##// END OF EJS Templates
merge: flush any deferred writes just before recordupdates()...
Phil Cohen -
r34127:57dc78d7 default
parent child Browse files
Show More
@@ -1,1772 +1,1773 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 hashlib
11 import hashlib
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 addednodeid,
17 addednodeid,
18 bin,
18 bin,
19 hex,
19 hex,
20 modifiednodeid,
20 modifiednodeid,
21 nullhex,
21 nullhex,
22 nullid,
22 nullid,
23 nullrev,
23 nullrev,
24 )
24 )
25 from . import (
25 from . import (
26 copies,
26 copies,
27 error,
27 error,
28 filemerge,
28 filemerge,
29 match as matchmod,
29 match as matchmod,
30 obsutil,
30 obsutil,
31 pycompat,
31 pycompat,
32 scmutil,
32 scmutil,
33 subrepo,
33 subrepo,
34 util,
34 util,
35 worker,
35 worker,
36 )
36 )
37
37
38 _pack = struct.pack
38 _pack = struct.pack
39 _unpack = struct.unpack
39 _unpack = struct.unpack
40
40
41 def _droponode(data):
41 def _droponode(data):
42 # used for compatibility for v1
42 # used for compatibility for v1
43 bits = data.split('\0')
43 bits = data.split('\0')
44 bits = bits[:-2] + bits[-1:]
44 bits = bits[:-2] + bits[-1:]
45 return '\0'.join(bits)
45 return '\0'.join(bits)
46
46
47 class mergestate(object):
47 class mergestate(object):
48 '''track 3-way merge state of individual files
48 '''track 3-way merge state of individual files
49
49
50 The merge state is stored on disk when needed. Two files are used: one with
50 The merge state is stored on disk when needed. Two files are used: one with
51 an old format (version 1), and one with a new format (version 2). Version 2
51 an old format (version 1), and one with a new format (version 2). Version 2
52 stores a superset of the data in version 1, including new kinds of records
52 stores a superset of the data in version 1, including new kinds of records
53 in the future. For more about the new format, see the documentation for
53 in the future. For more about the new format, see the documentation for
54 `_readrecordsv2`.
54 `_readrecordsv2`.
55
55
56 Each record can contain arbitrary content, and has an associated type. This
56 Each record can contain arbitrary content, and has an associated type. This
57 `type` should be a letter. If `type` is uppercase, the record is mandatory:
57 `type` should be a letter. If `type` is uppercase, the record is mandatory:
58 versions of Mercurial that don't support it should abort. If `type` is
58 versions of Mercurial that don't support it should abort. If `type` is
59 lowercase, the record can be safely ignored.
59 lowercase, the record can be safely ignored.
60
60
61 Currently known records:
61 Currently known records:
62
62
63 L: the node of the "local" part of the merge (hexified version)
63 L: the node of the "local" part of the merge (hexified version)
64 O: the node of the "other" part of the merge (hexified version)
64 O: the node of the "other" part of the merge (hexified version)
65 F: a file to be merged entry
65 F: a file to be merged entry
66 C: a change/delete or delete/change conflict
66 C: a change/delete or delete/change conflict
67 D: a file that the external merge driver will merge internally
67 D: a file that the external merge driver will merge internally
68 (experimental)
68 (experimental)
69 m: the external merge driver defined for this merge plus its run state
69 m: the external merge driver defined for this merge plus its run state
70 (experimental)
70 (experimental)
71 f: a (filename, dictionary) tuple of optional values for a given file
71 f: a (filename, dictionary) tuple of optional values for a given file
72 X: unsupported mandatory record type (used in tests)
72 X: unsupported mandatory record type (used in tests)
73 x: unsupported advisory record type (used in tests)
73 x: unsupported advisory record type (used in tests)
74 l: the labels for the parts of the merge.
74 l: the labels for the parts of the merge.
75
75
76 Merge driver run states (experimental):
76 Merge driver run states (experimental):
77 u: driver-resolved files unmarked -- needs to be run next time we're about
77 u: driver-resolved files unmarked -- needs to be run next time we're about
78 to resolve or commit
78 to resolve or commit
79 m: driver-resolved files marked -- only needs to be run before commit
79 m: driver-resolved files marked -- only needs to be run before commit
80 s: success/skipped -- does not need to be run any more
80 s: success/skipped -- does not need to be run any more
81
81
82 '''
82 '''
83 statepathv1 = 'merge/state'
83 statepathv1 = 'merge/state'
84 statepathv2 = 'merge/state2'
84 statepathv2 = 'merge/state2'
85
85
86 @staticmethod
86 @staticmethod
87 def clean(repo, node=None, other=None, labels=None):
87 def clean(repo, node=None, other=None, labels=None):
88 """Initialize a brand new merge state, removing any existing state on
88 """Initialize a brand new merge state, removing any existing state on
89 disk."""
89 disk."""
90 ms = mergestate(repo)
90 ms = mergestate(repo)
91 ms.reset(node, other, labels)
91 ms.reset(node, other, labels)
92 return ms
92 return ms
93
93
94 @staticmethod
94 @staticmethod
95 def read(repo):
95 def read(repo):
96 """Initialize the merge state, reading it from disk."""
96 """Initialize the merge state, reading it from disk."""
97 ms = mergestate(repo)
97 ms = mergestate(repo)
98 ms._read()
98 ms._read()
99 return ms
99 return ms
100
100
101 def __init__(self, repo):
101 def __init__(self, repo):
102 """Initialize the merge state.
102 """Initialize the merge state.
103
103
104 Do not use this directly! Instead call read() or clean()."""
104 Do not use this directly! Instead call read() or clean()."""
105 self._repo = repo
105 self._repo = repo
106 self._dirty = False
106 self._dirty = False
107 self._labels = None
107 self._labels = None
108
108
109 def reset(self, node=None, other=None, labels=None):
109 def reset(self, node=None, other=None, labels=None):
110 self._state = {}
110 self._state = {}
111 self._stateextras = {}
111 self._stateextras = {}
112 self._local = None
112 self._local = None
113 self._other = None
113 self._other = None
114 self._labels = labels
114 self._labels = labels
115 for var in ('localctx', 'otherctx'):
115 for var in ('localctx', 'otherctx'):
116 if var in vars(self):
116 if var in vars(self):
117 delattr(self, var)
117 delattr(self, var)
118 if node:
118 if node:
119 self._local = node
119 self._local = node
120 self._other = other
120 self._other = other
121 self._readmergedriver = None
121 self._readmergedriver = None
122 if self.mergedriver:
122 if self.mergedriver:
123 self._mdstate = 's'
123 self._mdstate = 's'
124 else:
124 else:
125 self._mdstate = 'u'
125 self._mdstate = 'u'
126 shutil.rmtree(self._repo.vfs.join('merge'), True)
126 shutil.rmtree(self._repo.vfs.join('merge'), True)
127 self._results = {}
127 self._results = {}
128 self._dirty = False
128 self._dirty = False
129
129
130 def _read(self):
130 def _read(self):
131 """Analyse each record content to restore a serialized state from disk
131 """Analyse each record content to restore a serialized state from disk
132
132
133 This function process "record" entry produced by the de-serialization
133 This function process "record" entry produced by the de-serialization
134 of on disk file.
134 of on disk file.
135 """
135 """
136 self._state = {}
136 self._state = {}
137 self._stateextras = {}
137 self._stateextras = {}
138 self._local = None
138 self._local = None
139 self._other = None
139 self._other = None
140 for var in ('localctx', 'otherctx'):
140 for var in ('localctx', 'otherctx'):
141 if var in vars(self):
141 if var in vars(self):
142 delattr(self, var)
142 delattr(self, var)
143 self._readmergedriver = None
143 self._readmergedriver = None
144 self._mdstate = 's'
144 self._mdstate = 's'
145 unsupported = set()
145 unsupported = set()
146 records = self._readrecords()
146 records = self._readrecords()
147 for rtype, record in records:
147 for rtype, record in records:
148 if rtype == 'L':
148 if rtype == 'L':
149 self._local = bin(record)
149 self._local = bin(record)
150 elif rtype == 'O':
150 elif rtype == 'O':
151 self._other = bin(record)
151 self._other = bin(record)
152 elif rtype == 'm':
152 elif rtype == 'm':
153 bits = record.split('\0', 1)
153 bits = record.split('\0', 1)
154 mdstate = bits[1]
154 mdstate = bits[1]
155 if len(mdstate) != 1 or mdstate not in 'ums':
155 if len(mdstate) != 1 or mdstate not in 'ums':
156 # the merge driver should be idempotent, so just rerun it
156 # the merge driver should be idempotent, so just rerun it
157 mdstate = 'u'
157 mdstate = 'u'
158
158
159 self._readmergedriver = bits[0]
159 self._readmergedriver = bits[0]
160 self._mdstate = mdstate
160 self._mdstate = mdstate
161 elif rtype in 'FDC':
161 elif rtype in 'FDC':
162 bits = record.split('\0')
162 bits = record.split('\0')
163 self._state[bits[0]] = bits[1:]
163 self._state[bits[0]] = bits[1:]
164 elif rtype == 'f':
164 elif rtype == 'f':
165 filename, rawextras = record.split('\0', 1)
165 filename, rawextras = record.split('\0', 1)
166 extraparts = rawextras.split('\0')
166 extraparts = rawextras.split('\0')
167 extras = {}
167 extras = {}
168 i = 0
168 i = 0
169 while i < len(extraparts):
169 while i < len(extraparts):
170 extras[extraparts[i]] = extraparts[i + 1]
170 extras[extraparts[i]] = extraparts[i + 1]
171 i += 2
171 i += 2
172
172
173 self._stateextras[filename] = extras
173 self._stateextras[filename] = extras
174 elif rtype == 'l':
174 elif rtype == 'l':
175 labels = record.split('\0', 2)
175 labels = record.split('\0', 2)
176 self._labels = [l for l in labels if len(l) > 0]
176 self._labels = [l for l in labels if len(l) > 0]
177 elif not rtype.islower():
177 elif not rtype.islower():
178 unsupported.add(rtype)
178 unsupported.add(rtype)
179 self._results = {}
179 self._results = {}
180 self._dirty = False
180 self._dirty = False
181
181
182 if unsupported:
182 if unsupported:
183 raise error.UnsupportedMergeRecords(unsupported)
183 raise error.UnsupportedMergeRecords(unsupported)
184
184
185 def _readrecords(self):
185 def _readrecords(self):
186 """Read merge state from disk and return a list of record (TYPE, data)
186 """Read merge state from disk and return a list of record (TYPE, data)
187
187
188 We read data from both v1 and v2 files and decide which one to use.
188 We read data from both v1 and v2 files and decide which one to use.
189
189
190 V1 has been used by version prior to 2.9.1 and contains less data than
190 V1 has been used by version prior to 2.9.1 and contains less data than
191 v2. We read both versions and check if no data in v2 contradicts
191 v2. We read both versions and check if no data in v2 contradicts
192 v1. If there is not contradiction we can safely assume that both v1
192 v1. If there is not contradiction we can safely assume that both v1
193 and v2 were written at the same time and use the extract data in v2. If
193 and v2 were written at the same time and use the extract data in v2. If
194 there is contradiction we ignore v2 content as we assume an old version
194 there is contradiction we ignore v2 content as we assume an old version
195 of Mercurial has overwritten the mergestate file and left an old v2
195 of Mercurial has overwritten the mergestate file and left an old v2
196 file around.
196 file around.
197
197
198 returns list of record [(TYPE, data), ...]"""
198 returns list of record [(TYPE, data), ...]"""
199 v1records = self._readrecordsv1()
199 v1records = self._readrecordsv1()
200 v2records = self._readrecordsv2()
200 v2records = self._readrecordsv2()
201 if self._v1v2match(v1records, v2records):
201 if self._v1v2match(v1records, v2records):
202 return v2records
202 return v2records
203 else:
203 else:
204 # v1 file is newer than v2 file, use it
204 # v1 file is newer than v2 file, use it
205 # we have to infer the "other" changeset of the merge
205 # we have to infer the "other" changeset of the merge
206 # we cannot do better than that with v1 of the format
206 # we cannot do better than that with v1 of the format
207 mctx = self._repo[None].parents()[-1]
207 mctx = self._repo[None].parents()[-1]
208 v1records.append(('O', mctx.hex()))
208 v1records.append(('O', mctx.hex()))
209 # add place holder "other" file node information
209 # add place holder "other" file node information
210 # nobody is using it yet so we do no need to fetch the data
210 # nobody is using it yet so we do no need to fetch the data
211 # if mctx was wrong `mctx[bits[-2]]` may fails.
211 # if mctx was wrong `mctx[bits[-2]]` may fails.
212 for idx, r in enumerate(v1records):
212 for idx, r in enumerate(v1records):
213 if r[0] == 'F':
213 if r[0] == 'F':
214 bits = r[1].split('\0')
214 bits = r[1].split('\0')
215 bits.insert(-2, '')
215 bits.insert(-2, '')
216 v1records[idx] = (r[0], '\0'.join(bits))
216 v1records[idx] = (r[0], '\0'.join(bits))
217 return v1records
217 return v1records
218
218
219 def _v1v2match(self, v1records, v2records):
219 def _v1v2match(self, v1records, v2records):
220 oldv2 = set() # old format version of v2 record
220 oldv2 = set() # old format version of v2 record
221 for rec in v2records:
221 for rec in v2records:
222 if rec[0] == 'L':
222 if rec[0] == 'L':
223 oldv2.add(rec)
223 oldv2.add(rec)
224 elif rec[0] == 'F':
224 elif rec[0] == 'F':
225 # drop the onode data (not contained in v1)
225 # drop the onode data (not contained in v1)
226 oldv2.add(('F', _droponode(rec[1])))
226 oldv2.add(('F', _droponode(rec[1])))
227 for rec in v1records:
227 for rec in v1records:
228 if rec not in oldv2:
228 if rec not in oldv2:
229 return False
229 return False
230 else:
230 else:
231 return True
231 return True
232
232
233 def _readrecordsv1(self):
233 def _readrecordsv1(self):
234 """read on disk merge state for version 1 file
234 """read on disk merge state for version 1 file
235
235
236 returns list of record [(TYPE, data), ...]
236 returns list of record [(TYPE, data), ...]
237
237
238 Note: the "F" data from this file are one entry short
238 Note: the "F" data from this file are one entry short
239 (no "other file node" entry)
239 (no "other file node" entry)
240 """
240 """
241 records = []
241 records = []
242 try:
242 try:
243 f = self._repo.vfs(self.statepathv1)
243 f = self._repo.vfs(self.statepathv1)
244 for i, l in enumerate(f):
244 for i, l in enumerate(f):
245 if i == 0:
245 if i == 0:
246 records.append(('L', l[:-1]))
246 records.append(('L', l[:-1]))
247 else:
247 else:
248 records.append(('F', l[:-1]))
248 records.append(('F', l[:-1]))
249 f.close()
249 f.close()
250 except IOError as err:
250 except IOError as err:
251 if err.errno != errno.ENOENT:
251 if err.errno != errno.ENOENT:
252 raise
252 raise
253 return records
253 return records
254
254
255 def _readrecordsv2(self):
255 def _readrecordsv2(self):
256 """read on disk merge state for version 2 file
256 """read on disk merge state for version 2 file
257
257
258 This format is a list of arbitrary records of the form:
258 This format is a list of arbitrary records of the form:
259
259
260 [type][length][content]
260 [type][length][content]
261
261
262 `type` is a single character, `length` is a 4 byte integer, and
262 `type` is a single character, `length` is a 4 byte integer, and
263 `content` is an arbitrary byte sequence of length `length`.
263 `content` is an arbitrary byte sequence of length `length`.
264
264
265 Mercurial versions prior to 3.7 have a bug where if there are
265 Mercurial versions prior to 3.7 have a bug where if there are
266 unsupported mandatory merge records, attempting to clear out the merge
266 unsupported mandatory merge records, attempting to clear out the merge
267 state with hg update --clean or similar aborts. The 't' record type
267 state with hg update --clean or similar aborts. The 't' record type
268 works around that by writing out what those versions treat as an
268 works around that by writing out what those versions treat as an
269 advisory record, but later versions interpret as special: the first
269 advisory record, but later versions interpret as special: the first
270 character is the 'real' record type and everything onwards is the data.
270 character is the 'real' record type and everything onwards is the data.
271
271
272 Returns list of records [(TYPE, data), ...]."""
272 Returns list of records [(TYPE, data), ...]."""
273 records = []
273 records = []
274 try:
274 try:
275 f = self._repo.vfs(self.statepathv2)
275 f = self._repo.vfs(self.statepathv2)
276 data = f.read()
276 data = f.read()
277 off = 0
277 off = 0
278 end = len(data)
278 end = len(data)
279 while off < end:
279 while off < end:
280 rtype = data[off]
280 rtype = data[off]
281 off += 1
281 off += 1
282 length = _unpack('>I', data[off:(off + 4)])[0]
282 length = _unpack('>I', data[off:(off + 4)])[0]
283 off += 4
283 off += 4
284 record = data[off:(off + length)]
284 record = data[off:(off + length)]
285 off += length
285 off += length
286 if rtype == 't':
286 if rtype == 't':
287 rtype, record = record[0], record[1:]
287 rtype, record = record[0], record[1:]
288 records.append((rtype, record))
288 records.append((rtype, record))
289 f.close()
289 f.close()
290 except IOError as err:
290 except IOError as err:
291 if err.errno != errno.ENOENT:
291 if err.errno != errno.ENOENT:
292 raise
292 raise
293 return records
293 return records
294
294
295 @util.propertycache
295 @util.propertycache
296 def mergedriver(self):
296 def mergedriver(self):
297 # protect against the following:
297 # protect against the following:
298 # - A configures a malicious merge driver in their hgrc, then
298 # - A configures a malicious merge driver in their hgrc, then
299 # pauses the merge
299 # pauses the merge
300 # - A edits their hgrc to remove references to the merge driver
300 # - A edits their hgrc to remove references to the merge driver
301 # - A gives a copy of their entire repo, including .hg, to B
301 # - A gives a copy of their entire repo, including .hg, to B
302 # - B inspects .hgrc and finds it to be clean
302 # - B inspects .hgrc and finds it to be clean
303 # - B then continues the merge and the malicious merge driver
303 # - B then continues the merge and the malicious merge driver
304 # gets invoked
304 # gets invoked
305 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
305 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
306 if (self._readmergedriver is not None
306 if (self._readmergedriver is not None
307 and self._readmergedriver != configmergedriver):
307 and self._readmergedriver != configmergedriver):
308 raise error.ConfigError(
308 raise error.ConfigError(
309 _("merge driver changed since merge started"),
309 _("merge driver changed since merge started"),
310 hint=_("revert merge driver change or abort merge"))
310 hint=_("revert merge driver change or abort merge"))
311
311
312 return configmergedriver
312 return configmergedriver
313
313
314 @util.propertycache
314 @util.propertycache
315 def localctx(self):
315 def localctx(self):
316 if self._local is None:
316 if self._local is None:
317 msg = "localctx accessed but self._local isn't set"
317 msg = "localctx accessed but self._local isn't set"
318 raise error.ProgrammingError(msg)
318 raise error.ProgrammingError(msg)
319 return self._repo[self._local]
319 return self._repo[self._local]
320
320
321 @util.propertycache
321 @util.propertycache
322 def otherctx(self):
322 def otherctx(self):
323 if self._other is None:
323 if self._other is None:
324 msg = "otherctx accessed but self._other isn't set"
324 msg = "otherctx accessed but self._other isn't set"
325 raise error.ProgrammingError(msg)
325 raise error.ProgrammingError(msg)
326 return self._repo[self._other]
326 return self._repo[self._other]
327
327
328 def active(self):
328 def active(self):
329 """Whether mergestate is active.
329 """Whether mergestate is active.
330
330
331 Returns True if there appears to be mergestate. This is a rough proxy
331 Returns True if there appears to be mergestate. This is a rough proxy
332 for "is a merge in progress."
332 for "is a merge in progress."
333 """
333 """
334 # Check local variables before looking at filesystem for performance
334 # Check local variables before looking at filesystem for performance
335 # reasons.
335 # reasons.
336 return bool(self._local) or bool(self._state) or \
336 return bool(self._local) or bool(self._state) or \
337 self._repo.vfs.exists(self.statepathv1) or \
337 self._repo.vfs.exists(self.statepathv1) or \
338 self._repo.vfs.exists(self.statepathv2)
338 self._repo.vfs.exists(self.statepathv2)
339
339
340 def commit(self):
340 def commit(self):
341 """Write current state on disk (if necessary)"""
341 """Write current state on disk (if necessary)"""
342 if self._dirty:
342 if self._dirty:
343 records = self._makerecords()
343 records = self._makerecords()
344 self._writerecords(records)
344 self._writerecords(records)
345 self._dirty = False
345 self._dirty = False
346
346
347 def _makerecords(self):
347 def _makerecords(self):
348 records = []
348 records = []
349 records.append(('L', hex(self._local)))
349 records.append(('L', hex(self._local)))
350 records.append(('O', hex(self._other)))
350 records.append(('O', hex(self._other)))
351 if self.mergedriver:
351 if self.mergedriver:
352 records.append(('m', '\0'.join([
352 records.append(('m', '\0'.join([
353 self.mergedriver, self._mdstate])))
353 self.mergedriver, self._mdstate])))
354 for d, v in self._state.iteritems():
354 for d, v in self._state.iteritems():
355 if v[0] == 'd':
355 if v[0] == 'd':
356 records.append(('D', '\0'.join([d] + v)))
356 records.append(('D', '\0'.join([d] + v)))
357 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
357 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
358 # older versions of Mercurial
358 # older versions of Mercurial
359 elif v[1] == nullhex or v[6] == nullhex:
359 elif v[1] == nullhex or v[6] == nullhex:
360 records.append(('C', '\0'.join([d] + v)))
360 records.append(('C', '\0'.join([d] + v)))
361 else:
361 else:
362 records.append(('F', '\0'.join([d] + v)))
362 records.append(('F', '\0'.join([d] + v)))
363 for filename, extras in sorted(self._stateextras.iteritems()):
363 for filename, extras in sorted(self._stateextras.iteritems()):
364 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
364 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
365 extras.iteritems())
365 extras.iteritems())
366 records.append(('f', '%s\0%s' % (filename, rawextras)))
366 records.append(('f', '%s\0%s' % (filename, rawextras)))
367 if self._labels is not None:
367 if self._labels is not None:
368 labels = '\0'.join(self._labels)
368 labels = '\0'.join(self._labels)
369 records.append(('l', labels))
369 records.append(('l', labels))
370 return records
370 return records
371
371
372 def _writerecords(self, records):
372 def _writerecords(self, records):
373 """Write current state on disk (both v1 and v2)"""
373 """Write current state on disk (both v1 and v2)"""
374 self._writerecordsv1(records)
374 self._writerecordsv1(records)
375 self._writerecordsv2(records)
375 self._writerecordsv2(records)
376
376
377 def _writerecordsv1(self, records):
377 def _writerecordsv1(self, records):
378 """Write current state on disk in a version 1 file"""
378 """Write current state on disk in a version 1 file"""
379 f = self._repo.vfs(self.statepathv1, 'w')
379 f = self._repo.vfs(self.statepathv1, 'w')
380 irecords = iter(records)
380 irecords = iter(records)
381 lrecords = next(irecords)
381 lrecords = next(irecords)
382 assert lrecords[0] == 'L'
382 assert lrecords[0] == 'L'
383 f.write(hex(self._local) + '\n')
383 f.write(hex(self._local) + '\n')
384 for rtype, data in irecords:
384 for rtype, data in irecords:
385 if rtype == 'F':
385 if rtype == 'F':
386 f.write('%s\n' % _droponode(data))
386 f.write('%s\n' % _droponode(data))
387 f.close()
387 f.close()
388
388
389 def _writerecordsv2(self, records):
389 def _writerecordsv2(self, records):
390 """Write current state on disk in a version 2 file
390 """Write current state on disk in a version 2 file
391
391
392 See the docstring for _readrecordsv2 for why we use 't'."""
392 See the docstring for _readrecordsv2 for why we use 't'."""
393 # these are the records that all version 2 clients can read
393 # these are the records that all version 2 clients can read
394 whitelist = 'LOF'
394 whitelist = 'LOF'
395 f = self._repo.vfs(self.statepathv2, 'w')
395 f = self._repo.vfs(self.statepathv2, 'w')
396 for key, data in records:
396 for key, data in records:
397 assert len(key) == 1
397 assert len(key) == 1
398 if key not in whitelist:
398 if key not in whitelist:
399 key, data = 't', '%s%s' % (key, data)
399 key, data = 't', '%s%s' % (key, data)
400 format = '>sI%is' % len(data)
400 format = '>sI%is' % len(data)
401 f.write(_pack(format, key, len(data), data))
401 f.write(_pack(format, key, len(data), data))
402 f.close()
402 f.close()
403
403
404 def add(self, fcl, fco, fca, fd):
404 def add(self, fcl, fco, fca, fd):
405 """add a new (potentially?) conflicting file the merge state
405 """add a new (potentially?) conflicting file the merge state
406 fcl: file context for local,
406 fcl: file context for local,
407 fco: file context for remote,
407 fco: file context for remote,
408 fca: file context for ancestors,
408 fca: file context for ancestors,
409 fd: file path of the resulting merge.
409 fd: file path of the resulting merge.
410
410
411 note: also write the local version to the `.hg/merge` directory.
411 note: also write the local version to the `.hg/merge` directory.
412 """
412 """
413 if fcl.isabsent():
413 if fcl.isabsent():
414 hash = nullhex
414 hash = nullhex
415 else:
415 else:
416 hash = hex(hashlib.sha1(fcl.path()).digest())
416 hash = hex(hashlib.sha1(fcl.path()).digest())
417 self._repo.vfs.write('merge/' + hash, fcl.data())
417 self._repo.vfs.write('merge/' + hash, fcl.data())
418 self._state[fd] = ['u', hash, fcl.path(),
418 self._state[fd] = ['u', hash, fcl.path(),
419 fca.path(), hex(fca.filenode()),
419 fca.path(), hex(fca.filenode()),
420 fco.path(), hex(fco.filenode()),
420 fco.path(), hex(fco.filenode()),
421 fcl.flags()]
421 fcl.flags()]
422 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
422 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
423 self._dirty = True
423 self._dirty = True
424
424
425 def __contains__(self, dfile):
425 def __contains__(self, dfile):
426 return dfile in self._state
426 return dfile in self._state
427
427
428 def __getitem__(self, dfile):
428 def __getitem__(self, dfile):
429 return self._state[dfile][0]
429 return self._state[dfile][0]
430
430
431 def __iter__(self):
431 def __iter__(self):
432 return iter(sorted(self._state))
432 return iter(sorted(self._state))
433
433
434 def files(self):
434 def files(self):
435 return self._state.keys()
435 return self._state.keys()
436
436
437 def mark(self, dfile, state):
437 def mark(self, dfile, state):
438 self._state[dfile][0] = state
438 self._state[dfile][0] = state
439 self._dirty = True
439 self._dirty = True
440
440
441 def mdstate(self):
441 def mdstate(self):
442 return self._mdstate
442 return self._mdstate
443
443
444 def unresolved(self):
444 def unresolved(self):
445 """Obtain the paths of unresolved files."""
445 """Obtain the paths of unresolved files."""
446
446
447 for f, entry in self._state.iteritems():
447 for f, entry in self._state.iteritems():
448 if entry[0] == 'u':
448 if entry[0] == 'u':
449 yield f
449 yield f
450
450
451 def driverresolved(self):
451 def driverresolved(self):
452 """Obtain the paths of driver-resolved files."""
452 """Obtain the paths of driver-resolved files."""
453
453
454 for f, entry in self._state.items():
454 for f, entry in self._state.items():
455 if entry[0] == 'd':
455 if entry[0] == 'd':
456 yield f
456 yield f
457
457
458 def extras(self, filename):
458 def extras(self, filename):
459 return self._stateextras.setdefault(filename, {})
459 return self._stateextras.setdefault(filename, {})
460
460
461 def _resolve(self, preresolve, dfile, wctx):
461 def _resolve(self, preresolve, dfile, wctx):
462 """rerun merge process for file path `dfile`"""
462 """rerun merge process for file path `dfile`"""
463 if self[dfile] in 'rd':
463 if self[dfile] in 'rd':
464 return True, 0
464 return True, 0
465 stateentry = self._state[dfile]
465 stateentry = self._state[dfile]
466 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
466 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
467 octx = self._repo[self._other]
467 octx = self._repo[self._other]
468 extras = self.extras(dfile)
468 extras = self.extras(dfile)
469 anccommitnode = extras.get('ancestorlinknode')
469 anccommitnode = extras.get('ancestorlinknode')
470 if anccommitnode:
470 if anccommitnode:
471 actx = self._repo[anccommitnode]
471 actx = self._repo[anccommitnode]
472 else:
472 else:
473 actx = None
473 actx = None
474 fcd = self._filectxorabsent(hash, wctx, dfile)
474 fcd = self._filectxorabsent(hash, wctx, dfile)
475 fco = self._filectxorabsent(onode, octx, ofile)
475 fco = self._filectxorabsent(onode, octx, ofile)
476 # TODO: move this to filectxorabsent
476 # TODO: move this to filectxorabsent
477 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
477 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
478 # "premerge" x flags
478 # "premerge" x flags
479 flo = fco.flags()
479 flo = fco.flags()
480 fla = fca.flags()
480 fla = fca.flags()
481 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
481 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
482 if fca.node() == nullid and flags != flo:
482 if fca.node() == nullid and flags != flo:
483 if preresolve:
483 if preresolve:
484 self._repo.ui.warn(
484 self._repo.ui.warn(
485 _('warning: cannot merge flags for %s '
485 _('warning: cannot merge flags for %s '
486 'without common ancestor - keeping local flags\n')
486 'without common ancestor - keeping local flags\n')
487 % afile)
487 % afile)
488 elif flags == fla:
488 elif flags == fla:
489 flags = flo
489 flags = flo
490 if preresolve:
490 if preresolve:
491 # restore local
491 # restore local
492 if hash != nullhex:
492 if hash != nullhex:
493 f = self._repo.vfs('merge/' + hash)
493 f = self._repo.vfs('merge/' + hash)
494 wctx[dfile].write(f.read(), flags)
494 wctx[dfile].write(f.read(), flags)
495 f.close()
495 f.close()
496 else:
496 else:
497 wctx[dfile].remove(ignoremissing=True)
497 wctx[dfile].remove(ignoremissing=True)
498 complete, r, deleted = filemerge.premerge(self._repo, wctx,
498 complete, r, deleted = filemerge.premerge(self._repo, wctx,
499 self._local, lfile, fcd,
499 self._local, lfile, fcd,
500 fco, fca,
500 fco, fca,
501 labels=self._labels)
501 labels=self._labels)
502 else:
502 else:
503 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
503 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
504 self._local, lfile, fcd,
504 self._local, lfile, fcd,
505 fco, fca,
505 fco, fca,
506 labels=self._labels)
506 labels=self._labels)
507 if r is None:
507 if r is None:
508 # no real conflict
508 # no real conflict
509 del self._state[dfile]
509 del self._state[dfile]
510 self._stateextras.pop(dfile, None)
510 self._stateextras.pop(dfile, None)
511 self._dirty = True
511 self._dirty = True
512 elif not r:
512 elif not r:
513 self.mark(dfile, 'r')
513 self.mark(dfile, 'r')
514
514
515 if complete:
515 if complete:
516 action = None
516 action = None
517 if deleted:
517 if deleted:
518 if fcd.isabsent():
518 if fcd.isabsent():
519 # dc: local picked. Need to drop if present, which may
519 # dc: local picked. Need to drop if present, which may
520 # happen on re-resolves.
520 # happen on re-resolves.
521 action = 'f'
521 action = 'f'
522 else:
522 else:
523 # cd: remote picked (or otherwise deleted)
523 # cd: remote picked (or otherwise deleted)
524 action = 'r'
524 action = 'r'
525 else:
525 else:
526 if fcd.isabsent(): # dc: remote picked
526 if fcd.isabsent(): # dc: remote picked
527 action = 'g'
527 action = 'g'
528 elif fco.isabsent(): # cd: local picked
528 elif fco.isabsent(): # cd: local picked
529 if dfile in self.localctx:
529 if dfile in self.localctx:
530 action = 'am'
530 action = 'am'
531 else:
531 else:
532 action = 'a'
532 action = 'a'
533 # else: regular merges (no action necessary)
533 # else: regular merges (no action necessary)
534 self._results[dfile] = r, action
534 self._results[dfile] = r, action
535
535
536 return complete, r
536 return complete, r
537
537
538 def _filectxorabsent(self, hexnode, ctx, f):
538 def _filectxorabsent(self, hexnode, ctx, f):
539 if hexnode == nullhex:
539 if hexnode == nullhex:
540 return filemerge.absentfilectx(ctx, f)
540 return filemerge.absentfilectx(ctx, f)
541 else:
541 else:
542 return ctx[f]
542 return ctx[f]
543
543
544 def preresolve(self, dfile, wctx):
544 def preresolve(self, dfile, wctx):
545 """run premerge process for dfile
545 """run premerge process for dfile
546
546
547 Returns whether the merge is complete, and the exit code."""
547 Returns whether the merge is complete, and the exit code."""
548 return self._resolve(True, dfile, wctx)
548 return self._resolve(True, dfile, wctx)
549
549
550 def resolve(self, dfile, wctx):
550 def resolve(self, dfile, wctx):
551 """run merge process (assuming premerge was run) for dfile
551 """run merge process (assuming premerge was run) for dfile
552
552
553 Returns the exit code of the merge."""
553 Returns the exit code of the merge."""
554 return self._resolve(False, dfile, wctx)[1]
554 return self._resolve(False, dfile, wctx)[1]
555
555
556 def counts(self):
556 def counts(self):
557 """return counts for updated, merged and removed files in this
557 """return counts for updated, merged and removed files in this
558 session"""
558 session"""
559 updated, merged, removed = 0, 0, 0
559 updated, merged, removed = 0, 0, 0
560 for r, action in self._results.itervalues():
560 for r, action in self._results.itervalues():
561 if r is None:
561 if r is None:
562 updated += 1
562 updated += 1
563 elif r == 0:
563 elif r == 0:
564 if action == 'r':
564 if action == 'r':
565 removed += 1
565 removed += 1
566 else:
566 else:
567 merged += 1
567 merged += 1
568 return updated, merged, removed
568 return updated, merged, removed
569
569
570 def unresolvedcount(self):
570 def unresolvedcount(self):
571 """get unresolved count for this merge (persistent)"""
571 """get unresolved count for this merge (persistent)"""
572 return len(list(self.unresolved()))
572 return len(list(self.unresolved()))
573
573
574 def actions(self):
574 def actions(self):
575 """return lists of actions to perform on the dirstate"""
575 """return lists of actions to perform on the dirstate"""
576 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
576 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
577 for f, (r, action) in self._results.iteritems():
577 for f, (r, action) in self._results.iteritems():
578 if action is not None:
578 if action is not None:
579 actions[action].append((f, None, "merge result"))
579 actions[action].append((f, None, "merge result"))
580 return actions
580 return actions
581
581
582 def recordactions(self):
582 def recordactions(self):
583 """record remove/add/get actions in the dirstate"""
583 """record remove/add/get actions in the dirstate"""
584 branchmerge = self._repo.dirstate.p2() != nullid
584 branchmerge = self._repo.dirstate.p2() != nullid
585 recordupdates(self._repo, self.actions(), branchmerge)
585 recordupdates(self._repo, self.actions(), branchmerge)
586
586
587 def queueremove(self, f):
587 def queueremove(self, f):
588 """queues a file to be removed from the dirstate
588 """queues a file to be removed from the dirstate
589
589
590 Meant for use by custom merge drivers."""
590 Meant for use by custom merge drivers."""
591 self._results[f] = 0, 'r'
591 self._results[f] = 0, 'r'
592
592
593 def queueadd(self, f):
593 def queueadd(self, f):
594 """queues a file to be added to the dirstate
594 """queues a file to be added to the dirstate
595
595
596 Meant for use by custom merge drivers."""
596 Meant for use by custom merge drivers."""
597 self._results[f] = 0, 'a'
597 self._results[f] = 0, 'a'
598
598
599 def queueget(self, f):
599 def queueget(self, f):
600 """queues a file to be marked modified in the dirstate
600 """queues a file to be marked modified in the dirstate
601
601
602 Meant for use by custom merge drivers."""
602 Meant for use by custom merge drivers."""
603 self._results[f] = 0, 'g'
603 self._results[f] = 0, 'g'
604
604
605 def _getcheckunknownconfig(repo, section, name):
605 def _getcheckunknownconfig(repo, section, name):
606 config = repo.ui.config(section, name, default='abort')
606 config = repo.ui.config(section, name, default='abort')
607 valid = ['abort', 'ignore', 'warn']
607 valid = ['abort', 'ignore', 'warn']
608 if config not in valid:
608 if config not in valid:
609 validstr = ', '.join(["'" + v + "'" for v in valid])
609 validstr = ', '.join(["'" + v + "'" for v in valid])
610 raise error.ConfigError(_("%s.%s not valid "
610 raise error.ConfigError(_("%s.%s not valid "
611 "('%s' is none of %s)")
611 "('%s' is none of %s)")
612 % (section, name, config, validstr))
612 % (section, name, config, validstr))
613 return config
613 return config
614
614
615 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
615 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
616 if f2 is None:
616 if f2 is None:
617 f2 = f
617 f2 = f
618 return (repo.wvfs.audit.check(f)
618 return (repo.wvfs.audit.check(f)
619 and repo.wvfs.isfileorlink(f)
619 and repo.wvfs.isfileorlink(f)
620 and repo.dirstate.normalize(f) not in repo.dirstate
620 and repo.dirstate.normalize(f) not in repo.dirstate
621 and mctx[f2].cmp(wctx[f]))
621 and mctx[f2].cmp(wctx[f]))
622
622
623 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
623 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
624 """
624 """
625 Considers any actions that care about the presence of conflicting unknown
625 Considers any actions that care about the presence of conflicting unknown
626 files. For some actions, the result is to abort; for others, it is to
626 files. For some actions, the result is to abort; for others, it is to
627 choose a different action.
627 choose a different action.
628 """
628 """
629 conflicts = set()
629 conflicts = set()
630 warnconflicts = set()
630 warnconflicts = set()
631 abortconflicts = set()
631 abortconflicts = set()
632 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
632 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
633 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
633 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
634 if not force:
634 if not force:
635 def collectconflicts(conflicts, config):
635 def collectconflicts(conflicts, config):
636 if config == 'abort':
636 if config == 'abort':
637 abortconflicts.update(conflicts)
637 abortconflicts.update(conflicts)
638 elif config == 'warn':
638 elif config == 'warn':
639 warnconflicts.update(conflicts)
639 warnconflicts.update(conflicts)
640
640
641 for f, (m, args, msg) in actions.iteritems():
641 for f, (m, args, msg) in actions.iteritems():
642 if m in ('c', 'dc'):
642 if m in ('c', 'dc'):
643 if _checkunknownfile(repo, wctx, mctx, f):
643 if _checkunknownfile(repo, wctx, mctx, f):
644 conflicts.add(f)
644 conflicts.add(f)
645 elif m == 'dg':
645 elif m == 'dg':
646 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
646 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
647 conflicts.add(f)
647 conflicts.add(f)
648
648
649 ignoredconflicts = set([c for c in conflicts
649 ignoredconflicts = set([c for c in conflicts
650 if repo.dirstate._ignore(c)])
650 if repo.dirstate._ignore(c)])
651 unknownconflicts = conflicts - ignoredconflicts
651 unknownconflicts = conflicts - ignoredconflicts
652 collectconflicts(ignoredconflicts, ignoredconfig)
652 collectconflicts(ignoredconflicts, ignoredconfig)
653 collectconflicts(unknownconflicts, unknownconfig)
653 collectconflicts(unknownconflicts, unknownconfig)
654 else:
654 else:
655 for f, (m, args, msg) in actions.iteritems():
655 for f, (m, args, msg) in actions.iteritems():
656 if m == 'cm':
656 if m == 'cm':
657 fl2, anc = args
657 fl2, anc = args
658 different = _checkunknownfile(repo, wctx, mctx, f)
658 different = _checkunknownfile(repo, wctx, mctx, f)
659 if repo.dirstate._ignore(f):
659 if repo.dirstate._ignore(f):
660 config = ignoredconfig
660 config = ignoredconfig
661 else:
661 else:
662 config = unknownconfig
662 config = unknownconfig
663
663
664 # The behavior when force is True is described by this table:
664 # The behavior when force is True is described by this table:
665 # config different mergeforce | action backup
665 # config different mergeforce | action backup
666 # * n * | get n
666 # * n * | get n
667 # * y y | merge -
667 # * y y | merge -
668 # abort y n | merge - (1)
668 # abort y n | merge - (1)
669 # warn y n | warn + get y
669 # warn y n | warn + get y
670 # ignore y n | get y
670 # ignore y n | get y
671 #
671 #
672 # (1) this is probably the wrong behavior here -- we should
672 # (1) this is probably the wrong behavior here -- we should
673 # probably abort, but some actions like rebases currently
673 # probably abort, but some actions like rebases currently
674 # don't like an abort happening in the middle of
674 # don't like an abort happening in the middle of
675 # merge.update.
675 # merge.update.
676 if not different:
676 if not different:
677 actions[f] = ('g', (fl2, False), "remote created")
677 actions[f] = ('g', (fl2, False), "remote created")
678 elif mergeforce or config == 'abort':
678 elif mergeforce or config == 'abort':
679 actions[f] = ('m', (f, f, None, False, anc),
679 actions[f] = ('m', (f, f, None, False, anc),
680 "remote differs from untracked local")
680 "remote differs from untracked local")
681 elif config == 'abort':
681 elif config == 'abort':
682 abortconflicts.add(f)
682 abortconflicts.add(f)
683 else:
683 else:
684 if config == 'warn':
684 if config == 'warn':
685 warnconflicts.add(f)
685 warnconflicts.add(f)
686 actions[f] = ('g', (fl2, True), "remote created")
686 actions[f] = ('g', (fl2, True), "remote created")
687
687
688 for f in sorted(abortconflicts):
688 for f in sorted(abortconflicts):
689 repo.ui.warn(_("%s: untracked file differs\n") % f)
689 repo.ui.warn(_("%s: untracked file differs\n") % f)
690 if abortconflicts:
690 if abortconflicts:
691 raise error.Abort(_("untracked files in working directory "
691 raise error.Abort(_("untracked files in working directory "
692 "differ from files in requested revision"))
692 "differ from files in requested revision"))
693
693
694 for f in sorted(warnconflicts):
694 for f in sorted(warnconflicts):
695 repo.ui.warn(_("%s: replacing untracked file\n") % f)
695 repo.ui.warn(_("%s: replacing untracked file\n") % f)
696
696
697 for f, (m, args, msg) in actions.iteritems():
697 for f, (m, args, msg) in actions.iteritems():
698 backup = f in conflicts
698 backup = f in conflicts
699 if m == 'c':
699 if m == 'c':
700 flags, = args
700 flags, = args
701 actions[f] = ('g', (flags, backup), msg)
701 actions[f] = ('g', (flags, backup), msg)
702
702
703 def _forgetremoved(wctx, mctx, branchmerge):
703 def _forgetremoved(wctx, mctx, branchmerge):
704 """
704 """
705 Forget removed files
705 Forget removed files
706
706
707 If we're jumping between revisions (as opposed to merging), and if
707 If we're jumping between revisions (as opposed to merging), and if
708 neither the working directory nor the target rev has the file,
708 neither the working directory nor the target rev has the file,
709 then we need to remove it from the dirstate, to prevent the
709 then we need to remove it from the dirstate, to prevent the
710 dirstate from listing the file when it is no longer in the
710 dirstate from listing the file when it is no longer in the
711 manifest.
711 manifest.
712
712
713 If we're merging, and the other revision has removed a file
713 If we're merging, and the other revision has removed a file
714 that is not present in the working directory, we need to mark it
714 that is not present in the working directory, we need to mark it
715 as removed.
715 as removed.
716 """
716 """
717
717
718 actions = {}
718 actions = {}
719 m = 'f'
719 m = 'f'
720 if branchmerge:
720 if branchmerge:
721 m = 'r'
721 m = 'r'
722 for f in wctx.deleted():
722 for f in wctx.deleted():
723 if f not in mctx:
723 if f not in mctx:
724 actions[f] = m, None, "forget deleted"
724 actions[f] = m, None, "forget deleted"
725
725
726 if not branchmerge:
726 if not branchmerge:
727 for f in wctx.removed():
727 for f in wctx.removed():
728 if f not in mctx:
728 if f not in mctx:
729 actions[f] = 'f', None, "forget removed"
729 actions[f] = 'f', None, "forget removed"
730
730
731 return actions
731 return actions
732
732
733 def _checkcollision(repo, wmf, actions):
733 def _checkcollision(repo, wmf, actions):
734 # build provisional merged manifest up
734 # build provisional merged manifest up
735 pmmf = set(wmf)
735 pmmf = set(wmf)
736
736
737 if actions:
737 if actions:
738 # k, dr, e and rd are no-op
738 # k, dr, e and rd are no-op
739 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
739 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
740 for f, args, msg in actions[m]:
740 for f, args, msg in actions[m]:
741 pmmf.add(f)
741 pmmf.add(f)
742 for f, args, msg in actions['r']:
742 for f, args, msg in actions['r']:
743 pmmf.discard(f)
743 pmmf.discard(f)
744 for f, args, msg in actions['dm']:
744 for f, args, msg in actions['dm']:
745 f2, flags = args
745 f2, flags = args
746 pmmf.discard(f2)
746 pmmf.discard(f2)
747 pmmf.add(f)
747 pmmf.add(f)
748 for f, args, msg in actions['dg']:
748 for f, args, msg in actions['dg']:
749 pmmf.add(f)
749 pmmf.add(f)
750 for f, args, msg in actions['m']:
750 for f, args, msg in actions['m']:
751 f1, f2, fa, move, anc = args
751 f1, f2, fa, move, anc = args
752 if move:
752 if move:
753 pmmf.discard(f1)
753 pmmf.discard(f1)
754 pmmf.add(f)
754 pmmf.add(f)
755
755
756 # check case-folding collision in provisional merged manifest
756 # check case-folding collision in provisional merged manifest
757 foldmap = {}
757 foldmap = {}
758 for f in pmmf:
758 for f in pmmf:
759 fold = util.normcase(f)
759 fold = util.normcase(f)
760 if fold in foldmap:
760 if fold in foldmap:
761 raise error.Abort(_("case-folding collision between %s and %s")
761 raise error.Abort(_("case-folding collision between %s and %s")
762 % (f, foldmap[fold]))
762 % (f, foldmap[fold]))
763 foldmap[fold] = f
763 foldmap[fold] = f
764
764
765 # check case-folding of directories
765 # check case-folding of directories
766 foldprefix = unfoldprefix = lastfull = ''
766 foldprefix = unfoldprefix = lastfull = ''
767 for fold, f in sorted(foldmap.items()):
767 for fold, f in sorted(foldmap.items()):
768 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
768 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
769 # the folded prefix matches but actual casing is different
769 # the folded prefix matches but actual casing is different
770 raise error.Abort(_("case-folding collision between "
770 raise error.Abort(_("case-folding collision between "
771 "%s and directory of %s") % (lastfull, f))
771 "%s and directory of %s") % (lastfull, f))
772 foldprefix = fold + '/'
772 foldprefix = fold + '/'
773 unfoldprefix = f + '/'
773 unfoldprefix = f + '/'
774 lastfull = f
774 lastfull = f
775
775
776 def driverpreprocess(repo, ms, wctx, labels=None):
776 def driverpreprocess(repo, ms, wctx, labels=None):
777 """run the preprocess step of the merge driver, if any
777 """run the preprocess step of the merge driver, if any
778
778
779 This is currently not implemented -- it's an extension point."""
779 This is currently not implemented -- it's an extension point."""
780 return True
780 return True
781
781
782 def driverconclude(repo, ms, wctx, labels=None):
782 def driverconclude(repo, ms, wctx, labels=None):
783 """run the conclude step of the merge driver, if any
783 """run the conclude step of the merge driver, if any
784
784
785 This is currently not implemented -- it's an extension point."""
785 This is currently not implemented -- it's an extension point."""
786 return True
786 return True
787
787
788 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
788 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
789 acceptremote, followcopies, forcefulldiff=False):
789 acceptremote, followcopies, forcefulldiff=False):
790 """
790 """
791 Merge wctx and p2 with ancestor pa and generate merge action list
791 Merge wctx and p2 with ancestor pa and generate merge action list
792
792
793 branchmerge and force are as passed in to update
793 branchmerge and force are as passed in to update
794 matcher = matcher to filter file lists
794 matcher = matcher to filter file lists
795 acceptremote = accept the incoming changes without prompting
795 acceptremote = accept the incoming changes without prompting
796 """
796 """
797 if matcher is not None and matcher.always():
797 if matcher is not None and matcher.always():
798 matcher = None
798 matcher = None
799
799
800 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
800 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
801
801
802 # manifests fetched in order are going to be faster, so prime the caches
802 # manifests fetched in order are going to be faster, so prime the caches
803 [x.manifest() for x in
803 [x.manifest() for x in
804 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
804 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
805
805
806 if followcopies:
806 if followcopies:
807 ret = copies.mergecopies(repo, wctx, p2, pa)
807 ret = copies.mergecopies(repo, wctx, p2, pa)
808 copy, movewithdir, diverge, renamedelete, dirmove = ret
808 copy, movewithdir, diverge, renamedelete, dirmove = ret
809
809
810 boolbm = pycompat.bytestr(bool(branchmerge))
810 boolbm = pycompat.bytestr(bool(branchmerge))
811 boolf = pycompat.bytestr(bool(force))
811 boolf = pycompat.bytestr(bool(force))
812 boolm = pycompat.bytestr(bool(matcher))
812 boolm = pycompat.bytestr(bool(matcher))
813 repo.ui.note(_("resolving manifests\n"))
813 repo.ui.note(_("resolving manifests\n"))
814 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
814 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
815 % (boolbm, boolf, boolm))
815 % (boolbm, boolf, boolm))
816 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
816 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
817
817
818 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
818 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
819 copied = set(copy.values())
819 copied = set(copy.values())
820 copied.update(movewithdir.values())
820 copied.update(movewithdir.values())
821
821
822 if '.hgsubstate' in m1:
822 if '.hgsubstate' in m1:
823 # check whether sub state is modified
823 # check whether sub state is modified
824 if any(wctx.sub(s).dirty() for s in wctx.substate):
824 if any(wctx.sub(s).dirty() for s in wctx.substate):
825 m1['.hgsubstate'] = modifiednodeid
825 m1['.hgsubstate'] = modifiednodeid
826
826
827 # Don't use m2-vs-ma optimization if:
827 # Don't use m2-vs-ma optimization if:
828 # - ma is the same as m1 or m2, which we're just going to diff again later
828 # - ma is the same as m1 or m2, which we're just going to diff again later
829 # - The caller specifically asks for a full diff, which is useful during bid
829 # - The caller specifically asks for a full diff, which is useful during bid
830 # merge.
830 # merge.
831 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
831 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
832 # Identify which files are relevant to the merge, so we can limit the
832 # Identify which files are relevant to the merge, so we can limit the
833 # total m1-vs-m2 diff to just those files. This has significant
833 # total m1-vs-m2 diff to just those files. This has significant
834 # performance benefits in large repositories.
834 # performance benefits in large repositories.
835 relevantfiles = set(ma.diff(m2).keys())
835 relevantfiles = set(ma.diff(m2).keys())
836
836
837 # For copied and moved files, we need to add the source file too.
837 # For copied and moved files, we need to add the source file too.
838 for copykey, copyvalue in copy.iteritems():
838 for copykey, copyvalue in copy.iteritems():
839 if copyvalue in relevantfiles:
839 if copyvalue in relevantfiles:
840 relevantfiles.add(copykey)
840 relevantfiles.add(copykey)
841 for movedirkey in movewithdir:
841 for movedirkey in movewithdir:
842 relevantfiles.add(movedirkey)
842 relevantfiles.add(movedirkey)
843 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
843 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
844 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
844 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
845
845
846 diff = m1.diff(m2, match=matcher)
846 diff = m1.diff(m2, match=matcher)
847
847
848 if matcher is None:
848 if matcher is None:
849 matcher = matchmod.always('', '')
849 matcher = matchmod.always('', '')
850
850
851 actions = {}
851 actions = {}
852 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
852 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
853 if n1 and n2: # file exists on both local and remote side
853 if n1 and n2: # file exists on both local and remote side
854 if f not in ma:
854 if f not in ma:
855 fa = copy.get(f, None)
855 fa = copy.get(f, None)
856 if fa is not None:
856 if fa is not None:
857 actions[f] = ('m', (f, f, fa, False, pa.node()),
857 actions[f] = ('m', (f, f, fa, False, pa.node()),
858 "both renamed from " + fa)
858 "both renamed from " + fa)
859 else:
859 else:
860 actions[f] = ('m', (f, f, None, False, pa.node()),
860 actions[f] = ('m', (f, f, None, False, pa.node()),
861 "both created")
861 "both created")
862 else:
862 else:
863 a = ma[f]
863 a = ma[f]
864 fla = ma.flags(f)
864 fla = ma.flags(f)
865 nol = 'l' not in fl1 + fl2 + fla
865 nol = 'l' not in fl1 + fl2 + fla
866 if n2 == a and fl2 == fla:
866 if n2 == a and fl2 == fla:
867 actions[f] = ('k' , (), "remote unchanged")
867 actions[f] = ('k' , (), "remote unchanged")
868 elif n1 == a and fl1 == fla: # local unchanged - use remote
868 elif n1 == a and fl1 == fla: # local unchanged - use remote
869 if n1 == n2: # optimization: keep local content
869 if n1 == n2: # optimization: keep local content
870 actions[f] = ('e', (fl2,), "update permissions")
870 actions[f] = ('e', (fl2,), "update permissions")
871 else:
871 else:
872 actions[f] = ('g', (fl2, False), "remote is newer")
872 actions[f] = ('g', (fl2, False), "remote is newer")
873 elif nol and n2 == a: # remote only changed 'x'
873 elif nol and n2 == a: # remote only changed 'x'
874 actions[f] = ('e', (fl2,), "update permissions")
874 actions[f] = ('e', (fl2,), "update permissions")
875 elif nol and n1 == a: # local only changed 'x'
875 elif nol and n1 == a: # local only changed 'x'
876 actions[f] = ('g', (fl1, False), "remote is newer")
876 actions[f] = ('g', (fl1, False), "remote is newer")
877 else: # both changed something
877 else: # both changed something
878 actions[f] = ('m', (f, f, f, False, pa.node()),
878 actions[f] = ('m', (f, f, f, False, pa.node()),
879 "versions differ")
879 "versions differ")
880 elif n1: # file exists only on local side
880 elif n1: # file exists only on local side
881 if f in copied:
881 if f in copied:
882 pass # we'll deal with it on m2 side
882 pass # we'll deal with it on m2 side
883 elif f in movewithdir: # directory rename, move local
883 elif f in movewithdir: # directory rename, move local
884 f2 = movewithdir[f]
884 f2 = movewithdir[f]
885 if f2 in m2:
885 if f2 in m2:
886 actions[f2] = ('m', (f, f2, None, True, pa.node()),
886 actions[f2] = ('m', (f, f2, None, True, pa.node()),
887 "remote directory rename, both created")
887 "remote directory rename, both created")
888 else:
888 else:
889 actions[f2] = ('dm', (f, fl1),
889 actions[f2] = ('dm', (f, fl1),
890 "remote directory rename - move from " + f)
890 "remote directory rename - move from " + f)
891 elif f in copy:
891 elif f in copy:
892 f2 = copy[f]
892 f2 = copy[f]
893 actions[f] = ('m', (f, f2, f2, False, pa.node()),
893 actions[f] = ('m', (f, f2, f2, False, pa.node()),
894 "local copied/moved from " + f2)
894 "local copied/moved from " + f2)
895 elif f in ma: # clean, a different, no remote
895 elif f in ma: # clean, a different, no remote
896 if n1 != ma[f]:
896 if n1 != ma[f]:
897 if acceptremote:
897 if acceptremote:
898 actions[f] = ('r', None, "remote delete")
898 actions[f] = ('r', None, "remote delete")
899 else:
899 else:
900 actions[f] = ('cd', (f, None, f, False, pa.node()),
900 actions[f] = ('cd', (f, None, f, False, pa.node()),
901 "prompt changed/deleted")
901 "prompt changed/deleted")
902 elif n1 == addednodeid:
902 elif n1 == addednodeid:
903 # This extra 'a' is added by working copy manifest to mark
903 # This extra 'a' is added by working copy manifest to mark
904 # the file as locally added. We should forget it instead of
904 # the file as locally added. We should forget it instead of
905 # deleting it.
905 # deleting it.
906 actions[f] = ('f', None, "remote deleted")
906 actions[f] = ('f', None, "remote deleted")
907 else:
907 else:
908 actions[f] = ('r', None, "other deleted")
908 actions[f] = ('r', None, "other deleted")
909 elif n2: # file exists only on remote side
909 elif n2: # file exists only on remote side
910 if f in copied:
910 if f in copied:
911 pass # we'll deal with it on m1 side
911 pass # we'll deal with it on m1 side
912 elif f in movewithdir:
912 elif f in movewithdir:
913 f2 = movewithdir[f]
913 f2 = movewithdir[f]
914 if f2 in m1:
914 if f2 in m1:
915 actions[f2] = ('m', (f2, f, None, False, pa.node()),
915 actions[f2] = ('m', (f2, f, None, False, pa.node()),
916 "local directory rename, both created")
916 "local directory rename, both created")
917 else:
917 else:
918 actions[f2] = ('dg', (f, fl2),
918 actions[f2] = ('dg', (f, fl2),
919 "local directory rename - get from " + f)
919 "local directory rename - get from " + f)
920 elif f in copy:
920 elif f in copy:
921 f2 = copy[f]
921 f2 = copy[f]
922 if f2 in m2:
922 if f2 in m2:
923 actions[f] = ('m', (f2, f, f2, False, pa.node()),
923 actions[f] = ('m', (f2, f, f2, False, pa.node()),
924 "remote copied from " + f2)
924 "remote copied from " + f2)
925 else:
925 else:
926 actions[f] = ('m', (f2, f, f2, True, pa.node()),
926 actions[f] = ('m', (f2, f, f2, True, pa.node()),
927 "remote moved from " + f2)
927 "remote moved from " + f2)
928 elif f not in ma:
928 elif f not in ma:
929 # local unknown, remote created: the logic is described by the
929 # local unknown, remote created: the logic is described by the
930 # following table:
930 # following table:
931 #
931 #
932 # force branchmerge different | action
932 # force branchmerge different | action
933 # n * * | create
933 # n * * | create
934 # y n * | create
934 # y n * | create
935 # y y n | create
935 # y y n | create
936 # y y y | merge
936 # y y y | merge
937 #
937 #
938 # Checking whether the files are different is expensive, so we
938 # Checking whether the files are different is expensive, so we
939 # don't do that when we can avoid it.
939 # don't do that when we can avoid it.
940 if not force:
940 if not force:
941 actions[f] = ('c', (fl2,), "remote created")
941 actions[f] = ('c', (fl2,), "remote created")
942 elif not branchmerge:
942 elif not branchmerge:
943 actions[f] = ('c', (fl2,), "remote created")
943 actions[f] = ('c', (fl2,), "remote created")
944 else:
944 else:
945 actions[f] = ('cm', (fl2, pa.node()),
945 actions[f] = ('cm', (fl2, pa.node()),
946 "remote created, get or merge")
946 "remote created, get or merge")
947 elif n2 != ma[f]:
947 elif n2 != ma[f]:
948 df = None
948 df = None
949 for d in dirmove:
949 for d in dirmove:
950 if f.startswith(d):
950 if f.startswith(d):
951 # new file added in a directory that was moved
951 # new file added in a directory that was moved
952 df = dirmove[d] + f[len(d):]
952 df = dirmove[d] + f[len(d):]
953 break
953 break
954 if df is not None and df in m1:
954 if df is not None and df in m1:
955 actions[df] = ('m', (df, f, f, False, pa.node()),
955 actions[df] = ('m', (df, f, f, False, pa.node()),
956 "local directory rename - respect move from " + f)
956 "local directory rename - respect move from " + f)
957 elif acceptremote:
957 elif acceptremote:
958 actions[f] = ('c', (fl2,), "remote recreating")
958 actions[f] = ('c', (fl2,), "remote recreating")
959 else:
959 else:
960 actions[f] = ('dc', (None, f, f, False, pa.node()),
960 actions[f] = ('dc', (None, f, f, False, pa.node()),
961 "prompt deleted/changed")
961 "prompt deleted/changed")
962
962
963 return actions, diverge, renamedelete
963 return actions, diverge, renamedelete
964
964
965 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
965 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
966 """Resolves false conflicts where the nodeid changed but the content
966 """Resolves false conflicts where the nodeid changed but the content
967 remained the same."""
967 remained the same."""
968
968
969 for f, (m, args, msg) in actions.items():
969 for f, (m, args, msg) in actions.items():
970 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
970 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
971 # local did change but ended up with same content
971 # local did change but ended up with same content
972 actions[f] = 'r', None, "prompt same"
972 actions[f] = 'r', None, "prompt same"
973 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
973 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
974 # remote did change but ended up with same content
974 # remote did change but ended up with same content
975 del actions[f] # don't get = keep local deleted
975 del actions[f] # don't get = keep local deleted
976
976
977 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
977 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
978 acceptremote, followcopies, matcher=None,
978 acceptremote, followcopies, matcher=None,
979 mergeforce=False):
979 mergeforce=False):
980 """Calculate the actions needed to merge mctx into wctx using ancestors"""
980 """Calculate the actions needed to merge mctx into wctx using ancestors"""
981 # Avoid cycle.
981 # Avoid cycle.
982 from . import sparse
982 from . import sparse
983
983
984 if len(ancestors) == 1: # default
984 if len(ancestors) == 1: # default
985 actions, diverge, renamedelete = manifestmerge(
985 actions, diverge, renamedelete = manifestmerge(
986 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
986 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
987 acceptremote, followcopies)
987 acceptremote, followcopies)
988 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
988 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
989
989
990 else: # only when merge.preferancestor=* - the default
990 else: # only when merge.preferancestor=* - the default
991 repo.ui.note(
991 repo.ui.note(
992 _("note: merging %s and %s using bids from ancestors %s\n") %
992 _("note: merging %s and %s using bids from ancestors %s\n") %
993 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
993 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
994
994
995 # Call for bids
995 # Call for bids
996 fbids = {} # mapping filename to bids (action method to list af actions)
996 fbids = {} # mapping filename to bids (action method to list af actions)
997 diverge, renamedelete = None, None
997 diverge, renamedelete = None, None
998 for ancestor in ancestors:
998 for ancestor in ancestors:
999 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
999 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1000 actions, diverge1, renamedelete1 = manifestmerge(
1000 actions, diverge1, renamedelete1 = manifestmerge(
1001 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1001 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1002 acceptremote, followcopies, forcefulldiff=True)
1002 acceptremote, followcopies, forcefulldiff=True)
1003 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1003 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1004
1004
1005 # Track the shortest set of warning on the theory that bid
1005 # Track the shortest set of warning on the theory that bid
1006 # merge will correctly incorporate more information
1006 # merge will correctly incorporate more information
1007 if diverge is None or len(diverge1) < len(diverge):
1007 if diverge is None or len(diverge1) < len(diverge):
1008 diverge = diverge1
1008 diverge = diverge1
1009 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1009 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1010 renamedelete = renamedelete1
1010 renamedelete = renamedelete1
1011
1011
1012 for f, a in sorted(actions.iteritems()):
1012 for f, a in sorted(actions.iteritems()):
1013 m, args, msg = a
1013 m, args, msg = a
1014 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1014 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1015 if f in fbids:
1015 if f in fbids:
1016 d = fbids[f]
1016 d = fbids[f]
1017 if m in d:
1017 if m in d:
1018 d[m].append(a)
1018 d[m].append(a)
1019 else:
1019 else:
1020 d[m] = [a]
1020 d[m] = [a]
1021 else:
1021 else:
1022 fbids[f] = {m: [a]}
1022 fbids[f] = {m: [a]}
1023
1023
1024 # Pick the best bid for each file
1024 # Pick the best bid for each file
1025 repo.ui.note(_('\nauction for merging merge bids\n'))
1025 repo.ui.note(_('\nauction for merging merge bids\n'))
1026 actions = {}
1026 actions = {}
1027 dms = [] # filenames that have dm actions
1027 dms = [] # filenames that have dm actions
1028 for f, bids in sorted(fbids.items()):
1028 for f, bids in sorted(fbids.items()):
1029 # bids is a mapping from action method to list af actions
1029 # bids is a mapping from action method to list af actions
1030 # Consensus?
1030 # Consensus?
1031 if len(bids) == 1: # all bids are the same kind of method
1031 if len(bids) == 1: # all bids are the same kind of method
1032 m, l = bids.items()[0]
1032 m, l = bids.items()[0]
1033 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1033 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1034 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1034 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1035 actions[f] = l[0]
1035 actions[f] = l[0]
1036 if m == 'dm':
1036 if m == 'dm':
1037 dms.append(f)
1037 dms.append(f)
1038 continue
1038 continue
1039 # If keep is an option, just do it.
1039 # If keep is an option, just do it.
1040 if 'k' in bids:
1040 if 'k' in bids:
1041 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1041 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1042 actions[f] = bids['k'][0]
1042 actions[f] = bids['k'][0]
1043 continue
1043 continue
1044 # If there are gets and they all agree [how could they not?], do it.
1044 # If there are gets and they all agree [how could they not?], do it.
1045 if 'g' in bids:
1045 if 'g' in bids:
1046 ga0 = bids['g'][0]
1046 ga0 = bids['g'][0]
1047 if all(a == ga0 for a in bids['g'][1:]):
1047 if all(a == ga0 for a in bids['g'][1:]):
1048 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1048 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1049 actions[f] = ga0
1049 actions[f] = ga0
1050 continue
1050 continue
1051 # TODO: Consider other simple actions such as mode changes
1051 # TODO: Consider other simple actions such as mode changes
1052 # Handle inefficient democrazy.
1052 # Handle inefficient democrazy.
1053 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1053 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1054 for m, l in sorted(bids.items()):
1054 for m, l in sorted(bids.items()):
1055 for _f, args, msg in l:
1055 for _f, args, msg in l:
1056 repo.ui.note(' %s -> %s\n' % (msg, m))
1056 repo.ui.note(' %s -> %s\n' % (msg, m))
1057 # Pick random action. TODO: Instead, prompt user when resolving
1057 # Pick random action. TODO: Instead, prompt user when resolving
1058 m, l = bids.items()[0]
1058 m, l = bids.items()[0]
1059 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1059 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1060 (f, m))
1060 (f, m))
1061 actions[f] = l[0]
1061 actions[f] = l[0]
1062 if m == 'dm':
1062 if m == 'dm':
1063 dms.append(f)
1063 dms.append(f)
1064 continue
1064 continue
1065 # Work around 'dm' that can cause multiple actions for the same file
1065 # Work around 'dm' that can cause multiple actions for the same file
1066 for f in dms:
1066 for f in dms:
1067 dm, (f0, flags), msg = actions[f]
1067 dm, (f0, flags), msg = actions[f]
1068 assert dm == 'dm', dm
1068 assert dm == 'dm', dm
1069 if f0 in actions and actions[f0][0] == 'r':
1069 if f0 in actions and actions[f0][0] == 'r':
1070 # We have one bid for removing a file and another for moving it.
1070 # We have one bid for removing a file and another for moving it.
1071 # These two could be merged as first move and then delete ...
1071 # These two could be merged as first move and then delete ...
1072 # but instead drop moving and just delete.
1072 # but instead drop moving and just delete.
1073 del actions[f]
1073 del actions[f]
1074 repo.ui.note(_('end of auction\n\n'))
1074 repo.ui.note(_('end of auction\n\n'))
1075
1075
1076 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1076 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1077
1077
1078 if wctx.rev() is None:
1078 if wctx.rev() is None:
1079 fractions = _forgetremoved(wctx, mctx, branchmerge)
1079 fractions = _forgetremoved(wctx, mctx, branchmerge)
1080 actions.update(fractions)
1080 actions.update(fractions)
1081
1081
1082 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1082 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1083 actions)
1083 actions)
1084
1084
1085 return prunedactions, diverge, renamedelete
1085 return prunedactions, diverge, renamedelete
1086
1086
1087 def batchremove(repo, wctx, actions):
1087 def batchremove(repo, wctx, actions):
1088 """apply removes to the working directory
1088 """apply removes to the working directory
1089
1089
1090 yields tuples for progress updates
1090 yields tuples for progress updates
1091 """
1091 """
1092 verbose = repo.ui.verbose
1092 verbose = repo.ui.verbose
1093 try:
1093 try:
1094 cwd = pycompat.getcwd()
1094 cwd = pycompat.getcwd()
1095 except OSError as err:
1095 except OSError as err:
1096 if err.errno != errno.ENOENT:
1096 if err.errno != errno.ENOENT:
1097 raise
1097 raise
1098 cwd = None
1098 cwd = None
1099 i = 0
1099 i = 0
1100 for f, args, msg in actions:
1100 for f, args, msg in actions:
1101 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1101 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1102 if verbose:
1102 if verbose:
1103 repo.ui.note(_("removing %s\n") % f)
1103 repo.ui.note(_("removing %s\n") % f)
1104 wctx[f].audit()
1104 wctx[f].audit()
1105 try:
1105 try:
1106 wctx[f].remove(ignoremissing=True)
1106 wctx[f].remove(ignoremissing=True)
1107 except OSError as inst:
1107 except OSError as inst:
1108 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1108 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1109 (f, inst.strerror))
1109 (f, inst.strerror))
1110 if i == 100:
1110 if i == 100:
1111 yield i, f
1111 yield i, f
1112 i = 0
1112 i = 0
1113 i += 1
1113 i += 1
1114 if i > 0:
1114 if i > 0:
1115 yield i, f
1115 yield i, f
1116 if cwd:
1116 if cwd:
1117 # cwd was present before we started to remove files
1117 # cwd was present before we started to remove files
1118 # let's check if it is present after we removed them
1118 # let's check if it is present after we removed them
1119 try:
1119 try:
1120 pycompat.getcwd()
1120 pycompat.getcwd()
1121 except OSError as err:
1121 except OSError as err:
1122 if err.errno != errno.ENOENT:
1122 if err.errno != errno.ENOENT:
1123 raise
1123 raise
1124 # Print a warning if cwd was deleted
1124 # Print a warning if cwd was deleted
1125 repo.ui.warn(_("current directory was removed\n"
1125 repo.ui.warn(_("current directory was removed\n"
1126 "(consider changing to repo root: %s)\n") %
1126 "(consider changing to repo root: %s)\n") %
1127 repo.root)
1127 repo.root)
1128
1128
1129 # It's necessary to flush here in case we're inside a worker fork and will
1129 # It's necessary to flush here in case we're inside a worker fork and will
1130 # quit after this function.
1130 # quit after this function.
1131 wctx.flushall()
1131 wctx.flushall()
1132
1132
1133 def batchget(repo, mctx, wctx, actions):
1133 def batchget(repo, mctx, wctx, actions):
1134 """apply gets to the working directory
1134 """apply gets to the working directory
1135
1135
1136 mctx is the context to get from
1136 mctx is the context to get from
1137
1137
1138 yields tuples for progress updates
1138 yields tuples for progress updates
1139 """
1139 """
1140 verbose = repo.ui.verbose
1140 verbose = repo.ui.verbose
1141 fctx = mctx.filectx
1141 fctx = mctx.filectx
1142 ui = repo.ui
1142 ui = repo.ui
1143 i = 0
1143 i = 0
1144 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1144 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1145 for f, (flags, backup), msg in actions:
1145 for f, (flags, backup), msg in actions:
1146 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1146 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1147 if verbose:
1147 if verbose:
1148 repo.ui.note(_("getting %s\n") % f)
1148 repo.ui.note(_("getting %s\n") % f)
1149
1149
1150 if backup:
1150 if backup:
1151 absf = repo.wjoin(f)
1151 absf = repo.wjoin(f)
1152 orig = scmutil.origpath(ui, repo, absf)
1152 orig = scmutil.origpath(ui, repo, absf)
1153 try:
1153 try:
1154 if repo.wvfs.isfileorlink(f):
1154 if repo.wvfs.isfileorlink(f):
1155 util.rename(absf, orig)
1155 util.rename(absf, orig)
1156 except OSError as e:
1156 except OSError as e:
1157 if e.errno != errno.ENOENT:
1157 if e.errno != errno.ENOENT:
1158 raise
1158 raise
1159 wctx[f].clearunknown()
1159 wctx[f].clearunknown()
1160 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1160 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1161 if i == 100:
1161 if i == 100:
1162 yield i, f
1162 yield i, f
1163 i = 0
1163 i = 0
1164 i += 1
1164 i += 1
1165 if i > 0:
1165 if i > 0:
1166 yield i, f
1166 yield i, f
1167
1167
1168 # It's necessary to flush here in case we're inside a worker fork and will
1168 # It's necessary to flush here in case we're inside a worker fork and will
1169 # quit after this function.
1169 # quit after this function.
1170 wctx.flushall()
1170 wctx.flushall()
1171
1171
1172 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1172 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1173 """apply the merge action list to the working directory
1173 """apply the merge action list to the working directory
1174
1174
1175 wctx is the working copy context
1175 wctx is the working copy context
1176 mctx is the context to be merged into the working copy
1176 mctx is the context to be merged into the working copy
1177
1177
1178 Return a tuple of counts (updated, merged, removed, unresolved) that
1178 Return a tuple of counts (updated, merged, removed, unresolved) that
1179 describes how many files were affected by the update.
1179 describes how many files were affected by the update.
1180 """
1180 """
1181
1181
1182 updated, merged, removed = 0, 0, 0
1182 updated, merged, removed = 0, 0, 0
1183 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1183 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1184 moves = []
1184 moves = []
1185 for m, l in actions.items():
1185 for m, l in actions.items():
1186 l.sort()
1186 l.sort()
1187
1187
1188 # 'cd' and 'dc' actions are treated like other merge conflicts
1188 # 'cd' and 'dc' actions are treated like other merge conflicts
1189 mergeactions = sorted(actions['cd'])
1189 mergeactions = sorted(actions['cd'])
1190 mergeactions.extend(sorted(actions['dc']))
1190 mergeactions.extend(sorted(actions['dc']))
1191 mergeactions.extend(actions['m'])
1191 mergeactions.extend(actions['m'])
1192 for f, args, msg in mergeactions:
1192 for f, args, msg in mergeactions:
1193 f1, f2, fa, move, anc = args
1193 f1, f2, fa, move, anc = args
1194 if f == '.hgsubstate': # merged internally
1194 if f == '.hgsubstate': # merged internally
1195 continue
1195 continue
1196 if f1 is None:
1196 if f1 is None:
1197 fcl = filemerge.absentfilectx(wctx, fa)
1197 fcl = filemerge.absentfilectx(wctx, fa)
1198 else:
1198 else:
1199 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1199 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1200 fcl = wctx[f1]
1200 fcl = wctx[f1]
1201 if f2 is None:
1201 if f2 is None:
1202 fco = filemerge.absentfilectx(mctx, fa)
1202 fco = filemerge.absentfilectx(mctx, fa)
1203 else:
1203 else:
1204 fco = mctx[f2]
1204 fco = mctx[f2]
1205 actx = repo[anc]
1205 actx = repo[anc]
1206 if fa in actx:
1206 if fa in actx:
1207 fca = actx[fa]
1207 fca = actx[fa]
1208 else:
1208 else:
1209 # TODO: move to absentfilectx
1209 # TODO: move to absentfilectx
1210 fca = repo.filectx(f1, fileid=nullrev)
1210 fca = repo.filectx(f1, fileid=nullrev)
1211 ms.add(fcl, fco, fca, f)
1211 ms.add(fcl, fco, fca, f)
1212 if f1 != f and move:
1212 if f1 != f and move:
1213 moves.append(f1)
1213 moves.append(f1)
1214
1214
1215 _updating = _('updating')
1215 _updating = _('updating')
1216 _files = _('files')
1216 _files = _('files')
1217 progress = repo.ui.progress
1217 progress = repo.ui.progress
1218
1218
1219 # remove renamed files after safely stored
1219 # remove renamed files after safely stored
1220 for f in moves:
1220 for f in moves:
1221 if wctx[f].lexists():
1221 if wctx[f].lexists():
1222 repo.ui.debug("removing %s\n" % f)
1222 repo.ui.debug("removing %s\n" % f)
1223 wctx[f].audit()
1223 wctx[f].audit()
1224 wctx[f].remove()
1224 wctx[f].remove()
1225
1225
1226 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1226 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1227
1227
1228 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1228 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1229 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1229 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1230
1230
1231 # remove in parallel (must come first)
1231 # remove in parallel (must come first)
1232 z = 0
1232 z = 0
1233 prog = worker.worker(repo.ui, 0.001, batchremove, (repo, wctx),
1233 prog = worker.worker(repo.ui, 0.001, batchremove, (repo, wctx),
1234 actions['r'])
1234 actions['r'])
1235 for i, item in prog:
1235 for i, item in prog:
1236 z += i
1236 z += i
1237 progress(_updating, z, item=item, total=numupdates, unit=_files)
1237 progress(_updating, z, item=item, total=numupdates, unit=_files)
1238 removed = len(actions['r'])
1238 removed = len(actions['r'])
1239
1239
1240 # We should flush before forking into worker processes, since those workers
1240 # We should flush before forking into worker processes, since those workers
1241 # flush when they complete, and we don't want to duplicate work.
1241 # flush when they complete, and we don't want to duplicate work.
1242 wctx.flushall()
1242 wctx.flushall()
1243
1243
1244 # get in parallel
1244 # get in parallel
1245 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
1245 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
1246 actions['g'])
1246 actions['g'])
1247 for i, item in prog:
1247 for i, item in prog:
1248 z += i
1248 z += i
1249 progress(_updating, z, item=item, total=numupdates, unit=_files)
1249 progress(_updating, z, item=item, total=numupdates, unit=_files)
1250 updated = len(actions['g'])
1250 updated = len(actions['g'])
1251
1251
1252 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1252 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1253 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1253 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1254
1254
1255 # forget (manifest only, just log it) (must come first)
1255 # forget (manifest only, just log it) (must come first)
1256 for f, args, msg in actions['f']:
1256 for f, args, msg in actions['f']:
1257 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1257 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1258 z += 1
1258 z += 1
1259 progress(_updating, z, item=f, total=numupdates, unit=_files)
1259 progress(_updating, z, item=f, total=numupdates, unit=_files)
1260
1260
1261 # re-add (manifest only, just log it)
1261 # re-add (manifest only, just log it)
1262 for f, args, msg in actions['a']:
1262 for f, args, msg in actions['a']:
1263 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1263 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1264 z += 1
1264 z += 1
1265 progress(_updating, z, item=f, total=numupdates, unit=_files)
1265 progress(_updating, z, item=f, total=numupdates, unit=_files)
1266
1266
1267 # re-add/mark as modified (manifest only, just log it)
1267 # re-add/mark as modified (manifest only, just log it)
1268 for f, args, msg in actions['am']:
1268 for f, args, msg in actions['am']:
1269 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1269 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1270 z += 1
1270 z += 1
1271 progress(_updating, z, item=f, total=numupdates, unit=_files)
1271 progress(_updating, z, item=f, total=numupdates, unit=_files)
1272
1272
1273 # keep (noop, just log it)
1273 # keep (noop, just log it)
1274 for f, args, msg in actions['k']:
1274 for f, args, msg in actions['k']:
1275 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1275 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1276 # no progress
1276 # no progress
1277
1277
1278 # directory rename, move local
1278 # directory rename, move local
1279 for f, args, msg in actions['dm']:
1279 for f, args, msg in actions['dm']:
1280 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1280 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1281 z += 1
1281 z += 1
1282 progress(_updating, z, item=f, total=numupdates, unit=_files)
1282 progress(_updating, z, item=f, total=numupdates, unit=_files)
1283 f0, flags = args
1283 f0, flags = args
1284 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1284 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1285 wctx[f].audit()
1285 wctx[f].audit()
1286 wctx[f].write(wctx.filectx(f0).data(), flags)
1286 wctx[f].write(wctx.filectx(f0).data(), flags)
1287 wctx[f0].remove()
1287 wctx[f0].remove()
1288 updated += 1
1288 updated += 1
1289
1289
1290 # local directory rename, get
1290 # local directory rename, get
1291 for f, args, msg in actions['dg']:
1291 for f, args, msg in actions['dg']:
1292 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1292 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1293 z += 1
1293 z += 1
1294 progress(_updating, z, item=f, total=numupdates, unit=_files)
1294 progress(_updating, z, item=f, total=numupdates, unit=_files)
1295 f0, flags = args
1295 f0, flags = args
1296 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1296 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1297 wctx[f].write(mctx.filectx(f0).data(), flags)
1297 wctx[f].write(mctx.filectx(f0).data(), flags)
1298 updated += 1
1298 updated += 1
1299
1299
1300 # exec
1300 # exec
1301 for f, args, msg in actions['e']:
1301 for f, args, msg in actions['e']:
1302 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1302 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1303 z += 1
1303 z += 1
1304 progress(_updating, z, item=f, total=numupdates, unit=_files)
1304 progress(_updating, z, item=f, total=numupdates, unit=_files)
1305 flags, = args
1305 flags, = args
1306 wctx[f].audit()
1306 wctx[f].audit()
1307 wctx[f].setflags('l' in flags, 'x' in flags)
1307 wctx[f].setflags('l' in flags, 'x' in flags)
1308 updated += 1
1308 updated += 1
1309
1309
1310 # the ordering is important here -- ms.mergedriver will raise if the merge
1310 # the ordering is important here -- ms.mergedriver will raise if the merge
1311 # driver has changed, and we want to be able to bypass it when overwrite is
1311 # driver has changed, and we want to be able to bypass it when overwrite is
1312 # True
1312 # True
1313 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1313 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1314
1314
1315 if usemergedriver:
1315 if usemergedriver:
1316 ms.commit()
1316 ms.commit()
1317 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1317 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1318 # the driver might leave some files unresolved
1318 # the driver might leave some files unresolved
1319 unresolvedf = set(ms.unresolved())
1319 unresolvedf = set(ms.unresolved())
1320 if not proceed:
1320 if not proceed:
1321 # XXX setting unresolved to at least 1 is a hack to make sure we
1321 # XXX setting unresolved to at least 1 is a hack to make sure we
1322 # error out
1322 # error out
1323 return updated, merged, removed, max(len(unresolvedf), 1)
1323 return updated, merged, removed, max(len(unresolvedf), 1)
1324 newactions = []
1324 newactions = []
1325 for f, args, msg in mergeactions:
1325 for f, args, msg in mergeactions:
1326 if f in unresolvedf:
1326 if f in unresolvedf:
1327 newactions.append((f, args, msg))
1327 newactions.append((f, args, msg))
1328 mergeactions = newactions
1328 mergeactions = newactions
1329
1329
1330 # premerge
1330 # premerge
1331 tocomplete = []
1331 tocomplete = []
1332 for f, args, msg in mergeactions:
1332 for f, args, msg in mergeactions:
1333 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1333 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1334 z += 1
1334 z += 1
1335 progress(_updating, z, item=f, total=numupdates, unit=_files)
1335 progress(_updating, z, item=f, total=numupdates, unit=_files)
1336 if f == '.hgsubstate': # subrepo states need updating
1336 if f == '.hgsubstate': # subrepo states need updating
1337 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1337 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1338 overwrite, labels)
1338 overwrite, labels)
1339 continue
1339 continue
1340 wctx[f].audit()
1340 wctx[f].audit()
1341 complete, r = ms.preresolve(f, wctx)
1341 complete, r = ms.preresolve(f, wctx)
1342 if not complete:
1342 if not complete:
1343 numupdates += 1
1343 numupdates += 1
1344 tocomplete.append((f, args, msg))
1344 tocomplete.append((f, args, msg))
1345
1345
1346 # merge
1346 # merge
1347 for f, args, msg in tocomplete:
1347 for f, args, msg in tocomplete:
1348 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1348 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1349 z += 1
1349 z += 1
1350 progress(_updating, z, item=f, total=numupdates, unit=_files)
1350 progress(_updating, z, item=f, total=numupdates, unit=_files)
1351 ms.resolve(f, wctx)
1351 ms.resolve(f, wctx)
1352
1352
1353 ms.commit()
1353 ms.commit()
1354
1354
1355 unresolved = ms.unresolvedcount()
1355 unresolved = ms.unresolvedcount()
1356
1356
1357 if usemergedriver and not unresolved and ms.mdstate() != 's':
1357 if usemergedriver and not unresolved and ms.mdstate() != 's':
1358 if not driverconclude(repo, ms, wctx, labels=labels):
1358 if not driverconclude(repo, ms, wctx, labels=labels):
1359 # XXX setting unresolved to at least 1 is a hack to make sure we
1359 # XXX setting unresolved to at least 1 is a hack to make sure we
1360 # error out
1360 # error out
1361 unresolved = max(unresolved, 1)
1361 unresolved = max(unresolved, 1)
1362
1362
1363 ms.commit()
1363 ms.commit()
1364
1364
1365 msupdated, msmerged, msremoved = ms.counts()
1365 msupdated, msmerged, msremoved = ms.counts()
1366 updated += msupdated
1366 updated += msupdated
1367 merged += msmerged
1367 merged += msmerged
1368 removed += msremoved
1368 removed += msremoved
1369
1369
1370 extraactions = ms.actions()
1370 extraactions = ms.actions()
1371 if extraactions:
1371 if extraactions:
1372 mfiles = set(a[0] for a in actions['m'])
1372 mfiles = set(a[0] for a in actions['m'])
1373 for k, acts in extraactions.iteritems():
1373 for k, acts in extraactions.iteritems():
1374 actions[k].extend(acts)
1374 actions[k].extend(acts)
1375 # Remove these files from actions['m'] as well. This is important
1375 # Remove these files from actions['m'] as well. This is important
1376 # because in recordupdates, files in actions['m'] are processed
1376 # because in recordupdates, files in actions['m'] are processed
1377 # after files in other actions, and the merge driver might add
1377 # after files in other actions, and the merge driver might add
1378 # files to those actions via extraactions above. This can lead to a
1378 # files to those actions via extraactions above. This can lead to a
1379 # file being recorded twice, with poor results. This is especially
1379 # file being recorded twice, with poor results. This is especially
1380 # problematic for actions['r'] (currently only possible with the
1380 # problematic for actions['r'] (currently only possible with the
1381 # merge driver in the initial merge process; interrupted merges
1381 # merge driver in the initial merge process; interrupted merges
1382 # don't go through this flow).
1382 # don't go through this flow).
1383 #
1383 #
1384 # The real fix here is to have indexes by both file and action so
1384 # The real fix here is to have indexes by both file and action so
1385 # that when the action for a file is changed it is automatically
1385 # that when the action for a file is changed it is automatically
1386 # reflected in the other action lists. But that involves a more
1386 # reflected in the other action lists. But that involves a more
1387 # complex data structure, so this will do for now.
1387 # complex data structure, so this will do for now.
1388 #
1388 #
1389 # We don't need to do the same operation for 'dc' and 'cd' because
1389 # We don't need to do the same operation for 'dc' and 'cd' because
1390 # those lists aren't consulted again.
1390 # those lists aren't consulted again.
1391 mfiles.difference_update(a[0] for a in acts)
1391 mfiles.difference_update(a[0] for a in acts)
1392
1392
1393 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1393 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1394
1394
1395 progress(_updating, None, total=numupdates, unit=_files)
1395 progress(_updating, None, total=numupdates, unit=_files)
1396
1396
1397 return updated, merged, removed, unresolved
1397 return updated, merged, removed, unresolved
1398
1398
1399 def recordupdates(repo, actions, branchmerge):
1399 def recordupdates(repo, actions, branchmerge):
1400 "record merge actions to the dirstate"
1400 "record merge actions to the dirstate"
1401 # remove (must come first)
1401 # remove (must come first)
1402 for f, args, msg in actions.get('r', []):
1402 for f, args, msg in actions.get('r', []):
1403 if branchmerge:
1403 if branchmerge:
1404 repo.dirstate.remove(f)
1404 repo.dirstate.remove(f)
1405 else:
1405 else:
1406 repo.dirstate.drop(f)
1406 repo.dirstate.drop(f)
1407
1407
1408 # forget (must come first)
1408 # forget (must come first)
1409 for f, args, msg in actions.get('f', []):
1409 for f, args, msg in actions.get('f', []):
1410 repo.dirstate.drop(f)
1410 repo.dirstate.drop(f)
1411
1411
1412 # re-add
1412 # re-add
1413 for f, args, msg in actions.get('a', []):
1413 for f, args, msg in actions.get('a', []):
1414 repo.dirstate.add(f)
1414 repo.dirstate.add(f)
1415
1415
1416 # re-add/mark as modified
1416 # re-add/mark as modified
1417 for f, args, msg in actions.get('am', []):
1417 for f, args, msg in actions.get('am', []):
1418 if branchmerge:
1418 if branchmerge:
1419 repo.dirstate.normallookup(f)
1419 repo.dirstate.normallookup(f)
1420 else:
1420 else:
1421 repo.dirstate.add(f)
1421 repo.dirstate.add(f)
1422
1422
1423 # exec change
1423 # exec change
1424 for f, args, msg in actions.get('e', []):
1424 for f, args, msg in actions.get('e', []):
1425 repo.dirstate.normallookup(f)
1425 repo.dirstate.normallookup(f)
1426
1426
1427 # keep
1427 # keep
1428 for f, args, msg in actions.get('k', []):
1428 for f, args, msg in actions.get('k', []):
1429 pass
1429 pass
1430
1430
1431 # get
1431 # get
1432 for f, args, msg in actions.get('g', []):
1432 for f, args, msg in actions.get('g', []):
1433 if branchmerge:
1433 if branchmerge:
1434 repo.dirstate.otherparent(f)
1434 repo.dirstate.otherparent(f)
1435 else:
1435 else:
1436 repo.dirstate.normal(f)
1436 repo.dirstate.normal(f)
1437
1437
1438 # merge
1438 # merge
1439 for f, args, msg in actions.get('m', []):
1439 for f, args, msg in actions.get('m', []):
1440 f1, f2, fa, move, anc = args
1440 f1, f2, fa, move, anc = args
1441 if branchmerge:
1441 if branchmerge:
1442 # We've done a branch merge, mark this file as merged
1442 # We've done a branch merge, mark this file as merged
1443 # so that we properly record the merger later
1443 # so that we properly record the merger later
1444 repo.dirstate.merge(f)
1444 repo.dirstate.merge(f)
1445 if f1 != f2: # copy/rename
1445 if f1 != f2: # copy/rename
1446 if move:
1446 if move:
1447 repo.dirstate.remove(f1)
1447 repo.dirstate.remove(f1)
1448 if f1 != f:
1448 if f1 != f:
1449 repo.dirstate.copy(f1, f)
1449 repo.dirstate.copy(f1, f)
1450 else:
1450 else:
1451 repo.dirstate.copy(f2, f)
1451 repo.dirstate.copy(f2, f)
1452 else:
1452 else:
1453 # We've update-merged a locally modified file, so
1453 # We've update-merged a locally modified file, so
1454 # we set the dirstate to emulate a normal checkout
1454 # we set the dirstate to emulate a normal checkout
1455 # of that file some time in the past. Thus our
1455 # of that file some time in the past. Thus our
1456 # merge will appear as a normal local file
1456 # merge will appear as a normal local file
1457 # modification.
1457 # modification.
1458 if f2 == f: # file not locally copied/moved
1458 if f2 == f: # file not locally copied/moved
1459 repo.dirstate.normallookup(f)
1459 repo.dirstate.normallookup(f)
1460 if move:
1460 if move:
1461 repo.dirstate.drop(f1)
1461 repo.dirstate.drop(f1)
1462
1462
1463 # directory rename, move local
1463 # directory rename, move local
1464 for f, args, msg in actions.get('dm', []):
1464 for f, args, msg in actions.get('dm', []):
1465 f0, flag = args
1465 f0, flag = args
1466 if branchmerge:
1466 if branchmerge:
1467 repo.dirstate.add(f)
1467 repo.dirstate.add(f)
1468 repo.dirstate.remove(f0)
1468 repo.dirstate.remove(f0)
1469 repo.dirstate.copy(f0, f)
1469 repo.dirstate.copy(f0, f)
1470 else:
1470 else:
1471 repo.dirstate.normal(f)
1471 repo.dirstate.normal(f)
1472 repo.dirstate.drop(f0)
1472 repo.dirstate.drop(f0)
1473
1473
1474 # directory rename, get
1474 # directory rename, get
1475 for f, args, msg in actions.get('dg', []):
1475 for f, args, msg in actions.get('dg', []):
1476 f0, flag = args
1476 f0, flag = args
1477 if branchmerge:
1477 if branchmerge:
1478 repo.dirstate.add(f)
1478 repo.dirstate.add(f)
1479 repo.dirstate.copy(f0, f)
1479 repo.dirstate.copy(f0, f)
1480 else:
1480 else:
1481 repo.dirstate.normal(f)
1481 repo.dirstate.normal(f)
1482
1482
1483 def update(repo, node, branchmerge, force, ancestor=None,
1483 def update(repo, node, branchmerge, force, ancestor=None,
1484 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1484 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1485 updatecheck=None):
1485 updatecheck=None):
1486 """
1486 """
1487 Perform a merge between the working directory and the given node
1487 Perform a merge between the working directory and the given node
1488
1488
1489 node = the node to update to
1489 node = the node to update to
1490 branchmerge = whether to merge between branches
1490 branchmerge = whether to merge between branches
1491 force = whether to force branch merging or file overwriting
1491 force = whether to force branch merging or file overwriting
1492 matcher = a matcher to filter file lists (dirstate not updated)
1492 matcher = a matcher to filter file lists (dirstate not updated)
1493 mergeancestor = whether it is merging with an ancestor. If true,
1493 mergeancestor = whether it is merging with an ancestor. If true,
1494 we should accept the incoming changes for any prompts that occur.
1494 we should accept the incoming changes for any prompts that occur.
1495 If false, merging with an ancestor (fast-forward) is only allowed
1495 If false, merging with an ancestor (fast-forward) is only allowed
1496 between different named branches. This flag is used by rebase extension
1496 between different named branches. This flag is used by rebase extension
1497 as a temporary fix and should be avoided in general.
1497 as a temporary fix and should be avoided in general.
1498 labels = labels to use for base, local and other
1498 labels = labels to use for base, local and other
1499 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1499 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1500 this is True, then 'force' should be True as well.
1500 this is True, then 'force' should be True as well.
1501
1501
1502 The table below shows all the behaviors of the update command
1502 The table below shows all the behaviors of the update command
1503 given the -c and -C or no options, whether the working directory
1503 given the -c and -C or no options, whether the working directory
1504 is dirty, whether a revision is specified, and the relationship of
1504 is dirty, whether a revision is specified, and the relationship of
1505 the parent rev to the target rev (linear or not). Match from top first. The
1505 the parent rev to the target rev (linear or not). Match from top first. The
1506 -n option doesn't exist on the command line, but represents the
1506 -n option doesn't exist on the command line, but represents the
1507 experimental.updatecheck=noconflict option.
1507 experimental.updatecheck=noconflict option.
1508
1508
1509 This logic is tested by test-update-branches.t.
1509 This logic is tested by test-update-branches.t.
1510
1510
1511 -c -C -n -m dirty rev linear | result
1511 -c -C -n -m dirty rev linear | result
1512 y y * * * * * | (1)
1512 y y * * * * * | (1)
1513 y * y * * * * | (1)
1513 y * y * * * * | (1)
1514 y * * y * * * | (1)
1514 y * * y * * * | (1)
1515 * y y * * * * | (1)
1515 * y y * * * * | (1)
1516 * y * y * * * | (1)
1516 * y * y * * * | (1)
1517 * * y y * * * | (1)
1517 * * y y * * * | (1)
1518 * * * * * n n | x
1518 * * * * * n n | x
1519 * * * * n * * | ok
1519 * * * * n * * | ok
1520 n n n n y * y | merge
1520 n n n n y * y | merge
1521 n n n n y y n | (2)
1521 n n n n y y n | (2)
1522 n n n y y * * | merge
1522 n n n y y * * | merge
1523 n n y n y * * | merge if no conflict
1523 n n y n y * * | merge if no conflict
1524 n y n n y * * | discard
1524 n y n n y * * | discard
1525 y n n n y * * | (3)
1525 y n n n y * * | (3)
1526
1526
1527 x = can't happen
1527 x = can't happen
1528 * = don't-care
1528 * = don't-care
1529 1 = incompatible options (checked in commands.py)
1529 1 = incompatible options (checked in commands.py)
1530 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1530 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1531 3 = abort: uncommitted changes (checked in commands.py)
1531 3 = abort: uncommitted changes (checked in commands.py)
1532
1532
1533 Return the same tuple as applyupdates().
1533 Return the same tuple as applyupdates().
1534 """
1534 """
1535 # Avoid cycle.
1535 # Avoid cycle.
1536 from . import sparse
1536 from . import sparse
1537
1537
1538 # This function used to find the default destination if node was None, but
1538 # This function used to find the default destination if node was None, but
1539 # that's now in destutil.py.
1539 # that's now in destutil.py.
1540 assert node is not None
1540 assert node is not None
1541 if not branchmerge and not force:
1541 if not branchmerge and not force:
1542 # TODO: remove the default once all callers that pass branchmerge=False
1542 # TODO: remove the default once all callers that pass branchmerge=False
1543 # and force=False pass a value for updatecheck. We may want to allow
1543 # and force=False pass a value for updatecheck. We may want to allow
1544 # updatecheck='abort' to better suppport some of these callers.
1544 # updatecheck='abort' to better suppport some of these callers.
1545 if updatecheck is None:
1545 if updatecheck is None:
1546 updatecheck = 'linear'
1546 updatecheck = 'linear'
1547 assert updatecheck in ('none', 'linear', 'noconflict')
1547 assert updatecheck in ('none', 'linear', 'noconflict')
1548 # If we're doing a partial update, we need to skip updating
1548 # If we're doing a partial update, we need to skip updating
1549 # the dirstate, so make a note of any partial-ness to the
1549 # the dirstate, so make a note of any partial-ness to the
1550 # update here.
1550 # update here.
1551 if matcher is None or matcher.always():
1551 if matcher is None or matcher.always():
1552 partial = False
1552 partial = False
1553 else:
1553 else:
1554 partial = True
1554 partial = True
1555 with repo.wlock():
1555 with repo.wlock():
1556 wc = repo[None]
1556 wc = repo[None]
1557 pl = wc.parents()
1557 pl = wc.parents()
1558 p1 = pl[0]
1558 p1 = pl[0]
1559 pas = [None]
1559 pas = [None]
1560 if ancestor is not None:
1560 if ancestor is not None:
1561 pas = [repo[ancestor]]
1561 pas = [repo[ancestor]]
1562
1562
1563 overwrite = force and not branchmerge
1563 overwrite = force and not branchmerge
1564
1564
1565 p2 = repo[node]
1565 p2 = repo[node]
1566 if pas[0] is None:
1566 if pas[0] is None:
1567 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1567 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1568 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1568 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1569 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1569 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1570 else:
1570 else:
1571 pas = [p1.ancestor(p2, warn=branchmerge)]
1571 pas = [p1.ancestor(p2, warn=branchmerge)]
1572
1572
1573 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1573 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1574
1574
1575 ### check phase
1575 ### check phase
1576 if not overwrite:
1576 if not overwrite:
1577 if len(pl) > 1:
1577 if len(pl) > 1:
1578 raise error.Abort(_("outstanding uncommitted merge"))
1578 raise error.Abort(_("outstanding uncommitted merge"))
1579 ms = mergestate.read(repo)
1579 ms = mergestate.read(repo)
1580 if list(ms.unresolved()):
1580 if list(ms.unresolved()):
1581 raise error.Abort(_("outstanding merge conflicts"))
1581 raise error.Abort(_("outstanding merge conflicts"))
1582 if branchmerge:
1582 if branchmerge:
1583 if pas == [p2]:
1583 if pas == [p2]:
1584 raise error.Abort(_("merging with a working directory ancestor"
1584 raise error.Abort(_("merging with a working directory ancestor"
1585 " has no effect"))
1585 " has no effect"))
1586 elif pas == [p1]:
1586 elif pas == [p1]:
1587 if not mergeancestor and wc.branch() == p2.branch():
1587 if not mergeancestor and wc.branch() == p2.branch():
1588 raise error.Abort(_("nothing to merge"),
1588 raise error.Abort(_("nothing to merge"),
1589 hint=_("use 'hg update' "
1589 hint=_("use 'hg update' "
1590 "or check 'hg heads'"))
1590 "or check 'hg heads'"))
1591 if not force and (wc.files() or wc.deleted()):
1591 if not force and (wc.files() or wc.deleted()):
1592 raise error.Abort(_("uncommitted changes"),
1592 raise error.Abort(_("uncommitted changes"),
1593 hint=_("use 'hg status' to list changes"))
1593 hint=_("use 'hg status' to list changes"))
1594 for s in sorted(wc.substate):
1594 for s in sorted(wc.substate):
1595 wc.sub(s).bailifchanged()
1595 wc.sub(s).bailifchanged()
1596
1596
1597 elif not overwrite:
1597 elif not overwrite:
1598 if p1 == p2: # no-op update
1598 if p1 == p2: # no-op update
1599 # call the hooks and exit early
1599 # call the hooks and exit early
1600 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1600 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1601 repo.hook('update', parent1=xp2, parent2='', error=0)
1601 repo.hook('update', parent1=xp2, parent2='', error=0)
1602 return 0, 0, 0, 0
1602 return 0, 0, 0, 0
1603
1603
1604 if (updatecheck == 'linear' and
1604 if (updatecheck == 'linear' and
1605 pas not in ([p1], [p2])): # nonlinear
1605 pas not in ([p1], [p2])): # nonlinear
1606 dirty = wc.dirty(missing=True)
1606 dirty = wc.dirty(missing=True)
1607 if dirty:
1607 if dirty:
1608 # Branching is a bit strange to ensure we do the minimal
1608 # Branching is a bit strange to ensure we do the minimal
1609 # amount of call to obsutil.foreground.
1609 # amount of call to obsutil.foreground.
1610 foreground = obsutil.foreground(repo, [p1.node()])
1610 foreground = obsutil.foreground(repo, [p1.node()])
1611 # note: the <node> variable contains a random identifier
1611 # note: the <node> variable contains a random identifier
1612 if repo[node].node() in foreground:
1612 if repo[node].node() in foreground:
1613 pass # allow updating to successors
1613 pass # allow updating to successors
1614 else:
1614 else:
1615 msg = _("uncommitted changes")
1615 msg = _("uncommitted changes")
1616 hint = _("commit or update --clean to discard changes")
1616 hint = _("commit or update --clean to discard changes")
1617 raise error.UpdateAbort(msg, hint=hint)
1617 raise error.UpdateAbort(msg, hint=hint)
1618 else:
1618 else:
1619 # Allow jumping branches if clean and specific rev given
1619 # Allow jumping branches if clean and specific rev given
1620 pass
1620 pass
1621
1621
1622 if overwrite:
1622 if overwrite:
1623 pas = [wc]
1623 pas = [wc]
1624 elif not branchmerge:
1624 elif not branchmerge:
1625 pas = [p1]
1625 pas = [p1]
1626
1626
1627 # deprecated config: merge.followcopies
1627 # deprecated config: merge.followcopies
1628 followcopies = repo.ui.configbool('merge', 'followcopies')
1628 followcopies = repo.ui.configbool('merge', 'followcopies')
1629 if overwrite:
1629 if overwrite:
1630 followcopies = False
1630 followcopies = False
1631 elif not pas[0]:
1631 elif not pas[0]:
1632 followcopies = False
1632 followcopies = False
1633 if not branchmerge and not wc.dirty(missing=True):
1633 if not branchmerge and not wc.dirty(missing=True):
1634 followcopies = False
1634 followcopies = False
1635
1635
1636 ### calculate phase
1636 ### calculate phase
1637 actionbyfile, diverge, renamedelete = calculateupdates(
1637 actionbyfile, diverge, renamedelete = calculateupdates(
1638 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1638 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1639 followcopies, matcher=matcher, mergeforce=mergeforce)
1639 followcopies, matcher=matcher, mergeforce=mergeforce)
1640
1640
1641 if updatecheck == 'noconflict':
1641 if updatecheck == 'noconflict':
1642 for f, (m, args, msg) in actionbyfile.iteritems():
1642 for f, (m, args, msg) in actionbyfile.iteritems():
1643 if m not in ('g', 'k', 'e', 'r'):
1643 if m not in ('g', 'k', 'e', 'r'):
1644 msg = _("conflicting changes")
1644 msg = _("conflicting changes")
1645 hint = _("commit or update --clean to discard changes")
1645 hint = _("commit or update --clean to discard changes")
1646 raise error.Abort(msg, hint=hint)
1646 raise error.Abort(msg, hint=hint)
1647
1647
1648 # Prompt and create actions. Most of this is in the resolve phase
1648 # Prompt and create actions. Most of this is in the resolve phase
1649 # already, but we can't handle .hgsubstate in filemerge or
1649 # already, but we can't handle .hgsubstate in filemerge or
1650 # subrepo.submerge yet so we have to keep prompting for it.
1650 # subrepo.submerge yet so we have to keep prompting for it.
1651 if '.hgsubstate' in actionbyfile:
1651 if '.hgsubstate' in actionbyfile:
1652 f = '.hgsubstate'
1652 f = '.hgsubstate'
1653 m, args, msg = actionbyfile[f]
1653 m, args, msg = actionbyfile[f]
1654 prompts = filemerge.partextras(labels)
1654 prompts = filemerge.partextras(labels)
1655 prompts['f'] = f
1655 prompts['f'] = f
1656 if m == 'cd':
1656 if m == 'cd':
1657 if repo.ui.promptchoice(
1657 if repo.ui.promptchoice(
1658 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1658 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1659 "use (c)hanged version or (d)elete?"
1659 "use (c)hanged version or (d)elete?"
1660 "$$ &Changed $$ &Delete") % prompts, 0):
1660 "$$ &Changed $$ &Delete") % prompts, 0):
1661 actionbyfile[f] = ('r', None, "prompt delete")
1661 actionbyfile[f] = ('r', None, "prompt delete")
1662 elif f in p1:
1662 elif f in p1:
1663 actionbyfile[f] = ('am', None, "prompt keep")
1663 actionbyfile[f] = ('am', None, "prompt keep")
1664 else:
1664 else:
1665 actionbyfile[f] = ('a', None, "prompt keep")
1665 actionbyfile[f] = ('a', None, "prompt keep")
1666 elif m == 'dc':
1666 elif m == 'dc':
1667 f1, f2, fa, move, anc = args
1667 f1, f2, fa, move, anc = args
1668 flags = p2[f2].flags()
1668 flags = p2[f2].flags()
1669 if repo.ui.promptchoice(
1669 if repo.ui.promptchoice(
1670 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1670 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1671 "use (c)hanged version or leave (d)eleted?"
1671 "use (c)hanged version or leave (d)eleted?"
1672 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1672 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1673 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1673 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1674 else:
1674 else:
1675 del actionbyfile[f]
1675 del actionbyfile[f]
1676
1676
1677 # Convert to dictionary-of-lists format
1677 # Convert to dictionary-of-lists format
1678 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1678 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1679 for f, (m, args, msg) in actionbyfile.iteritems():
1679 for f, (m, args, msg) in actionbyfile.iteritems():
1680 if m not in actions:
1680 if m not in actions:
1681 actions[m] = []
1681 actions[m] = []
1682 actions[m].append((f, args, msg))
1682 actions[m].append((f, args, msg))
1683
1683
1684 if not util.fscasesensitive(repo.path):
1684 if not util.fscasesensitive(repo.path):
1685 # check collision between files only in p2 for clean update
1685 # check collision between files only in p2 for clean update
1686 if (not branchmerge and
1686 if (not branchmerge and
1687 (force or not wc.dirty(missing=True, branch=False))):
1687 (force or not wc.dirty(missing=True, branch=False))):
1688 _checkcollision(repo, p2.manifest(), None)
1688 _checkcollision(repo, p2.manifest(), None)
1689 else:
1689 else:
1690 _checkcollision(repo, wc.manifest(), actions)
1690 _checkcollision(repo, wc.manifest(), actions)
1691
1691
1692 # divergent renames
1692 # divergent renames
1693 for f, fl in sorted(diverge.iteritems()):
1693 for f, fl in sorted(diverge.iteritems()):
1694 repo.ui.warn(_("note: possible conflict - %s was renamed "
1694 repo.ui.warn(_("note: possible conflict - %s was renamed "
1695 "multiple times to:\n") % f)
1695 "multiple times to:\n") % f)
1696 for nf in fl:
1696 for nf in fl:
1697 repo.ui.warn(" %s\n" % nf)
1697 repo.ui.warn(" %s\n" % nf)
1698
1698
1699 # rename and delete
1699 # rename and delete
1700 for f, fl in sorted(renamedelete.iteritems()):
1700 for f, fl in sorted(renamedelete.iteritems()):
1701 repo.ui.warn(_("note: possible conflict - %s was deleted "
1701 repo.ui.warn(_("note: possible conflict - %s was deleted "
1702 "and renamed to:\n") % f)
1702 "and renamed to:\n") % f)
1703 for nf in fl:
1703 for nf in fl:
1704 repo.ui.warn(" %s\n" % nf)
1704 repo.ui.warn(" %s\n" % nf)
1705
1705
1706 ### apply phase
1706 ### apply phase
1707 if not branchmerge: # just jump to the new rev
1707 if not branchmerge: # just jump to the new rev
1708 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1708 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1709 if not partial:
1709 if not partial:
1710 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1710 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1711 # note that we're in the middle of an update
1711 # note that we're in the middle of an update
1712 repo.vfs.write('updatestate', p2.hex())
1712 repo.vfs.write('updatestate', p2.hex())
1713
1713
1714 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1714 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1715 wc.flushall()
1715
1716
1716 if not partial:
1717 if not partial:
1717 with repo.dirstate.parentchange():
1718 with repo.dirstate.parentchange():
1718 repo.setparents(fp1, fp2)
1719 repo.setparents(fp1, fp2)
1719 recordupdates(repo, actions, branchmerge)
1720 recordupdates(repo, actions, branchmerge)
1720 # update completed, clear state
1721 # update completed, clear state
1721 util.unlink(repo.vfs.join('updatestate'))
1722 util.unlink(repo.vfs.join('updatestate'))
1722
1723
1723 if not branchmerge:
1724 if not branchmerge:
1724 repo.dirstate.setbranch(p2.branch())
1725 repo.dirstate.setbranch(p2.branch())
1725
1726
1726 # If we're updating to a location, clean up any stale temporary includes
1727 # If we're updating to a location, clean up any stale temporary includes
1727 # (ex: this happens during hg rebase --abort).
1728 # (ex: this happens during hg rebase --abort).
1728 if not branchmerge:
1729 if not branchmerge:
1729 sparse.prunetemporaryincludes(repo)
1730 sparse.prunetemporaryincludes(repo)
1730
1731
1731 if not partial:
1732 if not partial:
1732 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1733 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1733 return stats
1734 return stats
1734
1735
1735 def graft(repo, ctx, pctx, labels, keepparent=False):
1736 def graft(repo, ctx, pctx, labels, keepparent=False):
1736 """Do a graft-like merge.
1737 """Do a graft-like merge.
1737
1738
1738 This is a merge where the merge ancestor is chosen such that one
1739 This is a merge where the merge ancestor is chosen such that one
1739 or more changesets are grafted onto the current changeset. In
1740 or more changesets are grafted onto the current changeset. In
1740 addition to the merge, this fixes up the dirstate to include only
1741 addition to the merge, this fixes up the dirstate to include only
1741 a single parent (if keepparent is False) and tries to duplicate any
1742 a single parent (if keepparent is False) and tries to duplicate any
1742 renames/copies appropriately.
1743 renames/copies appropriately.
1743
1744
1744 ctx - changeset to rebase
1745 ctx - changeset to rebase
1745 pctx - merge base, usually ctx.p1()
1746 pctx - merge base, usually ctx.p1()
1746 labels - merge labels eg ['local', 'graft']
1747 labels - merge labels eg ['local', 'graft']
1747 keepparent - keep second parent if any
1748 keepparent - keep second parent if any
1748
1749
1749 """
1750 """
1750 # If we're grafting a descendant onto an ancestor, be sure to pass
1751 # If we're grafting a descendant onto an ancestor, be sure to pass
1751 # mergeancestor=True to update. This does two things: 1) allows the merge if
1752 # mergeancestor=True to update. This does two things: 1) allows the merge if
1752 # the destination is the same as the parent of the ctx (so we can use graft
1753 # the destination is the same as the parent of the ctx (so we can use graft
1753 # to copy commits), and 2) informs update that the incoming changes are
1754 # to copy commits), and 2) informs update that the incoming changes are
1754 # newer than the destination so it doesn't prompt about "remote changed foo
1755 # newer than the destination so it doesn't prompt about "remote changed foo
1755 # which local deleted".
1756 # which local deleted".
1756 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1757 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1757
1758
1758 stats = update(repo, ctx.node(), True, True, pctx.node(),
1759 stats = update(repo, ctx.node(), True, True, pctx.node(),
1759 mergeancestor=mergeancestor, labels=labels)
1760 mergeancestor=mergeancestor, labels=labels)
1760
1761
1761 pother = nullid
1762 pother = nullid
1762 parents = ctx.parents()
1763 parents = ctx.parents()
1763 if keepparent and len(parents) == 2 and pctx in parents:
1764 if keepparent and len(parents) == 2 and pctx in parents:
1764 parents.remove(pctx)
1765 parents.remove(pctx)
1765 pother = parents[0].node()
1766 pother = parents[0].node()
1766
1767
1767 with repo.dirstate.parentchange():
1768 with repo.dirstate.parentchange():
1768 repo.setparents(repo['.'].node(), pother)
1769 repo.setparents(repo['.'].node(), pother)
1769 repo.dirstate.write(repo.currenttransaction())
1770 repo.dirstate.write(repo.currenttransaction())
1770 # fix up dirstate for copies and renames
1771 # fix up dirstate for copies and renames
1771 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1772 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1772 return stats
1773 return stats
General Comments 0
You need to be logged in to leave comments. Login now