##// END OF EJS Templates
checkunknown: audit path before checking if it's a file or link...
Durham Goode -
r28088:19424f96 default
parent child Browse files
Show More
@@ -1,1645 +1,1645
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("localctx accessed but self._local 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.isfileorlink(f)
601 return (repo.wvfs.audit.check(f)
602 and repo.wvfs.audit.check(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 for s in sorted(wctx.substate):
804 for s in sorted(wctx.substate):
805 if wctx.sub(s).dirty():
805 if wctx.sub(s).dirty():
806 m1['.hgsubstate'] += '+'
806 m1['.hgsubstate'] += '+'
807 break
807 break
808
808
809 # Compare manifests
809 # Compare manifests
810 if matcher is not None:
810 if matcher is not None:
811 m1 = m1.matches(matcher)
811 m1 = m1.matches(matcher)
812 m2 = m2.matches(matcher)
812 m2 = m2.matches(matcher)
813 diff = m1.diff(m2)
813 diff = m1.diff(m2)
814
814
815 actions = {}
815 actions = {}
816 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
816 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
817 if n1 and n2: # file exists on both local and remote side
817 if n1 and n2: # file exists on both local and remote side
818 if f not in ma:
818 if f not in ma:
819 fa = copy.get(f, None)
819 fa = copy.get(f, None)
820 if fa is not None:
820 if fa is not None:
821 actions[f] = ('m', (f, f, fa, False, pa.node()),
821 actions[f] = ('m', (f, f, fa, False, pa.node()),
822 "both renamed from " + fa)
822 "both renamed from " + fa)
823 else:
823 else:
824 actions[f] = ('m', (f, f, None, False, pa.node()),
824 actions[f] = ('m', (f, f, None, False, pa.node()),
825 "both created")
825 "both created")
826 else:
826 else:
827 a = ma[f]
827 a = ma[f]
828 fla = ma.flags(f)
828 fla = ma.flags(f)
829 nol = 'l' not in fl1 + fl2 + fla
829 nol = 'l' not in fl1 + fl2 + fla
830 if n2 == a and fl2 == fla:
830 if n2 == a and fl2 == fla:
831 actions[f] = ('k' , (), "remote unchanged")
831 actions[f] = ('k' , (), "remote unchanged")
832 elif n1 == a and fl1 == fla: # local unchanged - use remote
832 elif n1 == a and fl1 == fla: # local unchanged - use remote
833 if n1 == n2: # optimization: keep local content
833 if n1 == n2: # optimization: keep local content
834 actions[f] = ('e', (fl2,), "update permissions")
834 actions[f] = ('e', (fl2,), "update permissions")
835 else:
835 else:
836 actions[f] = ('g', (fl2, False), "remote is newer")
836 actions[f] = ('g', (fl2, False), "remote is newer")
837 elif nol and n2 == a: # remote only changed 'x'
837 elif nol and n2 == a: # remote only changed 'x'
838 actions[f] = ('e', (fl2,), "update permissions")
838 actions[f] = ('e', (fl2,), "update permissions")
839 elif nol and n1 == a: # local only changed 'x'
839 elif nol and n1 == a: # local only changed 'x'
840 actions[f] = ('g', (fl1, False), "remote is newer")
840 actions[f] = ('g', (fl1, False), "remote is newer")
841 else: # both changed something
841 else: # both changed something
842 actions[f] = ('m', (f, f, f, False, pa.node()),
842 actions[f] = ('m', (f, f, f, False, pa.node()),
843 "versions differ")
843 "versions differ")
844 elif n1: # file exists only on local side
844 elif n1: # file exists only on local side
845 if f in copied:
845 if f in copied:
846 pass # we'll deal with it on m2 side
846 pass # we'll deal with it on m2 side
847 elif f in movewithdir: # directory rename, move local
847 elif f in movewithdir: # directory rename, move local
848 f2 = movewithdir[f]
848 f2 = movewithdir[f]
849 if f2 in m2:
849 if f2 in m2:
850 actions[f2] = ('m', (f, f2, None, True, pa.node()),
850 actions[f2] = ('m', (f, f2, None, True, pa.node()),
851 "remote directory rename, both created")
851 "remote directory rename, both created")
852 else:
852 else:
853 actions[f2] = ('dm', (f, fl1),
853 actions[f2] = ('dm', (f, fl1),
854 "remote directory rename - move from " + f)
854 "remote directory rename - move from " + f)
855 elif f in copy:
855 elif f in copy:
856 f2 = copy[f]
856 f2 = copy[f]
857 actions[f] = ('m', (f, f2, f2, False, pa.node()),
857 actions[f] = ('m', (f, f2, f2, False, pa.node()),
858 "local copied/moved from " + f2)
858 "local copied/moved from " + f2)
859 elif f in ma: # clean, a different, no remote
859 elif f in ma: # clean, a different, no remote
860 if n1 != ma[f]:
860 if n1 != ma[f]:
861 if acceptremote:
861 if acceptremote:
862 actions[f] = ('r', None, "remote delete")
862 actions[f] = ('r', None, "remote delete")
863 else:
863 else:
864 actions[f] = ('cd', (f, None, f, False, pa.node()),
864 actions[f] = ('cd', (f, None, f, False, pa.node()),
865 "prompt changed/deleted")
865 "prompt changed/deleted")
866 elif n1[20:] == 'a':
866 elif n1[20:] == 'a':
867 # This extra 'a' is added by working copy manifest to mark
867 # This extra 'a' is added by working copy manifest to mark
868 # the file as locally added. We should forget it instead of
868 # the file as locally added. We should forget it instead of
869 # deleting it.
869 # deleting it.
870 actions[f] = ('f', None, "remote deleted")
870 actions[f] = ('f', None, "remote deleted")
871 else:
871 else:
872 actions[f] = ('r', None, "other deleted")
872 actions[f] = ('r', None, "other deleted")
873 elif n2: # file exists only on remote side
873 elif n2: # file exists only on remote side
874 if f in copied:
874 if f in copied:
875 pass # we'll deal with it on m1 side
875 pass # we'll deal with it on m1 side
876 elif f in movewithdir:
876 elif f in movewithdir:
877 f2 = movewithdir[f]
877 f2 = movewithdir[f]
878 if f2 in m1:
878 if f2 in m1:
879 actions[f2] = ('m', (f2, f, None, False, pa.node()),
879 actions[f2] = ('m', (f2, f, None, False, pa.node()),
880 "local directory rename, both created")
880 "local directory rename, both created")
881 else:
881 else:
882 actions[f2] = ('dg', (f, fl2),
882 actions[f2] = ('dg', (f, fl2),
883 "local directory rename - get from " + f)
883 "local directory rename - get from " + f)
884 elif f in copy:
884 elif f in copy:
885 f2 = copy[f]
885 f2 = copy[f]
886 if f2 in m2:
886 if f2 in m2:
887 actions[f] = ('m', (f2, f, f2, False, pa.node()),
887 actions[f] = ('m', (f2, f, f2, False, pa.node()),
888 "remote copied from " + f2)
888 "remote copied from " + f2)
889 else:
889 else:
890 actions[f] = ('m', (f2, f, f2, True, pa.node()),
890 actions[f] = ('m', (f2, f, f2, True, pa.node()),
891 "remote moved from " + f2)
891 "remote moved from " + f2)
892 elif f not in ma:
892 elif f not in ma:
893 # local unknown, remote created: the logic is described by the
893 # local unknown, remote created: the logic is described by the
894 # following table:
894 # following table:
895 #
895 #
896 # force branchmerge different | action
896 # force branchmerge different | action
897 # n * * | create
897 # n * * | create
898 # y n * | create
898 # y n * | create
899 # y y n | create
899 # y y n | create
900 # y y y | merge
900 # y y y | merge
901 #
901 #
902 # Checking whether the files are different is expensive, so we
902 # Checking whether the files are different is expensive, so we
903 # don't do that when we can avoid it.
903 # don't do that when we can avoid it.
904 if not force:
904 if not force:
905 actions[f] = ('c', (fl2,), "remote created")
905 actions[f] = ('c', (fl2,), "remote created")
906 elif not branchmerge:
906 elif not branchmerge:
907 actions[f] = ('c', (fl2,), "remote created")
907 actions[f] = ('c', (fl2,), "remote created")
908 else:
908 else:
909 actions[f] = ('cm', (fl2, pa.node()),
909 actions[f] = ('cm', (fl2, pa.node()),
910 "remote created, get or merge")
910 "remote created, get or merge")
911 elif n2 != ma[f]:
911 elif n2 != ma[f]:
912 if acceptremote:
912 if acceptremote:
913 actions[f] = ('c', (fl2,), "remote recreating")
913 actions[f] = ('c', (fl2,), "remote recreating")
914 else:
914 else:
915 actions[f] = ('dc', (None, f, f, False, pa.node()),
915 actions[f] = ('dc', (None, f, f, False, pa.node()),
916 "prompt deleted/changed")
916 "prompt deleted/changed")
917
917
918 return actions, diverge, renamedelete
918 return actions, diverge, renamedelete
919
919
920 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
920 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
921 """Resolves false conflicts where the nodeid changed but the content
921 """Resolves false conflicts where the nodeid changed but the content
922 remained the same."""
922 remained the same."""
923
923
924 for f, (m, args, msg) in actions.items():
924 for f, (m, args, msg) in actions.items():
925 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
925 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
926 # local did change but ended up with same content
926 # local did change but ended up with same content
927 actions[f] = 'r', None, "prompt same"
927 actions[f] = 'r', None, "prompt same"
928 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
928 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
929 # remote did change but ended up with same content
929 # remote did change but ended up with same content
930 del actions[f] # don't get = keep local deleted
930 del actions[f] # don't get = keep local deleted
931
931
932 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
932 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
933 acceptremote, followcopies, matcher=None,
933 acceptremote, followcopies, matcher=None,
934 mergeforce=False):
934 mergeforce=False):
935 "Calculate the actions needed to merge mctx into wctx using ancestors"
935 "Calculate the actions needed to merge mctx into wctx using ancestors"
936 if len(ancestors) == 1: # default
936 if len(ancestors) == 1: # default
937 actions, diverge, renamedelete = manifestmerge(
937 actions, diverge, renamedelete = manifestmerge(
938 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
938 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
939 acceptremote, followcopies)
939 acceptremote, followcopies)
940 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
940 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
941
941
942 else: # only when merge.preferancestor=* - the default
942 else: # only when merge.preferancestor=* - the default
943 repo.ui.note(
943 repo.ui.note(
944 _("note: merging %s and %s using bids from ancestors %s\n") %
944 _("note: merging %s and %s using bids from ancestors %s\n") %
945 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
945 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
946
946
947 # Call for bids
947 # Call for bids
948 fbids = {} # mapping filename to bids (action method to list af actions)
948 fbids = {} # mapping filename to bids (action method to list af actions)
949 diverge, renamedelete = None, None
949 diverge, renamedelete = None, None
950 for ancestor in ancestors:
950 for ancestor in ancestors:
951 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
951 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
952 actions, diverge1, renamedelete1 = manifestmerge(
952 actions, diverge1, renamedelete1 = manifestmerge(
953 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
953 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
954 acceptremote, followcopies)
954 acceptremote, followcopies)
955 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
955 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
956
956
957 # Track the shortest set of warning on the theory that bid
957 # Track the shortest set of warning on the theory that bid
958 # merge will correctly incorporate more information
958 # merge will correctly incorporate more information
959 if diverge is None or len(diverge1) < len(diverge):
959 if diverge is None or len(diverge1) < len(diverge):
960 diverge = diverge1
960 diverge = diverge1
961 if renamedelete is None or len(renamedelete) < len(renamedelete1):
961 if renamedelete is None or len(renamedelete) < len(renamedelete1):
962 renamedelete = renamedelete1
962 renamedelete = renamedelete1
963
963
964 for f, a in sorted(actions.iteritems()):
964 for f, a in sorted(actions.iteritems()):
965 m, args, msg = a
965 m, args, msg = a
966 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
966 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
967 if f in fbids:
967 if f in fbids:
968 d = fbids[f]
968 d = fbids[f]
969 if m in d:
969 if m in d:
970 d[m].append(a)
970 d[m].append(a)
971 else:
971 else:
972 d[m] = [a]
972 d[m] = [a]
973 else:
973 else:
974 fbids[f] = {m: [a]}
974 fbids[f] = {m: [a]}
975
975
976 # Pick the best bid for each file
976 # Pick the best bid for each file
977 repo.ui.note(_('\nauction for merging merge bids\n'))
977 repo.ui.note(_('\nauction for merging merge bids\n'))
978 actions = {}
978 actions = {}
979 for f, bids in sorted(fbids.items()):
979 for f, bids in sorted(fbids.items()):
980 # bids is a mapping from action method to list af actions
980 # bids is a mapping from action method to list af actions
981 # Consensus?
981 # Consensus?
982 if len(bids) == 1: # all bids are the same kind of method
982 if len(bids) == 1: # all bids are the same kind of method
983 m, l = bids.items()[0]
983 m, l = bids.items()[0]
984 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
984 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
985 repo.ui.note(" %s: consensus for %s\n" % (f, m))
985 repo.ui.note(" %s: consensus for %s\n" % (f, m))
986 actions[f] = l[0]
986 actions[f] = l[0]
987 continue
987 continue
988 # If keep is an option, just do it.
988 # If keep is an option, just do it.
989 if 'k' in bids:
989 if 'k' in bids:
990 repo.ui.note(" %s: picking 'keep' action\n" % f)
990 repo.ui.note(" %s: picking 'keep' action\n" % f)
991 actions[f] = bids['k'][0]
991 actions[f] = bids['k'][0]
992 continue
992 continue
993 # If there are gets and they all agree [how could they not?], do it.
993 # If there are gets and they all agree [how could they not?], do it.
994 if 'g' in bids:
994 if 'g' in bids:
995 ga0 = bids['g'][0]
995 ga0 = bids['g'][0]
996 if all(a == ga0 for a in bids['g'][1:]):
996 if all(a == ga0 for a in bids['g'][1:]):
997 repo.ui.note(" %s: picking 'get' action\n" % f)
997 repo.ui.note(" %s: picking 'get' action\n" % f)
998 actions[f] = ga0
998 actions[f] = ga0
999 continue
999 continue
1000 # TODO: Consider other simple actions such as mode changes
1000 # TODO: Consider other simple actions such as mode changes
1001 # Handle inefficient democrazy.
1001 # Handle inefficient democrazy.
1002 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1002 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1003 for m, l in sorted(bids.items()):
1003 for m, l in sorted(bids.items()):
1004 for _f, args, msg in l:
1004 for _f, args, msg in l:
1005 repo.ui.note(' %s -> %s\n' % (msg, m))
1005 repo.ui.note(' %s -> %s\n' % (msg, m))
1006 # Pick random action. TODO: Instead, prompt user when resolving
1006 # Pick random action. TODO: Instead, prompt user when resolving
1007 m, l = bids.items()[0]
1007 m, l = bids.items()[0]
1008 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1008 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1009 (f, m))
1009 (f, m))
1010 actions[f] = l[0]
1010 actions[f] = l[0]
1011 continue
1011 continue
1012 repo.ui.note(_('end of auction\n\n'))
1012 repo.ui.note(_('end of auction\n\n'))
1013
1013
1014 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1014 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1015
1015
1016 if wctx.rev() is None:
1016 if wctx.rev() is None:
1017 fractions = _forgetremoved(wctx, mctx, branchmerge)
1017 fractions = _forgetremoved(wctx, mctx, branchmerge)
1018 actions.update(fractions)
1018 actions.update(fractions)
1019
1019
1020 return actions, diverge, renamedelete
1020 return actions, diverge, renamedelete
1021
1021
1022 def batchremove(repo, actions):
1022 def batchremove(repo, actions):
1023 """apply removes to the working directory
1023 """apply removes to the working directory
1024
1024
1025 yields tuples for progress updates
1025 yields tuples for progress updates
1026 """
1026 """
1027 verbose = repo.ui.verbose
1027 verbose = repo.ui.verbose
1028 unlink = util.unlinkpath
1028 unlink = util.unlinkpath
1029 wjoin = repo.wjoin
1029 wjoin = repo.wjoin
1030 audit = repo.wvfs.audit
1030 audit = repo.wvfs.audit
1031 i = 0
1031 i = 0
1032 for f, args, msg in actions:
1032 for f, args, msg in actions:
1033 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1033 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1034 if verbose:
1034 if verbose:
1035 repo.ui.note(_("removing %s\n") % f)
1035 repo.ui.note(_("removing %s\n") % f)
1036 audit(f)
1036 audit(f)
1037 try:
1037 try:
1038 unlink(wjoin(f), ignoremissing=True)
1038 unlink(wjoin(f), ignoremissing=True)
1039 except OSError as inst:
1039 except OSError as inst:
1040 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1040 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1041 (f, inst.strerror))
1041 (f, inst.strerror))
1042 if i == 100:
1042 if i == 100:
1043 yield i, f
1043 yield i, f
1044 i = 0
1044 i = 0
1045 i += 1
1045 i += 1
1046 if i > 0:
1046 if i > 0:
1047 yield i, f
1047 yield i, f
1048
1048
1049 def batchget(repo, mctx, actions):
1049 def batchget(repo, mctx, actions):
1050 """apply gets to the working directory
1050 """apply gets to the working directory
1051
1051
1052 mctx is the context to get from
1052 mctx is the context to get from
1053
1053
1054 yields tuples for progress updates
1054 yields tuples for progress updates
1055 """
1055 """
1056 verbose = repo.ui.verbose
1056 verbose = repo.ui.verbose
1057 fctx = mctx.filectx
1057 fctx = mctx.filectx
1058 wwrite = repo.wwrite
1058 wwrite = repo.wwrite
1059 ui = repo.ui
1059 ui = repo.ui
1060 i = 0
1060 i = 0
1061 for f, (flags, backup), msg in actions:
1061 for f, (flags, backup), msg in actions:
1062 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1062 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1063 if verbose:
1063 if verbose:
1064 repo.ui.note(_("getting %s\n") % f)
1064 repo.ui.note(_("getting %s\n") % f)
1065
1065
1066 if backup:
1066 if backup:
1067 absf = repo.wjoin(f)
1067 absf = repo.wjoin(f)
1068 orig = scmutil.origpath(ui, repo, absf)
1068 orig = scmutil.origpath(ui, repo, absf)
1069 try:
1069 try:
1070 # TODO Mercurial has always aborted if an untracked directory
1070 # TODO Mercurial has always aborted if an untracked directory
1071 # is replaced by a tracked file, or generally with
1071 # is replaced by a tracked file, or generally with
1072 # file/directory merges. This needs to be sorted out.
1072 # file/directory merges. This needs to be sorted out.
1073 if repo.wvfs.isfileorlink(f):
1073 if repo.wvfs.isfileorlink(f):
1074 util.rename(absf, orig)
1074 util.rename(absf, orig)
1075 except OSError as e:
1075 except OSError as e:
1076 if e.errno != errno.ENOENT:
1076 if e.errno != errno.ENOENT:
1077 raise
1077 raise
1078
1078
1079 wwrite(f, fctx(f).data(), flags)
1079 wwrite(f, fctx(f).data(), flags)
1080 if i == 100:
1080 if i == 100:
1081 yield i, f
1081 yield i, f
1082 i = 0
1082 i = 0
1083 i += 1
1083 i += 1
1084 if i > 0:
1084 if i > 0:
1085 yield i, f
1085 yield i, f
1086
1086
1087 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1087 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1088 """apply the merge action list to the working directory
1088 """apply the merge action list to the working directory
1089
1089
1090 wctx is the working copy context
1090 wctx is the working copy context
1091 mctx is the context to be merged into the working copy
1091 mctx is the context to be merged into the working copy
1092
1092
1093 Return a tuple of counts (updated, merged, removed, unresolved) that
1093 Return a tuple of counts (updated, merged, removed, unresolved) that
1094 describes how many files were affected by the update.
1094 describes how many files were affected by the update.
1095 """
1095 """
1096
1096
1097 updated, merged, removed = 0, 0, 0
1097 updated, merged, removed = 0, 0, 0
1098 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
1098 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
1099 moves = []
1099 moves = []
1100 for m, l in actions.items():
1100 for m, l in actions.items():
1101 l.sort()
1101 l.sort()
1102
1102
1103 # 'cd' and 'dc' actions are treated like other merge conflicts
1103 # 'cd' and 'dc' actions are treated like other merge conflicts
1104 mergeactions = sorted(actions['cd'])
1104 mergeactions = sorted(actions['cd'])
1105 mergeactions.extend(sorted(actions['dc']))
1105 mergeactions.extend(sorted(actions['dc']))
1106 mergeactions.extend(actions['m'])
1106 mergeactions.extend(actions['m'])
1107 for f, args, msg in mergeactions:
1107 for f, args, msg in mergeactions:
1108 f1, f2, fa, move, anc = args
1108 f1, f2, fa, move, anc = args
1109 if f == '.hgsubstate': # merged internally
1109 if f == '.hgsubstate': # merged internally
1110 continue
1110 continue
1111 if f1 is None:
1111 if f1 is None:
1112 fcl = filemerge.absentfilectx(wctx, fa)
1112 fcl = filemerge.absentfilectx(wctx, fa)
1113 else:
1113 else:
1114 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1114 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1115 fcl = wctx[f1]
1115 fcl = wctx[f1]
1116 if f2 is None:
1116 if f2 is None:
1117 fco = filemerge.absentfilectx(mctx, fa)
1117 fco = filemerge.absentfilectx(mctx, fa)
1118 else:
1118 else:
1119 fco = mctx[f2]
1119 fco = mctx[f2]
1120 actx = repo[anc]
1120 actx = repo[anc]
1121 if fa in actx:
1121 if fa in actx:
1122 fca = actx[fa]
1122 fca = actx[fa]
1123 else:
1123 else:
1124 # TODO: move to absentfilectx
1124 # TODO: move to absentfilectx
1125 fca = repo.filectx(f1, fileid=nullrev)
1125 fca = repo.filectx(f1, fileid=nullrev)
1126 ms.add(fcl, fco, fca, f)
1126 ms.add(fcl, fco, fca, f)
1127 if f1 != f and move:
1127 if f1 != f and move:
1128 moves.append(f1)
1128 moves.append(f1)
1129
1129
1130 audit = repo.wvfs.audit
1130 audit = repo.wvfs.audit
1131 _updating = _('updating')
1131 _updating = _('updating')
1132 _files = _('files')
1132 _files = _('files')
1133 progress = repo.ui.progress
1133 progress = repo.ui.progress
1134
1134
1135 # remove renamed files after safely stored
1135 # remove renamed files after safely stored
1136 for f in moves:
1136 for f in moves:
1137 if os.path.lexists(repo.wjoin(f)):
1137 if os.path.lexists(repo.wjoin(f)):
1138 repo.ui.debug("removing %s\n" % f)
1138 repo.ui.debug("removing %s\n" % f)
1139 audit(f)
1139 audit(f)
1140 util.unlinkpath(repo.wjoin(f))
1140 util.unlinkpath(repo.wjoin(f))
1141
1141
1142 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1142 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1143
1143
1144 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1144 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1145 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1145 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1146
1146
1147 # remove in parallel (must come first)
1147 # remove in parallel (must come first)
1148 z = 0
1148 z = 0
1149 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1149 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1150 for i, item in prog:
1150 for i, item in prog:
1151 z += i
1151 z += i
1152 progress(_updating, z, item=item, total=numupdates, unit=_files)
1152 progress(_updating, z, item=item, total=numupdates, unit=_files)
1153 removed = len(actions['r'])
1153 removed = len(actions['r'])
1154
1154
1155 # get in parallel
1155 # get in parallel
1156 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1156 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1157 for i, item in prog:
1157 for i, item in prog:
1158 z += i
1158 z += i
1159 progress(_updating, z, item=item, total=numupdates, unit=_files)
1159 progress(_updating, z, item=item, total=numupdates, unit=_files)
1160 updated = len(actions['g'])
1160 updated = len(actions['g'])
1161
1161
1162 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1162 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1163 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1163 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1164
1164
1165 # forget (manifest only, just log it) (must come first)
1165 # forget (manifest only, just log it) (must come first)
1166 for f, args, msg in actions['f']:
1166 for f, args, msg in actions['f']:
1167 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1167 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1168 z += 1
1168 z += 1
1169 progress(_updating, z, item=f, total=numupdates, unit=_files)
1169 progress(_updating, z, item=f, total=numupdates, unit=_files)
1170
1170
1171 # re-add (manifest only, just log it)
1171 # re-add (manifest only, just log it)
1172 for f, args, msg in actions['a']:
1172 for f, args, msg in actions['a']:
1173 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1173 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1174 z += 1
1174 z += 1
1175 progress(_updating, z, item=f, total=numupdates, unit=_files)
1175 progress(_updating, z, item=f, total=numupdates, unit=_files)
1176
1176
1177 # re-add/mark as modified (manifest only, just log it)
1177 # re-add/mark as modified (manifest only, just log it)
1178 for f, args, msg in actions['am']:
1178 for f, args, msg in actions['am']:
1179 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1179 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1180 z += 1
1180 z += 1
1181 progress(_updating, z, item=f, total=numupdates, unit=_files)
1181 progress(_updating, z, item=f, total=numupdates, unit=_files)
1182
1182
1183 # keep (noop, just log it)
1183 # keep (noop, just log it)
1184 for f, args, msg in actions['k']:
1184 for f, args, msg in actions['k']:
1185 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1185 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1186 # no progress
1186 # no progress
1187
1187
1188 # directory rename, move local
1188 # directory rename, move local
1189 for f, args, msg in actions['dm']:
1189 for f, args, msg in actions['dm']:
1190 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1190 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1191 z += 1
1191 z += 1
1192 progress(_updating, z, item=f, total=numupdates, unit=_files)
1192 progress(_updating, z, item=f, total=numupdates, unit=_files)
1193 f0, flags = args
1193 f0, flags = args
1194 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1194 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1195 audit(f)
1195 audit(f)
1196 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1196 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1197 util.unlinkpath(repo.wjoin(f0))
1197 util.unlinkpath(repo.wjoin(f0))
1198 updated += 1
1198 updated += 1
1199
1199
1200 # local directory rename, get
1200 # local directory rename, get
1201 for f, args, msg in actions['dg']:
1201 for f, args, msg in actions['dg']:
1202 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1202 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1203 z += 1
1203 z += 1
1204 progress(_updating, z, item=f, total=numupdates, unit=_files)
1204 progress(_updating, z, item=f, total=numupdates, unit=_files)
1205 f0, flags = args
1205 f0, flags = args
1206 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1206 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1207 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1207 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1208 updated += 1
1208 updated += 1
1209
1209
1210 # exec
1210 # exec
1211 for f, args, msg in actions['e']:
1211 for f, args, msg in actions['e']:
1212 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1212 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1213 z += 1
1213 z += 1
1214 progress(_updating, z, item=f, total=numupdates, unit=_files)
1214 progress(_updating, z, item=f, total=numupdates, unit=_files)
1215 flags, = args
1215 flags, = args
1216 audit(f)
1216 audit(f)
1217 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1217 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1218 updated += 1
1218 updated += 1
1219
1219
1220 # the ordering is important here -- ms.mergedriver will raise if the merge
1220 # the ordering is important here -- ms.mergedriver will raise if the merge
1221 # driver has changed, and we want to be able to bypass it when overwrite is
1221 # driver has changed, and we want to be able to bypass it when overwrite is
1222 # True
1222 # True
1223 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1223 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1224
1224
1225 if usemergedriver:
1225 if usemergedriver:
1226 ms.commit()
1226 ms.commit()
1227 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1227 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1228 # the driver might leave some files unresolved
1228 # the driver might leave some files unresolved
1229 unresolvedf = set(ms.unresolved())
1229 unresolvedf = set(ms.unresolved())
1230 if not proceed:
1230 if not proceed:
1231 # XXX setting unresolved to at least 1 is a hack to make sure we
1231 # XXX setting unresolved to at least 1 is a hack to make sure we
1232 # error out
1232 # error out
1233 return updated, merged, removed, max(len(unresolvedf), 1)
1233 return updated, merged, removed, max(len(unresolvedf), 1)
1234 newactions = []
1234 newactions = []
1235 for f, args, msg in mergeactions:
1235 for f, args, msg in mergeactions:
1236 if f in unresolvedf:
1236 if f in unresolvedf:
1237 newactions.append((f, args, msg))
1237 newactions.append((f, args, msg))
1238 mergeactions = newactions
1238 mergeactions = newactions
1239
1239
1240 # premerge
1240 # premerge
1241 tocomplete = []
1241 tocomplete = []
1242 for f, args, msg in mergeactions:
1242 for f, args, msg in mergeactions:
1243 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1243 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1244 z += 1
1244 z += 1
1245 progress(_updating, z, item=f, total=numupdates, unit=_files)
1245 progress(_updating, z, item=f, total=numupdates, unit=_files)
1246 if f == '.hgsubstate': # subrepo states need updating
1246 if f == '.hgsubstate': # subrepo states need updating
1247 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1247 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1248 overwrite)
1248 overwrite)
1249 continue
1249 continue
1250 audit(f)
1250 audit(f)
1251 complete, r = ms.preresolve(f, wctx, labels=labels)
1251 complete, r = ms.preresolve(f, wctx, labels=labels)
1252 if not complete:
1252 if not complete:
1253 numupdates += 1
1253 numupdates += 1
1254 tocomplete.append((f, args, msg))
1254 tocomplete.append((f, args, msg))
1255
1255
1256 # merge
1256 # merge
1257 for f, args, msg in tocomplete:
1257 for f, args, msg in tocomplete:
1258 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1258 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1259 z += 1
1259 z += 1
1260 progress(_updating, z, item=f, total=numupdates, unit=_files)
1260 progress(_updating, z, item=f, total=numupdates, unit=_files)
1261 ms.resolve(f, wctx, labels=labels)
1261 ms.resolve(f, wctx, labels=labels)
1262
1262
1263 ms.commit()
1263 ms.commit()
1264
1264
1265 unresolved = ms.unresolvedcount()
1265 unresolved = ms.unresolvedcount()
1266
1266
1267 if usemergedriver and not unresolved and ms.mdstate() != 's':
1267 if usemergedriver and not unresolved and ms.mdstate() != 's':
1268 if not driverconclude(repo, ms, wctx, labels=labels):
1268 if not driverconclude(repo, ms, wctx, labels=labels):
1269 # XXX setting unresolved to at least 1 is a hack to make sure we
1269 # XXX setting unresolved to at least 1 is a hack to make sure we
1270 # error out
1270 # error out
1271 unresolved = max(unresolved, 1)
1271 unresolved = max(unresolved, 1)
1272
1272
1273 ms.commit()
1273 ms.commit()
1274
1274
1275 msupdated, msmerged, msremoved = ms.counts()
1275 msupdated, msmerged, msremoved = ms.counts()
1276 updated += msupdated
1276 updated += msupdated
1277 merged += msmerged
1277 merged += msmerged
1278 removed += msremoved
1278 removed += msremoved
1279
1279
1280 extraactions = ms.actions()
1280 extraactions = ms.actions()
1281 for k, acts in extraactions.iteritems():
1281 for k, acts in extraactions.iteritems():
1282 actions[k].extend(acts)
1282 actions[k].extend(acts)
1283
1283
1284 progress(_updating, None, total=numupdates, unit=_files)
1284 progress(_updating, None, total=numupdates, unit=_files)
1285
1285
1286 return updated, merged, removed, unresolved
1286 return updated, merged, removed, unresolved
1287
1287
1288 def recordupdates(repo, actions, branchmerge):
1288 def recordupdates(repo, actions, branchmerge):
1289 "record merge actions to the dirstate"
1289 "record merge actions to the dirstate"
1290 # remove (must come first)
1290 # remove (must come first)
1291 for f, args, msg in actions.get('r', []):
1291 for f, args, msg in actions.get('r', []):
1292 if branchmerge:
1292 if branchmerge:
1293 repo.dirstate.remove(f)
1293 repo.dirstate.remove(f)
1294 else:
1294 else:
1295 repo.dirstate.drop(f)
1295 repo.dirstate.drop(f)
1296
1296
1297 # forget (must come first)
1297 # forget (must come first)
1298 for f, args, msg in actions.get('f', []):
1298 for f, args, msg in actions.get('f', []):
1299 repo.dirstate.drop(f)
1299 repo.dirstate.drop(f)
1300
1300
1301 # re-add
1301 # re-add
1302 for f, args, msg in actions.get('a', []):
1302 for f, args, msg in actions.get('a', []):
1303 repo.dirstate.add(f)
1303 repo.dirstate.add(f)
1304
1304
1305 # re-add/mark as modified
1305 # re-add/mark as modified
1306 for f, args, msg in actions.get('am', []):
1306 for f, args, msg in actions.get('am', []):
1307 if branchmerge:
1307 if branchmerge:
1308 repo.dirstate.normallookup(f)
1308 repo.dirstate.normallookup(f)
1309 else:
1309 else:
1310 repo.dirstate.add(f)
1310 repo.dirstate.add(f)
1311
1311
1312 # exec change
1312 # exec change
1313 for f, args, msg in actions.get('e', []):
1313 for f, args, msg in actions.get('e', []):
1314 repo.dirstate.normallookup(f)
1314 repo.dirstate.normallookup(f)
1315
1315
1316 # keep
1316 # keep
1317 for f, args, msg in actions.get('k', []):
1317 for f, args, msg in actions.get('k', []):
1318 pass
1318 pass
1319
1319
1320 # get
1320 # get
1321 for f, args, msg in actions.get('g', []):
1321 for f, args, msg in actions.get('g', []):
1322 if branchmerge:
1322 if branchmerge:
1323 repo.dirstate.otherparent(f)
1323 repo.dirstate.otherparent(f)
1324 else:
1324 else:
1325 repo.dirstate.normal(f)
1325 repo.dirstate.normal(f)
1326
1326
1327 # merge
1327 # merge
1328 for f, args, msg in actions.get('m', []):
1328 for f, args, msg in actions.get('m', []):
1329 f1, f2, fa, move, anc = args
1329 f1, f2, fa, move, anc = args
1330 if branchmerge:
1330 if branchmerge:
1331 # We've done a branch merge, mark this file as merged
1331 # We've done a branch merge, mark this file as merged
1332 # so that we properly record the merger later
1332 # so that we properly record the merger later
1333 repo.dirstate.merge(f)
1333 repo.dirstate.merge(f)
1334 if f1 != f2: # copy/rename
1334 if f1 != f2: # copy/rename
1335 if move:
1335 if move:
1336 repo.dirstate.remove(f1)
1336 repo.dirstate.remove(f1)
1337 if f1 != f:
1337 if f1 != f:
1338 repo.dirstate.copy(f1, f)
1338 repo.dirstate.copy(f1, f)
1339 else:
1339 else:
1340 repo.dirstate.copy(f2, f)
1340 repo.dirstate.copy(f2, f)
1341 else:
1341 else:
1342 # We've update-merged a locally modified file, so
1342 # We've update-merged a locally modified file, so
1343 # we set the dirstate to emulate a normal checkout
1343 # we set the dirstate to emulate a normal checkout
1344 # of that file some time in the past. Thus our
1344 # of that file some time in the past. Thus our
1345 # merge will appear as a normal local file
1345 # merge will appear as a normal local file
1346 # modification.
1346 # modification.
1347 if f2 == f: # file not locally copied/moved
1347 if f2 == f: # file not locally copied/moved
1348 repo.dirstate.normallookup(f)
1348 repo.dirstate.normallookup(f)
1349 if move:
1349 if move:
1350 repo.dirstate.drop(f1)
1350 repo.dirstate.drop(f1)
1351
1351
1352 # directory rename, move local
1352 # directory rename, move local
1353 for f, args, msg in actions.get('dm', []):
1353 for f, args, msg in actions.get('dm', []):
1354 f0, flag = args
1354 f0, flag = args
1355 if branchmerge:
1355 if branchmerge:
1356 repo.dirstate.add(f)
1356 repo.dirstate.add(f)
1357 repo.dirstate.remove(f0)
1357 repo.dirstate.remove(f0)
1358 repo.dirstate.copy(f0, f)
1358 repo.dirstate.copy(f0, f)
1359 else:
1359 else:
1360 repo.dirstate.normal(f)
1360 repo.dirstate.normal(f)
1361 repo.dirstate.drop(f0)
1361 repo.dirstate.drop(f0)
1362
1362
1363 # directory rename, get
1363 # directory rename, get
1364 for f, args, msg in actions.get('dg', []):
1364 for f, args, msg in actions.get('dg', []):
1365 f0, flag = args
1365 f0, flag = args
1366 if branchmerge:
1366 if branchmerge:
1367 repo.dirstate.add(f)
1367 repo.dirstate.add(f)
1368 repo.dirstate.copy(f0, f)
1368 repo.dirstate.copy(f0, f)
1369 else:
1369 else:
1370 repo.dirstate.normal(f)
1370 repo.dirstate.normal(f)
1371
1371
1372 def update(repo, node, branchmerge, force, ancestor=None,
1372 def update(repo, node, branchmerge, force, ancestor=None,
1373 mergeancestor=False, labels=None, matcher=None, mergeforce=False):
1373 mergeancestor=False, labels=None, matcher=None, mergeforce=False):
1374 """
1374 """
1375 Perform a merge between the working directory and the given node
1375 Perform a merge between the working directory and the given node
1376
1376
1377 node = the node to update to, or None if unspecified
1377 node = the node to update to, or None if unspecified
1378 branchmerge = whether to merge between branches
1378 branchmerge = whether to merge between branches
1379 force = whether to force branch merging or file overwriting
1379 force = whether to force branch merging or file overwriting
1380 matcher = a matcher to filter file lists (dirstate not updated)
1380 matcher = a matcher to filter file lists (dirstate not updated)
1381 mergeancestor = whether it is merging with an ancestor. If true,
1381 mergeancestor = whether it is merging with an ancestor. If true,
1382 we should accept the incoming changes for any prompts that occur.
1382 we should accept the incoming changes for any prompts that occur.
1383 If false, merging with an ancestor (fast-forward) is only allowed
1383 If false, merging with an ancestor (fast-forward) is only allowed
1384 between different named branches. This flag is used by rebase extension
1384 between different named branches. This flag is used by rebase extension
1385 as a temporary fix and should be avoided in general.
1385 as a temporary fix and should be avoided in general.
1386 labels = labels to use for base, local and other
1386 labels = labels to use for base, local and other
1387 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1387 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1388 this is True, then 'force' should be True as well.
1388 this is True, then 'force' should be True as well.
1389
1389
1390 The table below shows all the behaviors of the update command
1390 The table below shows all the behaviors of the update command
1391 given the -c and -C or no options, whether the working directory
1391 given the -c and -C or no options, whether the working directory
1392 is dirty, whether a revision is specified, and the relationship of
1392 is dirty, whether a revision is specified, and the relationship of
1393 the parent rev to the target rev (linear, on the same named
1393 the parent rev to the target rev (linear, on the same named
1394 branch, or on another named branch).
1394 branch, or on another named branch).
1395
1395
1396 This logic is tested by test-update-branches.t.
1396 This logic is tested by test-update-branches.t.
1397
1397
1398 -c -C dirty rev | linear same cross
1398 -c -C dirty rev | linear same cross
1399 n n n n | ok (1) x
1399 n n n n | ok (1) x
1400 n n n y | ok ok ok
1400 n n n y | ok ok ok
1401 n n y n | merge (2) (2)
1401 n n y n | merge (2) (2)
1402 n n y y | merge (3) (3)
1402 n n y y | merge (3) (3)
1403 n y * * | discard discard discard
1403 n y * * | discard discard discard
1404 y n y * | (4) (4) (4)
1404 y n y * | (4) (4) (4)
1405 y n n * | ok ok ok
1405 y n n * | ok ok ok
1406 y y * * | (5) (5) (5)
1406 y y * * | (5) (5) (5)
1407
1407
1408 x = can't happen
1408 x = can't happen
1409 * = don't-care
1409 * = don't-care
1410 1 = abort: not a linear update (merge or update --check to force update)
1410 1 = abort: not a linear update (merge or update --check to force update)
1411 2 = abort: uncommitted changes (commit and merge, or update --clean to
1411 2 = abort: uncommitted changes (commit and merge, or update --clean to
1412 discard changes)
1412 discard changes)
1413 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1413 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1414 4 = abort: uncommitted changes (checked in commands.py)
1414 4 = abort: uncommitted changes (checked in commands.py)
1415 5 = incompatible options (checked in commands.py)
1415 5 = incompatible options (checked in commands.py)
1416
1416
1417 Return the same tuple as applyupdates().
1417 Return the same tuple as applyupdates().
1418 """
1418 """
1419
1419
1420 onode = node
1420 onode = node
1421 # If we're doing a partial update, we need to skip updating
1421 # If we're doing a partial update, we need to skip updating
1422 # the dirstate, so make a note of any partial-ness to the
1422 # the dirstate, so make a note of any partial-ness to the
1423 # update here.
1423 # update here.
1424 if matcher is None or matcher.always():
1424 if matcher is None or matcher.always():
1425 partial = False
1425 partial = False
1426 else:
1426 else:
1427 partial = True
1427 partial = True
1428 with repo.wlock():
1428 with repo.wlock():
1429 wc = repo[None]
1429 wc = repo[None]
1430 pl = wc.parents()
1430 pl = wc.parents()
1431 p1 = pl[0]
1431 p1 = pl[0]
1432 pas = [None]
1432 pas = [None]
1433 if ancestor is not None:
1433 if ancestor is not None:
1434 pas = [repo[ancestor]]
1434 pas = [repo[ancestor]]
1435
1435
1436 if node is None:
1436 if node is None:
1437 if (repo.ui.configbool('devel', 'all-warnings')
1437 if (repo.ui.configbool('devel', 'all-warnings')
1438 or repo.ui.configbool('devel', 'oldapi')):
1438 or repo.ui.configbool('devel', 'oldapi')):
1439 repo.ui.develwarn('update with no target')
1439 repo.ui.develwarn('update with no target')
1440 rev, _mark, _act = destutil.destupdate(repo)
1440 rev, _mark, _act = destutil.destupdate(repo)
1441 node = repo[rev].node()
1441 node = repo[rev].node()
1442
1442
1443 overwrite = force and not branchmerge
1443 overwrite = force and not branchmerge
1444
1444
1445 p2 = repo[node]
1445 p2 = repo[node]
1446 if pas[0] is None:
1446 if pas[0] is None:
1447 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1447 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1448 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1448 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1449 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1449 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1450 else:
1450 else:
1451 pas = [p1.ancestor(p2, warn=branchmerge)]
1451 pas = [p1.ancestor(p2, warn=branchmerge)]
1452
1452
1453 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1453 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1454
1454
1455 ### check phase
1455 ### check phase
1456 if not overwrite:
1456 if not overwrite:
1457 if len(pl) > 1:
1457 if len(pl) > 1:
1458 raise error.Abort(_("outstanding uncommitted merge"))
1458 raise error.Abort(_("outstanding uncommitted merge"))
1459 ms = mergestate.read(repo)
1459 ms = mergestate.read(repo)
1460 if list(ms.unresolved()):
1460 if list(ms.unresolved()):
1461 raise error.Abort(_("outstanding merge conflicts"))
1461 raise error.Abort(_("outstanding merge conflicts"))
1462 if branchmerge:
1462 if branchmerge:
1463 if pas == [p2]:
1463 if pas == [p2]:
1464 raise error.Abort(_("merging with a working directory ancestor"
1464 raise error.Abort(_("merging with a working directory ancestor"
1465 " has no effect"))
1465 " has no effect"))
1466 elif pas == [p1]:
1466 elif pas == [p1]:
1467 if not mergeancestor and p1.branch() == p2.branch():
1467 if not mergeancestor and p1.branch() == p2.branch():
1468 raise error.Abort(_("nothing to merge"),
1468 raise error.Abort(_("nothing to merge"),
1469 hint=_("use 'hg update' "
1469 hint=_("use 'hg update' "
1470 "or check 'hg heads'"))
1470 "or check 'hg heads'"))
1471 if not force and (wc.files() or wc.deleted()):
1471 if not force and (wc.files() or wc.deleted()):
1472 raise error.Abort(_("uncommitted changes"),
1472 raise error.Abort(_("uncommitted changes"),
1473 hint=_("use 'hg status' to list changes"))
1473 hint=_("use 'hg status' to list changes"))
1474 for s in sorted(wc.substate):
1474 for s in sorted(wc.substate):
1475 wc.sub(s).bailifchanged()
1475 wc.sub(s).bailifchanged()
1476
1476
1477 elif not overwrite:
1477 elif not overwrite:
1478 if p1 == p2: # no-op update
1478 if p1 == p2: # no-op update
1479 # call the hooks and exit early
1479 # call the hooks and exit early
1480 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1480 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1481 repo.hook('update', parent1=xp2, parent2='', error=0)
1481 repo.hook('update', parent1=xp2, parent2='', error=0)
1482 return 0, 0, 0, 0
1482 return 0, 0, 0, 0
1483
1483
1484 if pas not in ([p1], [p2]): # nonlinear
1484 if pas not in ([p1], [p2]): # nonlinear
1485 dirty = wc.dirty(missing=True)
1485 dirty = wc.dirty(missing=True)
1486 if dirty or onode is None:
1486 if dirty or onode is None:
1487 # Branching is a bit strange to ensure we do the minimal
1487 # Branching is a bit strange to ensure we do the minimal
1488 # amount of call to obsolete.background.
1488 # amount of call to obsolete.background.
1489 foreground = obsolete.foreground(repo, [p1.node()])
1489 foreground = obsolete.foreground(repo, [p1.node()])
1490 # note: the <node> variable contains a random identifier
1490 # note: the <node> variable contains a random identifier
1491 if repo[node].node() in foreground:
1491 if repo[node].node() in foreground:
1492 pas = [p1] # allow updating to successors
1492 pas = [p1] # allow updating to successors
1493 elif dirty:
1493 elif dirty:
1494 msg = _("uncommitted changes")
1494 msg = _("uncommitted changes")
1495 if onode is None:
1495 if onode is None:
1496 hint = _("commit and merge, or update --clean to"
1496 hint = _("commit and merge, or update --clean to"
1497 " discard changes")
1497 " discard changes")
1498 else:
1498 else:
1499 hint = _("commit or update --clean to discard"
1499 hint = _("commit or update --clean to discard"
1500 " changes")
1500 " changes")
1501 raise error.Abort(msg, hint=hint)
1501 raise error.Abort(msg, hint=hint)
1502 else: # node is none
1502 else: # node is none
1503 msg = _("not a linear update")
1503 msg = _("not a linear update")
1504 hint = _("merge or update --check to force update")
1504 hint = _("merge or update --check to force update")
1505 raise error.Abort(msg, hint=hint)
1505 raise error.Abort(msg, hint=hint)
1506 else:
1506 else:
1507 # Allow jumping branches if clean and specific rev given
1507 # Allow jumping branches if clean and specific rev given
1508 pas = [p1]
1508 pas = [p1]
1509
1509
1510 # deprecated config: merge.followcopies
1510 # deprecated config: merge.followcopies
1511 followcopies = False
1511 followcopies = False
1512 if overwrite:
1512 if overwrite:
1513 pas = [wc]
1513 pas = [wc]
1514 elif pas == [p2]: # backwards
1514 elif pas == [p2]: # backwards
1515 pas = [wc.p1()]
1515 pas = [wc.p1()]
1516 elif not branchmerge and not wc.dirty(missing=True):
1516 elif not branchmerge and not wc.dirty(missing=True):
1517 pass
1517 pass
1518 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1518 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1519 followcopies = True
1519 followcopies = True
1520
1520
1521 ### calculate phase
1521 ### calculate phase
1522 actionbyfile, diverge, renamedelete = calculateupdates(
1522 actionbyfile, diverge, renamedelete = calculateupdates(
1523 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1523 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1524 followcopies, matcher=matcher, mergeforce=mergeforce)
1524 followcopies, matcher=matcher, mergeforce=mergeforce)
1525
1525
1526 # Prompt and create actions. Most of this is in the resolve phase
1526 # Prompt and create actions. Most of this is in the resolve phase
1527 # already, but we can't handle .hgsubstate in filemerge or
1527 # already, but we can't handle .hgsubstate in filemerge or
1528 # subrepo.submerge yet so we have to keep prompting for it.
1528 # subrepo.submerge yet so we have to keep prompting for it.
1529 if '.hgsubstate' in actionbyfile:
1529 if '.hgsubstate' in actionbyfile:
1530 f = '.hgsubstate'
1530 f = '.hgsubstate'
1531 m, args, msg = actionbyfile[f]
1531 m, args, msg = actionbyfile[f]
1532 if m == 'cd':
1532 if m == 'cd':
1533 if repo.ui.promptchoice(
1533 if repo.ui.promptchoice(
1534 _("local changed %s which remote deleted\n"
1534 _("local changed %s which remote deleted\n"
1535 "use (c)hanged version or (d)elete?"
1535 "use (c)hanged version or (d)elete?"
1536 "$$ &Changed $$ &Delete") % f, 0):
1536 "$$ &Changed $$ &Delete") % f, 0):
1537 actionbyfile[f] = ('r', None, "prompt delete")
1537 actionbyfile[f] = ('r', None, "prompt delete")
1538 elif f in p1:
1538 elif f in p1:
1539 actionbyfile[f] = ('am', None, "prompt keep")
1539 actionbyfile[f] = ('am', None, "prompt keep")
1540 else:
1540 else:
1541 actionbyfile[f] = ('a', None, "prompt keep")
1541 actionbyfile[f] = ('a', None, "prompt keep")
1542 elif m == 'dc':
1542 elif m == 'dc':
1543 f1, f2, fa, move, anc = args
1543 f1, f2, fa, move, anc = args
1544 flags = p2[f2].flags()
1544 flags = p2[f2].flags()
1545 if repo.ui.promptchoice(
1545 if repo.ui.promptchoice(
1546 _("remote changed %s which local deleted\n"
1546 _("remote changed %s which local deleted\n"
1547 "use (c)hanged version or leave (d)eleted?"
1547 "use (c)hanged version or leave (d)eleted?"
1548 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1548 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1549 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1549 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1550 else:
1550 else:
1551 del actionbyfile[f]
1551 del actionbyfile[f]
1552
1552
1553 # Convert to dictionary-of-lists format
1553 # Convert to dictionary-of-lists format
1554 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1554 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1555 for f, (m, args, msg) in actionbyfile.iteritems():
1555 for f, (m, args, msg) in actionbyfile.iteritems():
1556 if m not in actions:
1556 if m not in actions:
1557 actions[m] = []
1557 actions[m] = []
1558 actions[m].append((f, args, msg))
1558 actions[m].append((f, args, msg))
1559
1559
1560 if not util.checkcase(repo.path):
1560 if not util.checkcase(repo.path):
1561 # check collision between files only in p2 for clean update
1561 # check collision between files only in p2 for clean update
1562 if (not branchmerge and
1562 if (not branchmerge and
1563 (force or not wc.dirty(missing=True, branch=False))):
1563 (force or not wc.dirty(missing=True, branch=False))):
1564 _checkcollision(repo, p2.manifest(), None)
1564 _checkcollision(repo, p2.manifest(), None)
1565 else:
1565 else:
1566 _checkcollision(repo, wc.manifest(), actions)
1566 _checkcollision(repo, wc.manifest(), actions)
1567
1567
1568 # divergent renames
1568 # divergent renames
1569 for f, fl in sorted(diverge.iteritems()):
1569 for f, fl in sorted(diverge.iteritems()):
1570 repo.ui.warn(_("note: possible conflict - %s was renamed "
1570 repo.ui.warn(_("note: possible conflict - %s was renamed "
1571 "multiple times to:\n") % f)
1571 "multiple times to:\n") % f)
1572 for nf in fl:
1572 for nf in fl:
1573 repo.ui.warn(" %s\n" % nf)
1573 repo.ui.warn(" %s\n" % nf)
1574
1574
1575 # rename and delete
1575 # rename and delete
1576 for f, fl in sorted(renamedelete.iteritems()):
1576 for f, fl in sorted(renamedelete.iteritems()):
1577 repo.ui.warn(_("note: possible conflict - %s was deleted "
1577 repo.ui.warn(_("note: possible conflict - %s was deleted "
1578 "and renamed to:\n") % f)
1578 "and renamed to:\n") % f)
1579 for nf in fl:
1579 for nf in fl:
1580 repo.ui.warn(" %s\n" % nf)
1580 repo.ui.warn(" %s\n" % nf)
1581
1581
1582 ### apply phase
1582 ### apply phase
1583 if not branchmerge: # just jump to the new rev
1583 if not branchmerge: # just jump to the new rev
1584 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1584 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1585 if not partial:
1585 if not partial:
1586 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1586 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1587 # note that we're in the middle of an update
1587 # note that we're in the middle of an update
1588 repo.vfs.write('updatestate', p2.hex())
1588 repo.vfs.write('updatestate', p2.hex())
1589
1589
1590 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1590 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1591
1591
1592 if not partial:
1592 if not partial:
1593 repo.dirstate.beginparentchange()
1593 repo.dirstate.beginparentchange()
1594 repo.setparents(fp1, fp2)
1594 repo.setparents(fp1, fp2)
1595 recordupdates(repo, actions, branchmerge)
1595 recordupdates(repo, actions, branchmerge)
1596 # update completed, clear state
1596 # update completed, clear state
1597 util.unlink(repo.join('updatestate'))
1597 util.unlink(repo.join('updatestate'))
1598
1598
1599 if not branchmerge:
1599 if not branchmerge:
1600 repo.dirstate.setbranch(p2.branch())
1600 repo.dirstate.setbranch(p2.branch())
1601 repo.dirstate.endparentchange()
1601 repo.dirstate.endparentchange()
1602
1602
1603 if not partial:
1603 if not partial:
1604 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1604 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1605 return stats
1605 return stats
1606
1606
1607 def graft(repo, ctx, pctx, labels, keepparent=False):
1607 def graft(repo, ctx, pctx, labels, keepparent=False):
1608 """Do a graft-like merge.
1608 """Do a graft-like merge.
1609
1609
1610 This is a merge where the merge ancestor is chosen such that one
1610 This is a merge where the merge ancestor is chosen such that one
1611 or more changesets are grafted onto the current changeset. In
1611 or more changesets are grafted onto the current changeset. In
1612 addition to the merge, this fixes up the dirstate to include only
1612 addition to the merge, this fixes up the dirstate to include only
1613 a single parent (if keepparent is False) and tries to duplicate any
1613 a single parent (if keepparent is False) and tries to duplicate any
1614 renames/copies appropriately.
1614 renames/copies appropriately.
1615
1615
1616 ctx - changeset to rebase
1616 ctx - changeset to rebase
1617 pctx - merge base, usually ctx.p1()
1617 pctx - merge base, usually ctx.p1()
1618 labels - merge labels eg ['local', 'graft']
1618 labels - merge labels eg ['local', 'graft']
1619 keepparent - keep second parent if any
1619 keepparent - keep second parent if any
1620
1620
1621 """
1621 """
1622 # If we're grafting a descendant onto an ancestor, be sure to pass
1622 # If we're grafting a descendant onto an ancestor, be sure to pass
1623 # mergeancestor=True to update. This does two things: 1) allows the merge if
1623 # mergeancestor=True to update. This does two things: 1) allows the merge if
1624 # the destination is the same as the parent of the ctx (so we can use graft
1624 # the destination is the same as the parent of the ctx (so we can use graft
1625 # to copy commits), and 2) informs update that the incoming changes are
1625 # to copy commits), and 2) informs update that the incoming changes are
1626 # newer than the destination so it doesn't prompt about "remote changed foo
1626 # newer than the destination so it doesn't prompt about "remote changed foo
1627 # which local deleted".
1627 # which local deleted".
1628 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1628 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1629
1629
1630 stats = update(repo, ctx.node(), True, True, pctx.node(),
1630 stats = update(repo, ctx.node(), True, True, pctx.node(),
1631 mergeancestor=mergeancestor, labels=labels)
1631 mergeancestor=mergeancestor, labels=labels)
1632
1632
1633 pother = nullid
1633 pother = nullid
1634 parents = ctx.parents()
1634 parents = ctx.parents()
1635 if keepparent and len(parents) == 2 and pctx in parents:
1635 if keepparent and len(parents) == 2 and pctx in parents:
1636 parents.remove(pctx)
1636 parents.remove(pctx)
1637 pother = parents[0].node()
1637 pother = parents[0].node()
1638
1638
1639 repo.dirstate.beginparentchange()
1639 repo.dirstate.beginparentchange()
1640 repo.setparents(repo['.'].node(), pother)
1640 repo.setparents(repo['.'].node(), pother)
1641 repo.dirstate.write(repo.currenttransaction())
1641 repo.dirstate.write(repo.currenttransaction())
1642 # fix up dirstate for copies and renames
1642 # fix up dirstate for copies and renames
1643 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1643 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1644 repo.dirstate.endparentchange()
1644 repo.dirstate.endparentchange()
1645 return stats
1645 return stats
General Comments 0
You need to be logged in to leave comments. Login now