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