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