##// END OF EJS Templates
mergestate: make unresolved() use iteritems()...
Martin von Zweigbergk -
r33310:b4d517d7 default
parent child Browse files
Show More
@@ -1,1748 +1,1748 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.items():
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, self._local,
498 complete, r, deleted = filemerge.premerge(self._repo, self._local,
499 lfile, fcd, fco, fca,
499 lfile, fcd, fco, fca,
500 labels=self._labels)
500 labels=self._labels)
501 else:
501 else:
502 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
502 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
503 lfile, fcd, fco, fca,
503 lfile, fcd, fco, fca,
504 labels=self._labels)
504 labels=self._labels)
505 if r is None:
505 if r is None:
506 # no real conflict
506 # no real conflict
507 del self._state[dfile]
507 del self._state[dfile]
508 self._stateextras.pop(dfile, None)
508 self._stateextras.pop(dfile, None)
509 self._dirty = True
509 self._dirty = True
510 elif not r:
510 elif not r:
511 self.mark(dfile, 'r')
511 self.mark(dfile, 'r')
512
512
513 if complete:
513 if complete:
514 action = None
514 action = None
515 if deleted:
515 if deleted:
516 if fcd.isabsent():
516 if fcd.isabsent():
517 # dc: local picked. Need to drop if present, which may
517 # dc: local picked. Need to drop if present, which may
518 # happen on re-resolves.
518 # happen on re-resolves.
519 action = 'f'
519 action = 'f'
520 else:
520 else:
521 # cd: remote picked (or otherwise deleted)
521 # cd: remote picked (or otherwise deleted)
522 action = 'r'
522 action = 'r'
523 else:
523 else:
524 if fcd.isabsent(): # dc: remote picked
524 if fcd.isabsent(): # dc: remote picked
525 action = 'g'
525 action = 'g'
526 elif fco.isabsent(): # cd: local picked
526 elif fco.isabsent(): # cd: local picked
527 if dfile in self.localctx:
527 if dfile in self.localctx:
528 action = 'am'
528 action = 'am'
529 else:
529 else:
530 action = 'a'
530 action = 'a'
531 # else: regular merges (no action necessary)
531 # else: regular merges (no action necessary)
532 self._results[dfile] = r, action
532 self._results[dfile] = r, action
533
533
534 return complete, r
534 return complete, r
535
535
536 def _filectxorabsent(self, hexnode, ctx, f):
536 def _filectxorabsent(self, hexnode, ctx, f):
537 if hexnode == nullhex:
537 if hexnode == nullhex:
538 return filemerge.absentfilectx(ctx, f)
538 return filemerge.absentfilectx(ctx, f)
539 else:
539 else:
540 return ctx[f]
540 return ctx[f]
541
541
542 def preresolve(self, dfile, wctx):
542 def preresolve(self, dfile, wctx):
543 """run premerge process for dfile
543 """run premerge process for dfile
544
544
545 Returns whether the merge is complete, and the exit code."""
545 Returns whether the merge is complete, and the exit code."""
546 return self._resolve(True, dfile, wctx)
546 return self._resolve(True, dfile, wctx)
547
547
548 def resolve(self, dfile, wctx):
548 def resolve(self, dfile, wctx):
549 """run merge process (assuming premerge was run) for dfile
549 """run merge process (assuming premerge was run) for dfile
550
550
551 Returns the exit code of the merge."""
551 Returns the exit code of the merge."""
552 return self._resolve(False, dfile, wctx)[1]
552 return self._resolve(False, dfile, wctx)[1]
553
553
554 def counts(self):
554 def counts(self):
555 """return counts for updated, merged and removed files in this
555 """return counts for updated, merged and removed files in this
556 session"""
556 session"""
557 updated, merged, removed = 0, 0, 0
557 updated, merged, removed = 0, 0, 0
558 for r, action in self._results.itervalues():
558 for r, action in self._results.itervalues():
559 if r is None:
559 if r is None:
560 updated += 1
560 updated += 1
561 elif r == 0:
561 elif r == 0:
562 if action == 'r':
562 if action == 'r':
563 removed += 1
563 removed += 1
564 else:
564 else:
565 merged += 1
565 merged += 1
566 return updated, merged, removed
566 return updated, merged, removed
567
567
568 def unresolvedcount(self):
568 def unresolvedcount(self):
569 """get unresolved count for this merge (persistent)"""
569 """get unresolved count for this merge (persistent)"""
570 return len([True for f, entry in self._state.iteritems()
570 return len([True for f, entry in self._state.iteritems()
571 if entry[0] == 'u'])
571 if entry[0] == 'u'])
572
572
573 def actions(self):
573 def actions(self):
574 """return lists of actions to perform on the dirstate"""
574 """return lists of actions to perform on the dirstate"""
575 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
575 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
576 for f, (r, action) in self._results.iteritems():
576 for f, (r, action) in self._results.iteritems():
577 if action is not None:
577 if action is not None:
578 actions[action].append((f, None, "merge result"))
578 actions[action].append((f, None, "merge result"))
579 return actions
579 return actions
580
580
581 def recordactions(self):
581 def recordactions(self):
582 """record remove/add/get actions in the dirstate"""
582 """record remove/add/get actions in the dirstate"""
583 branchmerge = self._repo.dirstate.p2() != nullid
583 branchmerge = self._repo.dirstate.p2() != nullid
584 recordupdates(self._repo, self.actions(), branchmerge)
584 recordupdates(self._repo, self.actions(), branchmerge)
585
585
586 def queueremove(self, f):
586 def queueremove(self, f):
587 """queues a file to be removed from the dirstate
587 """queues a file to be removed from the dirstate
588
588
589 Meant for use by custom merge drivers."""
589 Meant for use by custom merge drivers."""
590 self._results[f] = 0, 'r'
590 self._results[f] = 0, 'r'
591
591
592 def queueadd(self, f):
592 def queueadd(self, f):
593 """queues a file to be added to the dirstate
593 """queues a file to be added to the dirstate
594
594
595 Meant for use by custom merge drivers."""
595 Meant for use by custom merge drivers."""
596 self._results[f] = 0, 'a'
596 self._results[f] = 0, 'a'
597
597
598 def queueget(self, f):
598 def queueget(self, f):
599 """queues a file to be marked modified in the dirstate
599 """queues a file to be marked modified in the dirstate
600
600
601 Meant for use by custom merge drivers."""
601 Meant for use by custom merge drivers."""
602 self._results[f] = 0, 'g'
602 self._results[f] = 0, 'g'
603
603
604 def _getcheckunknownconfig(repo, section, name):
604 def _getcheckunknownconfig(repo, section, name):
605 config = repo.ui.config(section, name, default='abort')
605 config = repo.ui.config(section, name, default='abort')
606 valid = ['abort', 'ignore', 'warn']
606 valid = ['abort', 'ignore', 'warn']
607 if config not in valid:
607 if config not in valid:
608 validstr = ', '.join(["'" + v + "'" for v in valid])
608 validstr = ', '.join(["'" + v + "'" for v in valid])
609 raise error.ConfigError(_("%s.%s not valid "
609 raise error.ConfigError(_("%s.%s not valid "
610 "('%s' is none of %s)")
610 "('%s' is none of %s)")
611 % (section, name, config, validstr))
611 % (section, name, config, validstr))
612 return config
612 return config
613
613
614 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
614 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
615 if f2 is None:
615 if f2 is None:
616 f2 = f
616 f2 = f
617 return (repo.wvfs.audit.check(f)
617 return (repo.wvfs.audit.check(f)
618 and repo.wvfs.isfileorlink(f)
618 and repo.wvfs.isfileorlink(f)
619 and repo.dirstate.normalize(f) not in repo.dirstate
619 and repo.dirstate.normalize(f) not in repo.dirstate
620 and mctx[f2].cmp(wctx[f]))
620 and mctx[f2].cmp(wctx[f]))
621
621
622 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
622 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
623 """
623 """
624 Considers any actions that care about the presence of conflicting unknown
624 Considers any actions that care about the presence of conflicting unknown
625 files. For some actions, the result is to abort; for others, it is to
625 files. For some actions, the result is to abort; for others, it is to
626 choose a different action.
626 choose a different action.
627 """
627 """
628 conflicts = set()
628 conflicts = set()
629 warnconflicts = set()
629 warnconflicts = set()
630 abortconflicts = set()
630 abortconflicts = set()
631 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
631 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
632 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
632 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
633 if not force:
633 if not force:
634 def collectconflicts(conflicts, config):
634 def collectconflicts(conflicts, config):
635 if config == 'abort':
635 if config == 'abort':
636 abortconflicts.update(conflicts)
636 abortconflicts.update(conflicts)
637 elif config == 'warn':
637 elif config == 'warn':
638 warnconflicts.update(conflicts)
638 warnconflicts.update(conflicts)
639
639
640 for f, (m, args, msg) in actions.iteritems():
640 for f, (m, args, msg) in actions.iteritems():
641 if m in ('c', 'dc'):
641 if m in ('c', 'dc'):
642 if _checkunknownfile(repo, wctx, mctx, f):
642 if _checkunknownfile(repo, wctx, mctx, f):
643 conflicts.add(f)
643 conflicts.add(f)
644 elif m == 'dg':
644 elif m == 'dg':
645 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
645 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
646 conflicts.add(f)
646 conflicts.add(f)
647
647
648 ignoredconflicts = set([c for c in conflicts
648 ignoredconflicts = set([c for c in conflicts
649 if repo.dirstate._ignore(c)])
649 if repo.dirstate._ignore(c)])
650 unknownconflicts = conflicts - ignoredconflicts
650 unknownconflicts = conflicts - ignoredconflicts
651 collectconflicts(ignoredconflicts, ignoredconfig)
651 collectconflicts(ignoredconflicts, ignoredconfig)
652 collectconflicts(unknownconflicts, unknownconfig)
652 collectconflicts(unknownconflicts, unknownconfig)
653 else:
653 else:
654 for f, (m, args, msg) in actions.iteritems():
654 for f, (m, args, msg) in actions.iteritems():
655 if m == 'cm':
655 if m == 'cm':
656 fl2, anc = args
656 fl2, anc = args
657 different = _checkunknownfile(repo, wctx, mctx, f)
657 different = _checkunknownfile(repo, wctx, mctx, f)
658 if repo.dirstate._ignore(f):
658 if repo.dirstate._ignore(f):
659 config = ignoredconfig
659 config = ignoredconfig
660 else:
660 else:
661 config = unknownconfig
661 config = unknownconfig
662
662
663 # The behavior when force is True is described by this table:
663 # The behavior when force is True is described by this table:
664 # config different mergeforce | action backup
664 # config different mergeforce | action backup
665 # * n * | get n
665 # * n * | get n
666 # * y y | merge -
666 # * y y | merge -
667 # abort y n | merge - (1)
667 # abort y n | merge - (1)
668 # warn y n | warn + get y
668 # warn y n | warn + get y
669 # ignore y n | get y
669 # ignore y n | get y
670 #
670 #
671 # (1) this is probably the wrong behavior here -- we should
671 # (1) this is probably the wrong behavior here -- we should
672 # probably abort, but some actions like rebases currently
672 # probably abort, but some actions like rebases currently
673 # don't like an abort happening in the middle of
673 # don't like an abort happening in the middle of
674 # merge.update.
674 # merge.update.
675 if not different:
675 if not different:
676 actions[f] = ('g', (fl2, False), "remote created")
676 actions[f] = ('g', (fl2, False), "remote created")
677 elif mergeforce or config == 'abort':
677 elif mergeforce or config == 'abort':
678 actions[f] = ('m', (f, f, None, False, anc),
678 actions[f] = ('m', (f, f, None, False, anc),
679 "remote differs from untracked local")
679 "remote differs from untracked local")
680 elif config == 'abort':
680 elif config == 'abort':
681 abortconflicts.add(f)
681 abortconflicts.add(f)
682 else:
682 else:
683 if config == 'warn':
683 if config == 'warn':
684 warnconflicts.add(f)
684 warnconflicts.add(f)
685 actions[f] = ('g', (fl2, True), "remote created")
685 actions[f] = ('g', (fl2, True), "remote created")
686
686
687 for f in sorted(abortconflicts):
687 for f in sorted(abortconflicts):
688 repo.ui.warn(_("%s: untracked file differs\n") % f)
688 repo.ui.warn(_("%s: untracked file differs\n") % f)
689 if abortconflicts:
689 if abortconflicts:
690 raise error.Abort(_("untracked files in working directory "
690 raise error.Abort(_("untracked files in working directory "
691 "differ from files in requested revision"))
691 "differ from files in requested revision"))
692
692
693 for f in sorted(warnconflicts):
693 for f in sorted(warnconflicts):
694 repo.ui.warn(_("%s: replacing untracked file\n") % f)
694 repo.ui.warn(_("%s: replacing untracked file\n") % f)
695
695
696 for f, (m, args, msg) in actions.iteritems():
696 for f, (m, args, msg) in actions.iteritems():
697 backup = f in conflicts
697 backup = f in conflicts
698 if m == 'c':
698 if m == 'c':
699 flags, = args
699 flags, = args
700 actions[f] = ('g', (flags, backup), msg)
700 actions[f] = ('g', (flags, backup), msg)
701
701
702 def _forgetremoved(wctx, mctx, branchmerge):
702 def _forgetremoved(wctx, mctx, branchmerge):
703 """
703 """
704 Forget removed files
704 Forget removed files
705
705
706 If we're jumping between revisions (as opposed to merging), and if
706 If we're jumping between revisions (as opposed to merging), and if
707 neither the working directory nor the target rev has the file,
707 neither the working directory nor the target rev has the file,
708 then we need to remove it from the dirstate, to prevent the
708 then we need to remove it from the dirstate, to prevent the
709 dirstate from listing the file when it is no longer in the
709 dirstate from listing the file when it is no longer in the
710 manifest.
710 manifest.
711
711
712 If we're merging, and the other revision has removed a file
712 If we're merging, and the other revision has removed a file
713 that is not present in the working directory, we need to mark it
713 that is not present in the working directory, we need to mark it
714 as removed.
714 as removed.
715 """
715 """
716
716
717 actions = {}
717 actions = {}
718 m = 'f'
718 m = 'f'
719 if branchmerge:
719 if branchmerge:
720 m = 'r'
720 m = 'r'
721 for f in wctx.deleted():
721 for f in wctx.deleted():
722 if f not in mctx:
722 if f not in mctx:
723 actions[f] = m, None, "forget deleted"
723 actions[f] = m, None, "forget deleted"
724
724
725 if not branchmerge:
725 if not branchmerge:
726 for f in wctx.removed():
726 for f in wctx.removed():
727 if f not in mctx:
727 if f not in mctx:
728 actions[f] = 'f', None, "forget removed"
728 actions[f] = 'f', None, "forget removed"
729
729
730 return actions
730 return actions
731
731
732 def _checkcollision(repo, wmf, actions):
732 def _checkcollision(repo, wmf, actions):
733 # build provisional merged manifest up
733 # build provisional merged manifest up
734 pmmf = set(wmf)
734 pmmf = set(wmf)
735
735
736 if actions:
736 if actions:
737 # k, dr, e and rd are no-op
737 # k, dr, e and rd are no-op
738 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
738 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
739 for f, args, msg in actions[m]:
739 for f, args, msg in actions[m]:
740 pmmf.add(f)
740 pmmf.add(f)
741 for f, args, msg in actions['r']:
741 for f, args, msg in actions['r']:
742 pmmf.discard(f)
742 pmmf.discard(f)
743 for f, args, msg in actions['dm']:
743 for f, args, msg in actions['dm']:
744 f2, flags = args
744 f2, flags = args
745 pmmf.discard(f2)
745 pmmf.discard(f2)
746 pmmf.add(f)
746 pmmf.add(f)
747 for f, args, msg in actions['dg']:
747 for f, args, msg in actions['dg']:
748 pmmf.add(f)
748 pmmf.add(f)
749 for f, args, msg in actions['m']:
749 for f, args, msg in actions['m']:
750 f1, f2, fa, move, anc = args
750 f1, f2, fa, move, anc = args
751 if move:
751 if move:
752 pmmf.discard(f1)
752 pmmf.discard(f1)
753 pmmf.add(f)
753 pmmf.add(f)
754
754
755 # check case-folding collision in provisional merged manifest
755 # check case-folding collision in provisional merged manifest
756 foldmap = {}
756 foldmap = {}
757 for f in sorted(pmmf):
757 for f in sorted(pmmf):
758 fold = util.normcase(f)
758 fold = util.normcase(f)
759 if fold in foldmap:
759 if fold in foldmap:
760 raise error.Abort(_("case-folding collision between %s and %s")
760 raise error.Abort(_("case-folding collision between %s and %s")
761 % (f, foldmap[fold]))
761 % (f, foldmap[fold]))
762 foldmap[fold] = f
762 foldmap[fold] = f
763
763
764 # check case-folding of directories
764 # check case-folding of directories
765 foldprefix = unfoldprefix = lastfull = ''
765 foldprefix = unfoldprefix = lastfull = ''
766 for fold, f in sorted(foldmap.items()):
766 for fold, f in sorted(foldmap.items()):
767 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
767 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
768 # the folded prefix matches but actual casing is different
768 # the folded prefix matches but actual casing is different
769 raise error.Abort(_("case-folding collision between "
769 raise error.Abort(_("case-folding collision between "
770 "%s and directory of %s") % (lastfull, f))
770 "%s and directory of %s") % (lastfull, f))
771 foldprefix = fold + '/'
771 foldprefix = fold + '/'
772 unfoldprefix = f + '/'
772 unfoldprefix = f + '/'
773 lastfull = f
773 lastfull = f
774
774
775 def driverpreprocess(repo, ms, wctx, labels=None):
775 def driverpreprocess(repo, ms, wctx, labels=None):
776 """run the preprocess step of the merge driver, if any
776 """run the preprocess step of the merge driver, if any
777
777
778 This is currently not implemented -- it's an extension point."""
778 This is currently not implemented -- it's an extension point."""
779 return True
779 return True
780
780
781 def driverconclude(repo, ms, wctx, labels=None):
781 def driverconclude(repo, ms, wctx, labels=None):
782 """run the conclude step of the merge driver, if any
782 """run the conclude step of the merge driver, if any
783
783
784 This is currently not implemented -- it's an extension point."""
784 This is currently not implemented -- it's an extension point."""
785 return True
785 return True
786
786
787 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
787 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
788 acceptremote, followcopies, forcefulldiff=False):
788 acceptremote, followcopies, forcefulldiff=False):
789 """
789 """
790 Merge wctx and p2 with ancestor pa and generate merge action list
790 Merge wctx and p2 with ancestor pa and generate merge action list
791
791
792 branchmerge and force are as passed in to update
792 branchmerge and force are as passed in to update
793 matcher = matcher to filter file lists
793 matcher = matcher to filter file lists
794 acceptremote = accept the incoming changes without prompting
794 acceptremote = accept the incoming changes without prompting
795 """
795 """
796 if matcher is not None and matcher.always():
796 if matcher is not None and matcher.always():
797 matcher = None
797 matcher = None
798
798
799 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
799 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
800
800
801 # manifests fetched in order are going to be faster, so prime the caches
801 # manifests fetched in order are going to be faster, so prime the caches
802 [x.manifest() for x in
802 [x.manifest() for x in
803 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
803 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
804
804
805 if followcopies:
805 if followcopies:
806 ret = copies.mergecopies(repo, wctx, p2, pa)
806 ret = copies.mergecopies(repo, wctx, p2, pa)
807 copy, movewithdir, diverge, renamedelete, dirmove = ret
807 copy, movewithdir, diverge, renamedelete, dirmove = ret
808
808
809 boolbm = pycompat.bytestr(bool(branchmerge))
809 boolbm = pycompat.bytestr(bool(branchmerge))
810 boolf = pycompat.bytestr(bool(force))
810 boolf = pycompat.bytestr(bool(force))
811 boolm = pycompat.bytestr(bool(matcher))
811 boolm = pycompat.bytestr(bool(matcher))
812 repo.ui.note(_("resolving manifests\n"))
812 repo.ui.note(_("resolving manifests\n"))
813 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
813 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
814 % (boolbm, boolf, boolm))
814 % (boolbm, boolf, boolm))
815 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
815 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
816
816
817 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
817 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
818 copied = set(copy.values())
818 copied = set(copy.values())
819 copied.update(movewithdir.values())
819 copied.update(movewithdir.values())
820
820
821 if '.hgsubstate' in m1:
821 if '.hgsubstate' in m1:
822 # check whether sub state is modified
822 # check whether sub state is modified
823 if any(wctx.sub(s).dirty() for s in wctx.substate):
823 if any(wctx.sub(s).dirty() for s in wctx.substate):
824 m1['.hgsubstate'] = modifiednodeid
824 m1['.hgsubstate'] = modifiednodeid
825
825
826 # Don't use m2-vs-ma optimization if:
826 # Don't use m2-vs-ma optimization if:
827 # - ma is the same as m1 or m2, which we're just going to diff again later
827 # - ma is the same as m1 or m2, which we're just going to diff again later
828 # - The caller specifically asks for a full diff, which is useful during bid
828 # - The caller specifically asks for a full diff, which is useful during bid
829 # merge.
829 # merge.
830 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
830 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
831 # Identify which files are relevant to the merge, so we can limit the
831 # Identify which files are relevant to the merge, so we can limit the
832 # total m1-vs-m2 diff to just those files. This has significant
832 # total m1-vs-m2 diff to just those files. This has significant
833 # performance benefits in large repositories.
833 # performance benefits in large repositories.
834 relevantfiles = set(ma.diff(m2).keys())
834 relevantfiles = set(ma.diff(m2).keys())
835
835
836 # For copied and moved files, we need to add the source file too.
836 # For copied and moved files, we need to add the source file too.
837 for copykey, copyvalue in copy.iteritems():
837 for copykey, copyvalue in copy.iteritems():
838 if copyvalue in relevantfiles:
838 if copyvalue in relevantfiles:
839 relevantfiles.add(copykey)
839 relevantfiles.add(copykey)
840 for movedirkey in movewithdir:
840 for movedirkey in movewithdir:
841 relevantfiles.add(movedirkey)
841 relevantfiles.add(movedirkey)
842 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
842 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
843 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
843 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
844
844
845 diff = m1.diff(m2, match=matcher)
845 diff = m1.diff(m2, match=matcher)
846
846
847 if matcher is None:
847 if matcher is None:
848 matcher = matchmod.always('', '')
848 matcher = matchmod.always('', '')
849
849
850 actions = {}
850 actions = {}
851 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
851 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
852 if n1 and n2: # file exists on both local and remote side
852 if n1 and n2: # file exists on both local and remote side
853 if f not in ma:
853 if f not in ma:
854 fa = copy.get(f, None)
854 fa = copy.get(f, None)
855 if fa is not None:
855 if fa is not None:
856 actions[f] = ('m', (f, f, fa, False, pa.node()),
856 actions[f] = ('m', (f, f, fa, False, pa.node()),
857 "both renamed from " + fa)
857 "both renamed from " + fa)
858 else:
858 else:
859 actions[f] = ('m', (f, f, None, False, pa.node()),
859 actions[f] = ('m', (f, f, None, False, pa.node()),
860 "both created")
860 "both created")
861 else:
861 else:
862 a = ma[f]
862 a = ma[f]
863 fla = ma.flags(f)
863 fla = ma.flags(f)
864 nol = 'l' not in fl1 + fl2 + fla
864 nol = 'l' not in fl1 + fl2 + fla
865 if n2 == a and fl2 == fla:
865 if n2 == a and fl2 == fla:
866 actions[f] = ('k' , (), "remote unchanged")
866 actions[f] = ('k' , (), "remote unchanged")
867 elif n1 == a and fl1 == fla: # local unchanged - use remote
867 elif n1 == a and fl1 == fla: # local unchanged - use remote
868 if n1 == n2: # optimization: keep local content
868 if n1 == n2: # optimization: keep local content
869 actions[f] = ('e', (fl2,), "update permissions")
869 actions[f] = ('e', (fl2,), "update permissions")
870 else:
870 else:
871 actions[f] = ('g', (fl2, False), "remote is newer")
871 actions[f] = ('g', (fl2, False), "remote is newer")
872 elif nol and n2 == a: # remote only changed 'x'
872 elif nol and n2 == a: # remote only changed 'x'
873 actions[f] = ('e', (fl2,), "update permissions")
873 actions[f] = ('e', (fl2,), "update permissions")
874 elif nol and n1 == a: # local only changed 'x'
874 elif nol and n1 == a: # local only changed 'x'
875 actions[f] = ('g', (fl1, False), "remote is newer")
875 actions[f] = ('g', (fl1, False), "remote is newer")
876 else: # both changed something
876 else: # both changed something
877 actions[f] = ('m', (f, f, f, False, pa.node()),
877 actions[f] = ('m', (f, f, f, False, pa.node()),
878 "versions differ")
878 "versions differ")
879 elif n1: # file exists only on local side
879 elif n1: # file exists only on local side
880 if f in copied:
880 if f in copied:
881 pass # we'll deal with it on m2 side
881 pass # we'll deal with it on m2 side
882 elif f in movewithdir: # directory rename, move local
882 elif f in movewithdir: # directory rename, move local
883 f2 = movewithdir[f]
883 f2 = movewithdir[f]
884 if f2 in m2:
884 if f2 in m2:
885 actions[f2] = ('m', (f, f2, None, True, pa.node()),
885 actions[f2] = ('m', (f, f2, None, True, pa.node()),
886 "remote directory rename, both created")
886 "remote directory rename, both created")
887 else:
887 else:
888 actions[f2] = ('dm', (f, fl1),
888 actions[f2] = ('dm', (f, fl1),
889 "remote directory rename - move from " + f)
889 "remote directory rename - move from " + f)
890 elif f in copy:
890 elif f in copy:
891 f2 = copy[f]
891 f2 = copy[f]
892 actions[f] = ('m', (f, f2, f2, False, pa.node()),
892 actions[f] = ('m', (f, f2, f2, False, pa.node()),
893 "local copied/moved from " + f2)
893 "local copied/moved from " + f2)
894 elif f in ma: # clean, a different, no remote
894 elif f in ma: # clean, a different, no remote
895 if n1 != ma[f]:
895 if n1 != ma[f]:
896 if acceptremote:
896 if acceptremote:
897 actions[f] = ('r', None, "remote delete")
897 actions[f] = ('r', None, "remote delete")
898 else:
898 else:
899 actions[f] = ('cd', (f, None, f, False, pa.node()),
899 actions[f] = ('cd', (f, None, f, False, pa.node()),
900 "prompt changed/deleted")
900 "prompt changed/deleted")
901 elif n1 == addednodeid:
901 elif n1 == addednodeid:
902 # This extra 'a' is added by working copy manifest to mark
902 # This extra 'a' is added by working copy manifest to mark
903 # the file as locally added. We should forget it instead of
903 # the file as locally added. We should forget it instead of
904 # deleting it.
904 # deleting it.
905 actions[f] = ('f', None, "remote deleted")
905 actions[f] = ('f', None, "remote deleted")
906 else:
906 else:
907 actions[f] = ('r', None, "other deleted")
907 actions[f] = ('r', None, "other deleted")
908 elif n2: # file exists only on remote side
908 elif n2: # file exists only on remote side
909 if f in copied:
909 if f in copied:
910 pass # we'll deal with it on m1 side
910 pass # we'll deal with it on m1 side
911 elif f in movewithdir:
911 elif f in movewithdir:
912 f2 = movewithdir[f]
912 f2 = movewithdir[f]
913 if f2 in m1:
913 if f2 in m1:
914 actions[f2] = ('m', (f2, f, None, False, pa.node()),
914 actions[f2] = ('m', (f2, f, None, False, pa.node()),
915 "local directory rename, both created")
915 "local directory rename, both created")
916 else:
916 else:
917 actions[f2] = ('dg', (f, fl2),
917 actions[f2] = ('dg', (f, fl2),
918 "local directory rename - get from " + f)
918 "local directory rename - get from " + f)
919 elif f in copy:
919 elif f in copy:
920 f2 = copy[f]
920 f2 = copy[f]
921 if f2 in m2:
921 if f2 in m2:
922 actions[f] = ('m', (f2, f, f2, False, pa.node()),
922 actions[f] = ('m', (f2, f, f2, False, pa.node()),
923 "remote copied from " + f2)
923 "remote copied from " + f2)
924 else:
924 else:
925 actions[f] = ('m', (f2, f, f2, True, pa.node()),
925 actions[f] = ('m', (f2, f, f2, True, pa.node()),
926 "remote moved from " + f2)
926 "remote moved from " + f2)
927 elif f not in ma:
927 elif f not in ma:
928 # local unknown, remote created: the logic is described by the
928 # local unknown, remote created: the logic is described by the
929 # following table:
929 # following table:
930 #
930 #
931 # force branchmerge different | action
931 # force branchmerge different | action
932 # n * * | create
932 # n * * | create
933 # y n * | create
933 # y n * | create
934 # y y n | create
934 # y y n | create
935 # y y y | merge
935 # y y y | merge
936 #
936 #
937 # Checking whether the files are different is expensive, so we
937 # Checking whether the files are different is expensive, so we
938 # don't do that when we can avoid it.
938 # don't do that when we can avoid it.
939 if not force:
939 if not force:
940 actions[f] = ('c', (fl2,), "remote created")
940 actions[f] = ('c', (fl2,), "remote created")
941 elif not branchmerge:
941 elif not branchmerge:
942 actions[f] = ('c', (fl2,), "remote created")
942 actions[f] = ('c', (fl2,), "remote created")
943 else:
943 else:
944 actions[f] = ('cm', (fl2, pa.node()),
944 actions[f] = ('cm', (fl2, pa.node()),
945 "remote created, get or merge")
945 "remote created, get or merge")
946 elif n2 != ma[f]:
946 elif n2 != ma[f]:
947 df = None
947 df = None
948 for d in dirmove:
948 for d in dirmove:
949 if f.startswith(d):
949 if f.startswith(d):
950 # new file added in a directory that was moved
950 # new file added in a directory that was moved
951 df = dirmove[d] + f[len(d):]
951 df = dirmove[d] + f[len(d):]
952 break
952 break
953 if df is not None and df in m1:
953 if df is not None and df in m1:
954 actions[df] = ('m', (df, f, f, False, pa.node()),
954 actions[df] = ('m', (df, f, f, False, pa.node()),
955 "local directory rename - respect move from " + f)
955 "local directory rename - respect move from " + f)
956 elif acceptremote:
956 elif acceptremote:
957 actions[f] = ('c', (fl2,), "remote recreating")
957 actions[f] = ('c', (fl2,), "remote recreating")
958 else:
958 else:
959 actions[f] = ('dc', (None, f, f, False, pa.node()),
959 actions[f] = ('dc', (None, f, f, False, pa.node()),
960 "prompt deleted/changed")
960 "prompt deleted/changed")
961
961
962 return actions, diverge, renamedelete
962 return actions, diverge, renamedelete
963
963
964 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
964 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
965 """Resolves false conflicts where the nodeid changed but the content
965 """Resolves false conflicts where the nodeid changed but the content
966 remained the same."""
966 remained the same."""
967
967
968 for f, (m, args, msg) in actions.items():
968 for f, (m, args, msg) in actions.items():
969 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
969 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
970 # local did change but ended up with same content
970 # local did change but ended up with same content
971 actions[f] = 'r', None, "prompt same"
971 actions[f] = 'r', None, "prompt same"
972 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
972 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
973 # remote did change but ended up with same content
973 # remote did change but ended up with same content
974 del actions[f] # don't get = keep local deleted
974 del actions[f] # don't get = keep local deleted
975
975
976 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
976 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
977 acceptremote, followcopies, matcher=None,
977 acceptremote, followcopies, matcher=None,
978 mergeforce=False):
978 mergeforce=False):
979 "Calculate the actions needed to merge mctx into wctx using ancestors"
979 "Calculate the actions needed to merge mctx into wctx using ancestors"
980 if len(ancestors) == 1: # default
980 if len(ancestors) == 1: # default
981 actions, diverge, renamedelete = manifestmerge(
981 actions, diverge, renamedelete = manifestmerge(
982 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
982 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
983 acceptremote, followcopies)
983 acceptremote, followcopies)
984 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
984 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
985
985
986 else: # only when merge.preferancestor=* - the default
986 else: # only when merge.preferancestor=* - the default
987 repo.ui.note(
987 repo.ui.note(
988 _("note: merging %s and %s using bids from ancestors %s\n") %
988 _("note: merging %s and %s using bids from ancestors %s\n") %
989 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
989 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
990
990
991 # Call for bids
991 # Call for bids
992 fbids = {} # mapping filename to bids (action method to list af actions)
992 fbids = {} # mapping filename to bids (action method to list af actions)
993 diverge, renamedelete = None, None
993 diverge, renamedelete = None, None
994 for ancestor in ancestors:
994 for ancestor in ancestors:
995 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
995 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
996 actions, diverge1, renamedelete1 = manifestmerge(
996 actions, diverge1, renamedelete1 = manifestmerge(
997 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
997 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
998 acceptremote, followcopies, forcefulldiff=True)
998 acceptremote, followcopies, forcefulldiff=True)
999 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
999 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1000
1000
1001 # Track the shortest set of warning on the theory that bid
1001 # Track the shortest set of warning on the theory that bid
1002 # merge will correctly incorporate more information
1002 # merge will correctly incorporate more information
1003 if diverge is None or len(diverge1) < len(diverge):
1003 if diverge is None or len(diverge1) < len(diverge):
1004 diverge = diverge1
1004 diverge = diverge1
1005 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1005 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1006 renamedelete = renamedelete1
1006 renamedelete = renamedelete1
1007
1007
1008 for f, a in sorted(actions.iteritems()):
1008 for f, a in sorted(actions.iteritems()):
1009 m, args, msg = a
1009 m, args, msg = a
1010 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1010 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1011 if f in fbids:
1011 if f in fbids:
1012 d = fbids[f]
1012 d = fbids[f]
1013 if m in d:
1013 if m in d:
1014 d[m].append(a)
1014 d[m].append(a)
1015 else:
1015 else:
1016 d[m] = [a]
1016 d[m] = [a]
1017 else:
1017 else:
1018 fbids[f] = {m: [a]}
1018 fbids[f] = {m: [a]}
1019
1019
1020 # Pick the best bid for each file
1020 # Pick the best bid for each file
1021 repo.ui.note(_('\nauction for merging merge bids\n'))
1021 repo.ui.note(_('\nauction for merging merge bids\n'))
1022 actions = {}
1022 actions = {}
1023 dms = [] # filenames that have dm actions
1023 dms = [] # filenames that have dm actions
1024 for f, bids in sorted(fbids.items()):
1024 for f, bids in sorted(fbids.items()):
1025 # bids is a mapping from action method to list af actions
1025 # bids is a mapping from action method to list af actions
1026 # Consensus?
1026 # Consensus?
1027 if len(bids) == 1: # all bids are the same kind of method
1027 if len(bids) == 1: # all bids are the same kind of method
1028 m, l = bids.items()[0]
1028 m, l = bids.items()[0]
1029 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1029 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1030 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1030 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1031 actions[f] = l[0]
1031 actions[f] = l[0]
1032 if m == 'dm':
1032 if m == 'dm':
1033 dms.append(f)
1033 dms.append(f)
1034 continue
1034 continue
1035 # If keep is an option, just do it.
1035 # If keep is an option, just do it.
1036 if 'k' in bids:
1036 if 'k' in bids:
1037 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1037 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1038 actions[f] = bids['k'][0]
1038 actions[f] = bids['k'][0]
1039 continue
1039 continue
1040 # If there are gets and they all agree [how could they not?], do it.
1040 # If there are gets and they all agree [how could they not?], do it.
1041 if 'g' in bids:
1041 if 'g' in bids:
1042 ga0 = bids['g'][0]
1042 ga0 = bids['g'][0]
1043 if all(a == ga0 for a in bids['g'][1:]):
1043 if all(a == ga0 for a in bids['g'][1:]):
1044 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1044 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1045 actions[f] = ga0
1045 actions[f] = ga0
1046 continue
1046 continue
1047 # TODO: Consider other simple actions such as mode changes
1047 # TODO: Consider other simple actions such as mode changes
1048 # Handle inefficient democrazy.
1048 # Handle inefficient democrazy.
1049 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1049 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1050 for m, l in sorted(bids.items()):
1050 for m, l in sorted(bids.items()):
1051 for _f, args, msg in l:
1051 for _f, args, msg in l:
1052 repo.ui.note(' %s -> %s\n' % (msg, m))
1052 repo.ui.note(' %s -> %s\n' % (msg, m))
1053 # Pick random action. TODO: Instead, prompt user when resolving
1053 # Pick random action. TODO: Instead, prompt user when resolving
1054 m, l = bids.items()[0]
1054 m, l = bids.items()[0]
1055 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1055 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1056 (f, m))
1056 (f, m))
1057 actions[f] = l[0]
1057 actions[f] = l[0]
1058 if m == 'dm':
1058 if m == 'dm':
1059 dms.append(f)
1059 dms.append(f)
1060 continue
1060 continue
1061 # Work around 'dm' that can cause multiple actions for the same file
1061 # Work around 'dm' that can cause multiple actions for the same file
1062 for f in dms:
1062 for f in dms:
1063 dm, (f0, flags), msg = actions[f]
1063 dm, (f0, flags), msg = actions[f]
1064 assert dm == 'dm', dm
1064 assert dm == 'dm', dm
1065 if f0 in actions and actions[f0][0] == 'r':
1065 if f0 in actions and actions[f0][0] == 'r':
1066 # We have one bid for removing a file and another for moving it.
1066 # We have one bid for removing a file and another for moving it.
1067 # These two could be merged as first move and then delete ...
1067 # These two could be merged as first move and then delete ...
1068 # but instead drop moving and just delete.
1068 # but instead drop moving and just delete.
1069 del actions[f]
1069 del actions[f]
1070 repo.ui.note(_('end of auction\n\n'))
1070 repo.ui.note(_('end of auction\n\n'))
1071
1071
1072 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1072 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1073
1073
1074 if wctx.rev() is None:
1074 if wctx.rev() is None:
1075 fractions = _forgetremoved(wctx, mctx, branchmerge)
1075 fractions = _forgetremoved(wctx, mctx, branchmerge)
1076 actions.update(fractions)
1076 actions.update(fractions)
1077
1077
1078 return actions, diverge, renamedelete
1078 return actions, diverge, renamedelete
1079
1079
1080 def batchremove(repo, wctx, actions):
1080 def batchremove(repo, wctx, actions):
1081 """apply removes to the working directory
1081 """apply removes to the working directory
1082
1082
1083 yields tuples for progress updates
1083 yields tuples for progress updates
1084 """
1084 """
1085 verbose = repo.ui.verbose
1085 verbose = repo.ui.verbose
1086 try:
1086 try:
1087 cwd = pycompat.getcwd()
1087 cwd = pycompat.getcwd()
1088 except OSError as err:
1088 except OSError as err:
1089 if err.errno != errno.ENOENT:
1089 if err.errno != errno.ENOENT:
1090 raise
1090 raise
1091 cwd = None
1091 cwd = None
1092 i = 0
1092 i = 0
1093 for f, args, msg in actions:
1093 for f, args, msg in actions:
1094 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1094 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1095 if verbose:
1095 if verbose:
1096 repo.ui.note(_("removing %s\n") % f)
1096 repo.ui.note(_("removing %s\n") % f)
1097 wctx[f].audit()
1097 wctx[f].audit()
1098 try:
1098 try:
1099 wctx[f].remove(ignoremissing=True)
1099 wctx[f].remove(ignoremissing=True)
1100 except OSError as inst:
1100 except OSError as inst:
1101 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1101 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1102 (f, inst.strerror))
1102 (f, inst.strerror))
1103 if i == 100:
1103 if i == 100:
1104 yield i, f
1104 yield i, f
1105 i = 0
1105 i = 0
1106 i += 1
1106 i += 1
1107 if i > 0:
1107 if i > 0:
1108 yield i, f
1108 yield i, f
1109 if cwd:
1109 if cwd:
1110 # cwd was present before we started to remove files
1110 # cwd was present before we started to remove files
1111 # let's check if it is present after we removed them
1111 # let's check if it is present after we removed them
1112 try:
1112 try:
1113 pycompat.getcwd()
1113 pycompat.getcwd()
1114 except OSError as err:
1114 except OSError as err:
1115 if err.errno != errno.ENOENT:
1115 if err.errno != errno.ENOENT:
1116 raise
1116 raise
1117 # Print a warning if cwd was deleted
1117 # Print a warning if cwd was deleted
1118 repo.ui.warn(_("current directory was removed\n"
1118 repo.ui.warn(_("current directory was removed\n"
1119 "(consider changing to repo root: %s)\n") %
1119 "(consider changing to repo root: %s)\n") %
1120 repo.root)
1120 repo.root)
1121
1121
1122 def batchget(repo, mctx, wctx, actions):
1122 def batchget(repo, mctx, wctx, actions):
1123 """apply gets to the working directory
1123 """apply gets to the working directory
1124
1124
1125 mctx is the context to get from
1125 mctx is the context to get from
1126
1126
1127 yields tuples for progress updates
1127 yields tuples for progress updates
1128 """
1128 """
1129 verbose = repo.ui.verbose
1129 verbose = repo.ui.verbose
1130 fctx = mctx.filectx
1130 fctx = mctx.filectx
1131 ui = repo.ui
1131 ui = repo.ui
1132 i = 0
1132 i = 0
1133 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1133 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1134 for f, (flags, backup), msg in actions:
1134 for f, (flags, backup), msg in actions:
1135 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1135 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1136 if verbose:
1136 if verbose:
1137 repo.ui.note(_("getting %s\n") % f)
1137 repo.ui.note(_("getting %s\n") % f)
1138
1138
1139 if backup:
1139 if backup:
1140 absf = repo.wjoin(f)
1140 absf = repo.wjoin(f)
1141 orig = scmutil.origpath(ui, repo, absf)
1141 orig = scmutil.origpath(ui, repo, absf)
1142 try:
1142 try:
1143 if repo.wvfs.isfileorlink(f):
1143 if repo.wvfs.isfileorlink(f):
1144 util.rename(absf, orig)
1144 util.rename(absf, orig)
1145 except OSError as e:
1145 except OSError as e:
1146 if e.errno != errno.ENOENT:
1146 if e.errno != errno.ENOENT:
1147 raise
1147 raise
1148
1148
1149 if repo.wvfs.isdir(f) and not repo.wvfs.islink(f):
1149 if repo.wvfs.isdir(f) and not repo.wvfs.islink(f):
1150 repo.wvfs.removedirs(f)
1150 repo.wvfs.removedirs(f)
1151 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1151 wctx[f].write(fctx(f).data(), flags, backgroundclose=True)
1152 if i == 100:
1152 if i == 100:
1153 yield i, f
1153 yield i, f
1154 i = 0
1154 i = 0
1155 i += 1
1155 i += 1
1156 if i > 0:
1156 if i > 0:
1157 yield i, f
1157 yield i, f
1158
1158
1159 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1159 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1160 """apply the merge action list to the working directory
1160 """apply the merge action list to the working directory
1161
1161
1162 wctx is the working copy context
1162 wctx is the working copy context
1163 mctx is the context to be merged into the working copy
1163 mctx is the context to be merged into the working copy
1164
1164
1165 Return a tuple of counts (updated, merged, removed, unresolved) that
1165 Return a tuple of counts (updated, merged, removed, unresolved) that
1166 describes how many files were affected by the update.
1166 describes how many files were affected by the update.
1167 """
1167 """
1168
1168
1169 updated, merged, removed = 0, 0, 0
1169 updated, merged, removed = 0, 0, 0
1170 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1170 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1171 moves = []
1171 moves = []
1172 for m, l in actions.items():
1172 for m, l in actions.items():
1173 l.sort()
1173 l.sort()
1174
1174
1175 # 'cd' and 'dc' actions are treated like other merge conflicts
1175 # 'cd' and 'dc' actions are treated like other merge conflicts
1176 mergeactions = sorted(actions['cd'])
1176 mergeactions = sorted(actions['cd'])
1177 mergeactions.extend(sorted(actions['dc']))
1177 mergeactions.extend(sorted(actions['dc']))
1178 mergeactions.extend(actions['m'])
1178 mergeactions.extend(actions['m'])
1179 for f, args, msg in mergeactions:
1179 for f, args, msg in mergeactions:
1180 f1, f2, fa, move, anc = args
1180 f1, f2, fa, move, anc = args
1181 if f == '.hgsubstate': # merged internally
1181 if f == '.hgsubstate': # merged internally
1182 continue
1182 continue
1183 if f1 is None:
1183 if f1 is None:
1184 fcl = filemerge.absentfilectx(wctx, fa)
1184 fcl = filemerge.absentfilectx(wctx, fa)
1185 else:
1185 else:
1186 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1186 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1187 fcl = wctx[f1]
1187 fcl = wctx[f1]
1188 if f2 is None:
1188 if f2 is None:
1189 fco = filemerge.absentfilectx(mctx, fa)
1189 fco = filemerge.absentfilectx(mctx, fa)
1190 else:
1190 else:
1191 fco = mctx[f2]
1191 fco = mctx[f2]
1192 actx = repo[anc]
1192 actx = repo[anc]
1193 if fa in actx:
1193 if fa in actx:
1194 fca = actx[fa]
1194 fca = actx[fa]
1195 else:
1195 else:
1196 # TODO: move to absentfilectx
1196 # TODO: move to absentfilectx
1197 fca = repo.filectx(f1, fileid=nullrev)
1197 fca = repo.filectx(f1, fileid=nullrev)
1198 ms.add(fcl, fco, fca, f)
1198 ms.add(fcl, fco, fca, f)
1199 if f1 != f and move:
1199 if f1 != f and move:
1200 moves.append(f1)
1200 moves.append(f1)
1201
1201
1202 _updating = _('updating')
1202 _updating = _('updating')
1203 _files = _('files')
1203 _files = _('files')
1204 progress = repo.ui.progress
1204 progress = repo.ui.progress
1205
1205
1206 # remove renamed files after safely stored
1206 # remove renamed files after safely stored
1207 for f in moves:
1207 for f in moves:
1208 if wctx[f].lexists():
1208 if wctx[f].lexists():
1209 repo.ui.debug("removing %s\n" % f)
1209 repo.ui.debug("removing %s\n" % f)
1210 wctx[f].audit()
1210 wctx[f].audit()
1211 wctx[f].remove()
1211 wctx[f].remove()
1212
1212
1213 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1213 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1214
1214
1215 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1215 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1216 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1216 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1217
1217
1218 # remove in parallel (must come first)
1218 # remove in parallel (must come first)
1219 z = 0
1219 z = 0
1220 prog = worker.worker(repo.ui, 0.001, batchremove, (repo, wctx),
1220 prog = worker.worker(repo.ui, 0.001, batchremove, (repo, wctx),
1221 actions['r'])
1221 actions['r'])
1222 for i, item in prog:
1222 for i, item in prog:
1223 z += i
1223 z += i
1224 progress(_updating, z, item=item, total=numupdates, unit=_files)
1224 progress(_updating, z, item=item, total=numupdates, unit=_files)
1225 removed = len(actions['r'])
1225 removed = len(actions['r'])
1226
1226
1227 # get in parallel
1227 # get in parallel
1228 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
1228 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
1229 actions['g'])
1229 actions['g'])
1230 for i, item in prog:
1230 for i, item in prog:
1231 z += i
1231 z += i
1232 progress(_updating, z, item=item, total=numupdates, unit=_files)
1232 progress(_updating, z, item=item, total=numupdates, unit=_files)
1233 updated = len(actions['g'])
1233 updated = len(actions['g'])
1234
1234
1235 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1235 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1236 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1236 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1237
1237
1238 # forget (manifest only, just log it) (must come first)
1238 # forget (manifest only, just log it) (must come first)
1239 for f, args, msg in actions['f']:
1239 for f, args, msg in actions['f']:
1240 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1240 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1241 z += 1
1241 z += 1
1242 progress(_updating, z, item=f, total=numupdates, unit=_files)
1242 progress(_updating, z, item=f, total=numupdates, unit=_files)
1243
1243
1244 # re-add (manifest only, just log it)
1244 # re-add (manifest only, just log it)
1245 for f, args, msg in actions['a']:
1245 for f, args, msg in actions['a']:
1246 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1246 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1247 z += 1
1247 z += 1
1248 progress(_updating, z, item=f, total=numupdates, unit=_files)
1248 progress(_updating, z, item=f, total=numupdates, unit=_files)
1249
1249
1250 # re-add/mark as modified (manifest only, just log it)
1250 # re-add/mark as modified (manifest only, just log it)
1251 for f, args, msg in actions['am']:
1251 for f, args, msg in actions['am']:
1252 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1252 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1253 z += 1
1253 z += 1
1254 progress(_updating, z, item=f, total=numupdates, unit=_files)
1254 progress(_updating, z, item=f, total=numupdates, unit=_files)
1255
1255
1256 # keep (noop, just log it)
1256 # keep (noop, just log it)
1257 for f, args, msg in actions['k']:
1257 for f, args, msg in actions['k']:
1258 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1258 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1259 # no progress
1259 # no progress
1260
1260
1261 # directory rename, move local
1261 # directory rename, move local
1262 for f, args, msg in actions['dm']:
1262 for f, args, msg in actions['dm']:
1263 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1263 repo.ui.debug(" %s: %s -> dm\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 f0, flags = args
1266 f0, flags = args
1267 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1267 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1268 wctx[f].audit()
1268 wctx[f].audit()
1269 wctx[f].write(wctx.filectx(f0).data(), flags)
1269 wctx[f].write(wctx.filectx(f0).data(), flags)
1270 wctx[f0].remove()
1270 wctx[f0].remove()
1271 updated += 1
1271 updated += 1
1272
1272
1273 # local directory rename, get
1273 # local directory rename, get
1274 for f, args, msg in actions['dg']:
1274 for f, args, msg in actions['dg']:
1275 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1275 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1276 z += 1
1276 z += 1
1277 progress(_updating, z, item=f, total=numupdates, unit=_files)
1277 progress(_updating, z, item=f, total=numupdates, unit=_files)
1278 f0, flags = args
1278 f0, flags = args
1279 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1279 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1280 wctx[f].write(mctx.filectx(f0).data(), flags)
1280 wctx[f].write(mctx.filectx(f0).data(), flags)
1281 updated += 1
1281 updated += 1
1282
1282
1283 # exec
1283 # exec
1284 for f, args, msg in actions['e']:
1284 for f, args, msg in actions['e']:
1285 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1285 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1286 z += 1
1286 z += 1
1287 progress(_updating, z, item=f, total=numupdates, unit=_files)
1287 progress(_updating, z, item=f, total=numupdates, unit=_files)
1288 flags, = args
1288 flags, = args
1289 wctx[f].audit()
1289 wctx[f].audit()
1290 wctx[f].setflags('l' in flags, 'x' in flags)
1290 wctx[f].setflags('l' in flags, 'x' in flags)
1291 updated += 1
1291 updated += 1
1292
1292
1293 # the ordering is important here -- ms.mergedriver will raise if the merge
1293 # the ordering is important here -- ms.mergedriver will raise if the merge
1294 # driver has changed, and we want to be able to bypass it when overwrite is
1294 # driver has changed, and we want to be able to bypass it when overwrite is
1295 # True
1295 # True
1296 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1296 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1297
1297
1298 if usemergedriver:
1298 if usemergedriver:
1299 ms.commit()
1299 ms.commit()
1300 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1300 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1301 # the driver might leave some files unresolved
1301 # the driver might leave some files unresolved
1302 unresolvedf = set(ms.unresolved())
1302 unresolvedf = set(ms.unresolved())
1303 if not proceed:
1303 if not proceed:
1304 # XXX setting unresolved to at least 1 is a hack to make sure we
1304 # XXX setting unresolved to at least 1 is a hack to make sure we
1305 # error out
1305 # error out
1306 return updated, merged, removed, max(len(unresolvedf), 1)
1306 return updated, merged, removed, max(len(unresolvedf), 1)
1307 newactions = []
1307 newactions = []
1308 for f, args, msg in mergeactions:
1308 for f, args, msg in mergeactions:
1309 if f in unresolvedf:
1309 if f in unresolvedf:
1310 newactions.append((f, args, msg))
1310 newactions.append((f, args, msg))
1311 mergeactions = newactions
1311 mergeactions = newactions
1312
1312
1313 # premerge
1313 # premerge
1314 tocomplete = []
1314 tocomplete = []
1315 for f, args, msg in mergeactions:
1315 for f, args, msg in mergeactions:
1316 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1316 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1317 z += 1
1317 z += 1
1318 progress(_updating, z, item=f, total=numupdates, unit=_files)
1318 progress(_updating, z, item=f, total=numupdates, unit=_files)
1319 if f == '.hgsubstate': # subrepo states need updating
1319 if f == '.hgsubstate': # subrepo states need updating
1320 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1320 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1321 overwrite, labels)
1321 overwrite, labels)
1322 continue
1322 continue
1323 wctx[f].audit()
1323 wctx[f].audit()
1324 complete, r = ms.preresolve(f, wctx)
1324 complete, r = ms.preresolve(f, wctx)
1325 if not complete:
1325 if not complete:
1326 numupdates += 1
1326 numupdates += 1
1327 tocomplete.append((f, args, msg))
1327 tocomplete.append((f, args, msg))
1328
1328
1329 # merge
1329 # merge
1330 for f, args, msg in tocomplete:
1330 for f, args, msg in tocomplete:
1331 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1331 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1332 z += 1
1332 z += 1
1333 progress(_updating, z, item=f, total=numupdates, unit=_files)
1333 progress(_updating, z, item=f, total=numupdates, unit=_files)
1334 ms.resolve(f, wctx)
1334 ms.resolve(f, wctx)
1335
1335
1336 ms.commit()
1336 ms.commit()
1337
1337
1338 unresolved = ms.unresolvedcount()
1338 unresolved = ms.unresolvedcount()
1339
1339
1340 if usemergedriver and not unresolved and ms.mdstate() != 's':
1340 if usemergedriver and not unresolved and ms.mdstate() != 's':
1341 if not driverconclude(repo, ms, wctx, labels=labels):
1341 if not driverconclude(repo, ms, wctx, labels=labels):
1342 # XXX setting unresolved to at least 1 is a hack to make sure we
1342 # XXX setting unresolved to at least 1 is a hack to make sure we
1343 # error out
1343 # error out
1344 unresolved = max(unresolved, 1)
1344 unresolved = max(unresolved, 1)
1345
1345
1346 ms.commit()
1346 ms.commit()
1347
1347
1348 msupdated, msmerged, msremoved = ms.counts()
1348 msupdated, msmerged, msremoved = ms.counts()
1349 updated += msupdated
1349 updated += msupdated
1350 merged += msmerged
1350 merged += msmerged
1351 removed += msremoved
1351 removed += msremoved
1352
1352
1353 extraactions = ms.actions()
1353 extraactions = ms.actions()
1354 if extraactions:
1354 if extraactions:
1355 mfiles = set(a[0] for a in actions['m'])
1355 mfiles = set(a[0] for a in actions['m'])
1356 for k, acts in extraactions.iteritems():
1356 for k, acts in extraactions.iteritems():
1357 actions[k].extend(acts)
1357 actions[k].extend(acts)
1358 # Remove these files from actions['m'] as well. This is important
1358 # Remove these files from actions['m'] as well. This is important
1359 # because in recordupdates, files in actions['m'] are processed
1359 # because in recordupdates, files in actions['m'] are processed
1360 # after files in other actions, and the merge driver might add
1360 # after files in other actions, and the merge driver might add
1361 # files to those actions via extraactions above. This can lead to a
1361 # files to those actions via extraactions above. This can lead to a
1362 # file being recorded twice, with poor results. This is especially
1362 # file being recorded twice, with poor results. This is especially
1363 # problematic for actions['r'] (currently only possible with the
1363 # problematic for actions['r'] (currently only possible with the
1364 # merge driver in the initial merge process; interrupted merges
1364 # merge driver in the initial merge process; interrupted merges
1365 # don't go through this flow).
1365 # don't go through this flow).
1366 #
1366 #
1367 # The real fix here is to have indexes by both file and action so
1367 # The real fix here is to have indexes by both file and action so
1368 # that when the action for a file is changed it is automatically
1368 # that when the action for a file is changed it is automatically
1369 # reflected in the other action lists. But that involves a more
1369 # reflected in the other action lists. But that involves a more
1370 # complex data structure, so this will do for now.
1370 # complex data structure, so this will do for now.
1371 #
1371 #
1372 # We don't need to do the same operation for 'dc' and 'cd' because
1372 # We don't need to do the same operation for 'dc' and 'cd' because
1373 # those lists aren't consulted again.
1373 # those lists aren't consulted again.
1374 mfiles.difference_update(a[0] for a in acts)
1374 mfiles.difference_update(a[0] for a in acts)
1375
1375
1376 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1376 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1377
1377
1378 progress(_updating, None, total=numupdates, unit=_files)
1378 progress(_updating, None, total=numupdates, unit=_files)
1379
1379
1380 return updated, merged, removed, unresolved
1380 return updated, merged, removed, unresolved
1381
1381
1382 def recordupdates(repo, actions, branchmerge):
1382 def recordupdates(repo, actions, branchmerge):
1383 "record merge actions to the dirstate"
1383 "record merge actions to the dirstate"
1384 # remove (must come first)
1384 # remove (must come first)
1385 for f, args, msg in actions.get('r', []):
1385 for f, args, msg in actions.get('r', []):
1386 if branchmerge:
1386 if branchmerge:
1387 repo.dirstate.remove(f)
1387 repo.dirstate.remove(f)
1388 else:
1388 else:
1389 repo.dirstate.drop(f)
1389 repo.dirstate.drop(f)
1390
1390
1391 # forget (must come first)
1391 # forget (must come first)
1392 for f, args, msg in actions.get('f', []):
1392 for f, args, msg in actions.get('f', []):
1393 repo.dirstate.drop(f)
1393 repo.dirstate.drop(f)
1394
1394
1395 # re-add
1395 # re-add
1396 for f, args, msg in actions.get('a', []):
1396 for f, args, msg in actions.get('a', []):
1397 repo.dirstate.add(f)
1397 repo.dirstate.add(f)
1398
1398
1399 # re-add/mark as modified
1399 # re-add/mark as modified
1400 for f, args, msg in actions.get('am', []):
1400 for f, args, msg in actions.get('am', []):
1401 if branchmerge:
1401 if branchmerge:
1402 repo.dirstate.normallookup(f)
1402 repo.dirstate.normallookup(f)
1403 else:
1403 else:
1404 repo.dirstate.add(f)
1404 repo.dirstate.add(f)
1405
1405
1406 # exec change
1406 # exec change
1407 for f, args, msg in actions.get('e', []):
1407 for f, args, msg in actions.get('e', []):
1408 repo.dirstate.normallookup(f)
1408 repo.dirstate.normallookup(f)
1409
1409
1410 # keep
1410 # keep
1411 for f, args, msg in actions.get('k', []):
1411 for f, args, msg in actions.get('k', []):
1412 pass
1412 pass
1413
1413
1414 # get
1414 # get
1415 for f, args, msg in actions.get('g', []):
1415 for f, args, msg in actions.get('g', []):
1416 if branchmerge:
1416 if branchmerge:
1417 repo.dirstate.otherparent(f)
1417 repo.dirstate.otherparent(f)
1418 else:
1418 else:
1419 repo.dirstate.normal(f)
1419 repo.dirstate.normal(f)
1420
1420
1421 # merge
1421 # merge
1422 for f, args, msg in actions.get('m', []):
1422 for f, args, msg in actions.get('m', []):
1423 f1, f2, fa, move, anc = args
1423 f1, f2, fa, move, anc = args
1424 if branchmerge:
1424 if branchmerge:
1425 # We've done a branch merge, mark this file as merged
1425 # We've done a branch merge, mark this file as merged
1426 # so that we properly record the merger later
1426 # so that we properly record the merger later
1427 repo.dirstate.merge(f)
1427 repo.dirstate.merge(f)
1428 if f1 != f2: # copy/rename
1428 if f1 != f2: # copy/rename
1429 if move:
1429 if move:
1430 repo.dirstate.remove(f1)
1430 repo.dirstate.remove(f1)
1431 if f1 != f:
1431 if f1 != f:
1432 repo.dirstate.copy(f1, f)
1432 repo.dirstate.copy(f1, f)
1433 else:
1433 else:
1434 repo.dirstate.copy(f2, f)
1434 repo.dirstate.copy(f2, f)
1435 else:
1435 else:
1436 # We've update-merged a locally modified file, so
1436 # We've update-merged a locally modified file, so
1437 # we set the dirstate to emulate a normal checkout
1437 # we set the dirstate to emulate a normal checkout
1438 # of that file some time in the past. Thus our
1438 # of that file some time in the past. Thus our
1439 # merge will appear as a normal local file
1439 # merge will appear as a normal local file
1440 # modification.
1440 # modification.
1441 if f2 == f: # file not locally copied/moved
1441 if f2 == f: # file not locally copied/moved
1442 repo.dirstate.normallookup(f)
1442 repo.dirstate.normallookup(f)
1443 if move:
1443 if move:
1444 repo.dirstate.drop(f1)
1444 repo.dirstate.drop(f1)
1445
1445
1446 # directory rename, move local
1446 # directory rename, move local
1447 for f, args, msg in actions.get('dm', []):
1447 for f, args, msg in actions.get('dm', []):
1448 f0, flag = args
1448 f0, flag = args
1449 if branchmerge:
1449 if branchmerge:
1450 repo.dirstate.add(f)
1450 repo.dirstate.add(f)
1451 repo.dirstate.remove(f0)
1451 repo.dirstate.remove(f0)
1452 repo.dirstate.copy(f0, f)
1452 repo.dirstate.copy(f0, f)
1453 else:
1453 else:
1454 repo.dirstate.normal(f)
1454 repo.dirstate.normal(f)
1455 repo.dirstate.drop(f0)
1455 repo.dirstate.drop(f0)
1456
1456
1457 # directory rename, get
1457 # directory rename, get
1458 for f, args, msg in actions.get('dg', []):
1458 for f, args, msg in actions.get('dg', []):
1459 f0, flag = args
1459 f0, flag = args
1460 if branchmerge:
1460 if branchmerge:
1461 repo.dirstate.add(f)
1461 repo.dirstate.add(f)
1462 repo.dirstate.copy(f0, f)
1462 repo.dirstate.copy(f0, f)
1463 else:
1463 else:
1464 repo.dirstate.normal(f)
1464 repo.dirstate.normal(f)
1465
1465
1466 def update(repo, node, branchmerge, force, ancestor=None,
1466 def update(repo, node, branchmerge, force, ancestor=None,
1467 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1467 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1468 updatecheck=None):
1468 updatecheck=None):
1469 """
1469 """
1470 Perform a merge between the working directory and the given node
1470 Perform a merge between the working directory and the given node
1471
1471
1472 node = the node to update to
1472 node = the node to update to
1473 branchmerge = whether to merge between branches
1473 branchmerge = whether to merge between branches
1474 force = whether to force branch merging or file overwriting
1474 force = whether to force branch merging or file overwriting
1475 matcher = a matcher to filter file lists (dirstate not updated)
1475 matcher = a matcher to filter file lists (dirstate not updated)
1476 mergeancestor = whether it is merging with an ancestor. If true,
1476 mergeancestor = whether it is merging with an ancestor. If true,
1477 we should accept the incoming changes for any prompts that occur.
1477 we should accept the incoming changes for any prompts that occur.
1478 If false, merging with an ancestor (fast-forward) is only allowed
1478 If false, merging with an ancestor (fast-forward) is only allowed
1479 between different named branches. This flag is used by rebase extension
1479 between different named branches. This flag is used by rebase extension
1480 as a temporary fix and should be avoided in general.
1480 as a temporary fix and should be avoided in general.
1481 labels = labels to use for base, local and other
1481 labels = labels to use for base, local and other
1482 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1482 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1483 this is True, then 'force' should be True as well.
1483 this is True, then 'force' should be True as well.
1484
1484
1485 The table below shows all the behaviors of the update command
1485 The table below shows all the behaviors of the update command
1486 given the -c and -C or no options, whether the working directory
1486 given the -c and -C or no options, whether the working directory
1487 is dirty, whether a revision is specified, and the relationship of
1487 is dirty, whether a revision is specified, and the relationship of
1488 the parent rev to the target rev (linear or not). Match from top first. The
1488 the parent rev to the target rev (linear or not). Match from top first. The
1489 -n option doesn't exist on the command line, but represents the
1489 -n option doesn't exist on the command line, but represents the
1490 experimental.updatecheck=noconflict option.
1490 experimental.updatecheck=noconflict option.
1491
1491
1492 This logic is tested by test-update-branches.t.
1492 This logic is tested by test-update-branches.t.
1493
1493
1494 -c -C -n -m dirty rev linear | result
1494 -c -C -n -m dirty rev linear | result
1495 y y * * * * * | (1)
1495 y y * * * * * | (1)
1496 y * y * * * * | (1)
1496 y * y * * * * | (1)
1497 y * * y * * * | (1)
1497 y * * y * * * | (1)
1498 * y y * * * * | (1)
1498 * y y * * * * | (1)
1499 * y * y * * * | (1)
1499 * y * y * * * | (1)
1500 * * y y * * * | (1)
1500 * * y y * * * | (1)
1501 * * * * * n n | x
1501 * * * * * n n | x
1502 * * * * n * * | ok
1502 * * * * n * * | ok
1503 n n n n y * y | merge
1503 n n n n y * y | merge
1504 n n n n y y n | (2)
1504 n n n n y y n | (2)
1505 n n n y y * * | merge
1505 n n n y y * * | merge
1506 n n y n y * * | merge if no conflict
1506 n n y n y * * | merge if no conflict
1507 n y n n y * * | discard
1507 n y n n y * * | discard
1508 y n n n y * * | (3)
1508 y n n n y * * | (3)
1509
1509
1510 x = can't happen
1510 x = can't happen
1511 * = don't-care
1511 * = don't-care
1512 1 = incompatible options (checked in commands.py)
1512 1 = incompatible options (checked in commands.py)
1513 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1513 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1514 3 = abort: uncommitted changes (checked in commands.py)
1514 3 = abort: uncommitted changes (checked in commands.py)
1515
1515
1516 Return the same tuple as applyupdates().
1516 Return the same tuple as applyupdates().
1517 """
1517 """
1518
1518
1519 # This function used to find the default destination if node was None, but
1519 # This function used to find the default destination if node was None, but
1520 # that's now in destutil.py.
1520 # that's now in destutil.py.
1521 assert node is not None
1521 assert node is not None
1522 if not branchmerge and not force:
1522 if not branchmerge and not force:
1523 # TODO: remove the default once all callers that pass branchmerge=False
1523 # TODO: remove the default once all callers that pass branchmerge=False
1524 # and force=False pass a value for updatecheck. We may want to allow
1524 # and force=False pass a value for updatecheck. We may want to allow
1525 # updatecheck='abort' to better suppport some of these callers.
1525 # updatecheck='abort' to better suppport some of these callers.
1526 if updatecheck is None:
1526 if updatecheck is None:
1527 updatecheck = 'linear'
1527 updatecheck = 'linear'
1528 assert updatecheck in ('none', 'linear', 'noconflict')
1528 assert updatecheck in ('none', 'linear', 'noconflict')
1529 # If we're doing a partial update, we need to skip updating
1529 # If we're doing a partial update, we need to skip updating
1530 # the dirstate, so make a note of any partial-ness to the
1530 # the dirstate, so make a note of any partial-ness to the
1531 # update here.
1531 # update here.
1532 if matcher is None or matcher.always():
1532 if matcher is None or matcher.always():
1533 partial = False
1533 partial = False
1534 else:
1534 else:
1535 partial = True
1535 partial = True
1536 with repo.wlock():
1536 with repo.wlock():
1537 wc = repo[None]
1537 wc = repo[None]
1538 pl = wc.parents()
1538 pl = wc.parents()
1539 p1 = pl[0]
1539 p1 = pl[0]
1540 pas = [None]
1540 pas = [None]
1541 if ancestor is not None:
1541 if ancestor is not None:
1542 pas = [repo[ancestor]]
1542 pas = [repo[ancestor]]
1543
1543
1544 overwrite = force and not branchmerge
1544 overwrite = force and not branchmerge
1545
1545
1546 p2 = repo[node]
1546 p2 = repo[node]
1547 if pas[0] is None:
1547 if pas[0] is None:
1548 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1548 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1549 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1549 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1550 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1550 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1551 else:
1551 else:
1552 pas = [p1.ancestor(p2, warn=branchmerge)]
1552 pas = [p1.ancestor(p2, warn=branchmerge)]
1553
1553
1554 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1554 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1555
1555
1556 ### check phase
1556 ### check phase
1557 if not overwrite:
1557 if not overwrite:
1558 if len(pl) > 1:
1558 if len(pl) > 1:
1559 raise error.Abort(_("outstanding uncommitted merge"))
1559 raise error.Abort(_("outstanding uncommitted merge"))
1560 ms = mergestate.read(repo)
1560 ms = mergestate.read(repo)
1561 if list(ms.unresolved()):
1561 if list(ms.unresolved()):
1562 raise error.Abort(_("outstanding merge conflicts"))
1562 raise error.Abort(_("outstanding merge conflicts"))
1563 if branchmerge:
1563 if branchmerge:
1564 if pas == [p2]:
1564 if pas == [p2]:
1565 raise error.Abort(_("merging with a working directory ancestor"
1565 raise error.Abort(_("merging with a working directory ancestor"
1566 " has no effect"))
1566 " has no effect"))
1567 elif pas == [p1]:
1567 elif pas == [p1]:
1568 if not mergeancestor and wc.branch() == p2.branch():
1568 if not mergeancestor and wc.branch() == p2.branch():
1569 raise error.Abort(_("nothing to merge"),
1569 raise error.Abort(_("nothing to merge"),
1570 hint=_("use 'hg update' "
1570 hint=_("use 'hg update' "
1571 "or check 'hg heads'"))
1571 "or check 'hg heads'"))
1572 if not force and (wc.files() or wc.deleted()):
1572 if not force and (wc.files() or wc.deleted()):
1573 raise error.Abort(_("uncommitted changes"),
1573 raise error.Abort(_("uncommitted changes"),
1574 hint=_("use 'hg status' to list changes"))
1574 hint=_("use 'hg status' to list changes"))
1575 for s in sorted(wc.substate):
1575 for s in sorted(wc.substate):
1576 wc.sub(s).bailifchanged()
1576 wc.sub(s).bailifchanged()
1577
1577
1578 elif not overwrite:
1578 elif not overwrite:
1579 if p1 == p2: # no-op update
1579 if p1 == p2: # no-op update
1580 # call the hooks and exit early
1580 # call the hooks and exit early
1581 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1581 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1582 repo.hook('update', parent1=xp2, parent2='', error=0)
1582 repo.hook('update', parent1=xp2, parent2='', error=0)
1583 return 0, 0, 0, 0
1583 return 0, 0, 0, 0
1584
1584
1585 if (updatecheck == 'linear' and
1585 if (updatecheck == 'linear' and
1586 pas not in ([p1], [p2])): # nonlinear
1586 pas not in ([p1], [p2])): # nonlinear
1587 dirty = wc.dirty(missing=True)
1587 dirty = wc.dirty(missing=True)
1588 if dirty:
1588 if dirty:
1589 # Branching is a bit strange to ensure we do the minimal
1589 # Branching is a bit strange to ensure we do the minimal
1590 # amount of call to obsutil.foreground.
1590 # amount of call to obsutil.foreground.
1591 foreground = obsutil.foreground(repo, [p1.node()])
1591 foreground = obsutil.foreground(repo, [p1.node()])
1592 # note: the <node> variable contains a random identifier
1592 # note: the <node> variable contains a random identifier
1593 if repo[node].node() in foreground:
1593 if repo[node].node() in foreground:
1594 pass # allow updating to successors
1594 pass # allow updating to successors
1595 else:
1595 else:
1596 msg = _("uncommitted changes")
1596 msg = _("uncommitted changes")
1597 hint = _("commit or update --clean to discard changes")
1597 hint = _("commit or update --clean to discard changes")
1598 raise error.UpdateAbort(msg, hint=hint)
1598 raise error.UpdateAbort(msg, hint=hint)
1599 else:
1599 else:
1600 # Allow jumping branches if clean and specific rev given
1600 # Allow jumping branches if clean and specific rev given
1601 pass
1601 pass
1602
1602
1603 if overwrite:
1603 if overwrite:
1604 pas = [wc]
1604 pas = [wc]
1605 elif not branchmerge:
1605 elif not branchmerge:
1606 pas = [p1]
1606 pas = [p1]
1607
1607
1608 # deprecated config: merge.followcopies
1608 # deprecated config: merge.followcopies
1609 followcopies = repo.ui.configbool('merge', 'followcopies', True)
1609 followcopies = repo.ui.configbool('merge', 'followcopies', True)
1610 if overwrite:
1610 if overwrite:
1611 followcopies = False
1611 followcopies = False
1612 elif not pas[0]:
1612 elif not pas[0]:
1613 followcopies = False
1613 followcopies = False
1614 if not branchmerge and not wc.dirty(missing=True):
1614 if not branchmerge and not wc.dirty(missing=True):
1615 followcopies = False
1615 followcopies = False
1616
1616
1617 ### calculate phase
1617 ### calculate phase
1618 actionbyfile, diverge, renamedelete = calculateupdates(
1618 actionbyfile, diverge, renamedelete = calculateupdates(
1619 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1619 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1620 followcopies, matcher=matcher, mergeforce=mergeforce)
1620 followcopies, matcher=matcher, mergeforce=mergeforce)
1621
1621
1622 if updatecheck == 'noconflict':
1622 if updatecheck == 'noconflict':
1623 for f, (m, args, msg) in actionbyfile.iteritems():
1623 for f, (m, args, msg) in actionbyfile.iteritems():
1624 if m not in ('g', 'k', 'e', 'r'):
1624 if m not in ('g', 'k', 'e', 'r'):
1625 msg = _("conflicting changes")
1625 msg = _("conflicting changes")
1626 hint = _("commit or update --clean to discard changes")
1626 hint = _("commit or update --clean to discard changes")
1627 raise error.Abort(msg, hint=hint)
1627 raise error.Abort(msg, hint=hint)
1628
1628
1629 # Prompt and create actions. Most of this is in the resolve phase
1629 # Prompt and create actions. Most of this is in the resolve phase
1630 # already, but we can't handle .hgsubstate in filemerge or
1630 # already, but we can't handle .hgsubstate in filemerge or
1631 # subrepo.submerge yet so we have to keep prompting for it.
1631 # subrepo.submerge yet so we have to keep prompting for it.
1632 if '.hgsubstate' in actionbyfile:
1632 if '.hgsubstate' in actionbyfile:
1633 f = '.hgsubstate'
1633 f = '.hgsubstate'
1634 m, args, msg = actionbyfile[f]
1634 m, args, msg = actionbyfile[f]
1635 prompts = filemerge.partextras(labels)
1635 prompts = filemerge.partextras(labels)
1636 prompts['f'] = f
1636 prompts['f'] = f
1637 if m == 'cd':
1637 if m == 'cd':
1638 if repo.ui.promptchoice(
1638 if repo.ui.promptchoice(
1639 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1639 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1640 "use (c)hanged version or (d)elete?"
1640 "use (c)hanged version or (d)elete?"
1641 "$$ &Changed $$ &Delete") % prompts, 0):
1641 "$$ &Changed $$ &Delete") % prompts, 0):
1642 actionbyfile[f] = ('r', None, "prompt delete")
1642 actionbyfile[f] = ('r', None, "prompt delete")
1643 elif f in p1:
1643 elif f in p1:
1644 actionbyfile[f] = ('am', None, "prompt keep")
1644 actionbyfile[f] = ('am', None, "prompt keep")
1645 else:
1645 else:
1646 actionbyfile[f] = ('a', None, "prompt keep")
1646 actionbyfile[f] = ('a', None, "prompt keep")
1647 elif m == 'dc':
1647 elif m == 'dc':
1648 f1, f2, fa, move, anc = args
1648 f1, f2, fa, move, anc = args
1649 flags = p2[f2].flags()
1649 flags = p2[f2].flags()
1650 if repo.ui.promptchoice(
1650 if repo.ui.promptchoice(
1651 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1651 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1652 "use (c)hanged version or leave (d)eleted?"
1652 "use (c)hanged version or leave (d)eleted?"
1653 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1653 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1654 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1654 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1655 else:
1655 else:
1656 del actionbyfile[f]
1656 del actionbyfile[f]
1657
1657
1658 # Convert to dictionary-of-lists format
1658 # Convert to dictionary-of-lists format
1659 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1659 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1660 for f, (m, args, msg) in actionbyfile.iteritems():
1660 for f, (m, args, msg) in actionbyfile.iteritems():
1661 if m not in actions:
1661 if m not in actions:
1662 actions[m] = []
1662 actions[m] = []
1663 actions[m].append((f, args, msg))
1663 actions[m].append((f, args, msg))
1664
1664
1665 if not util.fscasesensitive(repo.path):
1665 if not util.fscasesensitive(repo.path):
1666 # check collision between files only in p2 for clean update
1666 # check collision between files only in p2 for clean update
1667 if (not branchmerge and
1667 if (not branchmerge and
1668 (force or not wc.dirty(missing=True, branch=False))):
1668 (force or not wc.dirty(missing=True, branch=False))):
1669 _checkcollision(repo, p2.manifest(), None)
1669 _checkcollision(repo, p2.manifest(), None)
1670 else:
1670 else:
1671 _checkcollision(repo, wc.manifest(), actions)
1671 _checkcollision(repo, wc.manifest(), actions)
1672
1672
1673 # divergent renames
1673 # divergent renames
1674 for f, fl in sorted(diverge.iteritems()):
1674 for f, fl in sorted(diverge.iteritems()):
1675 repo.ui.warn(_("note: possible conflict - %s was renamed "
1675 repo.ui.warn(_("note: possible conflict - %s was renamed "
1676 "multiple times to:\n") % f)
1676 "multiple times to:\n") % f)
1677 for nf in fl:
1677 for nf in fl:
1678 repo.ui.warn(" %s\n" % nf)
1678 repo.ui.warn(" %s\n" % nf)
1679
1679
1680 # rename and delete
1680 # rename and delete
1681 for f, fl in sorted(renamedelete.iteritems()):
1681 for f, fl in sorted(renamedelete.iteritems()):
1682 repo.ui.warn(_("note: possible conflict - %s was deleted "
1682 repo.ui.warn(_("note: possible conflict - %s was deleted "
1683 "and renamed to:\n") % f)
1683 "and renamed to:\n") % f)
1684 for nf in fl:
1684 for nf in fl:
1685 repo.ui.warn(" %s\n" % nf)
1685 repo.ui.warn(" %s\n" % nf)
1686
1686
1687 ### apply phase
1687 ### apply phase
1688 if not branchmerge: # just jump to the new rev
1688 if not branchmerge: # just jump to the new rev
1689 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1689 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1690 if not partial:
1690 if not partial:
1691 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1691 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1692 # note that we're in the middle of an update
1692 # note that we're in the middle of an update
1693 repo.vfs.write('updatestate', p2.hex())
1693 repo.vfs.write('updatestate', p2.hex())
1694
1694
1695 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1695 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1696
1696
1697 if not partial:
1697 if not partial:
1698 with repo.dirstate.parentchange():
1698 with repo.dirstate.parentchange():
1699 repo.setparents(fp1, fp2)
1699 repo.setparents(fp1, fp2)
1700 recordupdates(repo, actions, branchmerge)
1700 recordupdates(repo, actions, branchmerge)
1701 # update completed, clear state
1701 # update completed, clear state
1702 util.unlink(repo.vfs.join('updatestate'))
1702 util.unlink(repo.vfs.join('updatestate'))
1703
1703
1704 if not branchmerge:
1704 if not branchmerge:
1705 repo.dirstate.setbranch(p2.branch())
1705 repo.dirstate.setbranch(p2.branch())
1706
1706
1707 if not partial:
1707 if not partial:
1708 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1708 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1709 return stats
1709 return stats
1710
1710
1711 def graft(repo, ctx, pctx, labels, keepparent=False):
1711 def graft(repo, ctx, pctx, labels, keepparent=False):
1712 """Do a graft-like merge.
1712 """Do a graft-like merge.
1713
1713
1714 This is a merge where the merge ancestor is chosen such that one
1714 This is a merge where the merge ancestor is chosen such that one
1715 or more changesets are grafted onto the current changeset. In
1715 or more changesets are grafted onto the current changeset. In
1716 addition to the merge, this fixes up the dirstate to include only
1716 addition to the merge, this fixes up the dirstate to include only
1717 a single parent (if keepparent is False) and tries to duplicate any
1717 a single parent (if keepparent is False) and tries to duplicate any
1718 renames/copies appropriately.
1718 renames/copies appropriately.
1719
1719
1720 ctx - changeset to rebase
1720 ctx - changeset to rebase
1721 pctx - merge base, usually ctx.p1()
1721 pctx - merge base, usually ctx.p1()
1722 labels - merge labels eg ['local', 'graft']
1722 labels - merge labels eg ['local', 'graft']
1723 keepparent - keep second parent if any
1723 keepparent - keep second parent if any
1724
1724
1725 """
1725 """
1726 # If we're grafting a descendant onto an ancestor, be sure to pass
1726 # If we're grafting a descendant onto an ancestor, be sure to pass
1727 # mergeancestor=True to update. This does two things: 1) allows the merge if
1727 # mergeancestor=True to update. This does two things: 1) allows the merge if
1728 # the destination is the same as the parent of the ctx (so we can use graft
1728 # the destination is the same as the parent of the ctx (so we can use graft
1729 # to copy commits), and 2) informs update that the incoming changes are
1729 # to copy commits), and 2) informs update that the incoming changes are
1730 # newer than the destination so it doesn't prompt about "remote changed foo
1730 # newer than the destination so it doesn't prompt about "remote changed foo
1731 # which local deleted".
1731 # which local deleted".
1732 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1732 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1733
1733
1734 stats = update(repo, ctx.node(), True, True, pctx.node(),
1734 stats = update(repo, ctx.node(), True, True, pctx.node(),
1735 mergeancestor=mergeancestor, labels=labels)
1735 mergeancestor=mergeancestor, labels=labels)
1736
1736
1737 pother = nullid
1737 pother = nullid
1738 parents = ctx.parents()
1738 parents = ctx.parents()
1739 if keepparent and len(parents) == 2 and pctx in parents:
1739 if keepparent and len(parents) == 2 and pctx in parents:
1740 parents.remove(pctx)
1740 parents.remove(pctx)
1741 pother = parents[0].node()
1741 pother = parents[0].node()
1742
1742
1743 with repo.dirstate.parentchange():
1743 with repo.dirstate.parentchange():
1744 repo.setparents(repo['.'].node(), pother)
1744 repo.setparents(repo['.'].node(), pother)
1745 repo.dirstate.write(repo.currenttransaction())
1745 repo.dirstate.write(repo.currenttransaction())
1746 # fix up dirstate for copies and renames
1746 # fix up dirstate for copies and renames
1747 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1747 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1748 return stats
1748 return stats
General Comments 0
You need to be logged in to leave comments. Login now