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