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