##// END OF EJS Templates
fsmonitor: increase the threshold before we recommend it, when using rust...
Valentin Gatien-Baron -
r46034:c1d6e930 default
parent child Browse files
Show More
@@ -1,984 +1,986 b''
1 # __init__.py - fsmonitor initialization and overrides
1 # __init__.py - fsmonitor initialization and overrides
2 #
2 #
3 # Copyright 2013-2016 Facebook, Inc.
3 # Copyright 2013-2016 Facebook, Inc.
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 '''Faster status operations with the Watchman file monitor (EXPERIMENTAL)
8 '''Faster status operations with the Watchman file monitor (EXPERIMENTAL)
9
9
10 Integrates the file-watching program Watchman with Mercurial to produce faster
10 Integrates the file-watching program Watchman with Mercurial to produce faster
11 status results.
11 status results.
12
12
13 On a particular Linux system, for a real-world repository with over 400,000
13 On a particular Linux system, for a real-world repository with over 400,000
14 files hosted on ext4, vanilla `hg status` takes 1.3 seconds. On the same
14 files hosted on ext4, vanilla `hg status` takes 1.3 seconds. On the same
15 system, with fsmonitor it takes about 0.3 seconds.
15 system, with fsmonitor it takes about 0.3 seconds.
16
16
17 fsmonitor requires no configuration -- it will tell Watchman about your
17 fsmonitor requires no configuration -- it will tell Watchman about your
18 repository as necessary. You'll need to install Watchman from
18 repository as necessary. You'll need to install Watchman from
19 https://facebook.github.io/watchman/ and make sure it is in your PATH.
19 https://facebook.github.io/watchman/ and make sure it is in your PATH.
20
20
21 fsmonitor is incompatible with the largefiles and eol extensions, and
21 fsmonitor is incompatible with the largefiles and eol extensions, and
22 will disable itself if any of those are active.
22 will disable itself if any of those are active.
23
23
24 The following configuration options exist:
24 The following configuration options exist:
25
25
26 ::
26 ::
27
27
28 [fsmonitor]
28 [fsmonitor]
29 mode = {off, on, paranoid}
29 mode = {off, on, paranoid}
30
30
31 When `mode = off`, fsmonitor will disable itself (similar to not loading the
31 When `mode = off`, fsmonitor will disable itself (similar to not loading the
32 extension at all). When `mode = on`, fsmonitor will be enabled (the default).
32 extension at all). When `mode = on`, fsmonitor will be enabled (the default).
33 When `mode = paranoid`, fsmonitor will query both Watchman and the filesystem,
33 When `mode = paranoid`, fsmonitor will query both Watchman and the filesystem,
34 and ensure that the results are consistent.
34 and ensure that the results are consistent.
35
35
36 ::
36 ::
37
37
38 [fsmonitor]
38 [fsmonitor]
39 timeout = (float)
39 timeout = (float)
40
40
41 A value, in seconds, that determines how long fsmonitor will wait for Watchman
41 A value, in seconds, that determines how long fsmonitor will wait for Watchman
42 to return results. Defaults to `2.0`.
42 to return results. Defaults to `2.0`.
43
43
44 ::
44 ::
45
45
46 [fsmonitor]
46 [fsmonitor]
47 blacklistusers = (list of userids)
47 blacklistusers = (list of userids)
48
48
49 A list of usernames for which fsmonitor will disable itself altogether.
49 A list of usernames for which fsmonitor will disable itself altogether.
50
50
51 ::
51 ::
52
52
53 [fsmonitor]
53 [fsmonitor]
54 walk_on_invalidate = (boolean)
54 walk_on_invalidate = (boolean)
55
55
56 Whether or not to walk the whole repo ourselves when our cached state has been
56 Whether or not to walk the whole repo ourselves when our cached state has been
57 invalidated, for example when Watchman has been restarted or .hgignore rules
57 invalidated, for example when Watchman has been restarted or .hgignore rules
58 have been changed. Walking the repo in that case can result in competing for
58 have been changed. Walking the repo in that case can result in competing for
59 I/O with Watchman. For large repos it is recommended to set this value to
59 I/O with Watchman. For large repos it is recommended to set this value to
60 false. You may wish to set this to true if you have a very fast filesystem
60 false. You may wish to set this to true if you have a very fast filesystem
61 that can outpace the IPC overhead of getting the result data for the full repo
61 that can outpace the IPC overhead of getting the result data for the full repo
62 from Watchman. Defaults to false.
62 from Watchman. Defaults to false.
63
63
64 ::
64 ::
65
65
66 [fsmonitor]
66 [fsmonitor]
67 warn_when_unused = (boolean)
67 warn_when_unused = (boolean)
68
68
69 Whether to print a warning during certain operations when fsmonitor would be
69 Whether to print a warning during certain operations when fsmonitor would be
70 beneficial to performance but isn't enabled.
70 beneficial to performance but isn't enabled.
71
71
72 ::
72 ::
73
73
74 [fsmonitor]
74 [fsmonitor]
75 warn_update_file_count = (integer)
75 warn_update_file_count = (integer)
76 # or when mercurial is built with rust support
77 warn_update_file_count_rust = (integer)
76
78
77 If ``warn_when_unused`` is set and fsmonitor isn't enabled, a warning will
79 If ``warn_when_unused`` is set and fsmonitor isn't enabled, a warning will
78 be printed during working directory updates if this many files will be
80 be printed during working directory updates if this many files will be
79 created.
81 created.
80 '''
82 '''
81
83
82 # Platforms Supported
84 # Platforms Supported
83 # ===================
85 # ===================
84 #
86 #
85 # **Linux:** *Stable*. Watchman and fsmonitor are both known to work reliably,
87 # **Linux:** *Stable*. Watchman and fsmonitor are both known to work reliably,
86 # even under severe loads.
88 # even under severe loads.
87 #
89 #
88 # **Mac OS X:** *Stable*. The Mercurial test suite passes with fsmonitor
90 # **Mac OS X:** *Stable*. The Mercurial test suite passes with fsmonitor
89 # turned on, on case-insensitive HFS+. There has been a reasonable amount of
91 # turned on, on case-insensitive HFS+. There has been a reasonable amount of
90 # user testing under normal loads.
92 # user testing under normal loads.
91 #
93 #
92 # **Solaris, BSD:** *Alpha*. watchman and fsmonitor are believed to work, but
94 # **Solaris, BSD:** *Alpha*. watchman and fsmonitor are believed to work, but
93 # very little testing has been done.
95 # very little testing has been done.
94 #
96 #
95 # **Windows:** *Alpha*. Not in a release version of watchman or fsmonitor yet.
97 # **Windows:** *Alpha*. Not in a release version of watchman or fsmonitor yet.
96 #
98 #
97 # Known Issues
99 # Known Issues
98 # ============
100 # ============
99 #
101 #
100 # * fsmonitor will disable itself if any of the following extensions are
102 # * fsmonitor will disable itself if any of the following extensions are
101 # enabled: largefiles, inotify, eol; or if the repository has subrepos.
103 # enabled: largefiles, inotify, eol; or if the repository has subrepos.
102 # * fsmonitor will produce incorrect results if nested repos that are not
104 # * fsmonitor will produce incorrect results if nested repos that are not
103 # subrepos exist. *Workaround*: add nested repo paths to your `.hgignore`.
105 # subrepos exist. *Workaround*: add nested repo paths to your `.hgignore`.
104 #
106 #
105 # The issues related to nested repos and subrepos are probably not fundamental
107 # The issues related to nested repos and subrepos are probably not fundamental
106 # ones. Patches to fix them are welcome.
108 # ones. Patches to fix them are welcome.
107
109
108 from __future__ import absolute_import
110 from __future__ import absolute_import
109
111
110 import codecs
112 import codecs
111 import os
113 import os
112 import stat
114 import stat
113 import sys
115 import sys
114 import tempfile
116 import tempfile
115 import weakref
117 import weakref
116
118
117 from mercurial.i18n import _
119 from mercurial.i18n import _
118 from mercurial.node import hex
120 from mercurial.node import hex
119 from mercurial.pycompat import open
121 from mercurial.pycompat import open
120 from mercurial import (
122 from mercurial import (
121 context,
123 context,
122 encoding,
124 encoding,
123 error,
125 error,
124 extensions,
126 extensions,
125 localrepo,
127 localrepo,
126 merge,
128 merge,
127 pathutil,
129 pathutil,
128 pycompat,
130 pycompat,
129 registrar,
131 registrar,
130 scmutil,
132 scmutil,
131 util,
133 util,
132 )
134 )
133 from mercurial import match as matchmod
135 from mercurial import match as matchmod
134 from mercurial.utils import (
136 from mercurial.utils import (
135 hashutil,
137 hashutil,
136 stringutil,
138 stringutil,
137 )
139 )
138
140
139 from . import (
141 from . import (
140 pywatchman,
142 pywatchman,
141 state,
143 state,
142 watchmanclient,
144 watchmanclient,
143 )
145 )
144
146
145 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
147 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
146 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
148 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
147 # be specifying the version(s) of Mercurial they are tested with, or
149 # be specifying the version(s) of Mercurial they are tested with, or
148 # leave the attribute unspecified.
150 # leave the attribute unspecified.
149 testedwith = b'ships-with-hg-core'
151 testedwith = b'ships-with-hg-core'
150
152
151 configtable = {}
153 configtable = {}
152 configitem = registrar.configitem(configtable)
154 configitem = registrar.configitem(configtable)
153
155
154 configitem(
156 configitem(
155 b'fsmonitor', b'mode', default=b'on',
157 b'fsmonitor', b'mode', default=b'on',
156 )
158 )
157 configitem(
159 configitem(
158 b'fsmonitor', b'walk_on_invalidate', default=False,
160 b'fsmonitor', b'walk_on_invalidate', default=False,
159 )
161 )
160 configitem(
162 configitem(
161 b'fsmonitor', b'timeout', default=b'2',
163 b'fsmonitor', b'timeout', default=b'2',
162 )
164 )
163 configitem(
165 configitem(
164 b'fsmonitor', b'blacklistusers', default=list,
166 b'fsmonitor', b'blacklistusers', default=list,
165 )
167 )
166 configitem(
168 configitem(
167 b'fsmonitor', b'watchman_exe', default=b'watchman',
169 b'fsmonitor', b'watchman_exe', default=b'watchman',
168 )
170 )
169 configitem(
171 configitem(
170 b'fsmonitor', b'verbose', default=True, experimental=True,
172 b'fsmonitor', b'verbose', default=True, experimental=True,
171 )
173 )
172 configitem(
174 configitem(
173 b'experimental', b'fsmonitor.transaction_notify', default=False,
175 b'experimental', b'fsmonitor.transaction_notify', default=False,
174 )
176 )
175
177
176 # This extension is incompatible with the following blacklisted extensions
178 # This extension is incompatible with the following blacklisted extensions
177 # and will disable itself when encountering one of these:
179 # and will disable itself when encountering one of these:
178 _blacklist = [b'largefiles', b'eol']
180 _blacklist = [b'largefiles', b'eol']
179
181
180
182
181 def debuginstall(ui, fm):
183 def debuginstall(ui, fm):
182 fm.write(
184 fm.write(
183 b"fsmonitor-watchman",
185 b"fsmonitor-watchman",
184 _(b"fsmonitor checking for watchman binary... (%s)\n"),
186 _(b"fsmonitor checking for watchman binary... (%s)\n"),
185 ui.configpath(b"fsmonitor", b"watchman_exe"),
187 ui.configpath(b"fsmonitor", b"watchman_exe"),
186 )
188 )
187 root = tempfile.mkdtemp()
189 root = tempfile.mkdtemp()
188 c = watchmanclient.client(ui, root)
190 c = watchmanclient.client(ui, root)
189 err = None
191 err = None
190 try:
192 try:
191 v = c.command(b"version")
193 v = c.command(b"version")
192 fm.write(
194 fm.write(
193 b"fsmonitor-watchman-version",
195 b"fsmonitor-watchman-version",
194 _(b" watchman binary version %s\n"),
196 _(b" watchman binary version %s\n"),
195 pycompat.bytestr(v["version"]),
197 pycompat.bytestr(v["version"]),
196 )
198 )
197 except watchmanclient.Unavailable as e:
199 except watchmanclient.Unavailable as e:
198 err = stringutil.forcebytestr(e)
200 err = stringutil.forcebytestr(e)
199 fm.condwrite(
201 fm.condwrite(
200 err,
202 err,
201 b"fsmonitor-watchman-error",
203 b"fsmonitor-watchman-error",
202 _(b" watchman binary missing or broken: %s\n"),
204 _(b" watchman binary missing or broken: %s\n"),
203 err,
205 err,
204 )
206 )
205 return 1 if err else 0
207 return 1 if err else 0
206
208
207
209
208 def _handleunavailable(ui, state, ex):
210 def _handleunavailable(ui, state, ex):
209 """Exception handler for Watchman interaction exceptions"""
211 """Exception handler for Watchman interaction exceptions"""
210 if isinstance(ex, watchmanclient.Unavailable):
212 if isinstance(ex, watchmanclient.Unavailable):
211 # experimental config: fsmonitor.verbose
213 # experimental config: fsmonitor.verbose
212 if ex.warn and ui.configbool(b'fsmonitor', b'verbose'):
214 if ex.warn and ui.configbool(b'fsmonitor', b'verbose'):
213 if b'illegal_fstypes' not in stringutil.forcebytestr(ex):
215 if b'illegal_fstypes' not in stringutil.forcebytestr(ex):
214 ui.warn(stringutil.forcebytestr(ex) + b'\n')
216 ui.warn(stringutil.forcebytestr(ex) + b'\n')
215 if ex.invalidate:
217 if ex.invalidate:
216 state.invalidate()
218 state.invalidate()
217 # experimental config: fsmonitor.verbose
219 # experimental config: fsmonitor.verbose
218 if ui.configbool(b'fsmonitor', b'verbose'):
220 if ui.configbool(b'fsmonitor', b'verbose'):
219 ui.log(
221 ui.log(
220 b'fsmonitor',
222 b'fsmonitor',
221 b'Watchman unavailable: %s\n',
223 b'Watchman unavailable: %s\n',
222 stringutil.forcebytestr(ex.msg),
224 stringutil.forcebytestr(ex.msg),
223 )
225 )
224 else:
226 else:
225 ui.log(
227 ui.log(
226 b'fsmonitor',
228 b'fsmonitor',
227 b'Watchman exception: %s\n',
229 b'Watchman exception: %s\n',
228 stringutil.forcebytestr(ex),
230 stringutil.forcebytestr(ex),
229 )
231 )
230
232
231
233
232 def _hashignore(ignore):
234 def _hashignore(ignore):
233 """Calculate hash for ignore patterns and filenames
235 """Calculate hash for ignore patterns and filenames
234
236
235 If this information changes between Mercurial invocations, we can't
237 If this information changes between Mercurial invocations, we can't
236 rely on Watchman information anymore and have to re-scan the working
238 rely on Watchman information anymore and have to re-scan the working
237 copy.
239 copy.
238
240
239 """
241 """
240 sha1 = hashutil.sha1()
242 sha1 = hashutil.sha1()
241 sha1.update(pycompat.byterepr(ignore))
243 sha1.update(pycompat.byterepr(ignore))
242 return pycompat.sysbytes(sha1.hexdigest())
244 return pycompat.sysbytes(sha1.hexdigest())
243
245
244
246
245 _watchmanencoding = pywatchman.encoding.get_local_encoding()
247 _watchmanencoding = pywatchman.encoding.get_local_encoding()
246 _fsencoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
248 _fsencoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
247 _fixencoding = codecs.lookup(_watchmanencoding) != codecs.lookup(_fsencoding)
249 _fixencoding = codecs.lookup(_watchmanencoding) != codecs.lookup(_fsencoding)
248
250
249
251
250 def _watchmantofsencoding(path):
252 def _watchmantofsencoding(path):
251 """Fix path to match watchman and local filesystem encoding
253 """Fix path to match watchman and local filesystem encoding
252
254
253 watchman's paths encoding can differ from filesystem encoding. For example,
255 watchman's paths encoding can differ from filesystem encoding. For example,
254 on Windows, it's always utf-8.
256 on Windows, it's always utf-8.
255 """
257 """
256 try:
258 try:
257 decoded = path.decode(_watchmanencoding)
259 decoded = path.decode(_watchmanencoding)
258 except UnicodeDecodeError as e:
260 except UnicodeDecodeError as e:
259 raise error.Abort(
261 raise error.Abort(
260 stringutil.forcebytestr(e), hint=b'watchman encoding error'
262 stringutil.forcebytestr(e), hint=b'watchman encoding error'
261 )
263 )
262
264
263 try:
265 try:
264 encoded = decoded.encode(_fsencoding, 'strict')
266 encoded = decoded.encode(_fsencoding, 'strict')
265 except UnicodeEncodeError as e:
267 except UnicodeEncodeError as e:
266 raise error.Abort(stringutil.forcebytestr(e))
268 raise error.Abort(stringutil.forcebytestr(e))
267
269
268 return encoded
270 return encoded
269
271
270
272
271 def overridewalk(orig, self, match, subrepos, unknown, ignored, full=True):
273 def overridewalk(orig, self, match, subrepos, unknown, ignored, full=True):
272 '''Replacement for dirstate.walk, hooking into Watchman.
274 '''Replacement for dirstate.walk, hooking into Watchman.
273
275
274 Whenever full is False, ignored is False, and the Watchman client is
276 Whenever full is False, ignored is False, and the Watchman client is
275 available, use Watchman combined with saved state to possibly return only a
277 available, use Watchman combined with saved state to possibly return only a
276 subset of files.'''
278 subset of files.'''
277
279
278 def bail(reason):
280 def bail(reason):
279 self._ui.debug(b'fsmonitor: fallback to core status, %s\n' % reason)
281 self._ui.debug(b'fsmonitor: fallback to core status, %s\n' % reason)
280 return orig(match, subrepos, unknown, ignored, full=True)
282 return orig(match, subrepos, unknown, ignored, full=True)
281
283
282 if full:
284 if full:
283 return bail(b'full rewalk requested')
285 return bail(b'full rewalk requested')
284 if ignored:
286 if ignored:
285 return bail(b'listing ignored files')
287 return bail(b'listing ignored files')
286 if not self._watchmanclient.available():
288 if not self._watchmanclient.available():
287 return bail(b'client unavailable')
289 return bail(b'client unavailable')
288 state = self._fsmonitorstate
290 state = self._fsmonitorstate
289 clock, ignorehash, notefiles = state.get()
291 clock, ignorehash, notefiles = state.get()
290 if not clock:
292 if not clock:
291 if state.walk_on_invalidate:
293 if state.walk_on_invalidate:
292 return bail(b'no clock')
294 return bail(b'no clock')
293 # Initial NULL clock value, see
295 # Initial NULL clock value, see
294 # https://facebook.github.io/watchman/docs/clockspec.html
296 # https://facebook.github.io/watchman/docs/clockspec.html
295 clock = b'c:0:0'
297 clock = b'c:0:0'
296 notefiles = []
298 notefiles = []
297
299
298 ignore = self._ignore
300 ignore = self._ignore
299 dirignore = self._dirignore
301 dirignore = self._dirignore
300 if unknown:
302 if unknown:
301 if _hashignore(ignore) != ignorehash and clock != b'c:0:0':
303 if _hashignore(ignore) != ignorehash and clock != b'c:0:0':
302 # ignore list changed -- can't rely on Watchman state any more
304 # ignore list changed -- can't rely on Watchman state any more
303 if state.walk_on_invalidate:
305 if state.walk_on_invalidate:
304 return bail(b'ignore rules changed')
306 return bail(b'ignore rules changed')
305 notefiles = []
307 notefiles = []
306 clock = b'c:0:0'
308 clock = b'c:0:0'
307 else:
309 else:
308 # always ignore
310 # always ignore
309 ignore = util.always
311 ignore = util.always
310 dirignore = util.always
312 dirignore = util.always
311
313
312 matchfn = match.matchfn
314 matchfn = match.matchfn
313 matchalways = match.always()
315 matchalways = match.always()
314 dmap = self._map
316 dmap = self._map
315 if util.safehasattr(dmap, b'_map'):
317 if util.safehasattr(dmap, b'_map'):
316 # for better performance, directly access the inner dirstate map if the
318 # for better performance, directly access the inner dirstate map if the
317 # standard dirstate implementation is in use.
319 # standard dirstate implementation is in use.
318 dmap = dmap._map
320 dmap = dmap._map
319 nonnormalset = self._map.nonnormalset
321 nonnormalset = self._map.nonnormalset
320
322
321 copymap = self._map.copymap
323 copymap = self._map.copymap
322 getkind = stat.S_IFMT
324 getkind = stat.S_IFMT
323 dirkind = stat.S_IFDIR
325 dirkind = stat.S_IFDIR
324 regkind = stat.S_IFREG
326 regkind = stat.S_IFREG
325 lnkkind = stat.S_IFLNK
327 lnkkind = stat.S_IFLNK
326 join = self._join
328 join = self._join
327 normcase = util.normcase
329 normcase = util.normcase
328 fresh_instance = False
330 fresh_instance = False
329
331
330 exact = skipstep3 = False
332 exact = skipstep3 = False
331 if match.isexact(): # match.exact
333 if match.isexact(): # match.exact
332 exact = True
334 exact = True
333 dirignore = util.always # skip step 2
335 dirignore = util.always # skip step 2
334 elif match.prefix(): # match.match, no patterns
336 elif match.prefix(): # match.match, no patterns
335 skipstep3 = True
337 skipstep3 = True
336
338
337 if not exact and self._checkcase:
339 if not exact and self._checkcase:
338 # note that even though we could receive directory entries, we're only
340 # note that even though we could receive directory entries, we're only
339 # interested in checking if a file with the same name exists. So only
341 # interested in checking if a file with the same name exists. So only
340 # normalize files if possible.
342 # normalize files if possible.
341 normalize = self._normalizefile
343 normalize = self._normalizefile
342 skipstep3 = False
344 skipstep3 = False
343 else:
345 else:
344 normalize = None
346 normalize = None
345
347
346 # step 1: find all explicit files
348 # step 1: find all explicit files
347 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
349 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
348
350
349 skipstep3 = skipstep3 and not (work or dirsnotfound)
351 skipstep3 = skipstep3 and not (work or dirsnotfound)
350 work = [d for d in work if not dirignore(d[0])]
352 work = [d for d in work if not dirignore(d[0])]
351
353
352 if not work and (exact or skipstep3):
354 if not work and (exact or skipstep3):
353 for s in subrepos:
355 for s in subrepos:
354 del results[s]
356 del results[s]
355 del results[b'.hg']
357 del results[b'.hg']
356 return results
358 return results
357
359
358 # step 2: query Watchman
360 # step 2: query Watchman
359 try:
361 try:
360 # Use the user-configured timeout for the query.
362 # Use the user-configured timeout for the query.
361 # Add a little slack over the top of the user query to allow for
363 # Add a little slack over the top of the user query to allow for
362 # overheads while transferring the data
364 # overheads while transferring the data
363 self._watchmanclient.settimeout(state.timeout + 0.1)
365 self._watchmanclient.settimeout(state.timeout + 0.1)
364 result = self._watchmanclient.command(
366 result = self._watchmanclient.command(
365 b'query',
367 b'query',
366 {
368 {
367 b'fields': [b'mode', b'mtime', b'size', b'exists', b'name'],
369 b'fields': [b'mode', b'mtime', b'size', b'exists', b'name'],
368 b'since': clock,
370 b'since': clock,
369 b'expression': [
371 b'expression': [
370 b'not',
372 b'not',
371 [
373 [
372 b'anyof',
374 b'anyof',
373 [b'dirname', b'.hg'],
375 [b'dirname', b'.hg'],
374 [b'name', b'.hg', b'wholename'],
376 [b'name', b'.hg', b'wholename'],
375 ],
377 ],
376 ],
378 ],
377 b'sync_timeout': int(state.timeout * 1000),
379 b'sync_timeout': int(state.timeout * 1000),
378 b'empty_on_fresh_instance': state.walk_on_invalidate,
380 b'empty_on_fresh_instance': state.walk_on_invalidate,
379 },
381 },
380 )
382 )
381 except Exception as ex:
383 except Exception as ex:
382 _handleunavailable(self._ui, state, ex)
384 _handleunavailable(self._ui, state, ex)
383 self._watchmanclient.clearconnection()
385 self._watchmanclient.clearconnection()
384 return bail(b'exception during run')
386 return bail(b'exception during run')
385 else:
387 else:
386 # We need to propagate the last observed clock up so that we
388 # We need to propagate the last observed clock up so that we
387 # can use it for our next query
389 # can use it for our next query
388 state.setlastclock(pycompat.sysbytes(result[b'clock']))
390 state.setlastclock(pycompat.sysbytes(result[b'clock']))
389 if result[b'is_fresh_instance']:
391 if result[b'is_fresh_instance']:
390 if state.walk_on_invalidate:
392 if state.walk_on_invalidate:
391 state.invalidate()
393 state.invalidate()
392 return bail(b'fresh instance')
394 return bail(b'fresh instance')
393 fresh_instance = True
395 fresh_instance = True
394 # Ignore any prior noteable files from the state info
396 # Ignore any prior noteable files from the state info
395 notefiles = []
397 notefiles = []
396
398
397 # for file paths which require normalization and we encounter a case
399 # for file paths which require normalization and we encounter a case
398 # collision, we store our own foldmap
400 # collision, we store our own foldmap
399 if normalize:
401 if normalize:
400 foldmap = {normcase(k): k for k in results}
402 foldmap = {normcase(k): k for k in results}
401
403
402 switch_slashes = pycompat.ossep == b'\\'
404 switch_slashes = pycompat.ossep == b'\\'
403 # The order of the results is, strictly speaking, undefined.
405 # The order of the results is, strictly speaking, undefined.
404 # For case changes on a case insensitive filesystem we may receive
406 # For case changes on a case insensitive filesystem we may receive
405 # two entries, one with exists=True and another with exists=False.
407 # two entries, one with exists=True and another with exists=False.
406 # The exists=True entries in the same response should be interpreted
408 # The exists=True entries in the same response should be interpreted
407 # as being happens-after the exists=False entries due to the way that
409 # as being happens-after the exists=False entries due to the way that
408 # Watchman tracks files. We use this property to reconcile deletes
410 # Watchman tracks files. We use this property to reconcile deletes
409 # for name case changes.
411 # for name case changes.
410 for entry in result[b'files']:
412 for entry in result[b'files']:
411 fname = entry[b'name']
413 fname = entry[b'name']
412
414
413 # Watchman always give us a str. Normalize to bytes on Python 3
415 # Watchman always give us a str. Normalize to bytes on Python 3
414 # using Watchman's encoding, if needed.
416 # using Watchman's encoding, if needed.
415 if not isinstance(fname, bytes):
417 if not isinstance(fname, bytes):
416 fname = fname.encode(_watchmanencoding)
418 fname = fname.encode(_watchmanencoding)
417
419
418 if _fixencoding:
420 if _fixencoding:
419 fname = _watchmantofsencoding(fname)
421 fname = _watchmantofsencoding(fname)
420
422
421 if switch_slashes:
423 if switch_slashes:
422 fname = fname.replace(b'\\', b'/')
424 fname = fname.replace(b'\\', b'/')
423 if normalize:
425 if normalize:
424 normed = normcase(fname)
426 normed = normcase(fname)
425 fname = normalize(fname, True, True)
427 fname = normalize(fname, True, True)
426 foldmap[normed] = fname
428 foldmap[normed] = fname
427 fmode = entry[b'mode']
429 fmode = entry[b'mode']
428 fexists = entry[b'exists']
430 fexists = entry[b'exists']
429 kind = getkind(fmode)
431 kind = getkind(fmode)
430
432
431 if b'/.hg/' in fname or fname.endswith(b'/.hg'):
433 if b'/.hg/' in fname or fname.endswith(b'/.hg'):
432 return bail(b'nested-repo-detected')
434 return bail(b'nested-repo-detected')
433
435
434 if not fexists:
436 if not fexists:
435 # if marked as deleted and we don't already have a change
437 # if marked as deleted and we don't already have a change
436 # record, mark it as deleted. If we already have an entry
438 # record, mark it as deleted. If we already have an entry
437 # for fname then it was either part of walkexplicit or was
439 # for fname then it was either part of walkexplicit or was
438 # an earlier result that was a case change
440 # an earlier result that was a case change
439 if (
441 if (
440 fname not in results
442 fname not in results
441 and fname in dmap
443 and fname in dmap
442 and (matchalways or matchfn(fname))
444 and (matchalways or matchfn(fname))
443 ):
445 ):
444 results[fname] = None
446 results[fname] = None
445 elif kind == dirkind:
447 elif kind == dirkind:
446 if fname in dmap and (matchalways or matchfn(fname)):
448 if fname in dmap and (matchalways or matchfn(fname)):
447 results[fname] = None
449 results[fname] = None
448 elif kind == regkind or kind == lnkkind:
450 elif kind == regkind or kind == lnkkind:
449 if fname in dmap:
451 if fname in dmap:
450 if matchalways or matchfn(fname):
452 if matchalways or matchfn(fname):
451 results[fname] = entry
453 results[fname] = entry
452 elif (matchalways or matchfn(fname)) and not ignore(fname):
454 elif (matchalways or matchfn(fname)) and not ignore(fname):
453 results[fname] = entry
455 results[fname] = entry
454 elif fname in dmap and (matchalways or matchfn(fname)):
456 elif fname in dmap and (matchalways or matchfn(fname)):
455 results[fname] = None
457 results[fname] = None
456
458
457 # step 3: query notable files we don't already know about
459 # step 3: query notable files we don't already know about
458 # XXX try not to iterate over the entire dmap
460 # XXX try not to iterate over the entire dmap
459 if normalize:
461 if normalize:
460 # any notable files that have changed case will already be handled
462 # any notable files that have changed case will already be handled
461 # above, so just check membership in the foldmap
463 # above, so just check membership in the foldmap
462 notefiles = {
464 notefiles = {
463 normalize(f, True, True)
465 normalize(f, True, True)
464 for f in notefiles
466 for f in notefiles
465 if normcase(f) not in foldmap
467 if normcase(f) not in foldmap
466 }
468 }
467 visit = {
469 visit = {
468 f
470 f
469 for f in notefiles
471 for f in notefiles
470 if (f not in results and matchfn(f) and (f in dmap or not ignore(f)))
472 if (f not in results and matchfn(f) and (f in dmap or not ignore(f)))
471 }
473 }
472
474
473 if not fresh_instance:
475 if not fresh_instance:
474 if matchalways:
476 if matchalways:
475 visit.update(f for f in nonnormalset if f not in results)
477 visit.update(f for f in nonnormalset if f not in results)
476 visit.update(f for f in copymap if f not in results)
478 visit.update(f for f in copymap if f not in results)
477 else:
479 else:
478 visit.update(
480 visit.update(
479 f for f in nonnormalset if f not in results and matchfn(f)
481 f for f in nonnormalset if f not in results and matchfn(f)
480 )
482 )
481 visit.update(f for f in copymap if f not in results and matchfn(f))
483 visit.update(f for f in copymap if f not in results and matchfn(f))
482 else:
484 else:
483 if matchalways:
485 if matchalways:
484 visit.update(
486 visit.update(
485 f for f, st in pycompat.iteritems(dmap) if f not in results
487 f for f, st in pycompat.iteritems(dmap) if f not in results
486 )
488 )
487 visit.update(f for f in copymap if f not in results)
489 visit.update(f for f in copymap if f not in results)
488 else:
490 else:
489 visit.update(
491 visit.update(
490 f
492 f
491 for f, st in pycompat.iteritems(dmap)
493 for f, st in pycompat.iteritems(dmap)
492 if f not in results and matchfn(f)
494 if f not in results and matchfn(f)
493 )
495 )
494 visit.update(f for f in copymap if f not in results and matchfn(f))
496 visit.update(f for f in copymap if f not in results and matchfn(f))
495
497
496 audit = pathutil.pathauditor(self._root, cached=True).check
498 audit = pathutil.pathauditor(self._root, cached=True).check
497 auditpass = [f for f in visit if audit(f)]
499 auditpass = [f for f in visit if audit(f)]
498 auditpass.sort()
500 auditpass.sort()
499 auditfail = visit.difference(auditpass)
501 auditfail = visit.difference(auditpass)
500 for f in auditfail:
502 for f in auditfail:
501 results[f] = None
503 results[f] = None
502
504
503 nf = iter(auditpass)
505 nf = iter(auditpass)
504 for st in util.statfiles([join(f) for f in auditpass]):
506 for st in util.statfiles([join(f) for f in auditpass]):
505 f = next(nf)
507 f = next(nf)
506 if st or f in dmap:
508 if st or f in dmap:
507 results[f] = st
509 results[f] = st
508
510
509 for s in subrepos:
511 for s in subrepos:
510 del results[s]
512 del results[s]
511 del results[b'.hg']
513 del results[b'.hg']
512 return results
514 return results
513
515
514
516
515 def overridestatus(
517 def overridestatus(
516 orig,
518 orig,
517 self,
519 self,
518 node1=b'.',
520 node1=b'.',
519 node2=None,
521 node2=None,
520 match=None,
522 match=None,
521 ignored=False,
523 ignored=False,
522 clean=False,
524 clean=False,
523 unknown=False,
525 unknown=False,
524 listsubrepos=False,
526 listsubrepos=False,
525 ):
527 ):
526 listignored = ignored
528 listignored = ignored
527 listclean = clean
529 listclean = clean
528 listunknown = unknown
530 listunknown = unknown
529
531
530 def _cmpsets(l1, l2):
532 def _cmpsets(l1, l2):
531 try:
533 try:
532 if b'FSMONITOR_LOG_FILE' in encoding.environ:
534 if b'FSMONITOR_LOG_FILE' in encoding.environ:
533 fn = encoding.environ[b'FSMONITOR_LOG_FILE']
535 fn = encoding.environ[b'FSMONITOR_LOG_FILE']
534 f = open(fn, b'wb')
536 f = open(fn, b'wb')
535 else:
537 else:
536 fn = b'fsmonitorfail.log'
538 fn = b'fsmonitorfail.log'
537 f = self.vfs.open(fn, b'wb')
539 f = self.vfs.open(fn, b'wb')
538 except (IOError, OSError):
540 except (IOError, OSError):
539 self.ui.warn(_(b'warning: unable to write to %s\n') % fn)
541 self.ui.warn(_(b'warning: unable to write to %s\n') % fn)
540 return
542 return
541
543
542 try:
544 try:
543 for i, (s1, s2) in enumerate(zip(l1, l2)):
545 for i, (s1, s2) in enumerate(zip(l1, l2)):
544 if set(s1) != set(s2):
546 if set(s1) != set(s2):
545 f.write(b'sets at position %d are unequal\n' % i)
547 f.write(b'sets at position %d are unequal\n' % i)
546 f.write(b'watchman returned: %s\n' % s1)
548 f.write(b'watchman returned: %s\n' % s1)
547 f.write(b'stat returned: %s\n' % s2)
549 f.write(b'stat returned: %s\n' % s2)
548 finally:
550 finally:
549 f.close()
551 f.close()
550
552
551 if isinstance(node1, context.changectx):
553 if isinstance(node1, context.changectx):
552 ctx1 = node1
554 ctx1 = node1
553 else:
555 else:
554 ctx1 = self[node1]
556 ctx1 = self[node1]
555 if isinstance(node2, context.changectx):
557 if isinstance(node2, context.changectx):
556 ctx2 = node2
558 ctx2 = node2
557 else:
559 else:
558 ctx2 = self[node2]
560 ctx2 = self[node2]
559
561
560 working = ctx2.rev() is None
562 working = ctx2.rev() is None
561 parentworking = working and ctx1 == self[b'.']
563 parentworking = working and ctx1 == self[b'.']
562 match = match or matchmod.always()
564 match = match or matchmod.always()
563
565
564 # Maybe we can use this opportunity to update Watchman's state.
566 # Maybe we can use this opportunity to update Watchman's state.
565 # Mercurial uses workingcommitctx and/or memctx to represent the part of
567 # Mercurial uses workingcommitctx and/or memctx to represent the part of
566 # the workingctx that is to be committed. So don't update the state in
568 # the workingctx that is to be committed. So don't update the state in
567 # that case.
569 # that case.
568 # HG_PENDING is set in the environment when the dirstate is being updated
570 # HG_PENDING is set in the environment when the dirstate is being updated
569 # in the middle of a transaction; we must not update our state in that
571 # in the middle of a transaction; we must not update our state in that
570 # case, or we risk forgetting about changes in the working copy.
572 # case, or we risk forgetting about changes in the working copy.
571 updatestate = (
573 updatestate = (
572 parentworking
574 parentworking
573 and match.always()
575 and match.always()
574 and not isinstance(ctx2, (context.workingcommitctx, context.memctx))
576 and not isinstance(ctx2, (context.workingcommitctx, context.memctx))
575 and b'HG_PENDING' not in encoding.environ
577 and b'HG_PENDING' not in encoding.environ
576 )
578 )
577
579
578 try:
580 try:
579 if self._fsmonitorstate.walk_on_invalidate:
581 if self._fsmonitorstate.walk_on_invalidate:
580 # Use a short timeout to query the current clock. If that
582 # Use a short timeout to query the current clock. If that
581 # takes too long then we assume that the service will be slow
583 # takes too long then we assume that the service will be slow
582 # to answer our query.
584 # to answer our query.
583 # walk_on_invalidate indicates that we prefer to walk the
585 # walk_on_invalidate indicates that we prefer to walk the
584 # tree ourselves because we can ignore portions that Watchman
586 # tree ourselves because we can ignore portions that Watchman
585 # cannot and we tend to be faster in the warmer buffer cache
587 # cannot and we tend to be faster in the warmer buffer cache
586 # cases.
588 # cases.
587 self._watchmanclient.settimeout(0.1)
589 self._watchmanclient.settimeout(0.1)
588 else:
590 else:
589 # Give Watchman more time to potentially complete its walk
591 # Give Watchman more time to potentially complete its walk
590 # and return the initial clock. In this mode we assume that
592 # and return the initial clock. In this mode we assume that
591 # the filesystem will be slower than parsing a potentially
593 # the filesystem will be slower than parsing a potentially
592 # very large Watchman result set.
594 # very large Watchman result set.
593 self._watchmanclient.settimeout(self._fsmonitorstate.timeout + 0.1)
595 self._watchmanclient.settimeout(self._fsmonitorstate.timeout + 0.1)
594 startclock = self._watchmanclient.getcurrentclock()
596 startclock = self._watchmanclient.getcurrentclock()
595 except Exception as ex:
597 except Exception as ex:
596 self._watchmanclient.clearconnection()
598 self._watchmanclient.clearconnection()
597 _handleunavailable(self.ui, self._fsmonitorstate, ex)
599 _handleunavailable(self.ui, self._fsmonitorstate, ex)
598 # boo, Watchman failed. bail
600 # boo, Watchman failed. bail
599 return orig(
601 return orig(
600 node1,
602 node1,
601 node2,
603 node2,
602 match,
604 match,
603 listignored,
605 listignored,
604 listclean,
606 listclean,
605 listunknown,
607 listunknown,
606 listsubrepos,
608 listsubrepos,
607 )
609 )
608
610
609 if updatestate:
611 if updatestate:
610 # We need info about unknown files. This may make things slower the
612 # We need info about unknown files. This may make things slower the
611 # first time, but whatever.
613 # first time, but whatever.
612 stateunknown = True
614 stateunknown = True
613 else:
615 else:
614 stateunknown = listunknown
616 stateunknown = listunknown
615
617
616 if updatestate:
618 if updatestate:
617 ps = poststatus(startclock)
619 ps = poststatus(startclock)
618 self.addpostdsstatus(ps)
620 self.addpostdsstatus(ps)
619
621
620 r = orig(
622 r = orig(
621 node1, node2, match, listignored, listclean, stateunknown, listsubrepos
623 node1, node2, match, listignored, listclean, stateunknown, listsubrepos
622 )
624 )
623 modified, added, removed, deleted, unknown, ignored, clean = r
625 modified, added, removed, deleted, unknown, ignored, clean = r
624
626
625 if not listunknown:
627 if not listunknown:
626 unknown = []
628 unknown = []
627
629
628 # don't do paranoid checks if we're not going to query Watchman anyway
630 # don't do paranoid checks if we're not going to query Watchman anyway
629 full = listclean or match.traversedir is not None
631 full = listclean or match.traversedir is not None
630 if self._fsmonitorstate.mode == b'paranoid' and not full:
632 if self._fsmonitorstate.mode == b'paranoid' and not full:
631 # run status again and fall back to the old walk this time
633 # run status again and fall back to the old walk this time
632 self.dirstate._fsmonitordisable = True
634 self.dirstate._fsmonitordisable = True
633
635
634 # shut the UI up
636 # shut the UI up
635 quiet = self.ui.quiet
637 quiet = self.ui.quiet
636 self.ui.quiet = True
638 self.ui.quiet = True
637 fout, ferr = self.ui.fout, self.ui.ferr
639 fout, ferr = self.ui.fout, self.ui.ferr
638 self.ui.fout = self.ui.ferr = open(os.devnull, b'wb')
640 self.ui.fout = self.ui.ferr = open(os.devnull, b'wb')
639
641
640 try:
642 try:
641 rv2 = orig(
643 rv2 = orig(
642 node1,
644 node1,
643 node2,
645 node2,
644 match,
646 match,
645 listignored,
647 listignored,
646 listclean,
648 listclean,
647 listunknown,
649 listunknown,
648 listsubrepos,
650 listsubrepos,
649 )
651 )
650 finally:
652 finally:
651 self.dirstate._fsmonitordisable = False
653 self.dirstate._fsmonitordisable = False
652 self.ui.quiet = quiet
654 self.ui.quiet = quiet
653 self.ui.fout, self.ui.ferr = fout, ferr
655 self.ui.fout, self.ui.ferr = fout, ferr
654
656
655 # clean isn't tested since it's set to True above
657 # clean isn't tested since it's set to True above
656 with self.wlock():
658 with self.wlock():
657 _cmpsets(
659 _cmpsets(
658 [modified, added, removed, deleted, unknown, ignored, clean],
660 [modified, added, removed, deleted, unknown, ignored, clean],
659 rv2,
661 rv2,
660 )
662 )
661 modified, added, removed, deleted, unknown, ignored, clean = rv2
663 modified, added, removed, deleted, unknown, ignored, clean = rv2
662
664
663 return scmutil.status(
665 return scmutil.status(
664 modified, added, removed, deleted, unknown, ignored, clean
666 modified, added, removed, deleted, unknown, ignored, clean
665 )
667 )
666
668
667
669
668 class poststatus(object):
670 class poststatus(object):
669 def __init__(self, startclock):
671 def __init__(self, startclock):
670 self._startclock = pycompat.sysbytes(startclock)
672 self._startclock = pycompat.sysbytes(startclock)
671
673
672 def __call__(self, wctx, status):
674 def __call__(self, wctx, status):
673 clock = wctx.repo()._fsmonitorstate.getlastclock() or self._startclock
675 clock = wctx.repo()._fsmonitorstate.getlastclock() or self._startclock
674 hashignore = _hashignore(wctx.repo().dirstate._ignore)
676 hashignore = _hashignore(wctx.repo().dirstate._ignore)
675 notefiles = (
677 notefiles = (
676 status.modified
678 status.modified
677 + status.added
679 + status.added
678 + status.removed
680 + status.removed
679 + status.deleted
681 + status.deleted
680 + status.unknown
682 + status.unknown
681 )
683 )
682 wctx.repo()._fsmonitorstate.set(clock, hashignore, notefiles)
684 wctx.repo()._fsmonitorstate.set(clock, hashignore, notefiles)
683
685
684
686
685 def makedirstate(repo, dirstate):
687 def makedirstate(repo, dirstate):
686 class fsmonitordirstate(dirstate.__class__):
688 class fsmonitordirstate(dirstate.__class__):
687 def _fsmonitorinit(self, repo):
689 def _fsmonitorinit(self, repo):
688 # _fsmonitordisable is used in paranoid mode
690 # _fsmonitordisable is used in paranoid mode
689 self._fsmonitordisable = False
691 self._fsmonitordisable = False
690 self._fsmonitorstate = repo._fsmonitorstate
692 self._fsmonitorstate = repo._fsmonitorstate
691 self._watchmanclient = repo._watchmanclient
693 self._watchmanclient = repo._watchmanclient
692 self._repo = weakref.proxy(repo)
694 self._repo = weakref.proxy(repo)
693
695
694 def walk(self, *args, **kwargs):
696 def walk(self, *args, **kwargs):
695 orig = super(fsmonitordirstate, self).walk
697 orig = super(fsmonitordirstate, self).walk
696 if self._fsmonitordisable:
698 if self._fsmonitordisable:
697 return orig(*args, **kwargs)
699 return orig(*args, **kwargs)
698 return overridewalk(orig, self, *args, **kwargs)
700 return overridewalk(orig, self, *args, **kwargs)
699
701
700 def rebuild(self, *args, **kwargs):
702 def rebuild(self, *args, **kwargs):
701 self._fsmonitorstate.invalidate()
703 self._fsmonitorstate.invalidate()
702 return super(fsmonitordirstate, self).rebuild(*args, **kwargs)
704 return super(fsmonitordirstate, self).rebuild(*args, **kwargs)
703
705
704 def invalidate(self, *args, **kwargs):
706 def invalidate(self, *args, **kwargs):
705 self._fsmonitorstate.invalidate()
707 self._fsmonitorstate.invalidate()
706 return super(fsmonitordirstate, self).invalidate(*args, **kwargs)
708 return super(fsmonitordirstate, self).invalidate(*args, **kwargs)
707
709
708 dirstate.__class__ = fsmonitordirstate
710 dirstate.__class__ = fsmonitordirstate
709 dirstate._fsmonitorinit(repo)
711 dirstate._fsmonitorinit(repo)
710
712
711
713
712 def wrapdirstate(orig, self):
714 def wrapdirstate(orig, self):
713 ds = orig(self)
715 ds = orig(self)
714 # only override the dirstate when Watchman is available for the repo
716 # only override the dirstate when Watchman is available for the repo
715 if util.safehasattr(self, b'_fsmonitorstate'):
717 if util.safehasattr(self, b'_fsmonitorstate'):
716 makedirstate(self, ds)
718 makedirstate(self, ds)
717 return ds
719 return ds
718
720
719
721
720 def extsetup(ui):
722 def extsetup(ui):
721 extensions.wrapfilecache(
723 extensions.wrapfilecache(
722 localrepo.localrepository, b'dirstate', wrapdirstate
724 localrepo.localrepository, b'dirstate', wrapdirstate
723 )
725 )
724 if pycompat.isdarwin:
726 if pycompat.isdarwin:
725 # An assist for avoiding the dangling-symlink fsevents bug
727 # An assist for avoiding the dangling-symlink fsevents bug
726 extensions.wrapfunction(os, b'symlink', wrapsymlink)
728 extensions.wrapfunction(os, b'symlink', wrapsymlink)
727
729
728 extensions.wrapfunction(merge, b'update', wrapupdate)
730 extensions.wrapfunction(merge, b'update', wrapupdate)
729
731
730
732
731 def wrapsymlink(orig, source, link_name):
733 def wrapsymlink(orig, source, link_name):
732 ''' if we create a dangling symlink, also touch the parent dir
734 ''' if we create a dangling symlink, also touch the parent dir
733 to encourage fsevents notifications to work more correctly '''
735 to encourage fsevents notifications to work more correctly '''
734 try:
736 try:
735 return orig(source, link_name)
737 return orig(source, link_name)
736 finally:
738 finally:
737 try:
739 try:
738 os.utime(os.path.dirname(link_name), None)
740 os.utime(os.path.dirname(link_name), None)
739 except OSError:
741 except OSError:
740 pass
742 pass
741
743
742
744
743 class state_update(object):
745 class state_update(object):
744 ''' This context manager is responsible for dispatching the state-enter
746 ''' This context manager is responsible for dispatching the state-enter
745 and state-leave signals to the watchman service. The enter and leave
747 and state-leave signals to the watchman service. The enter and leave
746 methods can be invoked manually (for scenarios where context manager
748 methods can be invoked manually (for scenarios where context manager
747 semantics are not possible). If parameters oldnode and newnode are None,
749 semantics are not possible). If parameters oldnode and newnode are None,
748 they will be populated based on current working copy in enter and
750 they will be populated based on current working copy in enter and
749 leave, respectively. Similarly, if the distance is none, it will be
751 leave, respectively. Similarly, if the distance is none, it will be
750 calculated based on the oldnode and newnode in the leave method.'''
752 calculated based on the oldnode and newnode in the leave method.'''
751
753
752 def __init__(
754 def __init__(
753 self,
755 self,
754 repo,
756 repo,
755 name,
757 name,
756 oldnode=None,
758 oldnode=None,
757 newnode=None,
759 newnode=None,
758 distance=None,
760 distance=None,
759 partial=False,
761 partial=False,
760 ):
762 ):
761 self.repo = repo.unfiltered()
763 self.repo = repo.unfiltered()
762 self.name = name
764 self.name = name
763 self.oldnode = oldnode
765 self.oldnode = oldnode
764 self.newnode = newnode
766 self.newnode = newnode
765 self.distance = distance
767 self.distance = distance
766 self.partial = partial
768 self.partial = partial
767 self._lock = None
769 self._lock = None
768 self.need_leave = False
770 self.need_leave = False
769
771
770 def __enter__(self):
772 def __enter__(self):
771 self.enter()
773 self.enter()
772
774
773 def enter(self):
775 def enter(self):
774 # Make sure we have a wlock prior to sending notifications to watchman.
776 # Make sure we have a wlock prior to sending notifications to watchman.
775 # We don't want to race with other actors. In the update case,
777 # We don't want to race with other actors. In the update case,
776 # merge.update is going to take the wlock almost immediately. We are
778 # merge.update is going to take the wlock almost immediately. We are
777 # effectively extending the lock around several short sanity checks.
779 # effectively extending the lock around several short sanity checks.
778 if self.oldnode is None:
780 if self.oldnode is None:
779 self.oldnode = self.repo[b'.'].node()
781 self.oldnode = self.repo[b'.'].node()
780
782
781 if self.repo.currentwlock() is None:
783 if self.repo.currentwlock() is None:
782 if util.safehasattr(self.repo, b'wlocknostateupdate'):
784 if util.safehasattr(self.repo, b'wlocknostateupdate'):
783 self._lock = self.repo.wlocknostateupdate()
785 self._lock = self.repo.wlocknostateupdate()
784 else:
786 else:
785 self._lock = self.repo.wlock()
787 self._lock = self.repo.wlock()
786 self.need_leave = self._state(b'state-enter', hex(self.oldnode))
788 self.need_leave = self._state(b'state-enter', hex(self.oldnode))
787 return self
789 return self
788
790
789 def __exit__(self, type_, value, tb):
791 def __exit__(self, type_, value, tb):
790 abort = True if type_ else False
792 abort = True if type_ else False
791 self.exit(abort=abort)
793 self.exit(abort=abort)
792
794
793 def exit(self, abort=False):
795 def exit(self, abort=False):
794 try:
796 try:
795 if self.need_leave:
797 if self.need_leave:
796 status = b'failed' if abort else b'ok'
798 status = b'failed' if abort else b'ok'
797 if self.newnode is None:
799 if self.newnode is None:
798 self.newnode = self.repo[b'.'].node()
800 self.newnode = self.repo[b'.'].node()
799 if self.distance is None:
801 if self.distance is None:
800 self.distance = calcdistance(
802 self.distance = calcdistance(
801 self.repo, self.oldnode, self.newnode
803 self.repo, self.oldnode, self.newnode
802 )
804 )
803 self._state(b'state-leave', hex(self.newnode), status=status)
805 self._state(b'state-leave', hex(self.newnode), status=status)
804 finally:
806 finally:
805 self.need_leave = False
807 self.need_leave = False
806 if self._lock:
808 if self._lock:
807 self._lock.release()
809 self._lock.release()
808
810
809 def _state(self, cmd, commithash, status=b'ok'):
811 def _state(self, cmd, commithash, status=b'ok'):
810 if not util.safehasattr(self.repo, b'_watchmanclient'):
812 if not util.safehasattr(self.repo, b'_watchmanclient'):
811 return False
813 return False
812 try:
814 try:
813 self.repo._watchmanclient.command(
815 self.repo._watchmanclient.command(
814 cmd,
816 cmd,
815 {
817 {
816 b'name': self.name,
818 b'name': self.name,
817 b'metadata': {
819 b'metadata': {
818 # the target revision
820 # the target revision
819 b'rev': commithash,
821 b'rev': commithash,
820 # approximate number of commits between current and target
822 # approximate number of commits between current and target
821 b'distance': self.distance if self.distance else 0,
823 b'distance': self.distance if self.distance else 0,
822 # success/failure (only really meaningful for state-leave)
824 # success/failure (only really meaningful for state-leave)
823 b'status': status,
825 b'status': status,
824 # whether the working copy parent is changing
826 # whether the working copy parent is changing
825 b'partial': self.partial,
827 b'partial': self.partial,
826 },
828 },
827 },
829 },
828 )
830 )
829 return True
831 return True
830 except Exception as e:
832 except Exception as e:
831 # Swallow any errors; fire and forget
833 # Swallow any errors; fire and forget
832 self.repo.ui.log(
834 self.repo.ui.log(
833 b'watchman', b'Exception %s while running %s\n', e, cmd
835 b'watchman', b'Exception %s while running %s\n', e, cmd
834 )
836 )
835 return False
837 return False
836
838
837
839
838 # Estimate the distance between two nodes
840 # Estimate the distance between two nodes
839 def calcdistance(repo, oldnode, newnode):
841 def calcdistance(repo, oldnode, newnode):
840 anc = repo.changelog.ancestor(oldnode, newnode)
842 anc = repo.changelog.ancestor(oldnode, newnode)
841 ancrev = repo[anc].rev()
843 ancrev = repo[anc].rev()
842 distance = abs(repo[oldnode].rev() - ancrev) + abs(
844 distance = abs(repo[oldnode].rev() - ancrev) + abs(
843 repo[newnode].rev() - ancrev
845 repo[newnode].rev() - ancrev
844 )
846 )
845 return distance
847 return distance
846
848
847
849
848 # Bracket working copy updates with calls to the watchman state-enter
850 # Bracket working copy updates with calls to the watchman state-enter
849 # and state-leave commands. This allows clients to perform more intelligent
851 # and state-leave commands. This allows clients to perform more intelligent
850 # settling during bulk file change scenarios
852 # settling during bulk file change scenarios
851 # https://facebook.github.io/watchman/docs/cmd/subscribe.html#advanced-settling
853 # https://facebook.github.io/watchman/docs/cmd/subscribe.html#advanced-settling
852 def wrapupdate(
854 def wrapupdate(
853 orig,
855 orig,
854 repo,
856 repo,
855 node,
857 node,
856 branchmerge,
858 branchmerge,
857 force,
859 force,
858 ancestor=None,
860 ancestor=None,
859 mergeancestor=False,
861 mergeancestor=False,
860 labels=None,
862 labels=None,
861 matcher=None,
863 matcher=None,
862 **kwargs
864 **kwargs
863 ):
865 ):
864
866
865 distance = 0
867 distance = 0
866 partial = True
868 partial = True
867 oldnode = repo[b'.'].node()
869 oldnode = repo[b'.'].node()
868 newnode = repo[node].node()
870 newnode = repo[node].node()
869 if matcher is None or matcher.always():
871 if matcher is None or matcher.always():
870 partial = False
872 partial = False
871 distance = calcdistance(repo.unfiltered(), oldnode, newnode)
873 distance = calcdistance(repo.unfiltered(), oldnode, newnode)
872
874
873 with state_update(
875 with state_update(
874 repo,
876 repo,
875 name=b"hg.update",
877 name=b"hg.update",
876 oldnode=oldnode,
878 oldnode=oldnode,
877 newnode=newnode,
879 newnode=newnode,
878 distance=distance,
880 distance=distance,
879 partial=partial,
881 partial=partial,
880 ):
882 ):
881 return orig(
883 return orig(
882 repo,
884 repo,
883 node,
885 node,
884 branchmerge,
886 branchmerge,
885 force,
887 force,
886 ancestor,
888 ancestor,
887 mergeancestor,
889 mergeancestor,
888 labels,
890 labels,
889 matcher,
891 matcher,
890 **kwargs
892 **kwargs
891 )
893 )
892
894
893
895
894 def repo_has_depth_one_nested_repo(repo):
896 def repo_has_depth_one_nested_repo(repo):
895 for f in repo.wvfs.listdir():
897 for f in repo.wvfs.listdir():
896 if os.path.isdir(os.path.join(repo.root, f, b'.hg')):
898 if os.path.isdir(os.path.join(repo.root, f, b'.hg')):
897 msg = b'fsmonitor: sub-repository %r detected, fsmonitor disabled\n'
899 msg = b'fsmonitor: sub-repository %r detected, fsmonitor disabled\n'
898 repo.ui.debug(msg % f)
900 repo.ui.debug(msg % f)
899 return True
901 return True
900 return False
902 return False
901
903
902
904
903 def reposetup(ui, repo):
905 def reposetup(ui, repo):
904 # We don't work with largefiles or inotify
906 # We don't work with largefiles or inotify
905 exts = extensions.enabled()
907 exts = extensions.enabled()
906 for ext in _blacklist:
908 for ext in _blacklist:
907 if ext in exts:
909 if ext in exts:
908 ui.warn(
910 ui.warn(
909 _(
911 _(
910 b'The fsmonitor extension is incompatible with the %s '
912 b'The fsmonitor extension is incompatible with the %s '
911 b'extension and has been disabled.\n'
913 b'extension and has been disabled.\n'
912 )
914 )
913 % ext
915 % ext
914 )
916 )
915 return
917 return
916
918
917 if repo.local():
919 if repo.local():
918 # We don't work with subrepos either.
920 # We don't work with subrepos either.
919 #
921 #
920 # if repo[None].substate can cause a dirstate parse, which is too
922 # if repo[None].substate can cause a dirstate parse, which is too
921 # slow. Instead, look for a file called hgsubstate,
923 # slow. Instead, look for a file called hgsubstate,
922 if repo.wvfs.exists(b'.hgsubstate') or repo.wvfs.exists(b'.hgsub'):
924 if repo.wvfs.exists(b'.hgsubstate') or repo.wvfs.exists(b'.hgsub'):
923 return
925 return
924
926
925 if repo_has_depth_one_nested_repo(repo):
927 if repo_has_depth_one_nested_repo(repo):
926 return
928 return
927
929
928 fsmonitorstate = state.state(repo)
930 fsmonitorstate = state.state(repo)
929 if fsmonitorstate.mode == b'off':
931 if fsmonitorstate.mode == b'off':
930 return
932 return
931
933
932 try:
934 try:
933 client = watchmanclient.client(repo.ui, repo.root)
935 client = watchmanclient.client(repo.ui, repo.root)
934 except Exception as ex:
936 except Exception as ex:
935 _handleunavailable(ui, fsmonitorstate, ex)
937 _handleunavailable(ui, fsmonitorstate, ex)
936 return
938 return
937
939
938 repo._fsmonitorstate = fsmonitorstate
940 repo._fsmonitorstate = fsmonitorstate
939 repo._watchmanclient = client
941 repo._watchmanclient = client
940
942
941 dirstate, cached = localrepo.isfilecached(repo, b'dirstate')
943 dirstate, cached = localrepo.isfilecached(repo, b'dirstate')
942 if cached:
944 if cached:
943 # at this point since fsmonitorstate wasn't present,
945 # at this point since fsmonitorstate wasn't present,
944 # repo.dirstate is not a fsmonitordirstate
946 # repo.dirstate is not a fsmonitordirstate
945 makedirstate(repo, dirstate)
947 makedirstate(repo, dirstate)
946
948
947 class fsmonitorrepo(repo.__class__):
949 class fsmonitorrepo(repo.__class__):
948 def status(self, *args, **kwargs):
950 def status(self, *args, **kwargs):
949 orig = super(fsmonitorrepo, self).status
951 orig = super(fsmonitorrepo, self).status
950 return overridestatus(orig, self, *args, **kwargs)
952 return overridestatus(orig, self, *args, **kwargs)
951
953
952 def wlocknostateupdate(self, *args, **kwargs):
954 def wlocknostateupdate(self, *args, **kwargs):
953 return super(fsmonitorrepo, self).wlock(*args, **kwargs)
955 return super(fsmonitorrepo, self).wlock(*args, **kwargs)
954
956
955 def wlock(self, *args, **kwargs):
957 def wlock(self, *args, **kwargs):
956 l = super(fsmonitorrepo, self).wlock(*args, **kwargs)
958 l = super(fsmonitorrepo, self).wlock(*args, **kwargs)
957 if not ui.configbool(
959 if not ui.configbool(
958 b"experimental", b"fsmonitor.transaction_notify"
960 b"experimental", b"fsmonitor.transaction_notify"
959 ):
961 ):
960 return l
962 return l
961 if l.held != 1:
963 if l.held != 1:
962 return l
964 return l
963 origrelease = l.releasefn
965 origrelease = l.releasefn
964
966
965 def staterelease():
967 def staterelease():
966 if origrelease:
968 if origrelease:
967 origrelease()
969 origrelease()
968 if l.stateupdate:
970 if l.stateupdate:
969 l.stateupdate.exit()
971 l.stateupdate.exit()
970 l.stateupdate = None
972 l.stateupdate = None
971
973
972 try:
974 try:
973 l.stateupdate = None
975 l.stateupdate = None
974 l.stateupdate = state_update(self, name=b"hg.transaction")
976 l.stateupdate = state_update(self, name=b"hg.transaction")
975 l.stateupdate.enter()
977 l.stateupdate.enter()
976 l.releasefn = staterelease
978 l.releasefn = staterelease
977 except Exception as e:
979 except Exception as e:
978 # Swallow any errors; fire and forget
980 # Swallow any errors; fire and forget
979 self.ui.log(
981 self.ui.log(
980 b'watchman', b'Exception in state update %s\n', e
982 b'watchman', b'Exception in state update %s\n', e
981 )
983 )
982 return l
984 return l
983
985
984 repo.__class__ = fsmonitorrepo
986 repo.__class__ = fsmonitorrepo
@@ -1,1585 +1,1588 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 functools
10 import functools
11 import re
11 import re
12
12
13 from . import (
13 from . import (
14 encoding,
14 encoding,
15 error,
15 error,
16 )
16 )
17
17
18
18
19 def loadconfigtable(ui, extname, configtable):
19 def loadconfigtable(ui, extname, configtable):
20 """update config item known to the ui with the extension ones"""
20 """update config item known to the ui with the extension ones"""
21 for section, items in sorted(configtable.items()):
21 for section, items in sorted(configtable.items()):
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 knownkeys = set(knownitems)
23 knownkeys = set(knownitems)
24 newkeys = set(items)
24 newkeys = set(items)
25 for key in sorted(knownkeys & newkeys):
25 for key in sorted(knownkeys & newkeys):
26 msg = b"extension '%s' overwrite config item '%s.%s'"
26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 msg %= (extname, section, key)
27 msg %= (extname, section, key)
28 ui.develwarn(msg, config=b'warn-config')
28 ui.develwarn(msg, config=b'warn-config')
29
29
30 knownitems.update(items)
30 knownitems.update(items)
31
31
32
32
33 class configitem(object):
33 class configitem(object):
34 """represent a known config item
34 """represent a known config item
35
35
36 :section: the official config section where to find this item,
36 :section: the official config section where to find this item,
37 :name: the official name within the section,
37 :name: the official name within the section,
38 :default: default value for this item,
38 :default: default value for this item,
39 :alias: optional list of tuples as alternatives,
39 :alias: optional list of tuples as alternatives,
40 :generic: this is a generic definition, match name using regular expression.
40 :generic: this is a generic definition, match name using regular expression.
41 """
41 """
42
42
43 def __init__(
43 def __init__(
44 self,
44 self,
45 section,
45 section,
46 name,
46 name,
47 default=None,
47 default=None,
48 alias=(),
48 alias=(),
49 generic=False,
49 generic=False,
50 priority=0,
50 priority=0,
51 experimental=False,
51 experimental=False,
52 ):
52 ):
53 self.section = section
53 self.section = section
54 self.name = name
54 self.name = name
55 self.default = default
55 self.default = default
56 self.alias = list(alias)
56 self.alias = list(alias)
57 self.generic = generic
57 self.generic = generic
58 self.priority = priority
58 self.priority = priority
59 self.experimental = experimental
59 self.experimental = experimental
60 self._re = None
60 self._re = None
61 if generic:
61 if generic:
62 self._re = re.compile(self.name)
62 self._re = re.compile(self.name)
63
63
64
64
65 class itemregister(dict):
65 class itemregister(dict):
66 """A specialized dictionary that can handle wild-card selection"""
66 """A specialized dictionary that can handle wild-card selection"""
67
67
68 def __init__(self):
68 def __init__(self):
69 super(itemregister, self).__init__()
69 super(itemregister, self).__init__()
70 self._generics = set()
70 self._generics = set()
71
71
72 def update(self, other):
72 def update(self, other):
73 super(itemregister, self).update(other)
73 super(itemregister, self).update(other)
74 self._generics.update(other._generics)
74 self._generics.update(other._generics)
75
75
76 def __setitem__(self, key, item):
76 def __setitem__(self, key, item):
77 super(itemregister, self).__setitem__(key, item)
77 super(itemregister, self).__setitem__(key, item)
78 if item.generic:
78 if item.generic:
79 self._generics.add(item)
79 self._generics.add(item)
80
80
81 def get(self, key):
81 def get(self, key):
82 baseitem = super(itemregister, self).get(key)
82 baseitem = super(itemregister, self).get(key)
83 if baseitem is not None and not baseitem.generic:
83 if baseitem is not None and not baseitem.generic:
84 return baseitem
84 return baseitem
85
85
86 # search for a matching generic item
86 # search for a matching generic item
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 for item in generics:
88 for item in generics:
89 # we use 'match' instead of 'search' to make the matching simpler
89 # we use 'match' instead of 'search' to make the matching simpler
90 # for people unfamiliar with regular expression. Having the match
90 # for people unfamiliar with regular expression. Having the match
91 # rooted to the start of the string will produce less surprising
91 # rooted to the start of the string will produce less surprising
92 # result for user writing simple regex for sub-attribute.
92 # result for user writing simple regex for sub-attribute.
93 #
93 #
94 # For example using "color\..*" match produces an unsurprising
94 # For example using "color\..*" match produces an unsurprising
95 # result, while using search could suddenly match apparently
95 # result, while using search could suddenly match apparently
96 # unrelated configuration that happens to contains "color."
96 # unrelated configuration that happens to contains "color."
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 # some match to avoid the need to prefix most pattern with "^".
98 # some match to avoid the need to prefix most pattern with "^".
99 # The "^" seems more error prone.
99 # The "^" seems more error prone.
100 if item._re.match(key):
100 if item._re.match(key):
101 return item
101 return item
102
102
103 return None
103 return None
104
104
105
105
106 coreitems = {}
106 coreitems = {}
107
107
108
108
109 def _register(configtable, *args, **kwargs):
109 def _register(configtable, *args, **kwargs):
110 item = configitem(*args, **kwargs)
110 item = configitem(*args, **kwargs)
111 section = configtable.setdefault(item.section, itemregister())
111 section = configtable.setdefault(item.section, itemregister())
112 if item.name in section:
112 if item.name in section:
113 msg = b"duplicated config item registration for '%s.%s'"
113 msg = b"duplicated config item registration for '%s.%s'"
114 raise error.ProgrammingError(msg % (item.section, item.name))
114 raise error.ProgrammingError(msg % (item.section, item.name))
115 section[item.name] = item
115 section[item.name] = item
116
116
117
117
118 # special value for case where the default is derived from other values
118 # special value for case where the default is derived from other values
119 dynamicdefault = object()
119 dynamicdefault = object()
120
120
121 # Registering actual config items
121 # Registering actual config items
122
122
123
123
124 def getitemregister(configtable):
124 def getitemregister(configtable):
125 f = functools.partial(_register, configtable)
125 f = functools.partial(_register, configtable)
126 # export pseudo enum as configitem.*
126 # export pseudo enum as configitem.*
127 f.dynamicdefault = dynamicdefault
127 f.dynamicdefault = dynamicdefault
128 return f
128 return f
129
129
130
130
131 coreconfigitem = getitemregister(coreitems)
131 coreconfigitem = getitemregister(coreitems)
132
132
133
133
134 def _registerdiffopts(section, configprefix=b''):
134 def _registerdiffopts(section, configprefix=b''):
135 coreconfigitem(
135 coreconfigitem(
136 section, configprefix + b'nodates', default=False,
136 section, configprefix + b'nodates', default=False,
137 )
137 )
138 coreconfigitem(
138 coreconfigitem(
139 section, configprefix + b'showfunc', default=False,
139 section, configprefix + b'showfunc', default=False,
140 )
140 )
141 coreconfigitem(
141 coreconfigitem(
142 section, configprefix + b'unified', default=None,
142 section, configprefix + b'unified', default=None,
143 )
143 )
144 coreconfigitem(
144 coreconfigitem(
145 section, configprefix + b'git', default=False,
145 section, configprefix + b'git', default=False,
146 )
146 )
147 coreconfigitem(
147 coreconfigitem(
148 section, configprefix + b'ignorews', default=False,
148 section, configprefix + b'ignorews', default=False,
149 )
149 )
150 coreconfigitem(
150 coreconfigitem(
151 section, configprefix + b'ignorewsamount', default=False,
151 section, configprefix + b'ignorewsamount', default=False,
152 )
152 )
153 coreconfigitem(
153 coreconfigitem(
154 section, configprefix + b'ignoreblanklines', default=False,
154 section, configprefix + b'ignoreblanklines', default=False,
155 )
155 )
156 coreconfigitem(
156 coreconfigitem(
157 section, configprefix + b'ignorewseol', default=False,
157 section, configprefix + b'ignorewseol', default=False,
158 )
158 )
159 coreconfigitem(
159 coreconfigitem(
160 section, configprefix + b'nobinary', default=False,
160 section, configprefix + b'nobinary', default=False,
161 )
161 )
162 coreconfigitem(
162 coreconfigitem(
163 section, configprefix + b'noprefix', default=False,
163 section, configprefix + b'noprefix', default=False,
164 )
164 )
165 coreconfigitem(
165 coreconfigitem(
166 section, configprefix + b'word-diff', default=False,
166 section, configprefix + b'word-diff', default=False,
167 )
167 )
168
168
169
169
170 coreconfigitem(
170 coreconfigitem(
171 b'alias', b'.*', default=dynamicdefault, generic=True,
171 b'alias', b'.*', default=dynamicdefault, generic=True,
172 )
172 )
173 coreconfigitem(
173 coreconfigitem(
174 b'auth', b'cookiefile', default=None,
174 b'auth', b'cookiefile', default=None,
175 )
175 )
176 _registerdiffopts(section=b'annotate')
176 _registerdiffopts(section=b'annotate')
177 # bookmarks.pushing: internal hack for discovery
177 # bookmarks.pushing: internal hack for discovery
178 coreconfigitem(
178 coreconfigitem(
179 b'bookmarks', b'pushing', default=list,
179 b'bookmarks', b'pushing', default=list,
180 )
180 )
181 # bundle.mainreporoot: internal hack for bundlerepo
181 # bundle.mainreporoot: internal hack for bundlerepo
182 coreconfigitem(
182 coreconfigitem(
183 b'bundle', b'mainreporoot', default=b'',
183 b'bundle', b'mainreporoot', default=b'',
184 )
184 )
185 coreconfigitem(
185 coreconfigitem(
186 b'censor', b'policy', default=b'abort', experimental=True,
186 b'censor', b'policy', default=b'abort', experimental=True,
187 )
187 )
188 coreconfigitem(
188 coreconfigitem(
189 b'chgserver', b'idletimeout', default=3600,
189 b'chgserver', b'idletimeout', default=3600,
190 )
190 )
191 coreconfigitem(
191 coreconfigitem(
192 b'chgserver', b'skiphash', default=False,
192 b'chgserver', b'skiphash', default=False,
193 )
193 )
194 coreconfigitem(
194 coreconfigitem(
195 b'cmdserver', b'log', default=None,
195 b'cmdserver', b'log', default=None,
196 )
196 )
197 coreconfigitem(
197 coreconfigitem(
198 b'cmdserver', b'max-log-files', default=7,
198 b'cmdserver', b'max-log-files', default=7,
199 )
199 )
200 coreconfigitem(
200 coreconfigitem(
201 b'cmdserver', b'max-log-size', default=b'1 MB',
201 b'cmdserver', b'max-log-size', default=b'1 MB',
202 )
202 )
203 coreconfigitem(
203 coreconfigitem(
204 b'cmdserver', b'max-repo-cache', default=0, experimental=True,
204 b'cmdserver', b'max-repo-cache', default=0, experimental=True,
205 )
205 )
206 coreconfigitem(
206 coreconfigitem(
207 b'cmdserver', b'message-encodings', default=list,
207 b'cmdserver', b'message-encodings', default=list,
208 )
208 )
209 coreconfigitem(
209 coreconfigitem(
210 b'cmdserver',
210 b'cmdserver',
211 b'track-log',
211 b'track-log',
212 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
212 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
213 )
213 )
214 coreconfigitem(
214 coreconfigitem(
215 b'cmdserver', b'shutdown-on-interrupt', default=True,
215 b'cmdserver', b'shutdown-on-interrupt', default=True,
216 )
216 )
217 coreconfigitem(
217 coreconfigitem(
218 b'color', b'.*', default=None, generic=True,
218 b'color', b'.*', default=None, generic=True,
219 )
219 )
220 coreconfigitem(
220 coreconfigitem(
221 b'color', b'mode', default=b'auto',
221 b'color', b'mode', default=b'auto',
222 )
222 )
223 coreconfigitem(
223 coreconfigitem(
224 b'color', b'pagermode', default=dynamicdefault,
224 b'color', b'pagermode', default=dynamicdefault,
225 )
225 )
226 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
226 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
227 coreconfigitem(
227 coreconfigitem(
228 b'commands', b'commit.post-status', default=False,
228 b'commands', b'commit.post-status', default=False,
229 )
229 )
230 coreconfigitem(
230 coreconfigitem(
231 b'commands', b'grep.all-files', default=False, experimental=True,
231 b'commands', b'grep.all-files', default=False, experimental=True,
232 )
232 )
233 coreconfigitem(
233 coreconfigitem(
234 b'commands', b'merge.require-rev', default=False,
234 b'commands', b'merge.require-rev', default=False,
235 )
235 )
236 coreconfigitem(
236 coreconfigitem(
237 b'commands', b'push.require-revs', default=False,
237 b'commands', b'push.require-revs', default=False,
238 )
238 )
239 coreconfigitem(
239 coreconfigitem(
240 b'commands', b'resolve.confirm', default=False,
240 b'commands', b'resolve.confirm', default=False,
241 )
241 )
242 coreconfigitem(
242 coreconfigitem(
243 b'commands', b'resolve.explicit-re-merge', default=False,
243 b'commands', b'resolve.explicit-re-merge', default=False,
244 )
244 )
245 coreconfigitem(
245 coreconfigitem(
246 b'commands', b'resolve.mark-check', default=b'none',
246 b'commands', b'resolve.mark-check', default=b'none',
247 )
247 )
248 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
248 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
249 coreconfigitem(
249 coreconfigitem(
250 b'commands', b'show.aliasprefix', default=list,
250 b'commands', b'show.aliasprefix', default=list,
251 )
251 )
252 coreconfigitem(
252 coreconfigitem(
253 b'commands', b'status.relative', default=False,
253 b'commands', b'status.relative', default=False,
254 )
254 )
255 coreconfigitem(
255 coreconfigitem(
256 b'commands', b'status.skipstates', default=[], experimental=True,
256 b'commands', b'status.skipstates', default=[], experimental=True,
257 )
257 )
258 coreconfigitem(
258 coreconfigitem(
259 b'commands', b'status.terse', default=b'',
259 b'commands', b'status.terse', default=b'',
260 )
260 )
261 coreconfigitem(
261 coreconfigitem(
262 b'commands', b'status.verbose', default=False,
262 b'commands', b'status.verbose', default=False,
263 )
263 )
264 coreconfigitem(
264 coreconfigitem(
265 b'commands', b'update.check', default=None,
265 b'commands', b'update.check', default=None,
266 )
266 )
267 coreconfigitem(
267 coreconfigitem(
268 b'commands', b'update.requiredest', default=False,
268 b'commands', b'update.requiredest', default=False,
269 )
269 )
270 coreconfigitem(
270 coreconfigitem(
271 b'committemplate', b'.*', default=None, generic=True,
271 b'committemplate', b'.*', default=None, generic=True,
272 )
272 )
273 coreconfigitem(
273 coreconfigitem(
274 b'convert', b'bzr.saverev', default=True,
274 b'convert', b'bzr.saverev', default=True,
275 )
275 )
276 coreconfigitem(
276 coreconfigitem(
277 b'convert', b'cvsps.cache', default=True,
277 b'convert', b'cvsps.cache', default=True,
278 )
278 )
279 coreconfigitem(
279 coreconfigitem(
280 b'convert', b'cvsps.fuzz', default=60,
280 b'convert', b'cvsps.fuzz', default=60,
281 )
281 )
282 coreconfigitem(
282 coreconfigitem(
283 b'convert', b'cvsps.logencoding', default=None,
283 b'convert', b'cvsps.logencoding', default=None,
284 )
284 )
285 coreconfigitem(
285 coreconfigitem(
286 b'convert', b'cvsps.mergefrom', default=None,
286 b'convert', b'cvsps.mergefrom', default=None,
287 )
287 )
288 coreconfigitem(
288 coreconfigitem(
289 b'convert', b'cvsps.mergeto', default=None,
289 b'convert', b'cvsps.mergeto', default=None,
290 )
290 )
291 coreconfigitem(
291 coreconfigitem(
292 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
292 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
293 )
293 )
294 coreconfigitem(
294 coreconfigitem(
295 b'convert', b'git.extrakeys', default=list,
295 b'convert', b'git.extrakeys', default=list,
296 )
296 )
297 coreconfigitem(
297 coreconfigitem(
298 b'convert', b'git.findcopiesharder', default=False,
298 b'convert', b'git.findcopiesharder', default=False,
299 )
299 )
300 coreconfigitem(
300 coreconfigitem(
301 b'convert', b'git.remoteprefix', default=b'remote',
301 b'convert', b'git.remoteprefix', default=b'remote',
302 )
302 )
303 coreconfigitem(
303 coreconfigitem(
304 b'convert', b'git.renamelimit', default=400,
304 b'convert', b'git.renamelimit', default=400,
305 )
305 )
306 coreconfigitem(
306 coreconfigitem(
307 b'convert', b'git.saverev', default=True,
307 b'convert', b'git.saverev', default=True,
308 )
308 )
309 coreconfigitem(
309 coreconfigitem(
310 b'convert', b'git.similarity', default=50,
310 b'convert', b'git.similarity', default=50,
311 )
311 )
312 coreconfigitem(
312 coreconfigitem(
313 b'convert', b'git.skipsubmodules', default=False,
313 b'convert', b'git.skipsubmodules', default=False,
314 )
314 )
315 coreconfigitem(
315 coreconfigitem(
316 b'convert', b'hg.clonebranches', default=False,
316 b'convert', b'hg.clonebranches', default=False,
317 )
317 )
318 coreconfigitem(
318 coreconfigitem(
319 b'convert', b'hg.ignoreerrors', default=False,
319 b'convert', b'hg.ignoreerrors', default=False,
320 )
320 )
321 coreconfigitem(
321 coreconfigitem(
322 b'convert', b'hg.preserve-hash', default=False,
322 b'convert', b'hg.preserve-hash', default=False,
323 )
323 )
324 coreconfigitem(
324 coreconfigitem(
325 b'convert', b'hg.revs', default=None,
325 b'convert', b'hg.revs', default=None,
326 )
326 )
327 coreconfigitem(
327 coreconfigitem(
328 b'convert', b'hg.saverev', default=False,
328 b'convert', b'hg.saverev', default=False,
329 )
329 )
330 coreconfigitem(
330 coreconfigitem(
331 b'convert', b'hg.sourcename', default=None,
331 b'convert', b'hg.sourcename', default=None,
332 )
332 )
333 coreconfigitem(
333 coreconfigitem(
334 b'convert', b'hg.startrev', default=None,
334 b'convert', b'hg.startrev', default=None,
335 )
335 )
336 coreconfigitem(
336 coreconfigitem(
337 b'convert', b'hg.tagsbranch', default=b'default',
337 b'convert', b'hg.tagsbranch', default=b'default',
338 )
338 )
339 coreconfigitem(
339 coreconfigitem(
340 b'convert', b'hg.usebranchnames', default=True,
340 b'convert', b'hg.usebranchnames', default=True,
341 )
341 )
342 coreconfigitem(
342 coreconfigitem(
343 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
343 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
344 )
344 )
345 coreconfigitem(
345 coreconfigitem(
346 b'convert', b'localtimezone', default=False,
346 b'convert', b'localtimezone', default=False,
347 )
347 )
348 coreconfigitem(
348 coreconfigitem(
349 b'convert', b'p4.encoding', default=dynamicdefault,
349 b'convert', b'p4.encoding', default=dynamicdefault,
350 )
350 )
351 coreconfigitem(
351 coreconfigitem(
352 b'convert', b'p4.startrev', default=0,
352 b'convert', b'p4.startrev', default=0,
353 )
353 )
354 coreconfigitem(
354 coreconfigitem(
355 b'convert', b'skiptags', default=False,
355 b'convert', b'skiptags', default=False,
356 )
356 )
357 coreconfigitem(
357 coreconfigitem(
358 b'convert', b'svn.debugsvnlog', default=True,
358 b'convert', b'svn.debugsvnlog', default=True,
359 )
359 )
360 coreconfigitem(
360 coreconfigitem(
361 b'convert', b'svn.trunk', default=None,
361 b'convert', b'svn.trunk', default=None,
362 )
362 )
363 coreconfigitem(
363 coreconfigitem(
364 b'convert', b'svn.tags', default=None,
364 b'convert', b'svn.tags', default=None,
365 )
365 )
366 coreconfigitem(
366 coreconfigitem(
367 b'convert', b'svn.branches', default=None,
367 b'convert', b'svn.branches', default=None,
368 )
368 )
369 coreconfigitem(
369 coreconfigitem(
370 b'convert', b'svn.startrev', default=0,
370 b'convert', b'svn.startrev', default=0,
371 )
371 )
372 coreconfigitem(
372 coreconfigitem(
373 b'debug', b'dirstate.delaywrite', default=0,
373 b'debug', b'dirstate.delaywrite', default=0,
374 )
374 )
375 coreconfigitem(
375 coreconfigitem(
376 b'defaults', b'.*', default=None, generic=True,
376 b'defaults', b'.*', default=None, generic=True,
377 )
377 )
378 coreconfigitem(
378 coreconfigitem(
379 b'devel', b'all-warnings', default=False,
379 b'devel', b'all-warnings', default=False,
380 )
380 )
381 coreconfigitem(
381 coreconfigitem(
382 b'devel', b'bundle2.debug', default=False,
382 b'devel', b'bundle2.debug', default=False,
383 )
383 )
384 coreconfigitem(
384 coreconfigitem(
385 b'devel', b'bundle.delta', default=b'',
385 b'devel', b'bundle.delta', default=b'',
386 )
386 )
387 coreconfigitem(
387 coreconfigitem(
388 b'devel', b'cache-vfs', default=None,
388 b'devel', b'cache-vfs', default=None,
389 )
389 )
390 coreconfigitem(
390 coreconfigitem(
391 b'devel', b'check-locks', default=False,
391 b'devel', b'check-locks', default=False,
392 )
392 )
393 coreconfigitem(
393 coreconfigitem(
394 b'devel', b'check-relroot', default=False,
394 b'devel', b'check-relroot', default=False,
395 )
395 )
396 coreconfigitem(
396 coreconfigitem(
397 b'devel', b'default-date', default=None,
397 b'devel', b'default-date', default=None,
398 )
398 )
399 coreconfigitem(
399 coreconfigitem(
400 b'devel', b'deprec-warn', default=False,
400 b'devel', b'deprec-warn', default=False,
401 )
401 )
402 coreconfigitem(
402 coreconfigitem(
403 b'devel', b'disableloaddefaultcerts', default=False,
403 b'devel', b'disableloaddefaultcerts', default=False,
404 )
404 )
405 coreconfigitem(
405 coreconfigitem(
406 b'devel', b'warn-empty-changegroup', default=False,
406 b'devel', b'warn-empty-changegroup', default=False,
407 )
407 )
408 coreconfigitem(
408 coreconfigitem(
409 b'devel', b'legacy.exchange', default=list,
409 b'devel', b'legacy.exchange', default=list,
410 )
410 )
411 coreconfigitem(
411 coreconfigitem(
412 b'devel', b'persistent-nodemap', default=False,
412 b'devel', b'persistent-nodemap', default=False,
413 )
413 )
414 coreconfigitem(
414 coreconfigitem(
415 b'devel', b'servercafile', default=b'',
415 b'devel', b'servercafile', default=b'',
416 )
416 )
417 coreconfigitem(
417 coreconfigitem(
418 b'devel', b'serverexactprotocol', default=b'',
418 b'devel', b'serverexactprotocol', default=b'',
419 )
419 )
420 coreconfigitem(
420 coreconfigitem(
421 b'devel', b'serverrequirecert', default=False,
421 b'devel', b'serverrequirecert', default=False,
422 )
422 )
423 coreconfigitem(
423 coreconfigitem(
424 b'devel', b'strip-obsmarkers', default=True,
424 b'devel', b'strip-obsmarkers', default=True,
425 )
425 )
426 coreconfigitem(
426 coreconfigitem(
427 b'devel', b'warn-config', default=None,
427 b'devel', b'warn-config', default=None,
428 )
428 )
429 coreconfigitem(
429 coreconfigitem(
430 b'devel', b'warn-config-default', default=None,
430 b'devel', b'warn-config-default', default=None,
431 )
431 )
432 coreconfigitem(
432 coreconfigitem(
433 b'devel', b'user.obsmarker', default=None,
433 b'devel', b'user.obsmarker', default=None,
434 )
434 )
435 coreconfigitem(
435 coreconfigitem(
436 b'devel', b'warn-config-unknown', default=None,
436 b'devel', b'warn-config-unknown', default=None,
437 )
437 )
438 coreconfigitem(
438 coreconfigitem(
439 b'devel', b'debug.copies', default=False,
439 b'devel', b'debug.copies', default=False,
440 )
440 )
441 coreconfigitem(
441 coreconfigitem(
442 b'devel', b'debug.extensions', default=False,
442 b'devel', b'debug.extensions', default=False,
443 )
443 )
444 coreconfigitem(
444 coreconfigitem(
445 b'devel', b'debug.repo-filters', default=False,
445 b'devel', b'debug.repo-filters', default=False,
446 )
446 )
447 coreconfigitem(
447 coreconfigitem(
448 b'devel', b'debug.peer-request', default=False,
448 b'devel', b'debug.peer-request', default=False,
449 )
449 )
450 coreconfigitem(
450 coreconfigitem(
451 b'devel', b'discovery.randomize', default=True,
451 b'devel', b'discovery.randomize', default=True,
452 )
452 )
453 _registerdiffopts(section=b'diff')
453 _registerdiffopts(section=b'diff')
454 coreconfigitem(
454 coreconfigitem(
455 b'email', b'bcc', default=None,
455 b'email', b'bcc', default=None,
456 )
456 )
457 coreconfigitem(
457 coreconfigitem(
458 b'email', b'cc', default=None,
458 b'email', b'cc', default=None,
459 )
459 )
460 coreconfigitem(
460 coreconfigitem(
461 b'email', b'charsets', default=list,
461 b'email', b'charsets', default=list,
462 )
462 )
463 coreconfigitem(
463 coreconfigitem(
464 b'email', b'from', default=None,
464 b'email', b'from', default=None,
465 )
465 )
466 coreconfigitem(
466 coreconfigitem(
467 b'email', b'method', default=b'smtp',
467 b'email', b'method', default=b'smtp',
468 )
468 )
469 coreconfigitem(
469 coreconfigitem(
470 b'email', b'reply-to', default=None,
470 b'email', b'reply-to', default=None,
471 )
471 )
472 coreconfigitem(
472 coreconfigitem(
473 b'email', b'to', default=None,
473 b'email', b'to', default=None,
474 )
474 )
475 coreconfigitem(
475 coreconfigitem(
476 b'experimental', b'archivemetatemplate', default=dynamicdefault,
476 b'experimental', b'archivemetatemplate', default=dynamicdefault,
477 )
477 )
478 coreconfigitem(
478 coreconfigitem(
479 b'experimental', b'auto-publish', default=b'publish',
479 b'experimental', b'auto-publish', default=b'publish',
480 )
480 )
481 coreconfigitem(
481 coreconfigitem(
482 b'experimental', b'bundle-phases', default=False,
482 b'experimental', b'bundle-phases', default=False,
483 )
483 )
484 coreconfigitem(
484 coreconfigitem(
485 b'experimental', b'bundle2-advertise', default=True,
485 b'experimental', b'bundle2-advertise', default=True,
486 )
486 )
487 coreconfigitem(
487 coreconfigitem(
488 b'experimental', b'bundle2-output-capture', default=False,
488 b'experimental', b'bundle2-output-capture', default=False,
489 )
489 )
490 coreconfigitem(
490 coreconfigitem(
491 b'experimental', b'bundle2.pushback', default=False,
491 b'experimental', b'bundle2.pushback', default=False,
492 )
492 )
493 coreconfigitem(
493 coreconfigitem(
494 b'experimental', b'bundle2lazylocking', default=False,
494 b'experimental', b'bundle2lazylocking', default=False,
495 )
495 )
496 coreconfigitem(
496 coreconfigitem(
497 b'experimental', b'bundlecomplevel', default=None,
497 b'experimental', b'bundlecomplevel', default=None,
498 )
498 )
499 coreconfigitem(
499 coreconfigitem(
500 b'experimental', b'bundlecomplevel.bzip2', default=None,
500 b'experimental', b'bundlecomplevel.bzip2', default=None,
501 )
501 )
502 coreconfigitem(
502 coreconfigitem(
503 b'experimental', b'bundlecomplevel.gzip', default=None,
503 b'experimental', b'bundlecomplevel.gzip', default=None,
504 )
504 )
505 coreconfigitem(
505 coreconfigitem(
506 b'experimental', b'bundlecomplevel.none', default=None,
506 b'experimental', b'bundlecomplevel.none', default=None,
507 )
507 )
508 coreconfigitem(
508 coreconfigitem(
509 b'experimental', b'bundlecomplevel.zstd', default=None,
509 b'experimental', b'bundlecomplevel.zstd', default=None,
510 )
510 )
511 coreconfigitem(
511 coreconfigitem(
512 b'experimental', b'changegroup3', default=False,
512 b'experimental', b'changegroup3', default=False,
513 )
513 )
514 coreconfigitem(
514 coreconfigitem(
515 b'experimental', b'cleanup-as-archived', default=False,
515 b'experimental', b'cleanup-as-archived', default=False,
516 )
516 )
517 coreconfigitem(
517 coreconfigitem(
518 b'experimental', b'clientcompressionengines', default=list,
518 b'experimental', b'clientcompressionengines', default=list,
519 )
519 )
520 coreconfigitem(
520 coreconfigitem(
521 b'experimental', b'copytrace', default=b'on',
521 b'experimental', b'copytrace', default=b'on',
522 )
522 )
523 coreconfigitem(
523 coreconfigitem(
524 b'experimental', b'copytrace.movecandidateslimit', default=100,
524 b'experimental', b'copytrace.movecandidateslimit', default=100,
525 )
525 )
526 coreconfigitem(
526 coreconfigitem(
527 b'experimental', b'copytrace.sourcecommitlimit', default=100,
527 b'experimental', b'copytrace.sourcecommitlimit', default=100,
528 )
528 )
529 coreconfigitem(
529 coreconfigitem(
530 b'experimental', b'copies.read-from', default=b"filelog-only",
530 b'experimental', b'copies.read-from', default=b"filelog-only",
531 )
531 )
532 coreconfigitem(
532 coreconfigitem(
533 b'experimental', b'copies.write-to', default=b'filelog-only',
533 b'experimental', b'copies.write-to', default=b'filelog-only',
534 )
534 )
535 coreconfigitem(
535 coreconfigitem(
536 b'experimental', b'crecordtest', default=None,
536 b'experimental', b'crecordtest', default=None,
537 )
537 )
538 coreconfigitem(
538 coreconfigitem(
539 b'experimental', b'directaccess', default=False,
539 b'experimental', b'directaccess', default=False,
540 )
540 )
541 coreconfigitem(
541 coreconfigitem(
542 b'experimental', b'directaccess.revnums', default=False,
542 b'experimental', b'directaccess.revnums', default=False,
543 )
543 )
544 coreconfigitem(
544 coreconfigitem(
545 b'experimental', b'editortmpinhg', default=False,
545 b'experimental', b'editortmpinhg', default=False,
546 )
546 )
547 coreconfigitem(
547 coreconfigitem(
548 b'experimental', b'evolution', default=list,
548 b'experimental', b'evolution', default=list,
549 )
549 )
550 coreconfigitem(
550 coreconfigitem(
551 b'experimental',
551 b'experimental',
552 b'evolution.allowdivergence',
552 b'evolution.allowdivergence',
553 default=False,
553 default=False,
554 alias=[(b'experimental', b'allowdivergence')],
554 alias=[(b'experimental', b'allowdivergence')],
555 )
555 )
556 coreconfigitem(
556 coreconfigitem(
557 b'experimental', b'evolution.allowunstable', default=None,
557 b'experimental', b'evolution.allowunstable', default=None,
558 )
558 )
559 coreconfigitem(
559 coreconfigitem(
560 b'experimental', b'evolution.createmarkers', default=None,
560 b'experimental', b'evolution.createmarkers', default=None,
561 )
561 )
562 coreconfigitem(
562 coreconfigitem(
563 b'experimental',
563 b'experimental',
564 b'evolution.effect-flags',
564 b'evolution.effect-flags',
565 default=True,
565 default=True,
566 alias=[(b'experimental', b'effect-flags')],
566 alias=[(b'experimental', b'effect-flags')],
567 )
567 )
568 coreconfigitem(
568 coreconfigitem(
569 b'experimental', b'evolution.exchange', default=None,
569 b'experimental', b'evolution.exchange', default=None,
570 )
570 )
571 coreconfigitem(
571 coreconfigitem(
572 b'experimental', b'evolution.bundle-obsmarker', default=False,
572 b'experimental', b'evolution.bundle-obsmarker', default=False,
573 )
573 )
574 coreconfigitem(
574 coreconfigitem(
575 b'experimental', b'log.topo', default=False,
575 b'experimental', b'log.topo', default=False,
576 )
576 )
577 coreconfigitem(
577 coreconfigitem(
578 b'experimental', b'evolution.report-instabilities', default=True,
578 b'experimental', b'evolution.report-instabilities', default=True,
579 )
579 )
580 coreconfigitem(
580 coreconfigitem(
581 b'experimental', b'evolution.track-operation', default=True,
581 b'experimental', b'evolution.track-operation', default=True,
582 )
582 )
583 # repo-level config to exclude a revset visibility
583 # repo-level config to exclude a revset visibility
584 #
584 #
585 # The target use case is to use `share` to expose different subset of the same
585 # The target use case is to use `share` to expose different subset of the same
586 # repository, especially server side. See also `server.view`.
586 # repository, especially server side. See also `server.view`.
587 coreconfigitem(
587 coreconfigitem(
588 b'experimental', b'extra-filter-revs', default=None,
588 b'experimental', b'extra-filter-revs', default=None,
589 )
589 )
590 coreconfigitem(
590 coreconfigitem(
591 b'experimental', b'maxdeltachainspan', default=-1,
591 b'experimental', b'maxdeltachainspan', default=-1,
592 )
592 )
593 coreconfigitem(
593 coreconfigitem(
594 b'experimental', b'mergetempdirprefix', default=None,
594 b'experimental', b'mergetempdirprefix', default=None,
595 )
595 )
596 coreconfigitem(
596 coreconfigitem(
597 b'experimental', b'mmapindexthreshold', default=None,
597 b'experimental', b'mmapindexthreshold', default=None,
598 )
598 )
599 coreconfigitem(
599 coreconfigitem(
600 b'experimental', b'narrow', default=False,
600 b'experimental', b'narrow', default=False,
601 )
601 )
602 coreconfigitem(
602 coreconfigitem(
603 b'experimental', b'nonnormalparanoidcheck', default=False,
603 b'experimental', b'nonnormalparanoidcheck', default=False,
604 )
604 )
605 coreconfigitem(
605 coreconfigitem(
606 b'experimental', b'exportableenviron', default=list,
606 b'experimental', b'exportableenviron', default=list,
607 )
607 )
608 coreconfigitem(
608 coreconfigitem(
609 b'experimental', b'extendedheader.index', default=None,
609 b'experimental', b'extendedheader.index', default=None,
610 )
610 )
611 coreconfigitem(
611 coreconfigitem(
612 b'experimental', b'extendedheader.similarity', default=False,
612 b'experimental', b'extendedheader.similarity', default=False,
613 )
613 )
614 coreconfigitem(
614 coreconfigitem(
615 b'experimental', b'graphshorten', default=False,
615 b'experimental', b'graphshorten', default=False,
616 )
616 )
617 coreconfigitem(
617 coreconfigitem(
618 b'experimental', b'graphstyle.parent', default=dynamicdefault,
618 b'experimental', b'graphstyle.parent', default=dynamicdefault,
619 )
619 )
620 coreconfigitem(
620 coreconfigitem(
621 b'experimental', b'graphstyle.missing', default=dynamicdefault,
621 b'experimental', b'graphstyle.missing', default=dynamicdefault,
622 )
622 )
623 coreconfigitem(
623 coreconfigitem(
624 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
624 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
625 )
625 )
626 coreconfigitem(
626 coreconfigitem(
627 b'experimental', b'hook-track-tags', default=False,
627 b'experimental', b'hook-track-tags', default=False,
628 )
628 )
629 coreconfigitem(
629 coreconfigitem(
630 b'experimental', b'httppeer.advertise-v2', default=False,
630 b'experimental', b'httppeer.advertise-v2', default=False,
631 )
631 )
632 coreconfigitem(
632 coreconfigitem(
633 b'experimental', b'httppeer.v2-encoder-order', default=None,
633 b'experimental', b'httppeer.v2-encoder-order', default=None,
634 )
634 )
635 coreconfigitem(
635 coreconfigitem(
636 b'experimental', b'httppostargs', default=False,
636 b'experimental', b'httppostargs', default=False,
637 )
637 )
638 coreconfigitem(
638 coreconfigitem(
639 b'experimental', b'mergedriver', default=None,
639 b'experimental', b'mergedriver', default=None,
640 )
640 )
641 coreconfigitem(b'experimental', b'nointerrupt', default=False)
641 coreconfigitem(b'experimental', b'nointerrupt', default=False)
642 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
642 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
643
643
644 coreconfigitem(
644 coreconfigitem(
645 b'experimental', b'obsmarkers-exchange-debug', default=False,
645 b'experimental', b'obsmarkers-exchange-debug', default=False,
646 )
646 )
647 coreconfigitem(
647 coreconfigitem(
648 b'experimental', b'remotenames', default=False,
648 b'experimental', b'remotenames', default=False,
649 )
649 )
650 coreconfigitem(
650 coreconfigitem(
651 b'experimental', b'removeemptydirs', default=True,
651 b'experimental', b'removeemptydirs', default=True,
652 )
652 )
653 coreconfigitem(
653 coreconfigitem(
654 b'experimental', b'revert.interactive.select-to-keep', default=False,
654 b'experimental', b'revert.interactive.select-to-keep', default=False,
655 )
655 )
656 coreconfigitem(
656 coreconfigitem(
657 b'experimental', b'revisions.prefixhexnode', default=False,
657 b'experimental', b'revisions.prefixhexnode', default=False,
658 )
658 )
659 coreconfigitem(
659 coreconfigitem(
660 b'experimental', b'revlogv2', default=None,
660 b'experimental', b'revlogv2', default=None,
661 )
661 )
662 coreconfigitem(
662 coreconfigitem(
663 b'experimental', b'revisions.disambiguatewithin', default=None,
663 b'experimental', b'revisions.disambiguatewithin', default=None,
664 )
664 )
665 coreconfigitem(
665 coreconfigitem(
666 b'experimental', b'rust.index', default=False,
666 b'experimental', b'rust.index', default=False,
667 )
667 )
668 coreconfigitem(
668 coreconfigitem(
669 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
669 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
670 )
670 )
671 coreconfigitem(
671 coreconfigitem(
672 b'experimental',
672 b'experimental',
673 b'server.manifestdata.recommended-batch-size',
673 b'server.manifestdata.recommended-batch-size',
674 default=100000,
674 default=100000,
675 )
675 )
676 coreconfigitem(
676 coreconfigitem(
677 b'experimental', b'server.stream-narrow-clones', default=False,
677 b'experimental', b'server.stream-narrow-clones', default=False,
678 )
678 )
679 coreconfigitem(
679 coreconfigitem(
680 b'experimental', b'single-head-per-branch', default=False,
680 b'experimental', b'single-head-per-branch', default=False,
681 )
681 )
682 coreconfigitem(
682 coreconfigitem(
683 b'experimental',
683 b'experimental',
684 b'single-head-per-branch:account-closed-heads',
684 b'single-head-per-branch:account-closed-heads',
685 default=False,
685 default=False,
686 )
686 )
687 coreconfigitem(
687 coreconfigitem(
688 b'experimental', b'sshserver.support-v2', default=False,
688 b'experimental', b'sshserver.support-v2', default=False,
689 )
689 )
690 coreconfigitem(
690 coreconfigitem(
691 b'experimental', b'sparse-read', default=False,
691 b'experimental', b'sparse-read', default=False,
692 )
692 )
693 coreconfigitem(
693 coreconfigitem(
694 b'experimental', b'sparse-read.density-threshold', default=0.50,
694 b'experimental', b'sparse-read.density-threshold', default=0.50,
695 )
695 )
696 coreconfigitem(
696 coreconfigitem(
697 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
697 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
698 )
698 )
699 coreconfigitem(
699 coreconfigitem(
700 b'experimental', b'treemanifest', default=False,
700 b'experimental', b'treemanifest', default=False,
701 )
701 )
702 coreconfigitem(
702 coreconfigitem(
703 b'experimental', b'update.atomic-file', default=False,
703 b'experimental', b'update.atomic-file', default=False,
704 )
704 )
705 coreconfigitem(
705 coreconfigitem(
706 b'experimental', b'sshpeer.advertise-v2', default=False,
706 b'experimental', b'sshpeer.advertise-v2', default=False,
707 )
707 )
708 coreconfigitem(
708 coreconfigitem(
709 b'experimental', b'web.apiserver', default=False,
709 b'experimental', b'web.apiserver', default=False,
710 )
710 )
711 coreconfigitem(
711 coreconfigitem(
712 b'experimental', b'web.api.http-v2', default=False,
712 b'experimental', b'web.api.http-v2', default=False,
713 )
713 )
714 coreconfigitem(
714 coreconfigitem(
715 b'experimental', b'web.api.debugreflect', default=False,
715 b'experimental', b'web.api.debugreflect', default=False,
716 )
716 )
717 coreconfigitem(
717 coreconfigitem(
718 b'experimental', b'worker.wdir-get-thread-safe', default=False,
718 b'experimental', b'worker.wdir-get-thread-safe', default=False,
719 )
719 )
720 coreconfigitem(
720 coreconfigitem(
721 b'experimental', b'worker.repository-upgrade', default=False,
721 b'experimental', b'worker.repository-upgrade', default=False,
722 )
722 )
723 coreconfigitem(
723 coreconfigitem(
724 b'experimental', b'xdiff', default=False,
724 b'experimental', b'xdiff', default=False,
725 )
725 )
726 coreconfigitem(
726 coreconfigitem(
727 b'extensions', b'.*', default=None, generic=True,
727 b'extensions', b'.*', default=None, generic=True,
728 )
728 )
729 coreconfigitem(
729 coreconfigitem(
730 b'extdata', b'.*', default=None, generic=True,
730 b'extdata', b'.*', default=None, generic=True,
731 )
731 )
732 coreconfigitem(
732 coreconfigitem(
733 b'format', b'bookmarks-in-store', default=False,
733 b'format', b'bookmarks-in-store', default=False,
734 )
734 )
735 coreconfigitem(
735 coreconfigitem(
736 b'format', b'chunkcachesize', default=None, experimental=True,
736 b'format', b'chunkcachesize', default=None, experimental=True,
737 )
737 )
738 coreconfigitem(
738 coreconfigitem(
739 b'format', b'dotencode', default=True,
739 b'format', b'dotencode', default=True,
740 )
740 )
741 coreconfigitem(
741 coreconfigitem(
742 b'format', b'generaldelta', default=False, experimental=True,
742 b'format', b'generaldelta', default=False, experimental=True,
743 )
743 )
744 coreconfigitem(
744 coreconfigitem(
745 b'format', b'manifestcachesize', default=None, experimental=True,
745 b'format', b'manifestcachesize', default=None, experimental=True,
746 )
746 )
747 coreconfigitem(
747 coreconfigitem(
748 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
748 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
749 )
749 )
750 coreconfigitem(
750 coreconfigitem(
751 b'format', b'obsstore-version', default=None,
751 b'format', b'obsstore-version', default=None,
752 )
752 )
753 coreconfigitem(
753 coreconfigitem(
754 b'format', b'sparse-revlog', default=True,
754 b'format', b'sparse-revlog', default=True,
755 )
755 )
756 coreconfigitem(
756 coreconfigitem(
757 b'format',
757 b'format',
758 b'revlog-compression',
758 b'revlog-compression',
759 default=lambda: [b'zlib'],
759 default=lambda: [b'zlib'],
760 alias=[(b'experimental', b'format.compression')],
760 alias=[(b'experimental', b'format.compression')],
761 )
761 )
762 coreconfigitem(
762 coreconfigitem(
763 b'format', b'usefncache', default=True,
763 b'format', b'usefncache', default=True,
764 )
764 )
765 coreconfigitem(
765 coreconfigitem(
766 b'format', b'usegeneraldelta', default=True,
766 b'format', b'usegeneraldelta', default=True,
767 )
767 )
768 coreconfigitem(
768 coreconfigitem(
769 b'format', b'usestore', default=True,
769 b'format', b'usestore', default=True,
770 )
770 )
771 # Right now, the only efficient implement of the nodemap logic is in Rust, so
771 # Right now, the only efficient implement of the nodemap logic is in Rust, so
772 # the persistent nodemap feature needs to stay experimental as long as the Rust
772 # the persistent nodemap feature needs to stay experimental as long as the Rust
773 # extensions are an experimental feature.
773 # extensions are an experimental feature.
774 coreconfigitem(
774 coreconfigitem(
775 b'format', b'use-persistent-nodemap', default=False, experimental=True
775 b'format', b'use-persistent-nodemap', default=False, experimental=True
776 )
776 )
777 coreconfigitem(
777 coreconfigitem(
778 b'format',
778 b'format',
779 b'exp-use-copies-side-data-changeset',
779 b'exp-use-copies-side-data-changeset',
780 default=False,
780 default=False,
781 experimental=True,
781 experimental=True,
782 )
782 )
783 coreconfigitem(
783 coreconfigitem(
784 b'format', b'exp-use-side-data', default=False, experimental=True,
784 b'format', b'exp-use-side-data', default=False, experimental=True,
785 )
785 )
786 coreconfigitem(
786 coreconfigitem(
787 b'format', b'internal-phase', default=False, experimental=True,
787 b'format', b'internal-phase', default=False, experimental=True,
788 )
788 )
789 coreconfigitem(
789 coreconfigitem(
790 b'fsmonitor', b'warn_when_unused', default=True,
790 b'fsmonitor', b'warn_when_unused', default=True,
791 )
791 )
792 coreconfigitem(
792 coreconfigitem(
793 b'fsmonitor', b'warn_update_file_count', default=50000,
793 b'fsmonitor', b'warn_update_file_count', default=50000,
794 )
794 )
795 coreconfigitem(
795 coreconfigitem(
796 b'fsmonitor', b'warn_update_file_count_rust', default=400000,
797 )
798 coreconfigitem(
796 b'help', br'hidden-command\..*', default=False, generic=True,
799 b'help', br'hidden-command\..*', default=False, generic=True,
797 )
800 )
798 coreconfigitem(
801 coreconfigitem(
799 b'help', br'hidden-topic\..*', default=False, generic=True,
802 b'help', br'hidden-topic\..*', default=False, generic=True,
800 )
803 )
801 coreconfigitem(
804 coreconfigitem(
802 b'hooks', b'.*', default=dynamicdefault, generic=True,
805 b'hooks', b'.*', default=dynamicdefault, generic=True,
803 )
806 )
804 coreconfigitem(
807 coreconfigitem(
805 b'hgweb-paths', b'.*', default=list, generic=True,
808 b'hgweb-paths', b'.*', default=list, generic=True,
806 )
809 )
807 coreconfigitem(
810 coreconfigitem(
808 b'hostfingerprints', b'.*', default=list, generic=True,
811 b'hostfingerprints', b'.*', default=list, generic=True,
809 )
812 )
810 coreconfigitem(
813 coreconfigitem(
811 b'hostsecurity', b'ciphers', default=None,
814 b'hostsecurity', b'ciphers', default=None,
812 )
815 )
813 coreconfigitem(
816 coreconfigitem(
814 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
817 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
815 )
818 )
816 coreconfigitem(
819 coreconfigitem(
817 b'hostsecurity',
820 b'hostsecurity',
818 b'.*:minimumprotocol$',
821 b'.*:minimumprotocol$',
819 default=dynamicdefault,
822 default=dynamicdefault,
820 generic=True,
823 generic=True,
821 )
824 )
822 coreconfigitem(
825 coreconfigitem(
823 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
826 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
824 )
827 )
825 coreconfigitem(
828 coreconfigitem(
826 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
829 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
827 )
830 )
828 coreconfigitem(
831 coreconfigitem(
829 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
832 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
830 )
833 )
831
834
832 coreconfigitem(
835 coreconfigitem(
833 b'http_proxy', b'always', default=False,
836 b'http_proxy', b'always', default=False,
834 )
837 )
835 coreconfigitem(
838 coreconfigitem(
836 b'http_proxy', b'host', default=None,
839 b'http_proxy', b'host', default=None,
837 )
840 )
838 coreconfigitem(
841 coreconfigitem(
839 b'http_proxy', b'no', default=list,
842 b'http_proxy', b'no', default=list,
840 )
843 )
841 coreconfigitem(
844 coreconfigitem(
842 b'http_proxy', b'passwd', default=None,
845 b'http_proxy', b'passwd', default=None,
843 )
846 )
844 coreconfigitem(
847 coreconfigitem(
845 b'http_proxy', b'user', default=None,
848 b'http_proxy', b'user', default=None,
846 )
849 )
847
850
848 coreconfigitem(
851 coreconfigitem(
849 b'http', b'timeout', default=None,
852 b'http', b'timeout', default=None,
850 )
853 )
851
854
852 coreconfigitem(
855 coreconfigitem(
853 b'logtoprocess', b'commandexception', default=None,
856 b'logtoprocess', b'commandexception', default=None,
854 )
857 )
855 coreconfigitem(
858 coreconfigitem(
856 b'logtoprocess', b'commandfinish', default=None,
859 b'logtoprocess', b'commandfinish', default=None,
857 )
860 )
858 coreconfigitem(
861 coreconfigitem(
859 b'logtoprocess', b'command', default=None,
862 b'logtoprocess', b'command', default=None,
860 )
863 )
861 coreconfigitem(
864 coreconfigitem(
862 b'logtoprocess', b'develwarn', default=None,
865 b'logtoprocess', b'develwarn', default=None,
863 )
866 )
864 coreconfigitem(
867 coreconfigitem(
865 b'logtoprocess', b'uiblocked', default=None,
868 b'logtoprocess', b'uiblocked', default=None,
866 )
869 )
867 coreconfigitem(
870 coreconfigitem(
868 b'merge', b'checkunknown', default=b'abort',
871 b'merge', b'checkunknown', default=b'abort',
869 )
872 )
870 coreconfigitem(
873 coreconfigitem(
871 b'merge', b'checkignored', default=b'abort',
874 b'merge', b'checkignored', default=b'abort',
872 )
875 )
873 coreconfigitem(
876 coreconfigitem(
874 b'experimental', b'merge.checkpathconflicts', default=False,
877 b'experimental', b'merge.checkpathconflicts', default=False,
875 )
878 )
876 coreconfigitem(
879 coreconfigitem(
877 b'merge', b'followcopies', default=True,
880 b'merge', b'followcopies', default=True,
878 )
881 )
879 coreconfigitem(
882 coreconfigitem(
880 b'merge', b'on-failure', default=b'continue',
883 b'merge', b'on-failure', default=b'continue',
881 )
884 )
882 coreconfigitem(
885 coreconfigitem(
883 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
886 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
884 )
887 )
885 coreconfigitem(
888 coreconfigitem(
886 b'merge', b'strict-capability-check', default=False,
889 b'merge', b'strict-capability-check', default=False,
887 )
890 )
888 coreconfigitem(
891 coreconfigitem(
889 b'merge-tools', b'.*', default=None, generic=True,
892 b'merge-tools', b'.*', default=None, generic=True,
890 )
893 )
891 coreconfigitem(
894 coreconfigitem(
892 b'merge-tools',
895 b'merge-tools',
893 br'.*\.args$',
896 br'.*\.args$',
894 default=b"$local $base $other",
897 default=b"$local $base $other",
895 generic=True,
898 generic=True,
896 priority=-1,
899 priority=-1,
897 )
900 )
898 coreconfigitem(
901 coreconfigitem(
899 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
902 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
900 )
903 )
901 coreconfigitem(
904 coreconfigitem(
902 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
905 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
903 )
906 )
904 coreconfigitem(
907 coreconfigitem(
905 b'merge-tools',
908 b'merge-tools',
906 br'.*\.checkchanged$',
909 br'.*\.checkchanged$',
907 default=False,
910 default=False,
908 generic=True,
911 generic=True,
909 priority=-1,
912 priority=-1,
910 )
913 )
911 coreconfigitem(
914 coreconfigitem(
912 b'merge-tools',
915 b'merge-tools',
913 br'.*\.executable$',
916 br'.*\.executable$',
914 default=dynamicdefault,
917 default=dynamicdefault,
915 generic=True,
918 generic=True,
916 priority=-1,
919 priority=-1,
917 )
920 )
918 coreconfigitem(
921 coreconfigitem(
919 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
922 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
920 )
923 )
921 coreconfigitem(
924 coreconfigitem(
922 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
925 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
923 )
926 )
924 coreconfigitem(
927 coreconfigitem(
925 b'merge-tools',
928 b'merge-tools',
926 br'.*\.mergemarkers$',
929 br'.*\.mergemarkers$',
927 default=b'basic',
930 default=b'basic',
928 generic=True,
931 generic=True,
929 priority=-1,
932 priority=-1,
930 )
933 )
931 coreconfigitem(
934 coreconfigitem(
932 b'merge-tools',
935 b'merge-tools',
933 br'.*\.mergemarkertemplate$',
936 br'.*\.mergemarkertemplate$',
934 default=dynamicdefault, # take from ui.mergemarkertemplate
937 default=dynamicdefault, # take from ui.mergemarkertemplate
935 generic=True,
938 generic=True,
936 priority=-1,
939 priority=-1,
937 )
940 )
938 coreconfigitem(
941 coreconfigitem(
939 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
942 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
940 )
943 )
941 coreconfigitem(
944 coreconfigitem(
942 b'merge-tools',
945 b'merge-tools',
943 br'.*\.premerge$',
946 br'.*\.premerge$',
944 default=dynamicdefault,
947 default=dynamicdefault,
945 generic=True,
948 generic=True,
946 priority=-1,
949 priority=-1,
947 )
950 )
948 coreconfigitem(
951 coreconfigitem(
949 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
952 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
950 )
953 )
951 coreconfigitem(
954 coreconfigitem(
952 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
955 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
953 )
956 )
954 coreconfigitem(
957 coreconfigitem(
955 b'pager', b'ignore', default=list,
958 b'pager', b'ignore', default=list,
956 )
959 )
957 coreconfigitem(
960 coreconfigitem(
958 b'pager', b'pager', default=dynamicdefault,
961 b'pager', b'pager', default=dynamicdefault,
959 )
962 )
960 coreconfigitem(
963 coreconfigitem(
961 b'patch', b'eol', default=b'strict',
964 b'patch', b'eol', default=b'strict',
962 )
965 )
963 coreconfigitem(
966 coreconfigitem(
964 b'patch', b'fuzz', default=2,
967 b'patch', b'fuzz', default=2,
965 )
968 )
966 coreconfigitem(
969 coreconfigitem(
967 b'paths', b'default', default=None,
970 b'paths', b'default', default=None,
968 )
971 )
969 coreconfigitem(
972 coreconfigitem(
970 b'paths', b'default-push', default=None,
973 b'paths', b'default-push', default=None,
971 )
974 )
972 coreconfigitem(
975 coreconfigitem(
973 b'paths', b'.*', default=None, generic=True,
976 b'paths', b'.*', default=None, generic=True,
974 )
977 )
975 coreconfigitem(
978 coreconfigitem(
976 b'phases', b'checksubrepos', default=b'follow',
979 b'phases', b'checksubrepos', default=b'follow',
977 )
980 )
978 coreconfigitem(
981 coreconfigitem(
979 b'phases', b'new-commit', default=b'draft',
982 b'phases', b'new-commit', default=b'draft',
980 )
983 )
981 coreconfigitem(
984 coreconfigitem(
982 b'phases', b'publish', default=True,
985 b'phases', b'publish', default=True,
983 )
986 )
984 coreconfigitem(
987 coreconfigitem(
985 b'profiling', b'enabled', default=False,
988 b'profiling', b'enabled', default=False,
986 )
989 )
987 coreconfigitem(
990 coreconfigitem(
988 b'profiling', b'format', default=b'text',
991 b'profiling', b'format', default=b'text',
989 )
992 )
990 coreconfigitem(
993 coreconfigitem(
991 b'profiling', b'freq', default=1000,
994 b'profiling', b'freq', default=1000,
992 )
995 )
993 coreconfigitem(
996 coreconfigitem(
994 b'profiling', b'limit', default=30,
997 b'profiling', b'limit', default=30,
995 )
998 )
996 coreconfigitem(
999 coreconfigitem(
997 b'profiling', b'nested', default=0,
1000 b'profiling', b'nested', default=0,
998 )
1001 )
999 coreconfigitem(
1002 coreconfigitem(
1000 b'profiling', b'output', default=None,
1003 b'profiling', b'output', default=None,
1001 )
1004 )
1002 coreconfigitem(
1005 coreconfigitem(
1003 b'profiling', b'showmax', default=0.999,
1006 b'profiling', b'showmax', default=0.999,
1004 )
1007 )
1005 coreconfigitem(
1008 coreconfigitem(
1006 b'profiling', b'showmin', default=dynamicdefault,
1009 b'profiling', b'showmin', default=dynamicdefault,
1007 )
1010 )
1008 coreconfigitem(
1011 coreconfigitem(
1009 b'profiling', b'showtime', default=True,
1012 b'profiling', b'showtime', default=True,
1010 )
1013 )
1011 coreconfigitem(
1014 coreconfigitem(
1012 b'profiling', b'sort', default=b'inlinetime',
1015 b'profiling', b'sort', default=b'inlinetime',
1013 )
1016 )
1014 coreconfigitem(
1017 coreconfigitem(
1015 b'profiling', b'statformat', default=b'hotpath',
1018 b'profiling', b'statformat', default=b'hotpath',
1016 )
1019 )
1017 coreconfigitem(
1020 coreconfigitem(
1018 b'profiling', b'time-track', default=dynamicdefault,
1021 b'profiling', b'time-track', default=dynamicdefault,
1019 )
1022 )
1020 coreconfigitem(
1023 coreconfigitem(
1021 b'profiling', b'type', default=b'stat',
1024 b'profiling', b'type', default=b'stat',
1022 )
1025 )
1023 coreconfigitem(
1026 coreconfigitem(
1024 b'progress', b'assume-tty', default=False,
1027 b'progress', b'assume-tty', default=False,
1025 )
1028 )
1026 coreconfigitem(
1029 coreconfigitem(
1027 b'progress', b'changedelay', default=1,
1030 b'progress', b'changedelay', default=1,
1028 )
1031 )
1029 coreconfigitem(
1032 coreconfigitem(
1030 b'progress', b'clear-complete', default=True,
1033 b'progress', b'clear-complete', default=True,
1031 )
1034 )
1032 coreconfigitem(
1035 coreconfigitem(
1033 b'progress', b'debug', default=False,
1036 b'progress', b'debug', default=False,
1034 )
1037 )
1035 coreconfigitem(
1038 coreconfigitem(
1036 b'progress', b'delay', default=3,
1039 b'progress', b'delay', default=3,
1037 )
1040 )
1038 coreconfigitem(
1041 coreconfigitem(
1039 b'progress', b'disable', default=False,
1042 b'progress', b'disable', default=False,
1040 )
1043 )
1041 coreconfigitem(
1044 coreconfigitem(
1042 b'progress', b'estimateinterval', default=60.0,
1045 b'progress', b'estimateinterval', default=60.0,
1043 )
1046 )
1044 coreconfigitem(
1047 coreconfigitem(
1045 b'progress',
1048 b'progress',
1046 b'format',
1049 b'format',
1047 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1050 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1048 )
1051 )
1049 coreconfigitem(
1052 coreconfigitem(
1050 b'progress', b'refresh', default=0.1,
1053 b'progress', b'refresh', default=0.1,
1051 )
1054 )
1052 coreconfigitem(
1055 coreconfigitem(
1053 b'progress', b'width', default=dynamicdefault,
1056 b'progress', b'width', default=dynamicdefault,
1054 )
1057 )
1055 coreconfigitem(
1058 coreconfigitem(
1056 b'pull', b'confirm', default=False,
1059 b'pull', b'confirm', default=False,
1057 )
1060 )
1058 coreconfigitem(
1061 coreconfigitem(
1059 b'push', b'pushvars.server', default=False,
1062 b'push', b'pushvars.server', default=False,
1060 )
1063 )
1061 coreconfigitem(
1064 coreconfigitem(
1062 b'rewrite',
1065 b'rewrite',
1063 b'backup-bundle',
1066 b'backup-bundle',
1064 default=True,
1067 default=True,
1065 alias=[(b'ui', b'history-editing-backup')],
1068 alias=[(b'ui', b'history-editing-backup')],
1066 )
1069 )
1067 coreconfigitem(
1070 coreconfigitem(
1068 b'rewrite', b'update-timestamp', default=False,
1071 b'rewrite', b'update-timestamp', default=False,
1069 )
1072 )
1070 coreconfigitem(
1073 coreconfigitem(
1071 b'rewrite', b'empty-successor', default=b'skip', experimental=True,
1074 b'rewrite', b'empty-successor', default=b'skip', experimental=True,
1072 )
1075 )
1073 coreconfigitem(
1076 coreconfigitem(
1074 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1077 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1075 )
1078 )
1076 coreconfigitem(
1079 coreconfigitem(
1077 b'storage',
1080 b'storage',
1078 b'revlog.optimize-delta-parent-choice',
1081 b'revlog.optimize-delta-parent-choice',
1079 default=True,
1082 default=True,
1080 alias=[(b'format', b'aggressivemergedeltas')],
1083 alias=[(b'format', b'aggressivemergedeltas')],
1081 )
1084 )
1082 # experimental as long as rust is experimental (or a C version is implemented)
1085 # experimental as long as rust is experimental (or a C version is implemented)
1083 coreconfigitem(
1086 coreconfigitem(
1084 b'storage', b'revlog.nodemap.mmap', default=True, experimental=True
1087 b'storage', b'revlog.nodemap.mmap', default=True, experimental=True
1085 )
1088 )
1086 # experimental as long as format.use-persistent-nodemap is.
1089 # experimental as long as format.use-persistent-nodemap is.
1087 coreconfigitem(
1090 coreconfigitem(
1088 b'storage', b'revlog.nodemap.mode', default=b'compat', experimental=True
1091 b'storage', b'revlog.nodemap.mode', default=b'compat', experimental=True
1089 )
1092 )
1090 coreconfigitem(
1093 coreconfigitem(
1091 b'storage', b'revlog.reuse-external-delta', default=True,
1094 b'storage', b'revlog.reuse-external-delta', default=True,
1092 )
1095 )
1093 coreconfigitem(
1096 coreconfigitem(
1094 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1097 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1095 )
1098 )
1096 coreconfigitem(
1099 coreconfigitem(
1097 b'storage', b'revlog.zlib.level', default=None,
1100 b'storage', b'revlog.zlib.level', default=None,
1098 )
1101 )
1099 coreconfigitem(
1102 coreconfigitem(
1100 b'storage', b'revlog.zstd.level', default=None,
1103 b'storage', b'revlog.zstd.level', default=None,
1101 )
1104 )
1102 coreconfigitem(
1105 coreconfigitem(
1103 b'server', b'bookmarks-pushkey-compat', default=True,
1106 b'server', b'bookmarks-pushkey-compat', default=True,
1104 )
1107 )
1105 coreconfigitem(
1108 coreconfigitem(
1106 b'server', b'bundle1', default=True,
1109 b'server', b'bundle1', default=True,
1107 )
1110 )
1108 coreconfigitem(
1111 coreconfigitem(
1109 b'server', b'bundle1gd', default=None,
1112 b'server', b'bundle1gd', default=None,
1110 )
1113 )
1111 coreconfigitem(
1114 coreconfigitem(
1112 b'server', b'bundle1.pull', default=None,
1115 b'server', b'bundle1.pull', default=None,
1113 )
1116 )
1114 coreconfigitem(
1117 coreconfigitem(
1115 b'server', b'bundle1gd.pull', default=None,
1118 b'server', b'bundle1gd.pull', default=None,
1116 )
1119 )
1117 coreconfigitem(
1120 coreconfigitem(
1118 b'server', b'bundle1.push', default=None,
1121 b'server', b'bundle1.push', default=None,
1119 )
1122 )
1120 coreconfigitem(
1123 coreconfigitem(
1121 b'server', b'bundle1gd.push', default=None,
1124 b'server', b'bundle1gd.push', default=None,
1122 )
1125 )
1123 coreconfigitem(
1126 coreconfigitem(
1124 b'server',
1127 b'server',
1125 b'bundle2.stream',
1128 b'bundle2.stream',
1126 default=True,
1129 default=True,
1127 alias=[(b'experimental', b'bundle2.stream')],
1130 alias=[(b'experimental', b'bundle2.stream')],
1128 )
1131 )
1129 coreconfigitem(
1132 coreconfigitem(
1130 b'server', b'compressionengines', default=list,
1133 b'server', b'compressionengines', default=list,
1131 )
1134 )
1132 coreconfigitem(
1135 coreconfigitem(
1133 b'server', b'concurrent-push-mode', default=b'check-related',
1136 b'server', b'concurrent-push-mode', default=b'check-related',
1134 )
1137 )
1135 coreconfigitem(
1138 coreconfigitem(
1136 b'server', b'disablefullbundle', default=False,
1139 b'server', b'disablefullbundle', default=False,
1137 )
1140 )
1138 coreconfigitem(
1141 coreconfigitem(
1139 b'server', b'maxhttpheaderlen', default=1024,
1142 b'server', b'maxhttpheaderlen', default=1024,
1140 )
1143 )
1141 coreconfigitem(
1144 coreconfigitem(
1142 b'server', b'pullbundle', default=False,
1145 b'server', b'pullbundle', default=False,
1143 )
1146 )
1144 coreconfigitem(
1147 coreconfigitem(
1145 b'server', b'preferuncompressed', default=False,
1148 b'server', b'preferuncompressed', default=False,
1146 )
1149 )
1147 coreconfigitem(
1150 coreconfigitem(
1148 b'server', b'streamunbundle', default=False,
1151 b'server', b'streamunbundle', default=False,
1149 )
1152 )
1150 coreconfigitem(
1153 coreconfigitem(
1151 b'server', b'uncompressed', default=True,
1154 b'server', b'uncompressed', default=True,
1152 )
1155 )
1153 coreconfigitem(
1156 coreconfigitem(
1154 b'server', b'uncompressedallowsecret', default=False,
1157 b'server', b'uncompressedallowsecret', default=False,
1155 )
1158 )
1156 coreconfigitem(
1159 coreconfigitem(
1157 b'server', b'view', default=b'served',
1160 b'server', b'view', default=b'served',
1158 )
1161 )
1159 coreconfigitem(
1162 coreconfigitem(
1160 b'server', b'validate', default=False,
1163 b'server', b'validate', default=False,
1161 )
1164 )
1162 coreconfigitem(
1165 coreconfigitem(
1163 b'server', b'zliblevel', default=-1,
1166 b'server', b'zliblevel', default=-1,
1164 )
1167 )
1165 coreconfigitem(
1168 coreconfigitem(
1166 b'server', b'zstdlevel', default=3,
1169 b'server', b'zstdlevel', default=3,
1167 )
1170 )
1168 coreconfigitem(
1171 coreconfigitem(
1169 b'share', b'pool', default=None,
1172 b'share', b'pool', default=None,
1170 )
1173 )
1171 coreconfigitem(
1174 coreconfigitem(
1172 b'share', b'poolnaming', default=b'identity',
1175 b'share', b'poolnaming', default=b'identity',
1173 )
1176 )
1174 coreconfigitem(
1177 coreconfigitem(
1175 b'shelve', b'maxbackups', default=10,
1178 b'shelve', b'maxbackups', default=10,
1176 )
1179 )
1177 coreconfigitem(
1180 coreconfigitem(
1178 b'smtp', b'host', default=None,
1181 b'smtp', b'host', default=None,
1179 )
1182 )
1180 coreconfigitem(
1183 coreconfigitem(
1181 b'smtp', b'local_hostname', default=None,
1184 b'smtp', b'local_hostname', default=None,
1182 )
1185 )
1183 coreconfigitem(
1186 coreconfigitem(
1184 b'smtp', b'password', default=None,
1187 b'smtp', b'password', default=None,
1185 )
1188 )
1186 coreconfigitem(
1189 coreconfigitem(
1187 b'smtp', b'port', default=dynamicdefault,
1190 b'smtp', b'port', default=dynamicdefault,
1188 )
1191 )
1189 coreconfigitem(
1192 coreconfigitem(
1190 b'smtp', b'tls', default=b'none',
1193 b'smtp', b'tls', default=b'none',
1191 )
1194 )
1192 coreconfigitem(
1195 coreconfigitem(
1193 b'smtp', b'username', default=None,
1196 b'smtp', b'username', default=None,
1194 )
1197 )
1195 coreconfigitem(
1198 coreconfigitem(
1196 b'sparse', b'missingwarning', default=True, experimental=True,
1199 b'sparse', b'missingwarning', default=True, experimental=True,
1197 )
1200 )
1198 coreconfigitem(
1201 coreconfigitem(
1199 b'subrepos',
1202 b'subrepos',
1200 b'allowed',
1203 b'allowed',
1201 default=dynamicdefault, # to make backporting simpler
1204 default=dynamicdefault, # to make backporting simpler
1202 )
1205 )
1203 coreconfigitem(
1206 coreconfigitem(
1204 b'subrepos', b'hg:allowed', default=dynamicdefault,
1207 b'subrepos', b'hg:allowed', default=dynamicdefault,
1205 )
1208 )
1206 coreconfigitem(
1209 coreconfigitem(
1207 b'subrepos', b'git:allowed', default=dynamicdefault,
1210 b'subrepos', b'git:allowed', default=dynamicdefault,
1208 )
1211 )
1209 coreconfigitem(
1212 coreconfigitem(
1210 b'subrepos', b'svn:allowed', default=dynamicdefault,
1213 b'subrepos', b'svn:allowed', default=dynamicdefault,
1211 )
1214 )
1212 coreconfigitem(
1215 coreconfigitem(
1213 b'templates', b'.*', default=None, generic=True,
1216 b'templates', b'.*', default=None, generic=True,
1214 )
1217 )
1215 coreconfigitem(
1218 coreconfigitem(
1216 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1219 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1217 )
1220 )
1218 coreconfigitem(
1221 coreconfigitem(
1219 b'trusted', b'groups', default=list,
1222 b'trusted', b'groups', default=list,
1220 )
1223 )
1221 coreconfigitem(
1224 coreconfigitem(
1222 b'trusted', b'users', default=list,
1225 b'trusted', b'users', default=list,
1223 )
1226 )
1224 coreconfigitem(
1227 coreconfigitem(
1225 b'ui', b'_usedassubrepo', default=False,
1228 b'ui', b'_usedassubrepo', default=False,
1226 )
1229 )
1227 coreconfigitem(
1230 coreconfigitem(
1228 b'ui', b'allowemptycommit', default=False,
1231 b'ui', b'allowemptycommit', default=False,
1229 )
1232 )
1230 coreconfigitem(
1233 coreconfigitem(
1231 b'ui', b'archivemeta', default=True,
1234 b'ui', b'archivemeta', default=True,
1232 )
1235 )
1233 coreconfigitem(
1236 coreconfigitem(
1234 b'ui', b'askusername', default=False,
1237 b'ui', b'askusername', default=False,
1235 )
1238 )
1236 coreconfigitem(
1239 coreconfigitem(
1237 b'ui', b'available-memory', default=None,
1240 b'ui', b'available-memory', default=None,
1238 )
1241 )
1239
1242
1240 coreconfigitem(
1243 coreconfigitem(
1241 b'ui', b'clonebundlefallback', default=False,
1244 b'ui', b'clonebundlefallback', default=False,
1242 )
1245 )
1243 coreconfigitem(
1246 coreconfigitem(
1244 b'ui', b'clonebundleprefers', default=list,
1247 b'ui', b'clonebundleprefers', default=list,
1245 )
1248 )
1246 coreconfigitem(
1249 coreconfigitem(
1247 b'ui', b'clonebundles', default=True,
1250 b'ui', b'clonebundles', default=True,
1248 )
1251 )
1249 coreconfigitem(
1252 coreconfigitem(
1250 b'ui', b'color', default=b'auto',
1253 b'ui', b'color', default=b'auto',
1251 )
1254 )
1252 coreconfigitem(
1255 coreconfigitem(
1253 b'ui', b'commitsubrepos', default=False,
1256 b'ui', b'commitsubrepos', default=False,
1254 )
1257 )
1255 coreconfigitem(
1258 coreconfigitem(
1256 b'ui', b'debug', default=False,
1259 b'ui', b'debug', default=False,
1257 )
1260 )
1258 coreconfigitem(
1261 coreconfigitem(
1259 b'ui', b'debugger', default=None,
1262 b'ui', b'debugger', default=None,
1260 )
1263 )
1261 coreconfigitem(
1264 coreconfigitem(
1262 b'ui', b'editor', default=dynamicdefault,
1265 b'ui', b'editor', default=dynamicdefault,
1263 )
1266 )
1264 coreconfigitem(
1267 coreconfigitem(
1265 b'ui', b'fallbackencoding', default=None,
1268 b'ui', b'fallbackencoding', default=None,
1266 )
1269 )
1267 coreconfigitem(
1270 coreconfigitem(
1268 b'ui', b'forcecwd', default=None,
1271 b'ui', b'forcecwd', default=None,
1269 )
1272 )
1270 coreconfigitem(
1273 coreconfigitem(
1271 b'ui', b'forcemerge', default=None,
1274 b'ui', b'forcemerge', default=None,
1272 )
1275 )
1273 coreconfigitem(
1276 coreconfigitem(
1274 b'ui', b'formatdebug', default=False,
1277 b'ui', b'formatdebug', default=False,
1275 )
1278 )
1276 coreconfigitem(
1279 coreconfigitem(
1277 b'ui', b'formatjson', default=False,
1280 b'ui', b'formatjson', default=False,
1278 )
1281 )
1279 coreconfigitem(
1282 coreconfigitem(
1280 b'ui', b'formatted', default=None,
1283 b'ui', b'formatted', default=None,
1281 )
1284 )
1282 coreconfigitem(
1285 coreconfigitem(
1283 b'ui', b'graphnodetemplate', default=None,
1286 b'ui', b'graphnodetemplate', default=None,
1284 )
1287 )
1285 coreconfigitem(
1288 coreconfigitem(
1286 b'ui', b'interactive', default=None,
1289 b'ui', b'interactive', default=None,
1287 )
1290 )
1288 coreconfigitem(
1291 coreconfigitem(
1289 b'ui', b'interface', default=None,
1292 b'ui', b'interface', default=None,
1290 )
1293 )
1291 coreconfigitem(
1294 coreconfigitem(
1292 b'ui', b'interface.chunkselector', default=None,
1295 b'ui', b'interface.chunkselector', default=None,
1293 )
1296 )
1294 coreconfigitem(
1297 coreconfigitem(
1295 b'ui', b'large-file-limit', default=10000000,
1298 b'ui', b'large-file-limit', default=10000000,
1296 )
1299 )
1297 coreconfigitem(
1300 coreconfigitem(
1298 b'ui', b'logblockedtimes', default=False,
1301 b'ui', b'logblockedtimes', default=False,
1299 )
1302 )
1300 coreconfigitem(
1303 coreconfigitem(
1301 b'ui', b'logtemplate', default=None,
1304 b'ui', b'logtemplate', default=None,
1302 )
1305 )
1303 coreconfigitem(
1306 coreconfigitem(
1304 b'ui', b'merge', default=None,
1307 b'ui', b'merge', default=None,
1305 )
1308 )
1306 coreconfigitem(
1309 coreconfigitem(
1307 b'ui', b'mergemarkers', default=b'basic',
1310 b'ui', b'mergemarkers', default=b'basic',
1308 )
1311 )
1309 coreconfigitem(
1312 coreconfigitem(
1310 b'ui',
1313 b'ui',
1311 b'mergemarkertemplate',
1314 b'mergemarkertemplate',
1312 default=(
1315 default=(
1313 b'{node|short} '
1316 b'{node|short} '
1314 b'{ifeq(tags, "tip", "", '
1317 b'{ifeq(tags, "tip", "", '
1315 b'ifeq(tags, "", "", "{tags} "))}'
1318 b'ifeq(tags, "", "", "{tags} "))}'
1316 b'{if(bookmarks, "{bookmarks} ")}'
1319 b'{if(bookmarks, "{bookmarks} ")}'
1317 b'{ifeq(branch, "default", "", "{branch} ")}'
1320 b'{ifeq(branch, "default", "", "{branch} ")}'
1318 b'- {author|user}: {desc|firstline}'
1321 b'- {author|user}: {desc|firstline}'
1319 ),
1322 ),
1320 )
1323 )
1321 coreconfigitem(
1324 coreconfigitem(
1322 b'ui', b'message-output', default=b'stdio',
1325 b'ui', b'message-output', default=b'stdio',
1323 )
1326 )
1324 coreconfigitem(
1327 coreconfigitem(
1325 b'ui', b'nontty', default=False,
1328 b'ui', b'nontty', default=False,
1326 )
1329 )
1327 coreconfigitem(
1330 coreconfigitem(
1328 b'ui', b'origbackuppath', default=None,
1331 b'ui', b'origbackuppath', default=None,
1329 )
1332 )
1330 coreconfigitem(
1333 coreconfigitem(
1331 b'ui', b'paginate', default=True,
1334 b'ui', b'paginate', default=True,
1332 )
1335 )
1333 coreconfigitem(
1336 coreconfigitem(
1334 b'ui', b'patch', default=None,
1337 b'ui', b'patch', default=None,
1335 )
1338 )
1336 coreconfigitem(
1339 coreconfigitem(
1337 b'ui', b'pre-merge-tool-output-template', default=None,
1340 b'ui', b'pre-merge-tool-output-template', default=None,
1338 )
1341 )
1339 coreconfigitem(
1342 coreconfigitem(
1340 b'ui', b'portablefilenames', default=b'warn',
1343 b'ui', b'portablefilenames', default=b'warn',
1341 )
1344 )
1342 coreconfigitem(
1345 coreconfigitem(
1343 b'ui', b'promptecho', default=False,
1346 b'ui', b'promptecho', default=False,
1344 )
1347 )
1345 coreconfigitem(
1348 coreconfigitem(
1346 b'ui', b'quiet', default=False,
1349 b'ui', b'quiet', default=False,
1347 )
1350 )
1348 coreconfigitem(
1351 coreconfigitem(
1349 b'ui', b'quietbookmarkmove', default=False,
1352 b'ui', b'quietbookmarkmove', default=False,
1350 )
1353 )
1351 coreconfigitem(
1354 coreconfigitem(
1352 b'ui', b'relative-paths', default=b'legacy',
1355 b'ui', b'relative-paths', default=b'legacy',
1353 )
1356 )
1354 coreconfigitem(
1357 coreconfigitem(
1355 b'ui', b'remotecmd', default=b'hg',
1358 b'ui', b'remotecmd', default=b'hg',
1356 )
1359 )
1357 coreconfigitem(
1360 coreconfigitem(
1358 b'ui', b'report_untrusted', default=True,
1361 b'ui', b'report_untrusted', default=True,
1359 )
1362 )
1360 coreconfigitem(
1363 coreconfigitem(
1361 b'ui', b'rollback', default=True,
1364 b'ui', b'rollback', default=True,
1362 )
1365 )
1363 coreconfigitem(
1366 coreconfigitem(
1364 b'ui', b'signal-safe-lock', default=True,
1367 b'ui', b'signal-safe-lock', default=True,
1365 )
1368 )
1366 coreconfigitem(
1369 coreconfigitem(
1367 b'ui', b'slash', default=False,
1370 b'ui', b'slash', default=False,
1368 )
1371 )
1369 coreconfigitem(
1372 coreconfigitem(
1370 b'ui', b'ssh', default=b'ssh',
1373 b'ui', b'ssh', default=b'ssh',
1371 )
1374 )
1372 coreconfigitem(
1375 coreconfigitem(
1373 b'ui', b'ssherrorhint', default=None,
1376 b'ui', b'ssherrorhint', default=None,
1374 )
1377 )
1375 coreconfigitem(
1378 coreconfigitem(
1376 b'ui', b'statuscopies', default=False,
1379 b'ui', b'statuscopies', default=False,
1377 )
1380 )
1378 coreconfigitem(
1381 coreconfigitem(
1379 b'ui', b'strict', default=False,
1382 b'ui', b'strict', default=False,
1380 )
1383 )
1381 coreconfigitem(
1384 coreconfigitem(
1382 b'ui', b'style', default=b'',
1385 b'ui', b'style', default=b'',
1383 )
1386 )
1384 coreconfigitem(
1387 coreconfigitem(
1385 b'ui', b'supportcontact', default=None,
1388 b'ui', b'supportcontact', default=None,
1386 )
1389 )
1387 coreconfigitem(
1390 coreconfigitem(
1388 b'ui', b'textwidth', default=78,
1391 b'ui', b'textwidth', default=78,
1389 )
1392 )
1390 coreconfigitem(
1393 coreconfigitem(
1391 b'ui', b'timeout', default=b'600',
1394 b'ui', b'timeout', default=b'600',
1392 )
1395 )
1393 coreconfigitem(
1396 coreconfigitem(
1394 b'ui', b'timeout.warn', default=0,
1397 b'ui', b'timeout.warn', default=0,
1395 )
1398 )
1396 coreconfigitem(
1399 coreconfigitem(
1397 b'ui', b'timestamp-output', default=False,
1400 b'ui', b'timestamp-output', default=False,
1398 )
1401 )
1399 coreconfigitem(
1402 coreconfigitem(
1400 b'ui', b'traceback', default=False,
1403 b'ui', b'traceback', default=False,
1401 )
1404 )
1402 coreconfigitem(
1405 coreconfigitem(
1403 b'ui', b'tweakdefaults', default=False,
1406 b'ui', b'tweakdefaults', default=False,
1404 )
1407 )
1405 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1408 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1406 coreconfigitem(
1409 coreconfigitem(
1407 b'ui', b'verbose', default=False,
1410 b'ui', b'verbose', default=False,
1408 )
1411 )
1409 coreconfigitem(
1412 coreconfigitem(
1410 b'verify', b'skipflags', default=None,
1413 b'verify', b'skipflags', default=None,
1411 )
1414 )
1412 coreconfigitem(
1415 coreconfigitem(
1413 b'web', b'allowbz2', default=False,
1416 b'web', b'allowbz2', default=False,
1414 )
1417 )
1415 coreconfigitem(
1418 coreconfigitem(
1416 b'web', b'allowgz', default=False,
1419 b'web', b'allowgz', default=False,
1417 )
1420 )
1418 coreconfigitem(
1421 coreconfigitem(
1419 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1422 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1420 )
1423 )
1421 coreconfigitem(
1424 coreconfigitem(
1422 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1425 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1423 )
1426 )
1424 coreconfigitem(
1427 coreconfigitem(
1425 b'web', b'allowzip', default=False,
1428 b'web', b'allowzip', default=False,
1426 )
1429 )
1427 coreconfigitem(
1430 coreconfigitem(
1428 b'web', b'archivesubrepos', default=False,
1431 b'web', b'archivesubrepos', default=False,
1429 )
1432 )
1430 coreconfigitem(
1433 coreconfigitem(
1431 b'web', b'cache', default=True,
1434 b'web', b'cache', default=True,
1432 )
1435 )
1433 coreconfigitem(
1436 coreconfigitem(
1434 b'web', b'comparisoncontext', default=5,
1437 b'web', b'comparisoncontext', default=5,
1435 )
1438 )
1436 coreconfigitem(
1439 coreconfigitem(
1437 b'web', b'contact', default=None,
1440 b'web', b'contact', default=None,
1438 )
1441 )
1439 coreconfigitem(
1442 coreconfigitem(
1440 b'web', b'deny_push', default=list,
1443 b'web', b'deny_push', default=list,
1441 )
1444 )
1442 coreconfigitem(
1445 coreconfigitem(
1443 b'web', b'guessmime', default=False,
1446 b'web', b'guessmime', default=False,
1444 )
1447 )
1445 coreconfigitem(
1448 coreconfigitem(
1446 b'web', b'hidden', default=False,
1449 b'web', b'hidden', default=False,
1447 )
1450 )
1448 coreconfigitem(
1451 coreconfigitem(
1449 b'web', b'labels', default=list,
1452 b'web', b'labels', default=list,
1450 )
1453 )
1451 coreconfigitem(
1454 coreconfigitem(
1452 b'web', b'logoimg', default=b'hglogo.png',
1455 b'web', b'logoimg', default=b'hglogo.png',
1453 )
1456 )
1454 coreconfigitem(
1457 coreconfigitem(
1455 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1458 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1456 )
1459 )
1457 coreconfigitem(
1460 coreconfigitem(
1458 b'web', b'accesslog', default=b'-',
1461 b'web', b'accesslog', default=b'-',
1459 )
1462 )
1460 coreconfigitem(
1463 coreconfigitem(
1461 b'web', b'address', default=b'',
1464 b'web', b'address', default=b'',
1462 )
1465 )
1463 coreconfigitem(
1466 coreconfigitem(
1464 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1467 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1465 )
1468 )
1466 coreconfigitem(
1469 coreconfigitem(
1467 b'web', b'allow_read', default=list,
1470 b'web', b'allow_read', default=list,
1468 )
1471 )
1469 coreconfigitem(
1472 coreconfigitem(
1470 b'web', b'baseurl', default=None,
1473 b'web', b'baseurl', default=None,
1471 )
1474 )
1472 coreconfigitem(
1475 coreconfigitem(
1473 b'web', b'cacerts', default=None,
1476 b'web', b'cacerts', default=None,
1474 )
1477 )
1475 coreconfigitem(
1478 coreconfigitem(
1476 b'web', b'certificate', default=None,
1479 b'web', b'certificate', default=None,
1477 )
1480 )
1478 coreconfigitem(
1481 coreconfigitem(
1479 b'web', b'collapse', default=False,
1482 b'web', b'collapse', default=False,
1480 )
1483 )
1481 coreconfigitem(
1484 coreconfigitem(
1482 b'web', b'csp', default=None,
1485 b'web', b'csp', default=None,
1483 )
1486 )
1484 coreconfigitem(
1487 coreconfigitem(
1485 b'web', b'deny_read', default=list,
1488 b'web', b'deny_read', default=list,
1486 )
1489 )
1487 coreconfigitem(
1490 coreconfigitem(
1488 b'web', b'descend', default=True,
1491 b'web', b'descend', default=True,
1489 )
1492 )
1490 coreconfigitem(
1493 coreconfigitem(
1491 b'web', b'description', default=b"",
1494 b'web', b'description', default=b"",
1492 )
1495 )
1493 coreconfigitem(
1496 coreconfigitem(
1494 b'web', b'encoding', default=lambda: encoding.encoding,
1497 b'web', b'encoding', default=lambda: encoding.encoding,
1495 )
1498 )
1496 coreconfigitem(
1499 coreconfigitem(
1497 b'web', b'errorlog', default=b'-',
1500 b'web', b'errorlog', default=b'-',
1498 )
1501 )
1499 coreconfigitem(
1502 coreconfigitem(
1500 b'web', b'ipv6', default=False,
1503 b'web', b'ipv6', default=False,
1501 )
1504 )
1502 coreconfigitem(
1505 coreconfigitem(
1503 b'web', b'maxchanges', default=10,
1506 b'web', b'maxchanges', default=10,
1504 )
1507 )
1505 coreconfigitem(
1508 coreconfigitem(
1506 b'web', b'maxfiles', default=10,
1509 b'web', b'maxfiles', default=10,
1507 )
1510 )
1508 coreconfigitem(
1511 coreconfigitem(
1509 b'web', b'maxshortchanges', default=60,
1512 b'web', b'maxshortchanges', default=60,
1510 )
1513 )
1511 coreconfigitem(
1514 coreconfigitem(
1512 b'web', b'motd', default=b'',
1515 b'web', b'motd', default=b'',
1513 )
1516 )
1514 coreconfigitem(
1517 coreconfigitem(
1515 b'web', b'name', default=dynamicdefault,
1518 b'web', b'name', default=dynamicdefault,
1516 )
1519 )
1517 coreconfigitem(
1520 coreconfigitem(
1518 b'web', b'port', default=8000,
1521 b'web', b'port', default=8000,
1519 )
1522 )
1520 coreconfigitem(
1523 coreconfigitem(
1521 b'web', b'prefix', default=b'',
1524 b'web', b'prefix', default=b'',
1522 )
1525 )
1523 coreconfigitem(
1526 coreconfigitem(
1524 b'web', b'push_ssl', default=True,
1527 b'web', b'push_ssl', default=True,
1525 )
1528 )
1526 coreconfigitem(
1529 coreconfigitem(
1527 b'web', b'refreshinterval', default=20,
1530 b'web', b'refreshinterval', default=20,
1528 )
1531 )
1529 coreconfigitem(
1532 coreconfigitem(
1530 b'web', b'server-header', default=None,
1533 b'web', b'server-header', default=None,
1531 )
1534 )
1532 coreconfigitem(
1535 coreconfigitem(
1533 b'web', b'static', default=None,
1536 b'web', b'static', default=None,
1534 )
1537 )
1535 coreconfigitem(
1538 coreconfigitem(
1536 b'web', b'staticurl', default=None,
1539 b'web', b'staticurl', default=None,
1537 )
1540 )
1538 coreconfigitem(
1541 coreconfigitem(
1539 b'web', b'stripes', default=1,
1542 b'web', b'stripes', default=1,
1540 )
1543 )
1541 coreconfigitem(
1544 coreconfigitem(
1542 b'web', b'style', default=b'paper',
1545 b'web', b'style', default=b'paper',
1543 )
1546 )
1544 coreconfigitem(
1547 coreconfigitem(
1545 b'web', b'templates', default=None,
1548 b'web', b'templates', default=None,
1546 )
1549 )
1547 coreconfigitem(
1550 coreconfigitem(
1548 b'web', b'view', default=b'served', experimental=True,
1551 b'web', b'view', default=b'served', experimental=True,
1549 )
1552 )
1550 coreconfigitem(
1553 coreconfigitem(
1551 b'worker', b'backgroundclose', default=dynamicdefault,
1554 b'worker', b'backgroundclose', default=dynamicdefault,
1552 )
1555 )
1553 # Windows defaults to a limit of 512 open files. A buffer of 128
1556 # Windows defaults to a limit of 512 open files. A buffer of 128
1554 # should give us enough headway.
1557 # should give us enough headway.
1555 coreconfigitem(
1558 coreconfigitem(
1556 b'worker', b'backgroundclosemaxqueue', default=384,
1559 b'worker', b'backgroundclosemaxqueue', default=384,
1557 )
1560 )
1558 coreconfigitem(
1561 coreconfigitem(
1559 b'worker', b'backgroundcloseminfilecount', default=2048,
1562 b'worker', b'backgroundcloseminfilecount', default=2048,
1560 )
1563 )
1561 coreconfigitem(
1564 coreconfigitem(
1562 b'worker', b'backgroundclosethreadcount', default=4,
1565 b'worker', b'backgroundclosethreadcount', default=4,
1563 )
1566 )
1564 coreconfigitem(
1567 coreconfigitem(
1565 b'worker', b'enabled', default=True,
1568 b'worker', b'enabled', default=True,
1566 )
1569 )
1567 coreconfigitem(
1570 coreconfigitem(
1568 b'worker', b'numcpus', default=None,
1571 b'worker', b'numcpus', default=None,
1569 )
1572 )
1570
1573
1571 # Rebase related configuration moved to core because other extension are doing
1574 # Rebase related configuration moved to core because other extension are doing
1572 # strange things. For example, shelve import the extensions to reuse some bit
1575 # strange things. For example, shelve import the extensions to reuse some bit
1573 # without formally loading it.
1576 # without formally loading it.
1574 coreconfigitem(
1577 coreconfigitem(
1575 b'commands', b'rebase.requiredest', default=False,
1578 b'commands', b'rebase.requiredest', default=False,
1576 )
1579 )
1577 coreconfigitem(
1580 coreconfigitem(
1578 b'experimental', b'rebaseskipobsolete', default=True,
1581 b'experimental', b'rebaseskipobsolete', default=True,
1579 )
1582 )
1580 coreconfigitem(
1583 coreconfigitem(
1581 b'rebase', b'singletransaction', default=False,
1584 b'rebase', b'singletransaction', default=False,
1582 )
1585 )
1583 coreconfigitem(
1586 coreconfigitem(
1584 b'rebase', b'experimental.inmemory', default=False,
1587 b'rebase', b'experimental.inmemory', default=False,
1585 )
1588 )
@@ -1,2274 +1,2283 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 collections
10 import collections
11 import errno
11 import errno
12 import stat
12 import stat
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 addednodeid,
17 addednodeid,
18 modifiednodeid,
18 modifiednodeid,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from .thirdparty import attr
22 from .thirdparty import attr
23 from . import (
23 from . import (
24 copies,
24 copies,
25 encoding,
25 encoding,
26 error,
26 error,
27 filemerge,
27 filemerge,
28 match as matchmod,
28 match as matchmod,
29 mergestate as mergestatemod,
29 mergestate as mergestatemod,
30 obsutil,
30 obsutil,
31 pathutil,
31 pathutil,
32 pycompat,
32 pycompat,
33 scmutil,
33 scmutil,
34 subrepoutil,
34 subrepoutil,
35 util,
35 util,
36 worker,
36 worker,
37 )
37 )
38
38
39 _pack = struct.pack
39 _pack = struct.pack
40 _unpack = struct.unpack
40 _unpack = struct.unpack
41
41
42
42
43 def _getcheckunknownconfig(repo, section, name):
43 def _getcheckunknownconfig(repo, section, name):
44 config = repo.ui.config(section, name)
44 config = repo.ui.config(section, name)
45 valid = [b'abort', b'ignore', b'warn']
45 valid = [b'abort', b'ignore', b'warn']
46 if config not in valid:
46 if config not in valid:
47 validstr = b', '.join([b"'" + v + b"'" for v in valid])
47 validstr = b', '.join([b"'" + v + b"'" for v in valid])
48 raise error.ConfigError(
48 raise error.ConfigError(
49 _(b"%s.%s not valid ('%s' is none of %s)")
49 _(b"%s.%s not valid ('%s' is none of %s)")
50 % (section, name, config, validstr)
50 % (section, name, config, validstr)
51 )
51 )
52 return config
52 return config
53
53
54
54
55 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
55 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
56 if wctx.isinmemory():
56 if wctx.isinmemory():
57 # Nothing to do in IMM because nothing in the "working copy" can be an
57 # Nothing to do in IMM because nothing in the "working copy" can be an
58 # unknown file.
58 # unknown file.
59 #
59 #
60 # Note that we should bail out here, not in ``_checkunknownfiles()``,
60 # Note that we should bail out here, not in ``_checkunknownfiles()``,
61 # because that function does other useful work.
61 # because that function does other useful work.
62 return False
62 return False
63
63
64 if f2 is None:
64 if f2 is None:
65 f2 = f
65 f2 = f
66 return (
66 return (
67 repo.wvfs.audit.check(f)
67 repo.wvfs.audit.check(f)
68 and repo.wvfs.isfileorlink(f)
68 and repo.wvfs.isfileorlink(f)
69 and repo.dirstate.normalize(f) not in repo.dirstate
69 and repo.dirstate.normalize(f) not in repo.dirstate
70 and mctx[f2].cmp(wctx[f])
70 and mctx[f2].cmp(wctx[f])
71 )
71 )
72
72
73
73
74 class _unknowndirschecker(object):
74 class _unknowndirschecker(object):
75 """
75 """
76 Look for any unknown files or directories that may have a path conflict
76 Look for any unknown files or directories that may have a path conflict
77 with a file. If any path prefix of the file exists as a file or link,
77 with a file. If any path prefix of the file exists as a file or link,
78 then it conflicts. If the file itself is a directory that contains any
78 then it conflicts. If the file itself is a directory that contains any
79 file that is not tracked, then it conflicts.
79 file that is not tracked, then it conflicts.
80
80
81 Returns the shortest path at which a conflict occurs, or None if there is
81 Returns the shortest path at which a conflict occurs, or None if there is
82 no conflict.
82 no conflict.
83 """
83 """
84
84
85 def __init__(self):
85 def __init__(self):
86 # A set of paths known to be good. This prevents repeated checking of
86 # A set of paths known to be good. This prevents repeated checking of
87 # dirs. It will be updated with any new dirs that are checked and found
87 # dirs. It will be updated with any new dirs that are checked and found
88 # to be safe.
88 # to be safe.
89 self._unknowndircache = set()
89 self._unknowndircache = set()
90
90
91 # A set of paths that are known to be absent. This prevents repeated
91 # A set of paths that are known to be absent. This prevents repeated
92 # checking of subdirectories that are known not to exist. It will be
92 # checking of subdirectories that are known not to exist. It will be
93 # updated with any new dirs that are checked and found to be absent.
93 # updated with any new dirs that are checked and found to be absent.
94 self._missingdircache = set()
94 self._missingdircache = set()
95
95
96 def __call__(self, repo, wctx, f):
96 def __call__(self, repo, wctx, f):
97 if wctx.isinmemory():
97 if wctx.isinmemory():
98 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
98 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
99 return False
99 return False
100
100
101 # Check for path prefixes that exist as unknown files.
101 # Check for path prefixes that exist as unknown files.
102 for p in reversed(list(pathutil.finddirs(f))):
102 for p in reversed(list(pathutil.finddirs(f))):
103 if p in self._missingdircache:
103 if p in self._missingdircache:
104 return
104 return
105 if p in self._unknowndircache:
105 if p in self._unknowndircache:
106 continue
106 continue
107 if repo.wvfs.audit.check(p):
107 if repo.wvfs.audit.check(p):
108 if (
108 if (
109 repo.wvfs.isfileorlink(p)
109 repo.wvfs.isfileorlink(p)
110 and repo.dirstate.normalize(p) not in repo.dirstate
110 and repo.dirstate.normalize(p) not in repo.dirstate
111 ):
111 ):
112 return p
112 return p
113 if not repo.wvfs.lexists(p):
113 if not repo.wvfs.lexists(p):
114 self._missingdircache.add(p)
114 self._missingdircache.add(p)
115 return
115 return
116 self._unknowndircache.add(p)
116 self._unknowndircache.add(p)
117
117
118 # Check if the file conflicts with a directory containing unknown files.
118 # Check if the file conflicts with a directory containing unknown files.
119 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
119 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
120 # Does the directory contain any files that are not in the dirstate?
120 # Does the directory contain any files that are not in the dirstate?
121 for p, dirs, files in repo.wvfs.walk(f):
121 for p, dirs, files in repo.wvfs.walk(f):
122 for fn in files:
122 for fn in files:
123 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
123 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
124 relf = repo.dirstate.normalize(relf, isknown=True)
124 relf = repo.dirstate.normalize(relf, isknown=True)
125 if relf not in repo.dirstate:
125 if relf not in repo.dirstate:
126 return f
126 return f
127 return None
127 return None
128
128
129
129
130 def _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce):
130 def _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce):
131 """
131 """
132 Considers any actions that care about the presence of conflicting unknown
132 Considers any actions that care about the presence of conflicting unknown
133 files. For some actions, the result is to abort; for others, it is to
133 files. For some actions, the result is to abort; for others, it is to
134 choose a different action.
134 choose a different action.
135 """
135 """
136 fileconflicts = set()
136 fileconflicts = set()
137 pathconflicts = set()
137 pathconflicts = set()
138 warnconflicts = set()
138 warnconflicts = set()
139 abortconflicts = set()
139 abortconflicts = set()
140 unknownconfig = _getcheckunknownconfig(repo, b'merge', b'checkunknown')
140 unknownconfig = _getcheckunknownconfig(repo, b'merge', b'checkunknown')
141 ignoredconfig = _getcheckunknownconfig(repo, b'merge', b'checkignored')
141 ignoredconfig = _getcheckunknownconfig(repo, b'merge', b'checkignored')
142 pathconfig = repo.ui.configbool(
142 pathconfig = repo.ui.configbool(
143 b'experimental', b'merge.checkpathconflicts'
143 b'experimental', b'merge.checkpathconflicts'
144 )
144 )
145 if not force:
145 if not force:
146
146
147 def collectconflicts(conflicts, config):
147 def collectconflicts(conflicts, config):
148 if config == b'abort':
148 if config == b'abort':
149 abortconflicts.update(conflicts)
149 abortconflicts.update(conflicts)
150 elif config == b'warn':
150 elif config == b'warn':
151 warnconflicts.update(conflicts)
151 warnconflicts.update(conflicts)
152
152
153 checkunknowndirs = _unknowndirschecker()
153 checkunknowndirs = _unknowndirschecker()
154 for f in mresult.files(
154 for f in mresult.files(
155 (
155 (
156 mergestatemod.ACTION_CREATED,
156 mergestatemod.ACTION_CREATED,
157 mergestatemod.ACTION_DELETED_CHANGED,
157 mergestatemod.ACTION_DELETED_CHANGED,
158 )
158 )
159 ):
159 ):
160 if _checkunknownfile(repo, wctx, mctx, f):
160 if _checkunknownfile(repo, wctx, mctx, f):
161 fileconflicts.add(f)
161 fileconflicts.add(f)
162 elif pathconfig and f not in wctx:
162 elif pathconfig and f not in wctx:
163 path = checkunknowndirs(repo, wctx, f)
163 path = checkunknowndirs(repo, wctx, f)
164 if path is not None:
164 if path is not None:
165 pathconflicts.add(path)
165 pathconflicts.add(path)
166 for f, args, msg in mresult.getactions(
166 for f, args, msg in mresult.getactions(
167 [mergestatemod.ACTION_LOCAL_DIR_RENAME_GET]
167 [mergestatemod.ACTION_LOCAL_DIR_RENAME_GET]
168 ):
168 ):
169 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
169 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
170 fileconflicts.add(f)
170 fileconflicts.add(f)
171
171
172 allconflicts = fileconflicts | pathconflicts
172 allconflicts = fileconflicts | pathconflicts
173 ignoredconflicts = {c for c in allconflicts if repo.dirstate._ignore(c)}
173 ignoredconflicts = {c for c in allconflicts if repo.dirstate._ignore(c)}
174 unknownconflicts = allconflicts - ignoredconflicts
174 unknownconflicts = allconflicts - ignoredconflicts
175 collectconflicts(ignoredconflicts, ignoredconfig)
175 collectconflicts(ignoredconflicts, ignoredconfig)
176 collectconflicts(unknownconflicts, unknownconfig)
176 collectconflicts(unknownconflicts, unknownconfig)
177 else:
177 else:
178 for f, args, msg in list(
178 for f, args, msg in list(
179 mresult.getactions([mergestatemod.ACTION_CREATED_MERGE])
179 mresult.getactions([mergestatemod.ACTION_CREATED_MERGE])
180 ):
180 ):
181 fl2, anc = args
181 fl2, anc = args
182 different = _checkunknownfile(repo, wctx, mctx, f)
182 different = _checkunknownfile(repo, wctx, mctx, f)
183 if repo.dirstate._ignore(f):
183 if repo.dirstate._ignore(f):
184 config = ignoredconfig
184 config = ignoredconfig
185 else:
185 else:
186 config = unknownconfig
186 config = unknownconfig
187
187
188 # The behavior when force is True is described by this table:
188 # The behavior when force is True is described by this table:
189 # config different mergeforce | action backup
189 # config different mergeforce | action backup
190 # * n * | get n
190 # * n * | get n
191 # * y y | merge -
191 # * y y | merge -
192 # abort y n | merge - (1)
192 # abort y n | merge - (1)
193 # warn y n | warn + get y
193 # warn y n | warn + get y
194 # ignore y n | get y
194 # ignore y n | get y
195 #
195 #
196 # (1) this is probably the wrong behavior here -- we should
196 # (1) this is probably the wrong behavior here -- we should
197 # probably abort, but some actions like rebases currently
197 # probably abort, but some actions like rebases currently
198 # don't like an abort happening in the middle of
198 # don't like an abort happening in the middle of
199 # merge.update.
199 # merge.update.
200 if not different:
200 if not different:
201 mresult.addfile(
201 mresult.addfile(
202 f,
202 f,
203 mergestatemod.ACTION_GET,
203 mergestatemod.ACTION_GET,
204 (fl2, False),
204 (fl2, False),
205 b'remote created',
205 b'remote created',
206 )
206 )
207 elif mergeforce or config == b'abort':
207 elif mergeforce or config == b'abort':
208 mresult.addfile(
208 mresult.addfile(
209 f,
209 f,
210 mergestatemod.ACTION_MERGE,
210 mergestatemod.ACTION_MERGE,
211 (f, f, None, False, anc),
211 (f, f, None, False, anc),
212 b'remote differs from untracked local',
212 b'remote differs from untracked local',
213 )
213 )
214 elif config == b'abort':
214 elif config == b'abort':
215 abortconflicts.add(f)
215 abortconflicts.add(f)
216 else:
216 else:
217 if config == b'warn':
217 if config == b'warn':
218 warnconflicts.add(f)
218 warnconflicts.add(f)
219 mresult.addfile(
219 mresult.addfile(
220 f, mergestatemod.ACTION_GET, (fl2, True), b'remote created',
220 f, mergestatemod.ACTION_GET, (fl2, True), b'remote created',
221 )
221 )
222
222
223 for f in sorted(abortconflicts):
223 for f in sorted(abortconflicts):
224 warn = repo.ui.warn
224 warn = repo.ui.warn
225 if f in pathconflicts:
225 if f in pathconflicts:
226 if repo.wvfs.isfileorlink(f):
226 if repo.wvfs.isfileorlink(f):
227 warn(_(b"%s: untracked file conflicts with directory\n") % f)
227 warn(_(b"%s: untracked file conflicts with directory\n") % f)
228 else:
228 else:
229 warn(_(b"%s: untracked directory conflicts with file\n") % f)
229 warn(_(b"%s: untracked directory conflicts with file\n") % f)
230 else:
230 else:
231 warn(_(b"%s: untracked file differs\n") % f)
231 warn(_(b"%s: untracked file differs\n") % f)
232 if abortconflicts:
232 if abortconflicts:
233 raise error.Abort(
233 raise error.Abort(
234 _(
234 _(
235 b"untracked files in working directory "
235 b"untracked files in working directory "
236 b"differ from files in requested revision"
236 b"differ from files in requested revision"
237 )
237 )
238 )
238 )
239
239
240 for f in sorted(warnconflicts):
240 for f in sorted(warnconflicts):
241 if repo.wvfs.isfileorlink(f):
241 if repo.wvfs.isfileorlink(f):
242 repo.ui.warn(_(b"%s: replacing untracked file\n") % f)
242 repo.ui.warn(_(b"%s: replacing untracked file\n") % f)
243 else:
243 else:
244 repo.ui.warn(_(b"%s: replacing untracked files in directory\n") % f)
244 repo.ui.warn(_(b"%s: replacing untracked files in directory\n") % f)
245
245
246 for f, args, msg in list(
246 for f, args, msg in list(
247 mresult.getactions([mergestatemod.ACTION_CREATED])
247 mresult.getactions([mergestatemod.ACTION_CREATED])
248 ):
248 ):
249 backup = (
249 backup = (
250 f in fileconflicts
250 f in fileconflicts
251 or f in pathconflicts
251 or f in pathconflicts
252 or any(p in pathconflicts for p in pathutil.finddirs(f))
252 or any(p in pathconflicts for p in pathutil.finddirs(f))
253 )
253 )
254 (flags,) = args
254 (flags,) = args
255 mresult.addfile(f, mergestatemod.ACTION_GET, (flags, backup), msg)
255 mresult.addfile(f, mergestatemod.ACTION_GET, (flags, backup), msg)
256
256
257
257
258 def _forgetremoved(wctx, mctx, branchmerge, mresult):
258 def _forgetremoved(wctx, mctx, branchmerge, mresult):
259 """
259 """
260 Forget removed files
260 Forget removed files
261
261
262 If we're jumping between revisions (as opposed to merging), and if
262 If we're jumping between revisions (as opposed to merging), and if
263 neither the working directory nor the target rev has the file,
263 neither the working directory nor the target rev has the file,
264 then we need to remove it from the dirstate, to prevent the
264 then we need to remove it from the dirstate, to prevent the
265 dirstate from listing the file when it is no longer in the
265 dirstate from listing the file when it is no longer in the
266 manifest.
266 manifest.
267
267
268 If we're merging, and the other revision has removed a file
268 If we're merging, and the other revision has removed a file
269 that is not present in the working directory, we need to mark it
269 that is not present in the working directory, we need to mark it
270 as removed.
270 as removed.
271 """
271 """
272
272
273 m = mergestatemod.ACTION_FORGET
273 m = mergestatemod.ACTION_FORGET
274 if branchmerge:
274 if branchmerge:
275 m = mergestatemod.ACTION_REMOVE
275 m = mergestatemod.ACTION_REMOVE
276 for f in wctx.deleted():
276 for f in wctx.deleted():
277 if f not in mctx:
277 if f not in mctx:
278 mresult.addfile(f, m, None, b"forget deleted")
278 mresult.addfile(f, m, None, b"forget deleted")
279
279
280 if not branchmerge:
280 if not branchmerge:
281 for f in wctx.removed():
281 for f in wctx.removed():
282 if f not in mctx:
282 if f not in mctx:
283 mresult.addfile(
283 mresult.addfile(
284 f, mergestatemod.ACTION_FORGET, None, b"forget removed",
284 f, mergestatemod.ACTION_FORGET, None, b"forget removed",
285 )
285 )
286
286
287
287
288 def _checkcollision(repo, wmf, mresult):
288 def _checkcollision(repo, wmf, mresult):
289 """
289 """
290 Check for case-folding collisions.
290 Check for case-folding collisions.
291 """
291 """
292 # If the repo is narrowed, filter out files outside the narrowspec.
292 # If the repo is narrowed, filter out files outside the narrowspec.
293 narrowmatch = repo.narrowmatch()
293 narrowmatch = repo.narrowmatch()
294 if not narrowmatch.always():
294 if not narrowmatch.always():
295 pmmf = set(wmf.walk(narrowmatch))
295 pmmf = set(wmf.walk(narrowmatch))
296 if mresult:
296 if mresult:
297 for f in list(mresult.files()):
297 for f in list(mresult.files()):
298 if not narrowmatch(f):
298 if not narrowmatch(f):
299 mresult.removefile(f)
299 mresult.removefile(f)
300 else:
300 else:
301 # build provisional merged manifest up
301 # build provisional merged manifest up
302 pmmf = set(wmf)
302 pmmf = set(wmf)
303
303
304 if mresult:
304 if mresult:
305 # KEEP and EXEC are no-op
305 # KEEP and EXEC are no-op
306 for f in mresult.files(
306 for f in mresult.files(
307 (
307 (
308 mergestatemod.ACTION_ADD,
308 mergestatemod.ACTION_ADD,
309 mergestatemod.ACTION_ADD_MODIFIED,
309 mergestatemod.ACTION_ADD_MODIFIED,
310 mergestatemod.ACTION_FORGET,
310 mergestatemod.ACTION_FORGET,
311 mergestatemod.ACTION_GET,
311 mergestatemod.ACTION_GET,
312 mergestatemod.ACTION_CHANGED_DELETED,
312 mergestatemod.ACTION_CHANGED_DELETED,
313 mergestatemod.ACTION_DELETED_CHANGED,
313 mergestatemod.ACTION_DELETED_CHANGED,
314 )
314 )
315 ):
315 ):
316 pmmf.add(f)
316 pmmf.add(f)
317 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
317 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
318 pmmf.discard(f)
318 pmmf.discard(f)
319 for f, args, msg in mresult.getactions(
319 for f, args, msg in mresult.getactions(
320 [mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL]
320 [mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL]
321 ):
321 ):
322 f2, flags = args
322 f2, flags = args
323 pmmf.discard(f2)
323 pmmf.discard(f2)
324 pmmf.add(f)
324 pmmf.add(f)
325 for f in mresult.files((mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,)):
325 for f in mresult.files((mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,)):
326 pmmf.add(f)
326 pmmf.add(f)
327 for f, args, msg in mresult.getactions([mergestatemod.ACTION_MERGE]):
327 for f, args, msg in mresult.getactions([mergestatemod.ACTION_MERGE]):
328 f1, f2, fa, move, anc = args
328 f1, f2, fa, move, anc = args
329 if move:
329 if move:
330 pmmf.discard(f1)
330 pmmf.discard(f1)
331 pmmf.add(f)
331 pmmf.add(f)
332
332
333 # check case-folding collision in provisional merged manifest
333 # check case-folding collision in provisional merged manifest
334 foldmap = {}
334 foldmap = {}
335 for f in pmmf:
335 for f in pmmf:
336 fold = util.normcase(f)
336 fold = util.normcase(f)
337 if fold in foldmap:
337 if fold in foldmap:
338 raise error.Abort(
338 raise error.Abort(
339 _(b"case-folding collision between %s and %s")
339 _(b"case-folding collision between %s and %s")
340 % (f, foldmap[fold])
340 % (f, foldmap[fold])
341 )
341 )
342 foldmap[fold] = f
342 foldmap[fold] = f
343
343
344 # check case-folding of directories
344 # check case-folding of directories
345 foldprefix = unfoldprefix = lastfull = b''
345 foldprefix = unfoldprefix = lastfull = b''
346 for fold, f in sorted(foldmap.items()):
346 for fold, f in sorted(foldmap.items()):
347 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
347 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
348 # the folded prefix matches but actual casing is different
348 # the folded prefix matches but actual casing is different
349 raise error.Abort(
349 raise error.Abort(
350 _(b"case-folding collision between %s and directory of %s")
350 _(b"case-folding collision between %s and directory of %s")
351 % (lastfull, f)
351 % (lastfull, f)
352 )
352 )
353 foldprefix = fold + b'/'
353 foldprefix = fold + b'/'
354 unfoldprefix = f + b'/'
354 unfoldprefix = f + b'/'
355 lastfull = f
355 lastfull = f
356
356
357
357
358 def driverpreprocess(repo, ms, wctx, labels=None):
358 def driverpreprocess(repo, ms, wctx, labels=None):
359 """run the preprocess step of the merge driver, if any
359 """run the preprocess step of the merge driver, if any
360
360
361 This is currently not implemented -- it's an extension point."""
361 This is currently not implemented -- it's an extension point."""
362 return True
362 return True
363
363
364
364
365 def driverconclude(repo, ms, wctx, labels=None):
365 def driverconclude(repo, ms, wctx, labels=None):
366 """run the conclude step of the merge driver, if any
366 """run the conclude step of the merge driver, if any
367
367
368 This is currently not implemented -- it's an extension point."""
368 This is currently not implemented -- it's an extension point."""
369 return True
369 return True
370
370
371
371
372 def _filesindirs(repo, manifest, dirs):
372 def _filesindirs(repo, manifest, dirs):
373 """
373 """
374 Generator that yields pairs of all the files in the manifest that are found
374 Generator that yields pairs of all the files in the manifest that are found
375 inside the directories listed in dirs, and which directory they are found
375 inside the directories listed in dirs, and which directory they are found
376 in.
376 in.
377 """
377 """
378 for f in manifest:
378 for f in manifest:
379 for p in pathutil.finddirs(f):
379 for p in pathutil.finddirs(f):
380 if p in dirs:
380 if p in dirs:
381 yield f, p
381 yield f, p
382 break
382 break
383
383
384
384
385 def checkpathconflicts(repo, wctx, mctx, mresult):
385 def checkpathconflicts(repo, wctx, mctx, mresult):
386 """
386 """
387 Check if any actions introduce path conflicts in the repository, updating
387 Check if any actions introduce path conflicts in the repository, updating
388 actions to record or handle the path conflict accordingly.
388 actions to record or handle the path conflict accordingly.
389 """
389 """
390 mf = wctx.manifest()
390 mf = wctx.manifest()
391
391
392 # The set of local files that conflict with a remote directory.
392 # The set of local files that conflict with a remote directory.
393 localconflicts = set()
393 localconflicts = set()
394
394
395 # The set of directories that conflict with a remote file, and so may cause
395 # The set of directories that conflict with a remote file, and so may cause
396 # conflicts if they still contain any files after the merge.
396 # conflicts if they still contain any files after the merge.
397 remoteconflicts = set()
397 remoteconflicts = set()
398
398
399 # The set of directories that appear as both a file and a directory in the
399 # The set of directories that appear as both a file and a directory in the
400 # remote manifest. These indicate an invalid remote manifest, which
400 # remote manifest. These indicate an invalid remote manifest, which
401 # can't be updated to cleanly.
401 # can't be updated to cleanly.
402 invalidconflicts = set()
402 invalidconflicts = set()
403
403
404 # The set of directories that contain files that are being created.
404 # The set of directories that contain files that are being created.
405 createdfiledirs = set()
405 createdfiledirs = set()
406
406
407 # The set of files deleted by all the actions.
407 # The set of files deleted by all the actions.
408 deletedfiles = set()
408 deletedfiles = set()
409
409
410 for f in mresult.files(
410 for f in mresult.files(
411 (
411 (
412 mergestatemod.ACTION_CREATED,
412 mergestatemod.ACTION_CREATED,
413 mergestatemod.ACTION_DELETED_CHANGED,
413 mergestatemod.ACTION_DELETED_CHANGED,
414 mergestatemod.ACTION_MERGE,
414 mergestatemod.ACTION_MERGE,
415 mergestatemod.ACTION_CREATED_MERGE,
415 mergestatemod.ACTION_CREATED_MERGE,
416 )
416 )
417 ):
417 ):
418 # This action may create a new local file.
418 # This action may create a new local file.
419 createdfiledirs.update(pathutil.finddirs(f))
419 createdfiledirs.update(pathutil.finddirs(f))
420 if mf.hasdir(f):
420 if mf.hasdir(f):
421 # The file aliases a local directory. This might be ok if all
421 # The file aliases a local directory. This might be ok if all
422 # the files in the local directory are being deleted. This
422 # the files in the local directory are being deleted. This
423 # will be checked once we know what all the deleted files are.
423 # will be checked once we know what all the deleted files are.
424 remoteconflicts.add(f)
424 remoteconflicts.add(f)
425 # Track the names of all deleted files.
425 # Track the names of all deleted files.
426 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
426 for f in mresult.files((mergestatemod.ACTION_REMOVE,)):
427 deletedfiles.add(f)
427 deletedfiles.add(f)
428 for (f, args, msg) in mresult.getactions((mergestatemod.ACTION_MERGE,)):
428 for (f, args, msg) in mresult.getactions((mergestatemod.ACTION_MERGE,)):
429 f1, f2, fa, move, anc = args
429 f1, f2, fa, move, anc = args
430 if move:
430 if move:
431 deletedfiles.add(f1)
431 deletedfiles.add(f1)
432 for (f, args, msg) in mresult.getactions(
432 for (f, args, msg) in mresult.getactions(
433 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,)
433 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,)
434 ):
434 ):
435 f2, flags = args
435 f2, flags = args
436 deletedfiles.add(f2)
436 deletedfiles.add(f2)
437
437
438 # Check all directories that contain created files for path conflicts.
438 # Check all directories that contain created files for path conflicts.
439 for p in createdfiledirs:
439 for p in createdfiledirs:
440 if p in mf:
440 if p in mf:
441 if p in mctx:
441 if p in mctx:
442 # A file is in a directory which aliases both a local
442 # A file is in a directory which aliases both a local
443 # and a remote file. This is an internal inconsistency
443 # and a remote file. This is an internal inconsistency
444 # within the remote manifest.
444 # within the remote manifest.
445 invalidconflicts.add(p)
445 invalidconflicts.add(p)
446 else:
446 else:
447 # A file is in a directory which aliases a local file.
447 # A file is in a directory which aliases a local file.
448 # We will need to rename the local file.
448 # We will need to rename the local file.
449 localconflicts.add(p)
449 localconflicts.add(p)
450 pd = mresult.getfile(p)
450 pd = mresult.getfile(p)
451 if pd and pd[0] in (
451 if pd and pd[0] in (
452 mergestatemod.ACTION_CREATED,
452 mergestatemod.ACTION_CREATED,
453 mergestatemod.ACTION_DELETED_CHANGED,
453 mergestatemod.ACTION_DELETED_CHANGED,
454 mergestatemod.ACTION_MERGE,
454 mergestatemod.ACTION_MERGE,
455 mergestatemod.ACTION_CREATED_MERGE,
455 mergestatemod.ACTION_CREATED_MERGE,
456 ):
456 ):
457 # The file is in a directory which aliases a remote file.
457 # The file is in a directory which aliases a remote file.
458 # This is an internal inconsistency within the remote
458 # This is an internal inconsistency within the remote
459 # manifest.
459 # manifest.
460 invalidconflicts.add(p)
460 invalidconflicts.add(p)
461
461
462 # Rename all local conflicting files that have not been deleted.
462 # Rename all local conflicting files that have not been deleted.
463 for p in localconflicts:
463 for p in localconflicts:
464 if p not in deletedfiles:
464 if p not in deletedfiles:
465 ctxname = bytes(wctx).rstrip(b'+')
465 ctxname = bytes(wctx).rstrip(b'+')
466 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
466 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
467 porig = wctx[p].copysource() or p
467 porig = wctx[p].copysource() or p
468 mresult.addfile(
468 mresult.addfile(
469 pnew,
469 pnew,
470 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
470 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
471 (p, porig),
471 (p, porig),
472 b'local path conflict',
472 b'local path conflict',
473 )
473 )
474 mresult.addfile(
474 mresult.addfile(
475 p,
475 p,
476 mergestatemod.ACTION_PATH_CONFLICT,
476 mergestatemod.ACTION_PATH_CONFLICT,
477 (pnew, b'l'),
477 (pnew, b'l'),
478 b'path conflict',
478 b'path conflict',
479 )
479 )
480
480
481 if remoteconflicts:
481 if remoteconflicts:
482 # Check if all files in the conflicting directories have been removed.
482 # Check if all files in the conflicting directories have been removed.
483 ctxname = bytes(mctx).rstrip(b'+')
483 ctxname = bytes(mctx).rstrip(b'+')
484 for f, p in _filesindirs(repo, mf, remoteconflicts):
484 for f, p in _filesindirs(repo, mf, remoteconflicts):
485 if f not in deletedfiles:
485 if f not in deletedfiles:
486 m, args, msg = mresult.getfile(p)
486 m, args, msg = mresult.getfile(p)
487 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
487 pnew = util.safename(p, ctxname, wctx, set(mresult.files()))
488 if m in (
488 if m in (
489 mergestatemod.ACTION_DELETED_CHANGED,
489 mergestatemod.ACTION_DELETED_CHANGED,
490 mergestatemod.ACTION_MERGE,
490 mergestatemod.ACTION_MERGE,
491 ):
491 ):
492 # Action was merge, just update target.
492 # Action was merge, just update target.
493 mresult.addfile(pnew, m, args, msg)
493 mresult.addfile(pnew, m, args, msg)
494 else:
494 else:
495 # Action was create, change to renamed get action.
495 # Action was create, change to renamed get action.
496 fl = args[0]
496 fl = args[0]
497 mresult.addfile(
497 mresult.addfile(
498 pnew,
498 pnew,
499 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
499 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
500 (p, fl),
500 (p, fl),
501 b'remote path conflict',
501 b'remote path conflict',
502 )
502 )
503 mresult.addfile(
503 mresult.addfile(
504 p,
504 p,
505 mergestatemod.ACTION_PATH_CONFLICT,
505 mergestatemod.ACTION_PATH_CONFLICT,
506 (pnew, mergestatemod.ACTION_REMOVE),
506 (pnew, mergestatemod.ACTION_REMOVE),
507 b'path conflict',
507 b'path conflict',
508 )
508 )
509 remoteconflicts.remove(p)
509 remoteconflicts.remove(p)
510 break
510 break
511
511
512 if invalidconflicts:
512 if invalidconflicts:
513 for p in invalidconflicts:
513 for p in invalidconflicts:
514 repo.ui.warn(_(b"%s: is both a file and a directory\n") % p)
514 repo.ui.warn(_(b"%s: is both a file and a directory\n") % p)
515 raise error.Abort(_(b"destination manifest contains path conflicts"))
515 raise error.Abort(_(b"destination manifest contains path conflicts"))
516
516
517
517
518 def _filternarrowactions(narrowmatch, branchmerge, mresult):
518 def _filternarrowactions(narrowmatch, branchmerge, mresult):
519 """
519 """
520 Filters out actions that can ignored because the repo is narrowed.
520 Filters out actions that can ignored because the repo is narrowed.
521
521
522 Raise an exception if the merge cannot be completed because the repo is
522 Raise an exception if the merge cannot be completed because the repo is
523 narrowed.
523 narrowed.
524 """
524 """
525 # TODO: handle with nonconflicttypes
525 # TODO: handle with nonconflicttypes
526 nooptypes = {mergestatemod.ACTION_KEEP}
526 nooptypes = {mergestatemod.ACTION_KEEP}
527 nonconflicttypes = {
527 nonconflicttypes = {
528 mergestatemod.ACTION_ADD,
528 mergestatemod.ACTION_ADD,
529 mergestatemod.ACTION_ADD_MODIFIED,
529 mergestatemod.ACTION_ADD_MODIFIED,
530 mergestatemod.ACTION_CREATED,
530 mergestatemod.ACTION_CREATED,
531 mergestatemod.ACTION_CREATED_MERGE,
531 mergestatemod.ACTION_CREATED_MERGE,
532 mergestatemod.ACTION_FORGET,
532 mergestatemod.ACTION_FORGET,
533 mergestatemod.ACTION_GET,
533 mergestatemod.ACTION_GET,
534 mergestatemod.ACTION_REMOVE,
534 mergestatemod.ACTION_REMOVE,
535 mergestatemod.ACTION_EXEC,
535 mergestatemod.ACTION_EXEC,
536 }
536 }
537 # We mutate the items in the dict during iteration, so iterate
537 # We mutate the items in the dict during iteration, so iterate
538 # over a copy.
538 # over a copy.
539 for f, action in mresult.filemap():
539 for f, action in mresult.filemap():
540 if narrowmatch(f):
540 if narrowmatch(f):
541 pass
541 pass
542 elif not branchmerge:
542 elif not branchmerge:
543 mresult.removefile(f) # just updating, ignore changes outside clone
543 mresult.removefile(f) # just updating, ignore changes outside clone
544 elif action[0] in nooptypes:
544 elif action[0] in nooptypes:
545 mresult.removefile(f) # merge does not affect file
545 mresult.removefile(f) # merge does not affect file
546 elif action[0] in nonconflicttypes:
546 elif action[0] in nonconflicttypes:
547 raise error.Abort(
547 raise error.Abort(
548 _(
548 _(
549 b'merge affects file \'%s\' outside narrow, '
549 b'merge affects file \'%s\' outside narrow, '
550 b'which is not yet supported'
550 b'which is not yet supported'
551 )
551 )
552 % f,
552 % f,
553 hint=_(b'merging in the other direction may work'),
553 hint=_(b'merging in the other direction may work'),
554 )
554 )
555 else:
555 else:
556 raise error.Abort(
556 raise error.Abort(
557 _(b'conflict in file \'%s\' is outside narrow clone') % f
557 _(b'conflict in file \'%s\' is outside narrow clone') % f
558 )
558 )
559
559
560
560
561 class mergeresult(object):
561 class mergeresult(object):
562 ''''An object representing result of merging manifests.
562 ''''An object representing result of merging manifests.
563
563
564 It has information about what actions need to be performed on dirstate
564 It has information about what actions need to be performed on dirstate
565 mapping of divergent renames and other such cases. '''
565 mapping of divergent renames and other such cases. '''
566
566
567 def __init__(self):
567 def __init__(self):
568 """
568 """
569 filemapping: dict of filename as keys and action related info as values
569 filemapping: dict of filename as keys and action related info as values
570 diverge: mapping of source name -> list of dest name for
570 diverge: mapping of source name -> list of dest name for
571 divergent renames
571 divergent renames
572 renamedelete: mapping of source name -> list of destinations for files
572 renamedelete: mapping of source name -> list of destinations for files
573 deleted on one side and renamed on other.
573 deleted on one side and renamed on other.
574 commitinfo: dict containing data which should be used on commit
574 commitinfo: dict containing data which should be used on commit
575 contains a filename -> info mapping
575 contains a filename -> info mapping
576 actionmapping: dict of action names as keys and values are dict of
576 actionmapping: dict of action names as keys and values are dict of
577 filename as key and related data as values
577 filename as key and related data as values
578 """
578 """
579 self._filemapping = {}
579 self._filemapping = {}
580 self._diverge = {}
580 self._diverge = {}
581 self._renamedelete = {}
581 self._renamedelete = {}
582 self._commitinfo = collections.defaultdict(dict)
582 self._commitinfo = collections.defaultdict(dict)
583 self._actionmapping = collections.defaultdict(dict)
583 self._actionmapping = collections.defaultdict(dict)
584
584
585 def updatevalues(self, diverge, renamedelete):
585 def updatevalues(self, diverge, renamedelete):
586 self._diverge = diverge
586 self._diverge = diverge
587 self._renamedelete = renamedelete
587 self._renamedelete = renamedelete
588
588
589 def addfile(self, filename, action, data, message):
589 def addfile(self, filename, action, data, message):
590 """ adds a new file to the mergeresult object
590 """ adds a new file to the mergeresult object
591
591
592 filename: file which we are adding
592 filename: file which we are adding
593 action: one of mergestatemod.ACTION_*
593 action: one of mergestatemod.ACTION_*
594 data: a tuple of information like fctx and ctx related to this merge
594 data: a tuple of information like fctx and ctx related to this merge
595 message: a message about the merge
595 message: a message about the merge
596 """
596 """
597 # if the file already existed, we need to delete it's old
597 # if the file already existed, we need to delete it's old
598 # entry form _actionmapping too
598 # entry form _actionmapping too
599 if filename in self._filemapping:
599 if filename in self._filemapping:
600 a, d, m = self._filemapping[filename]
600 a, d, m = self._filemapping[filename]
601 del self._actionmapping[a][filename]
601 del self._actionmapping[a][filename]
602
602
603 self._filemapping[filename] = (action, data, message)
603 self._filemapping[filename] = (action, data, message)
604 self._actionmapping[action][filename] = (data, message)
604 self._actionmapping[action][filename] = (data, message)
605
605
606 def getfile(self, filename, default_return=None):
606 def getfile(self, filename, default_return=None):
607 """ returns (action, args, msg) about this file
607 """ returns (action, args, msg) about this file
608
608
609 returns default_return if the file is not present """
609 returns default_return if the file is not present """
610 if filename in self._filemapping:
610 if filename in self._filemapping:
611 return self._filemapping[filename]
611 return self._filemapping[filename]
612 return default_return
612 return default_return
613
613
614 def files(self, actions=None):
614 def files(self, actions=None):
615 """ returns files on which provided action needs to perfromed
615 """ returns files on which provided action needs to perfromed
616
616
617 If actions is None, all files are returned
617 If actions is None, all files are returned
618 """
618 """
619 # TODO: think whether we should return renamedelete and
619 # TODO: think whether we should return renamedelete and
620 # diverge filenames also
620 # diverge filenames also
621 if actions is None:
621 if actions is None:
622 for f in self._filemapping:
622 for f in self._filemapping:
623 yield f
623 yield f
624
624
625 else:
625 else:
626 for a in actions:
626 for a in actions:
627 for f in self._actionmapping[a]:
627 for f in self._actionmapping[a]:
628 yield f
628 yield f
629
629
630 def removefile(self, filename):
630 def removefile(self, filename):
631 """ removes a file from the mergeresult object as the file might
631 """ removes a file from the mergeresult object as the file might
632 not merging anymore """
632 not merging anymore """
633 action, data, message = self._filemapping[filename]
633 action, data, message = self._filemapping[filename]
634 del self._filemapping[filename]
634 del self._filemapping[filename]
635 del self._actionmapping[action][filename]
635 del self._actionmapping[action][filename]
636
636
637 def getactions(self, actions, sort=False):
637 def getactions(self, actions, sort=False):
638 """ get list of files which are marked with these actions
638 """ get list of files which are marked with these actions
639 if sort is true, files for each action is sorted and then added
639 if sort is true, files for each action is sorted and then added
640
640
641 Returns a list of tuple of form (filename, data, message)
641 Returns a list of tuple of form (filename, data, message)
642 """
642 """
643 for a in actions:
643 for a in actions:
644 if sort:
644 if sort:
645 for f in sorted(self._actionmapping[a]):
645 for f in sorted(self._actionmapping[a]):
646 args, msg = self._actionmapping[a][f]
646 args, msg = self._actionmapping[a][f]
647 yield f, args, msg
647 yield f, args, msg
648 else:
648 else:
649 for f, (args, msg) in pycompat.iteritems(
649 for f, (args, msg) in pycompat.iteritems(
650 self._actionmapping[a]
650 self._actionmapping[a]
651 ):
651 ):
652 yield f, args, msg
652 yield f, args, msg
653
653
654 def len(self, actions=None):
654 def len(self, actions=None):
655 """ returns number of files which needs actions
655 """ returns number of files which needs actions
656
656
657 if actions is passed, total of number of files in that action
657 if actions is passed, total of number of files in that action
658 only is returned """
658 only is returned """
659
659
660 if actions is None:
660 if actions is None:
661 return len(self._filemapping)
661 return len(self._filemapping)
662
662
663 return sum(len(self._actionmapping[a]) for a in actions)
663 return sum(len(self._actionmapping[a]) for a in actions)
664
664
665 def filemap(self, sort=False):
665 def filemap(self, sort=False):
666 if sorted:
666 if sorted:
667 for key, val in sorted(pycompat.iteritems(self._filemapping)):
667 for key, val in sorted(pycompat.iteritems(self._filemapping)):
668 yield key, val
668 yield key, val
669 else:
669 else:
670 for key, val in pycompat.iteritems(self._filemapping):
670 for key, val in pycompat.iteritems(self._filemapping):
671 yield key, val
671 yield key, val
672
672
673 def addcommitinfo(self, filename, key, value):
673 def addcommitinfo(self, filename, key, value):
674 """ adds key-value information about filename which will be required
674 """ adds key-value information about filename which will be required
675 while committing this merge """
675 while committing this merge """
676 self._commitinfo[filename][key] = value
676 self._commitinfo[filename][key] = value
677
677
678 @property
678 @property
679 def diverge(self):
679 def diverge(self):
680 return self._diverge
680 return self._diverge
681
681
682 @property
682 @property
683 def renamedelete(self):
683 def renamedelete(self):
684 return self._renamedelete
684 return self._renamedelete
685
685
686 @property
686 @property
687 def commitinfo(self):
687 def commitinfo(self):
688 return self._commitinfo
688 return self._commitinfo
689
689
690 @property
690 @property
691 def actionsdict(self):
691 def actionsdict(self):
692 """ returns a dictionary of actions to be perfomed with action as key
692 """ returns a dictionary of actions to be perfomed with action as key
693 and a list of files and related arguments as values """
693 and a list of files and related arguments as values """
694 res = collections.defaultdict(list)
694 res = collections.defaultdict(list)
695 for a, d in pycompat.iteritems(self._actionmapping):
695 for a, d in pycompat.iteritems(self._actionmapping):
696 for f, (args, msg) in pycompat.iteritems(d):
696 for f, (args, msg) in pycompat.iteritems(d):
697 res[a].append((f, args, msg))
697 res[a].append((f, args, msg))
698 return res
698 return res
699
699
700 def setactions(self, actions):
700 def setactions(self, actions):
701 self._filemapping = actions
701 self._filemapping = actions
702 self._actionmapping = collections.defaultdict(dict)
702 self._actionmapping = collections.defaultdict(dict)
703 for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
703 for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
704 self._actionmapping[act][f] = data, msg
704 self._actionmapping[act][f] = data, msg
705
705
706 def hasconflicts(self):
706 def hasconflicts(self):
707 """ tells whether this merge resulted in some actions which can
707 """ tells whether this merge resulted in some actions which can
708 result in conflicts or not """
708 result in conflicts or not """
709 for a in self._actionmapping.keys():
709 for a in self._actionmapping.keys():
710 if (
710 if (
711 a
711 a
712 not in (
712 not in (
713 mergestatemod.ACTION_GET,
713 mergestatemod.ACTION_GET,
714 mergestatemod.ACTION_KEEP,
714 mergestatemod.ACTION_KEEP,
715 mergestatemod.ACTION_EXEC,
715 mergestatemod.ACTION_EXEC,
716 mergestatemod.ACTION_REMOVE,
716 mergestatemod.ACTION_REMOVE,
717 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
717 mergestatemod.ACTION_PATH_CONFLICT_RESOLVE,
718 )
718 )
719 and self._actionmapping[a]
719 and self._actionmapping[a]
720 ):
720 ):
721 return True
721 return True
722
722
723 return False
723 return False
724
724
725
725
726 def manifestmerge(
726 def manifestmerge(
727 repo,
727 repo,
728 wctx,
728 wctx,
729 p2,
729 p2,
730 pa,
730 pa,
731 branchmerge,
731 branchmerge,
732 force,
732 force,
733 matcher,
733 matcher,
734 acceptremote,
734 acceptremote,
735 followcopies,
735 followcopies,
736 forcefulldiff=False,
736 forcefulldiff=False,
737 ):
737 ):
738 """
738 """
739 Merge wctx and p2 with ancestor pa and generate merge action list
739 Merge wctx and p2 with ancestor pa and generate merge action list
740
740
741 branchmerge and force are as passed in to update
741 branchmerge and force are as passed in to update
742 matcher = matcher to filter file lists
742 matcher = matcher to filter file lists
743 acceptremote = accept the incoming changes without prompting
743 acceptremote = accept the incoming changes without prompting
744
744
745 Returns an object of mergeresult class
745 Returns an object of mergeresult class
746 """
746 """
747 mresult = mergeresult()
747 mresult = mergeresult()
748 if matcher is not None and matcher.always():
748 if matcher is not None and matcher.always():
749 matcher = None
749 matcher = None
750
750
751 # manifests fetched in order are going to be faster, so prime the caches
751 # manifests fetched in order are going to be faster, so prime the caches
752 [
752 [
753 x.manifest()
753 x.manifest()
754 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
754 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
755 ]
755 ]
756
756
757 branch_copies1 = copies.branch_copies()
757 branch_copies1 = copies.branch_copies()
758 branch_copies2 = copies.branch_copies()
758 branch_copies2 = copies.branch_copies()
759 diverge = {}
759 diverge = {}
760 # information from merge which is needed at commit time
760 # information from merge which is needed at commit time
761 # for example choosing filelog of which parent to commit
761 # for example choosing filelog of which parent to commit
762 # TODO: use specific constants in future for this mapping
762 # TODO: use specific constants in future for this mapping
763 if followcopies:
763 if followcopies:
764 branch_copies1, branch_copies2, diverge = copies.mergecopies(
764 branch_copies1, branch_copies2, diverge = copies.mergecopies(
765 repo, wctx, p2, pa
765 repo, wctx, p2, pa
766 )
766 )
767
767
768 boolbm = pycompat.bytestr(bool(branchmerge))
768 boolbm = pycompat.bytestr(bool(branchmerge))
769 boolf = pycompat.bytestr(bool(force))
769 boolf = pycompat.bytestr(bool(force))
770 boolm = pycompat.bytestr(bool(matcher))
770 boolm = pycompat.bytestr(bool(matcher))
771 repo.ui.note(_(b"resolving manifests\n"))
771 repo.ui.note(_(b"resolving manifests\n"))
772 repo.ui.debug(
772 repo.ui.debug(
773 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
773 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
774 )
774 )
775 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
775 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
776
776
777 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
777 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
778 copied1 = set(branch_copies1.copy.values())
778 copied1 = set(branch_copies1.copy.values())
779 copied1.update(branch_copies1.movewithdir.values())
779 copied1.update(branch_copies1.movewithdir.values())
780 copied2 = set(branch_copies2.copy.values())
780 copied2 = set(branch_copies2.copy.values())
781 copied2.update(branch_copies2.movewithdir.values())
781 copied2.update(branch_copies2.movewithdir.values())
782
782
783 if b'.hgsubstate' in m1 and wctx.rev() is None:
783 if b'.hgsubstate' in m1 and wctx.rev() is None:
784 # Check whether sub state is modified, and overwrite the manifest
784 # Check whether sub state is modified, and overwrite the manifest
785 # to flag the change. If wctx is a committed revision, we shouldn't
785 # to flag the change. If wctx is a committed revision, we shouldn't
786 # care for the dirty state of the working directory.
786 # care for the dirty state of the working directory.
787 if any(wctx.sub(s).dirty() for s in wctx.substate):
787 if any(wctx.sub(s).dirty() for s in wctx.substate):
788 m1[b'.hgsubstate'] = modifiednodeid
788 m1[b'.hgsubstate'] = modifiednodeid
789
789
790 # Don't use m2-vs-ma optimization if:
790 # Don't use m2-vs-ma optimization if:
791 # - ma is the same as m1 or m2, which we're just going to diff again later
791 # - ma is the same as m1 or m2, which we're just going to diff again later
792 # - The caller specifically asks for a full diff, which is useful during bid
792 # - The caller specifically asks for a full diff, which is useful during bid
793 # merge.
793 # merge.
794 if pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff:
794 if pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff:
795 # Identify which files are relevant to the merge, so we can limit the
795 # Identify which files are relevant to the merge, so we can limit the
796 # total m1-vs-m2 diff to just those files. This has significant
796 # total m1-vs-m2 diff to just those files. This has significant
797 # performance benefits in large repositories.
797 # performance benefits in large repositories.
798 relevantfiles = set(ma.diff(m2).keys())
798 relevantfiles = set(ma.diff(m2).keys())
799
799
800 # For copied and moved files, we need to add the source file too.
800 # For copied and moved files, we need to add the source file too.
801 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
801 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
802 if copyvalue in relevantfiles:
802 if copyvalue in relevantfiles:
803 relevantfiles.add(copykey)
803 relevantfiles.add(copykey)
804 for movedirkey in branch_copies1.movewithdir:
804 for movedirkey in branch_copies1.movewithdir:
805 relevantfiles.add(movedirkey)
805 relevantfiles.add(movedirkey)
806 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
806 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
807 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
807 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
808
808
809 diff = m1.diff(m2, match=matcher)
809 diff = m1.diff(m2, match=matcher)
810
810
811 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
811 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
812 if n1 and n2: # file exists on both local and remote side
812 if n1 and n2: # file exists on both local and remote side
813 if f not in ma:
813 if f not in ma:
814 # TODO: what if they're renamed from different sources?
814 # TODO: what if they're renamed from different sources?
815 fa = branch_copies1.copy.get(
815 fa = branch_copies1.copy.get(
816 f, None
816 f, None
817 ) or branch_copies2.copy.get(f, None)
817 ) or branch_copies2.copy.get(f, None)
818 args, msg = None, None
818 args, msg = None, None
819 if fa is not None:
819 if fa is not None:
820 args = (f, f, fa, False, pa.node())
820 args = (f, f, fa, False, pa.node())
821 msg = b'both renamed from %s' % fa
821 msg = b'both renamed from %s' % fa
822 else:
822 else:
823 args = (f, f, None, False, pa.node())
823 args = (f, f, None, False, pa.node())
824 msg = b'both created'
824 msg = b'both created'
825 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
825 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
826 else:
826 else:
827 a = ma[f]
827 a = ma[f]
828 fla = ma.flags(f)
828 fla = ma.flags(f)
829 nol = b'l' not in fl1 + fl2 + fla
829 nol = b'l' not in fl1 + fl2 + fla
830 if n2 == a and fl2 == fla:
830 if n2 == a and fl2 == fla:
831 mresult.addfile(
831 mresult.addfile(
832 f, mergestatemod.ACTION_KEEP, (), b'remote unchanged',
832 f, mergestatemod.ACTION_KEEP, (), b'remote unchanged',
833 )
833 )
834 elif n1 == a and fl1 == fla: # local unchanged - use remote
834 elif n1 == a and fl1 == fla: # local unchanged - use remote
835 if n1 == n2: # optimization: keep local content
835 if n1 == n2: # optimization: keep local content
836 mresult.addfile(
836 mresult.addfile(
837 f,
837 f,
838 mergestatemod.ACTION_EXEC,
838 mergestatemod.ACTION_EXEC,
839 (fl2,),
839 (fl2,),
840 b'update permissions',
840 b'update permissions',
841 )
841 )
842 else:
842 else:
843 mresult.addfile(
843 mresult.addfile(
844 f,
844 f,
845 mergestatemod.ACTION_GET,
845 mergestatemod.ACTION_GET,
846 (fl2, False),
846 (fl2, False),
847 b'remote is newer',
847 b'remote is newer',
848 )
848 )
849 if branchmerge:
849 if branchmerge:
850 mresult.addcommitinfo(
850 mresult.addcommitinfo(
851 f, b'filenode-source', b'other'
851 f, b'filenode-source', b'other'
852 )
852 )
853 elif nol and n2 == a: # remote only changed 'x'
853 elif nol and n2 == a: # remote only changed 'x'
854 mresult.addfile(
854 mresult.addfile(
855 f,
855 f,
856 mergestatemod.ACTION_EXEC,
856 mergestatemod.ACTION_EXEC,
857 (fl2,),
857 (fl2,),
858 b'update permissions',
858 b'update permissions',
859 )
859 )
860 elif nol and n1 == a: # local only changed 'x'
860 elif nol and n1 == a: # local only changed 'x'
861 mresult.addfile(
861 mresult.addfile(
862 f,
862 f,
863 mergestatemod.ACTION_GET,
863 mergestatemod.ACTION_GET,
864 (fl1, False),
864 (fl1, False),
865 b'remote is newer',
865 b'remote is newer',
866 )
866 )
867 if branchmerge:
867 if branchmerge:
868 mresult.addcommitinfo(f, b'filenode-source', b'other')
868 mresult.addcommitinfo(f, b'filenode-source', b'other')
869 else: # both changed something
869 else: # both changed something
870 mresult.addfile(
870 mresult.addfile(
871 f,
871 f,
872 mergestatemod.ACTION_MERGE,
872 mergestatemod.ACTION_MERGE,
873 (f, f, f, False, pa.node()),
873 (f, f, f, False, pa.node()),
874 b'versions differ',
874 b'versions differ',
875 )
875 )
876 elif n1: # file exists only on local side
876 elif n1: # file exists only on local side
877 if f in copied2:
877 if f in copied2:
878 pass # we'll deal with it on m2 side
878 pass # we'll deal with it on m2 side
879 elif (
879 elif (
880 f in branch_copies1.movewithdir
880 f in branch_copies1.movewithdir
881 ): # directory rename, move local
881 ): # directory rename, move local
882 f2 = branch_copies1.movewithdir[f]
882 f2 = branch_copies1.movewithdir[f]
883 if f2 in m2:
883 if f2 in m2:
884 mresult.addfile(
884 mresult.addfile(
885 f2,
885 f2,
886 mergestatemod.ACTION_MERGE,
886 mergestatemod.ACTION_MERGE,
887 (f, f2, None, True, pa.node()),
887 (f, f2, None, True, pa.node()),
888 b'remote directory rename, both created',
888 b'remote directory rename, both created',
889 )
889 )
890 else:
890 else:
891 mresult.addfile(
891 mresult.addfile(
892 f2,
892 f2,
893 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
893 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
894 (f, fl1),
894 (f, fl1),
895 b'remote directory rename - move from %s' % f,
895 b'remote directory rename - move from %s' % f,
896 )
896 )
897 elif f in branch_copies1.copy:
897 elif f in branch_copies1.copy:
898 f2 = branch_copies1.copy[f]
898 f2 = branch_copies1.copy[f]
899 mresult.addfile(
899 mresult.addfile(
900 f,
900 f,
901 mergestatemod.ACTION_MERGE,
901 mergestatemod.ACTION_MERGE,
902 (f, f2, f2, False, pa.node()),
902 (f, f2, f2, False, pa.node()),
903 b'local copied/moved from %s' % f2,
903 b'local copied/moved from %s' % f2,
904 )
904 )
905 elif f in ma: # clean, a different, no remote
905 elif f in ma: # clean, a different, no remote
906 if n1 != ma[f]:
906 if n1 != ma[f]:
907 if acceptremote:
907 if acceptremote:
908 mresult.addfile(
908 mresult.addfile(
909 f,
909 f,
910 mergestatemod.ACTION_REMOVE,
910 mergestatemod.ACTION_REMOVE,
911 None,
911 None,
912 b'remote delete',
912 b'remote delete',
913 )
913 )
914 else:
914 else:
915 mresult.addfile(
915 mresult.addfile(
916 f,
916 f,
917 mergestatemod.ACTION_CHANGED_DELETED,
917 mergestatemod.ACTION_CHANGED_DELETED,
918 (f, None, f, False, pa.node()),
918 (f, None, f, False, pa.node()),
919 b'prompt changed/deleted',
919 b'prompt changed/deleted',
920 )
920 )
921 elif n1 == addednodeid:
921 elif n1 == addednodeid:
922 # This file was locally added. We should forget it instead of
922 # This file was locally added. We should forget it instead of
923 # deleting it.
923 # deleting it.
924 mresult.addfile(
924 mresult.addfile(
925 f, mergestatemod.ACTION_FORGET, None, b'remote deleted',
925 f, mergestatemod.ACTION_FORGET, None, b'remote deleted',
926 )
926 )
927 else:
927 else:
928 mresult.addfile(
928 mresult.addfile(
929 f, mergestatemod.ACTION_REMOVE, None, b'other deleted',
929 f, mergestatemod.ACTION_REMOVE, None, b'other deleted',
930 )
930 )
931 elif n2: # file exists only on remote side
931 elif n2: # file exists only on remote side
932 if f in copied1:
932 if f in copied1:
933 pass # we'll deal with it on m1 side
933 pass # we'll deal with it on m1 side
934 elif f in branch_copies2.movewithdir:
934 elif f in branch_copies2.movewithdir:
935 f2 = branch_copies2.movewithdir[f]
935 f2 = branch_copies2.movewithdir[f]
936 if f2 in m1:
936 if f2 in m1:
937 mresult.addfile(
937 mresult.addfile(
938 f2,
938 f2,
939 mergestatemod.ACTION_MERGE,
939 mergestatemod.ACTION_MERGE,
940 (f2, f, None, False, pa.node()),
940 (f2, f, None, False, pa.node()),
941 b'local directory rename, both created',
941 b'local directory rename, both created',
942 )
942 )
943 else:
943 else:
944 mresult.addfile(
944 mresult.addfile(
945 f2,
945 f2,
946 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
946 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
947 (f, fl2),
947 (f, fl2),
948 b'local directory rename - get from %s' % f,
948 b'local directory rename - get from %s' % f,
949 )
949 )
950 elif f in branch_copies2.copy:
950 elif f in branch_copies2.copy:
951 f2 = branch_copies2.copy[f]
951 f2 = branch_copies2.copy[f]
952 msg, args = None, None
952 msg, args = None, None
953 if f2 in m2:
953 if f2 in m2:
954 args = (f2, f, f2, False, pa.node())
954 args = (f2, f, f2, False, pa.node())
955 msg = b'remote copied from %s' % f2
955 msg = b'remote copied from %s' % f2
956 else:
956 else:
957 args = (f2, f, f2, True, pa.node())
957 args = (f2, f, f2, True, pa.node())
958 msg = b'remote moved from %s' % f2
958 msg = b'remote moved from %s' % f2
959 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
959 mresult.addfile(f, mergestatemod.ACTION_MERGE, args, msg)
960 elif f not in ma:
960 elif f not in ma:
961 # local unknown, remote created: the logic is described by the
961 # local unknown, remote created: the logic is described by the
962 # following table:
962 # following table:
963 #
963 #
964 # force branchmerge different | action
964 # force branchmerge different | action
965 # n * * | create
965 # n * * | create
966 # y n * | create
966 # y n * | create
967 # y y n | create
967 # y y n | create
968 # y y y | merge
968 # y y y | merge
969 #
969 #
970 # Checking whether the files are different is expensive, so we
970 # Checking whether the files are different is expensive, so we
971 # don't do that when we can avoid it.
971 # don't do that when we can avoid it.
972 if not force:
972 if not force:
973 mresult.addfile(
973 mresult.addfile(
974 f,
974 f,
975 mergestatemod.ACTION_CREATED,
975 mergestatemod.ACTION_CREATED,
976 (fl2,),
976 (fl2,),
977 b'remote created',
977 b'remote created',
978 )
978 )
979 elif not branchmerge:
979 elif not branchmerge:
980 mresult.addfile(
980 mresult.addfile(
981 f,
981 f,
982 mergestatemod.ACTION_CREATED,
982 mergestatemod.ACTION_CREATED,
983 (fl2,),
983 (fl2,),
984 b'remote created',
984 b'remote created',
985 )
985 )
986 else:
986 else:
987 mresult.addfile(
987 mresult.addfile(
988 f,
988 f,
989 mergestatemod.ACTION_CREATED_MERGE,
989 mergestatemod.ACTION_CREATED_MERGE,
990 (fl2, pa.node()),
990 (fl2, pa.node()),
991 b'remote created, get or merge',
991 b'remote created, get or merge',
992 )
992 )
993 elif n2 != ma[f]:
993 elif n2 != ma[f]:
994 df = None
994 df = None
995 for d in branch_copies1.dirmove:
995 for d in branch_copies1.dirmove:
996 if f.startswith(d):
996 if f.startswith(d):
997 # new file added in a directory that was moved
997 # new file added in a directory that was moved
998 df = branch_copies1.dirmove[d] + f[len(d) :]
998 df = branch_copies1.dirmove[d] + f[len(d) :]
999 break
999 break
1000 if df is not None and df in m1:
1000 if df is not None and df in m1:
1001 mresult.addfile(
1001 mresult.addfile(
1002 df,
1002 df,
1003 mergestatemod.ACTION_MERGE,
1003 mergestatemod.ACTION_MERGE,
1004 (df, f, f, False, pa.node()),
1004 (df, f, f, False, pa.node()),
1005 b'local directory rename - respect move '
1005 b'local directory rename - respect move '
1006 b'from %s' % f,
1006 b'from %s' % f,
1007 )
1007 )
1008 elif acceptremote:
1008 elif acceptremote:
1009 mresult.addfile(
1009 mresult.addfile(
1010 f,
1010 f,
1011 mergestatemod.ACTION_CREATED,
1011 mergestatemod.ACTION_CREATED,
1012 (fl2,),
1012 (fl2,),
1013 b'remote recreating',
1013 b'remote recreating',
1014 )
1014 )
1015 else:
1015 else:
1016 mresult.addfile(
1016 mresult.addfile(
1017 f,
1017 f,
1018 mergestatemod.ACTION_DELETED_CHANGED,
1018 mergestatemod.ACTION_DELETED_CHANGED,
1019 (None, f, f, False, pa.node()),
1019 (None, f, f, False, pa.node()),
1020 b'prompt deleted/changed',
1020 b'prompt deleted/changed',
1021 )
1021 )
1022
1022
1023 if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'):
1023 if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'):
1024 # If we are merging, look for path conflicts.
1024 # If we are merging, look for path conflicts.
1025 checkpathconflicts(repo, wctx, p2, mresult)
1025 checkpathconflicts(repo, wctx, p2, mresult)
1026
1026
1027 narrowmatch = repo.narrowmatch()
1027 narrowmatch = repo.narrowmatch()
1028 if not narrowmatch.always():
1028 if not narrowmatch.always():
1029 # Updates "actions" in place
1029 # Updates "actions" in place
1030 _filternarrowactions(narrowmatch, branchmerge, mresult)
1030 _filternarrowactions(narrowmatch, branchmerge, mresult)
1031
1031
1032 renamedelete = branch_copies1.renamedelete
1032 renamedelete = branch_copies1.renamedelete
1033 renamedelete.update(branch_copies2.renamedelete)
1033 renamedelete.update(branch_copies2.renamedelete)
1034
1034
1035 mresult.updatevalues(diverge, renamedelete)
1035 mresult.updatevalues(diverge, renamedelete)
1036 return mresult
1036 return mresult
1037
1037
1038
1038
1039 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
1039 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
1040 """Resolves false conflicts where the nodeid changed but the content
1040 """Resolves false conflicts where the nodeid changed but the content
1041 remained the same."""
1041 remained the same."""
1042 # We force a copy of actions.items() because we're going to mutate
1042 # We force a copy of actions.items() because we're going to mutate
1043 # actions as we resolve trivial conflicts.
1043 # actions as we resolve trivial conflicts.
1044 for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
1044 for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
1045 if f in ancestor and not wctx[f].cmp(ancestor[f]):
1045 if f in ancestor and not wctx[f].cmp(ancestor[f]):
1046 # local did change but ended up with same content
1046 # local did change but ended up with same content
1047 mresult.addfile(
1047 mresult.addfile(
1048 f, mergestatemod.ACTION_REMOVE, None, b'prompt same'
1048 f, mergestatemod.ACTION_REMOVE, None, b'prompt same'
1049 )
1049 )
1050
1050
1051 for f in list(mresult.files((mergestatemod.ACTION_DELETED_CHANGED,))):
1051 for f in list(mresult.files((mergestatemod.ACTION_DELETED_CHANGED,))):
1052 if f in ancestor and not mctx[f].cmp(ancestor[f]):
1052 if f in ancestor and not mctx[f].cmp(ancestor[f]):
1053 # remote did change but ended up with same content
1053 # remote did change but ended up with same content
1054 mresult.removefile(f) # don't get = keep local deleted
1054 mresult.removefile(f) # don't get = keep local deleted
1055
1055
1056
1056
1057 def calculateupdates(
1057 def calculateupdates(
1058 repo,
1058 repo,
1059 wctx,
1059 wctx,
1060 mctx,
1060 mctx,
1061 ancestors,
1061 ancestors,
1062 branchmerge,
1062 branchmerge,
1063 force,
1063 force,
1064 acceptremote,
1064 acceptremote,
1065 followcopies,
1065 followcopies,
1066 matcher=None,
1066 matcher=None,
1067 mergeforce=False,
1067 mergeforce=False,
1068 ):
1068 ):
1069 """
1069 """
1070 Calculate the actions needed to merge mctx into wctx using ancestors
1070 Calculate the actions needed to merge mctx into wctx using ancestors
1071
1071
1072 Uses manifestmerge() to merge manifest and get list of actions required to
1072 Uses manifestmerge() to merge manifest and get list of actions required to
1073 perform for merging two manifests. If there are multiple ancestors, uses bid
1073 perform for merging two manifests. If there are multiple ancestors, uses bid
1074 merge if enabled.
1074 merge if enabled.
1075
1075
1076 Also filters out actions which are unrequired if repository is sparse.
1076 Also filters out actions which are unrequired if repository is sparse.
1077
1077
1078 Returns mergeresult object same as manifestmerge().
1078 Returns mergeresult object same as manifestmerge().
1079 """
1079 """
1080 # Avoid cycle.
1080 # Avoid cycle.
1081 from . import sparse
1081 from . import sparse
1082
1082
1083 mresult = None
1083 mresult = None
1084 if len(ancestors) == 1: # default
1084 if len(ancestors) == 1: # default
1085 mresult = manifestmerge(
1085 mresult = manifestmerge(
1086 repo,
1086 repo,
1087 wctx,
1087 wctx,
1088 mctx,
1088 mctx,
1089 ancestors[0],
1089 ancestors[0],
1090 branchmerge,
1090 branchmerge,
1091 force,
1091 force,
1092 matcher,
1092 matcher,
1093 acceptremote,
1093 acceptremote,
1094 followcopies,
1094 followcopies,
1095 )
1095 )
1096 _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce)
1096 _checkunknownfiles(repo, wctx, mctx, force, mresult, mergeforce)
1097
1097
1098 else: # only when merge.preferancestor=* - the default
1098 else: # only when merge.preferancestor=* - the default
1099 repo.ui.note(
1099 repo.ui.note(
1100 _(b"note: merging %s and %s using bids from ancestors %s\n")
1100 _(b"note: merging %s and %s using bids from ancestors %s\n")
1101 % (
1101 % (
1102 wctx,
1102 wctx,
1103 mctx,
1103 mctx,
1104 _(b' and ').join(pycompat.bytestr(anc) for anc in ancestors),
1104 _(b' and ').join(pycompat.bytestr(anc) for anc in ancestors),
1105 )
1105 )
1106 )
1106 )
1107
1107
1108 # mapping filename to bids (action method to list af actions)
1108 # mapping filename to bids (action method to list af actions)
1109 # {FILENAME1 : BID1, FILENAME2 : BID2}
1109 # {FILENAME1 : BID1, FILENAME2 : BID2}
1110 # BID is another dictionary which contains
1110 # BID is another dictionary which contains
1111 # mapping of following form:
1111 # mapping of following form:
1112 # {ACTION_X : [info, ..], ACTION_Y : [info, ..]}
1112 # {ACTION_X : [info, ..], ACTION_Y : [info, ..]}
1113 fbids = {}
1113 fbids = {}
1114 diverge, renamedelete = None, None
1114 diverge, renamedelete = None, None
1115 for ancestor in ancestors:
1115 for ancestor in ancestors:
1116 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
1116 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
1117 mresult1 = manifestmerge(
1117 mresult1 = manifestmerge(
1118 repo,
1118 repo,
1119 wctx,
1119 wctx,
1120 mctx,
1120 mctx,
1121 ancestor,
1121 ancestor,
1122 branchmerge,
1122 branchmerge,
1123 force,
1123 force,
1124 matcher,
1124 matcher,
1125 acceptremote,
1125 acceptremote,
1126 followcopies,
1126 followcopies,
1127 forcefulldiff=True,
1127 forcefulldiff=True,
1128 )
1128 )
1129 _checkunknownfiles(repo, wctx, mctx, force, mresult1, mergeforce)
1129 _checkunknownfiles(repo, wctx, mctx, force, mresult1, mergeforce)
1130
1130
1131 # Track the shortest set of warning on the theory that bid
1131 # Track the shortest set of warning on the theory that bid
1132 # merge will correctly incorporate more information
1132 # merge will correctly incorporate more information
1133 if diverge is None or len(mresult1.diverge) < len(diverge):
1133 if diverge is None or len(mresult1.diverge) < len(diverge):
1134 diverge = mresult1.diverge
1134 diverge = mresult1.diverge
1135 if renamedelete is None or len(renamedelete) < len(
1135 if renamedelete is None or len(renamedelete) < len(
1136 mresult1.renamedelete
1136 mresult1.renamedelete
1137 ):
1137 ):
1138 renamedelete = mresult1.renamedelete
1138 renamedelete = mresult1.renamedelete
1139
1139
1140 for f, a in mresult1.filemap(sort=True):
1140 for f, a in mresult1.filemap(sort=True):
1141 m, args, msg = a
1141 m, args, msg = a
1142 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m))
1142 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m))
1143 if f in fbids:
1143 if f in fbids:
1144 d = fbids[f]
1144 d = fbids[f]
1145 if m in d:
1145 if m in d:
1146 d[m].append(a)
1146 d[m].append(a)
1147 else:
1147 else:
1148 d[m] = [a]
1148 d[m] = [a]
1149 else:
1149 else:
1150 fbids[f] = {m: [a]}
1150 fbids[f] = {m: [a]}
1151
1151
1152 # Call for bids
1152 # Call for bids
1153 # Pick the best bid for each file
1153 # Pick the best bid for each file
1154 repo.ui.note(
1154 repo.ui.note(
1155 _(b'\nauction for merging merge bids (%d ancestors)\n')
1155 _(b'\nauction for merging merge bids (%d ancestors)\n')
1156 % len(ancestors)
1156 % len(ancestors)
1157 )
1157 )
1158 mresult = mergeresult()
1158 mresult = mergeresult()
1159 for f, bids in sorted(fbids.items()):
1159 for f, bids in sorted(fbids.items()):
1160 if repo.ui.debugflag:
1160 if repo.ui.debugflag:
1161 repo.ui.debug(b" list of bids for %s:\n" % f)
1161 repo.ui.debug(b" list of bids for %s:\n" % f)
1162 for m, l in sorted(bids.items()):
1162 for m, l in sorted(bids.items()):
1163 for _f, args, msg in l:
1163 for _f, args, msg in l:
1164 repo.ui.debug(b' %s -> %s\n' % (msg, m))
1164 repo.ui.debug(b' %s -> %s\n' % (msg, m))
1165 # bids is a mapping from action method to list af actions
1165 # bids is a mapping from action method to list af actions
1166 # Consensus?
1166 # Consensus?
1167 if len(bids) == 1: # all bids are the same kind of method
1167 if len(bids) == 1: # all bids are the same kind of method
1168 m, l = list(bids.items())[0]
1168 m, l = list(bids.items())[0]
1169 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1169 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1170 repo.ui.note(_(b" %s: consensus for %s\n") % (f, m))
1170 repo.ui.note(_(b" %s: consensus for %s\n") % (f, m))
1171 mresult.addfile(f, *l[0])
1171 mresult.addfile(f, *l[0])
1172 continue
1172 continue
1173 # If keep is an option, just do it.
1173 # If keep is an option, just do it.
1174 if mergestatemod.ACTION_KEEP in bids:
1174 if mergestatemod.ACTION_KEEP in bids:
1175 repo.ui.note(_(b" %s: picking 'keep' action\n") % f)
1175 repo.ui.note(_(b" %s: picking 'keep' action\n") % f)
1176 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0])
1176 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP][0])
1177 continue
1177 continue
1178 # If there are gets and they all agree [how could they not?], do it.
1178 # If there are gets and they all agree [how could they not?], do it.
1179 if mergestatemod.ACTION_GET in bids:
1179 if mergestatemod.ACTION_GET in bids:
1180 ga0 = bids[mergestatemod.ACTION_GET][0]
1180 ga0 = bids[mergestatemod.ACTION_GET][0]
1181 if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]):
1181 if all(a == ga0 for a in bids[mergestatemod.ACTION_GET][1:]):
1182 repo.ui.note(_(b" %s: picking 'get' action\n") % f)
1182 repo.ui.note(_(b" %s: picking 'get' action\n") % f)
1183 mresult.addfile(f, *ga0)
1183 mresult.addfile(f, *ga0)
1184 continue
1184 continue
1185 # TODO: Consider other simple actions such as mode changes
1185 # TODO: Consider other simple actions such as mode changes
1186 # Handle inefficient democrazy.
1186 # Handle inefficient democrazy.
1187 repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f)
1187 repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f)
1188 for m, l in sorted(bids.items()):
1188 for m, l in sorted(bids.items()):
1189 for _f, args, msg in l:
1189 for _f, args, msg in l:
1190 repo.ui.note(b' %s -> %s\n' % (msg, m))
1190 repo.ui.note(b' %s -> %s\n' % (msg, m))
1191 # Pick random action. TODO: Instead, prompt user when resolving
1191 # Pick random action. TODO: Instead, prompt user when resolving
1192 m, l = list(bids.items())[0]
1192 m, l = list(bids.items())[0]
1193 repo.ui.warn(
1193 repo.ui.warn(
1194 _(b' %s: ambiguous merge - picked %s action\n') % (f, m)
1194 _(b' %s: ambiguous merge - picked %s action\n') % (f, m)
1195 )
1195 )
1196 mresult.addfile(f, *l[0])
1196 mresult.addfile(f, *l[0])
1197 continue
1197 continue
1198 repo.ui.note(_(b'end of auction\n\n'))
1198 repo.ui.note(_(b'end of auction\n\n'))
1199 mresult.updatevalues(diverge, renamedelete)
1199 mresult.updatevalues(diverge, renamedelete)
1200
1200
1201 if wctx.rev() is None:
1201 if wctx.rev() is None:
1202 _forgetremoved(wctx, mctx, branchmerge, mresult)
1202 _forgetremoved(wctx, mctx, branchmerge, mresult)
1203
1203
1204 sparse.filterupdatesactions(repo, wctx, mctx, branchmerge, mresult)
1204 sparse.filterupdatesactions(repo, wctx, mctx, branchmerge, mresult)
1205 _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult)
1205 _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult)
1206
1206
1207 return mresult
1207 return mresult
1208
1208
1209
1209
1210 def _getcwd():
1210 def _getcwd():
1211 try:
1211 try:
1212 return encoding.getcwd()
1212 return encoding.getcwd()
1213 except OSError as err:
1213 except OSError as err:
1214 if err.errno == errno.ENOENT:
1214 if err.errno == errno.ENOENT:
1215 return None
1215 return None
1216 raise
1216 raise
1217
1217
1218
1218
1219 def batchremove(repo, wctx, actions):
1219 def batchremove(repo, wctx, actions):
1220 """apply removes to the working directory
1220 """apply removes to the working directory
1221
1221
1222 yields tuples for progress updates
1222 yields tuples for progress updates
1223 """
1223 """
1224 verbose = repo.ui.verbose
1224 verbose = repo.ui.verbose
1225 cwd = _getcwd()
1225 cwd = _getcwd()
1226 i = 0
1226 i = 0
1227 for f, args, msg in actions:
1227 for f, args, msg in actions:
1228 repo.ui.debug(b" %s: %s -> r\n" % (f, msg))
1228 repo.ui.debug(b" %s: %s -> r\n" % (f, msg))
1229 if verbose:
1229 if verbose:
1230 repo.ui.note(_(b"removing %s\n") % f)
1230 repo.ui.note(_(b"removing %s\n") % f)
1231 wctx[f].audit()
1231 wctx[f].audit()
1232 try:
1232 try:
1233 wctx[f].remove(ignoremissing=True)
1233 wctx[f].remove(ignoremissing=True)
1234 except OSError as inst:
1234 except OSError as inst:
1235 repo.ui.warn(
1235 repo.ui.warn(
1236 _(b"update failed to remove %s: %s!\n") % (f, inst.strerror)
1236 _(b"update failed to remove %s: %s!\n") % (f, inst.strerror)
1237 )
1237 )
1238 if i == 100:
1238 if i == 100:
1239 yield i, f
1239 yield i, f
1240 i = 0
1240 i = 0
1241 i += 1
1241 i += 1
1242 if i > 0:
1242 if i > 0:
1243 yield i, f
1243 yield i, f
1244
1244
1245 if cwd and not _getcwd():
1245 if cwd and not _getcwd():
1246 # cwd was removed in the course of removing files; print a helpful
1246 # cwd was removed in the course of removing files; print a helpful
1247 # warning.
1247 # warning.
1248 repo.ui.warn(
1248 repo.ui.warn(
1249 _(
1249 _(
1250 b"current directory was removed\n"
1250 b"current directory was removed\n"
1251 b"(consider changing to repo root: %s)\n"
1251 b"(consider changing to repo root: %s)\n"
1252 )
1252 )
1253 % repo.root
1253 % repo.root
1254 )
1254 )
1255
1255
1256
1256
1257 def batchget(repo, mctx, wctx, wantfiledata, actions):
1257 def batchget(repo, mctx, wctx, wantfiledata, actions):
1258 """apply gets to the working directory
1258 """apply gets to the working directory
1259
1259
1260 mctx is the context to get from
1260 mctx is the context to get from
1261
1261
1262 Yields arbitrarily many (False, tuple) for progress updates, followed by
1262 Yields arbitrarily many (False, tuple) for progress updates, followed by
1263 exactly one (True, filedata). When wantfiledata is false, filedata is an
1263 exactly one (True, filedata). When wantfiledata is false, filedata is an
1264 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1264 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1265 mtime) of the file f written for each action.
1265 mtime) of the file f written for each action.
1266 """
1266 """
1267 filedata = {}
1267 filedata = {}
1268 verbose = repo.ui.verbose
1268 verbose = repo.ui.verbose
1269 fctx = mctx.filectx
1269 fctx = mctx.filectx
1270 ui = repo.ui
1270 ui = repo.ui
1271 i = 0
1271 i = 0
1272 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1272 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1273 for f, (flags, backup), msg in actions:
1273 for f, (flags, backup), msg in actions:
1274 repo.ui.debug(b" %s: %s -> g\n" % (f, msg))
1274 repo.ui.debug(b" %s: %s -> g\n" % (f, msg))
1275 if verbose:
1275 if verbose:
1276 repo.ui.note(_(b"getting %s\n") % f)
1276 repo.ui.note(_(b"getting %s\n") % f)
1277
1277
1278 if backup:
1278 if backup:
1279 # If a file or directory exists with the same name, back that
1279 # If a file or directory exists with the same name, back that
1280 # up. Otherwise, look to see if there is a file that conflicts
1280 # up. Otherwise, look to see if there is a file that conflicts
1281 # with a directory this file is in, and if so, back that up.
1281 # with a directory this file is in, and if so, back that up.
1282 conflicting = f
1282 conflicting = f
1283 if not repo.wvfs.lexists(f):
1283 if not repo.wvfs.lexists(f):
1284 for p in pathutil.finddirs(f):
1284 for p in pathutil.finddirs(f):
1285 if repo.wvfs.isfileorlink(p):
1285 if repo.wvfs.isfileorlink(p):
1286 conflicting = p
1286 conflicting = p
1287 break
1287 break
1288 if repo.wvfs.lexists(conflicting):
1288 if repo.wvfs.lexists(conflicting):
1289 orig = scmutil.backuppath(ui, repo, conflicting)
1289 orig = scmutil.backuppath(ui, repo, conflicting)
1290 util.rename(repo.wjoin(conflicting), orig)
1290 util.rename(repo.wjoin(conflicting), orig)
1291 wfctx = wctx[f]
1291 wfctx = wctx[f]
1292 wfctx.clearunknown()
1292 wfctx.clearunknown()
1293 atomictemp = ui.configbool(b"experimental", b"update.atomic-file")
1293 atomictemp = ui.configbool(b"experimental", b"update.atomic-file")
1294 size = wfctx.write(
1294 size = wfctx.write(
1295 fctx(f).data(),
1295 fctx(f).data(),
1296 flags,
1296 flags,
1297 backgroundclose=True,
1297 backgroundclose=True,
1298 atomictemp=atomictemp,
1298 atomictemp=atomictemp,
1299 )
1299 )
1300 if wantfiledata:
1300 if wantfiledata:
1301 s = wfctx.lstat()
1301 s = wfctx.lstat()
1302 mode = s.st_mode
1302 mode = s.st_mode
1303 mtime = s[stat.ST_MTIME]
1303 mtime = s[stat.ST_MTIME]
1304 filedata[f] = (mode, size, mtime) # for dirstate.normal
1304 filedata[f] = (mode, size, mtime) # for dirstate.normal
1305 if i == 100:
1305 if i == 100:
1306 yield False, (i, f)
1306 yield False, (i, f)
1307 i = 0
1307 i = 0
1308 i += 1
1308 i += 1
1309 if i > 0:
1309 if i > 0:
1310 yield False, (i, f)
1310 yield False, (i, f)
1311 yield True, filedata
1311 yield True, filedata
1312
1312
1313
1313
1314 def _prefetchfiles(repo, ctx, mresult):
1314 def _prefetchfiles(repo, ctx, mresult):
1315 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1315 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1316 of merge actions. ``ctx`` is the context being merged in."""
1316 of merge actions. ``ctx`` is the context being merged in."""
1317
1317
1318 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1318 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1319 # don't touch the context to be merged in. 'cd' is skipped, because
1319 # don't touch the context to be merged in. 'cd' is skipped, because
1320 # changed/deleted never resolves to something from the remote side.
1320 # changed/deleted never resolves to something from the remote side.
1321 files = mresult.files(
1321 files = mresult.files(
1322 [
1322 [
1323 mergestatemod.ACTION_GET,
1323 mergestatemod.ACTION_GET,
1324 mergestatemod.ACTION_DELETED_CHANGED,
1324 mergestatemod.ACTION_DELETED_CHANGED,
1325 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1325 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1326 mergestatemod.ACTION_MERGE,
1326 mergestatemod.ACTION_MERGE,
1327 ]
1327 ]
1328 )
1328 )
1329
1329
1330 prefetch = scmutil.prefetchfiles
1330 prefetch = scmutil.prefetchfiles
1331 matchfiles = scmutil.matchfiles
1331 matchfiles = scmutil.matchfiles
1332 prefetch(
1332 prefetch(
1333 repo, [(ctx.rev(), matchfiles(repo, files),)],
1333 repo, [(ctx.rev(), matchfiles(repo, files),)],
1334 )
1334 )
1335
1335
1336
1336
1337 @attr.s(frozen=True)
1337 @attr.s(frozen=True)
1338 class updateresult(object):
1338 class updateresult(object):
1339 updatedcount = attr.ib()
1339 updatedcount = attr.ib()
1340 mergedcount = attr.ib()
1340 mergedcount = attr.ib()
1341 removedcount = attr.ib()
1341 removedcount = attr.ib()
1342 unresolvedcount = attr.ib()
1342 unresolvedcount = attr.ib()
1343
1343
1344 def isempty(self):
1344 def isempty(self):
1345 return not (
1345 return not (
1346 self.updatedcount
1346 self.updatedcount
1347 or self.mergedcount
1347 or self.mergedcount
1348 or self.removedcount
1348 or self.removedcount
1349 or self.unresolvedcount
1349 or self.unresolvedcount
1350 )
1350 )
1351
1351
1352
1352
1353 def applyupdates(
1353 def applyupdates(
1354 repo, mresult, wctx, mctx, overwrite, wantfiledata, labels=None,
1354 repo, mresult, wctx, mctx, overwrite, wantfiledata, labels=None,
1355 ):
1355 ):
1356 """apply the merge action list to the working directory
1356 """apply the merge action list to the working directory
1357
1357
1358 mresult is a mergeresult object representing result of the merge
1358 mresult is a mergeresult object representing result of the merge
1359 wctx is the working copy context
1359 wctx is the working copy context
1360 mctx is the context to be merged into the working copy
1360 mctx is the context to be merged into the working copy
1361
1361
1362 Return a tuple of (counts, filedata), where counts is a tuple
1362 Return a tuple of (counts, filedata), where counts is a tuple
1363 (updated, merged, removed, unresolved) that describes how many
1363 (updated, merged, removed, unresolved) that describes how many
1364 files were affected by the update, and filedata is as described in
1364 files were affected by the update, and filedata is as described in
1365 batchget.
1365 batchget.
1366 """
1366 """
1367
1367
1368 _prefetchfiles(repo, mctx, mresult)
1368 _prefetchfiles(repo, mctx, mresult)
1369
1369
1370 updated, merged, removed = 0, 0, 0
1370 updated, merged, removed = 0, 0, 0
1371 ms = mergestatemod.mergestate.clean(
1371 ms = mergestatemod.mergestate.clean(
1372 repo, wctx.p1().node(), mctx.node(), labels
1372 repo, wctx.p1().node(), mctx.node(), labels
1373 )
1373 )
1374
1374
1375 for f, op in pycompat.iteritems(mresult.commitinfo):
1375 for f, op in pycompat.iteritems(mresult.commitinfo):
1376 # the other side of filenode was choosen while merging, store this in
1376 # the other side of filenode was choosen while merging, store this in
1377 # mergestate so that it can be reused on commit
1377 # mergestate so that it can be reused on commit
1378 ms.addcommitinfo(f, op)
1378 ms.addcommitinfo(f, op)
1379
1379
1380 moves = []
1380 moves = []
1381
1381
1382 # 'cd' and 'dc' actions are treated like other merge conflicts
1382 # 'cd' and 'dc' actions are treated like other merge conflicts
1383 mergeactions = list(
1383 mergeactions = list(
1384 mresult.getactions(
1384 mresult.getactions(
1385 [
1385 [
1386 mergestatemod.ACTION_CHANGED_DELETED,
1386 mergestatemod.ACTION_CHANGED_DELETED,
1387 mergestatemod.ACTION_DELETED_CHANGED,
1387 mergestatemod.ACTION_DELETED_CHANGED,
1388 mergestatemod.ACTION_MERGE,
1388 mergestatemod.ACTION_MERGE,
1389 ],
1389 ],
1390 sort=True,
1390 sort=True,
1391 )
1391 )
1392 )
1392 )
1393 for f, args, msg in mergeactions:
1393 for f, args, msg in mergeactions:
1394 f1, f2, fa, move, anc = args
1394 f1, f2, fa, move, anc = args
1395 if f == b'.hgsubstate': # merged internally
1395 if f == b'.hgsubstate': # merged internally
1396 continue
1396 continue
1397 if f1 is None:
1397 if f1 is None:
1398 fcl = filemerge.absentfilectx(wctx, fa)
1398 fcl = filemerge.absentfilectx(wctx, fa)
1399 else:
1399 else:
1400 repo.ui.debug(b" preserving %s for resolve of %s\n" % (f1, f))
1400 repo.ui.debug(b" preserving %s for resolve of %s\n" % (f1, f))
1401 fcl = wctx[f1]
1401 fcl = wctx[f1]
1402 if f2 is None:
1402 if f2 is None:
1403 fco = filemerge.absentfilectx(mctx, fa)
1403 fco = filemerge.absentfilectx(mctx, fa)
1404 else:
1404 else:
1405 fco = mctx[f2]
1405 fco = mctx[f2]
1406 actx = repo[anc]
1406 actx = repo[anc]
1407 if fa in actx:
1407 if fa in actx:
1408 fca = actx[fa]
1408 fca = actx[fa]
1409 else:
1409 else:
1410 # TODO: move to absentfilectx
1410 # TODO: move to absentfilectx
1411 fca = repo.filectx(f1, fileid=nullrev)
1411 fca = repo.filectx(f1, fileid=nullrev)
1412 ms.add(fcl, fco, fca, f)
1412 ms.add(fcl, fco, fca, f)
1413 if f1 != f and move:
1413 if f1 != f and move:
1414 moves.append(f1)
1414 moves.append(f1)
1415
1415
1416 # remove renamed files after safely stored
1416 # remove renamed files after safely stored
1417 for f in moves:
1417 for f in moves:
1418 if wctx[f].lexists():
1418 if wctx[f].lexists():
1419 repo.ui.debug(b"removing %s\n" % f)
1419 repo.ui.debug(b"removing %s\n" % f)
1420 wctx[f].audit()
1420 wctx[f].audit()
1421 wctx[f].remove()
1421 wctx[f].remove()
1422
1422
1423 numupdates = mresult.len() - mresult.len((mergestatemod.ACTION_KEEP,))
1423 numupdates = mresult.len() - mresult.len((mergestatemod.ACTION_KEEP,))
1424 progress = repo.ui.makeprogress(
1424 progress = repo.ui.makeprogress(
1425 _(b'updating'), unit=_(b'files'), total=numupdates
1425 _(b'updating'), unit=_(b'files'), total=numupdates
1426 )
1426 )
1427
1427
1428 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_REMOVE]:
1428 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_REMOVE]:
1429 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1429 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1430
1430
1431 # record path conflicts
1431 # record path conflicts
1432 for f, args, msg in mresult.getactions(
1432 for f, args, msg in mresult.getactions(
1433 [mergestatemod.ACTION_PATH_CONFLICT], sort=True
1433 [mergestatemod.ACTION_PATH_CONFLICT], sort=True
1434 ):
1434 ):
1435 f1, fo = args
1435 f1, fo = args
1436 s = repo.ui.status
1436 s = repo.ui.status
1437 s(
1437 s(
1438 _(
1438 _(
1439 b"%s: path conflict - a file or link has the same name as a "
1439 b"%s: path conflict - a file or link has the same name as a "
1440 b"directory\n"
1440 b"directory\n"
1441 )
1441 )
1442 % f
1442 % f
1443 )
1443 )
1444 if fo == b'l':
1444 if fo == b'l':
1445 s(_(b"the local file has been renamed to %s\n") % f1)
1445 s(_(b"the local file has been renamed to %s\n") % f1)
1446 else:
1446 else:
1447 s(_(b"the remote file has been renamed to %s\n") % f1)
1447 s(_(b"the remote file has been renamed to %s\n") % f1)
1448 s(_(b"resolve manually then use 'hg resolve --mark %s'\n") % f)
1448 s(_(b"resolve manually then use 'hg resolve --mark %s'\n") % f)
1449 ms.addpathconflict(f, f1, fo)
1449 ms.addpathconflict(f, f1, fo)
1450 progress.increment(item=f)
1450 progress.increment(item=f)
1451
1451
1452 # When merging in-memory, we can't support worker processes, so set the
1452 # When merging in-memory, we can't support worker processes, so set the
1453 # per-item cost at 0 in that case.
1453 # per-item cost at 0 in that case.
1454 cost = 0 if wctx.isinmemory() else 0.001
1454 cost = 0 if wctx.isinmemory() else 0.001
1455
1455
1456 # remove in parallel (must come before resolving path conflicts and getting)
1456 # remove in parallel (must come before resolving path conflicts and getting)
1457 prog = worker.worker(
1457 prog = worker.worker(
1458 repo.ui,
1458 repo.ui,
1459 cost,
1459 cost,
1460 batchremove,
1460 batchremove,
1461 (repo, wctx),
1461 (repo, wctx),
1462 list(mresult.getactions([mergestatemod.ACTION_REMOVE], sort=True)),
1462 list(mresult.getactions([mergestatemod.ACTION_REMOVE], sort=True)),
1463 )
1463 )
1464 for i, item in prog:
1464 for i, item in prog:
1465 progress.increment(step=i, item=item)
1465 progress.increment(step=i, item=item)
1466 removed = mresult.len((mergestatemod.ACTION_REMOVE,))
1466 removed = mresult.len((mergestatemod.ACTION_REMOVE,))
1467
1467
1468 # resolve path conflicts (must come before getting)
1468 # resolve path conflicts (must come before getting)
1469 for f, args, msg in mresult.getactions(
1469 for f, args, msg in mresult.getactions(
1470 [mergestatemod.ACTION_PATH_CONFLICT_RESOLVE], sort=True
1470 [mergestatemod.ACTION_PATH_CONFLICT_RESOLVE], sort=True
1471 ):
1471 ):
1472 repo.ui.debug(b" %s: %s -> pr\n" % (f, msg))
1472 repo.ui.debug(b" %s: %s -> pr\n" % (f, msg))
1473 (f0, origf0) = args
1473 (f0, origf0) = args
1474 if wctx[f0].lexists():
1474 if wctx[f0].lexists():
1475 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1475 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1476 wctx[f].audit()
1476 wctx[f].audit()
1477 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1477 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1478 wctx[f0].remove()
1478 wctx[f0].remove()
1479 progress.increment(item=f)
1479 progress.increment(item=f)
1480
1480
1481 # get in parallel.
1481 # get in parallel.
1482 threadsafe = repo.ui.configbool(
1482 threadsafe = repo.ui.configbool(
1483 b'experimental', b'worker.wdir-get-thread-safe'
1483 b'experimental', b'worker.wdir-get-thread-safe'
1484 )
1484 )
1485 prog = worker.worker(
1485 prog = worker.worker(
1486 repo.ui,
1486 repo.ui,
1487 cost,
1487 cost,
1488 batchget,
1488 batchget,
1489 (repo, mctx, wctx, wantfiledata),
1489 (repo, mctx, wctx, wantfiledata),
1490 list(mresult.getactions([mergestatemod.ACTION_GET], sort=True)),
1490 list(mresult.getactions([mergestatemod.ACTION_GET], sort=True)),
1491 threadsafe=threadsafe,
1491 threadsafe=threadsafe,
1492 hasretval=True,
1492 hasretval=True,
1493 )
1493 )
1494 getfiledata = {}
1494 getfiledata = {}
1495 for final, res in prog:
1495 for final, res in prog:
1496 if final:
1496 if final:
1497 getfiledata = res
1497 getfiledata = res
1498 else:
1498 else:
1499 i, item = res
1499 i, item = res
1500 progress.increment(step=i, item=item)
1500 progress.increment(step=i, item=item)
1501
1501
1502 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_GET]:
1502 if b'.hgsubstate' in mresult._actionmapping[mergestatemod.ACTION_GET]:
1503 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1503 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1504
1504
1505 # forget (manifest only, just log it) (must come first)
1505 # forget (manifest only, just log it) (must come first)
1506 for f, args, msg in mresult.getactions(
1506 for f, args, msg in mresult.getactions(
1507 (mergestatemod.ACTION_FORGET,), sort=True
1507 (mergestatemod.ACTION_FORGET,), sort=True
1508 ):
1508 ):
1509 repo.ui.debug(b" %s: %s -> f\n" % (f, msg))
1509 repo.ui.debug(b" %s: %s -> f\n" % (f, msg))
1510 progress.increment(item=f)
1510 progress.increment(item=f)
1511
1511
1512 # re-add (manifest only, just log it)
1512 # re-add (manifest only, just log it)
1513 for f, args, msg in mresult.getactions(
1513 for f, args, msg in mresult.getactions(
1514 (mergestatemod.ACTION_ADD,), sort=True
1514 (mergestatemod.ACTION_ADD,), sort=True
1515 ):
1515 ):
1516 repo.ui.debug(b" %s: %s -> a\n" % (f, msg))
1516 repo.ui.debug(b" %s: %s -> a\n" % (f, msg))
1517 progress.increment(item=f)
1517 progress.increment(item=f)
1518
1518
1519 # re-add/mark as modified (manifest only, just log it)
1519 # re-add/mark as modified (manifest only, just log it)
1520 for f, args, msg in mresult.getactions(
1520 for f, args, msg in mresult.getactions(
1521 (mergestatemod.ACTION_ADD_MODIFIED,), sort=True
1521 (mergestatemod.ACTION_ADD_MODIFIED,), sort=True
1522 ):
1522 ):
1523 repo.ui.debug(b" %s: %s -> am\n" % (f, msg))
1523 repo.ui.debug(b" %s: %s -> am\n" % (f, msg))
1524 progress.increment(item=f)
1524 progress.increment(item=f)
1525
1525
1526 # keep (noop, just log it)
1526 # keep (noop, just log it)
1527 for f, args, msg in mresult.getactions(
1527 for f, args, msg in mresult.getactions(
1528 (mergestatemod.ACTION_KEEP,), sort=True
1528 (mergestatemod.ACTION_KEEP,), sort=True
1529 ):
1529 ):
1530 repo.ui.debug(b" %s: %s -> k\n" % (f, msg))
1530 repo.ui.debug(b" %s: %s -> k\n" % (f, msg))
1531 # no progress
1531 # no progress
1532
1532
1533 # directory rename, move local
1533 # directory rename, move local
1534 for f, args, msg in mresult.getactions(
1534 for f, args, msg in mresult.getactions(
1535 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True
1535 (mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,), sort=True
1536 ):
1536 ):
1537 repo.ui.debug(b" %s: %s -> dm\n" % (f, msg))
1537 repo.ui.debug(b" %s: %s -> dm\n" % (f, msg))
1538 progress.increment(item=f)
1538 progress.increment(item=f)
1539 f0, flags = args
1539 f0, flags = args
1540 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1540 repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
1541 wctx[f].audit()
1541 wctx[f].audit()
1542 wctx[f].write(wctx.filectx(f0).data(), flags)
1542 wctx[f].write(wctx.filectx(f0).data(), flags)
1543 wctx[f0].remove()
1543 wctx[f0].remove()
1544
1544
1545 # local directory rename, get
1545 # local directory rename, get
1546 for f, args, msg in mresult.getactions(
1546 for f, args, msg in mresult.getactions(
1547 (mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,), sort=True
1547 (mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,), sort=True
1548 ):
1548 ):
1549 repo.ui.debug(b" %s: %s -> dg\n" % (f, msg))
1549 repo.ui.debug(b" %s: %s -> dg\n" % (f, msg))
1550 progress.increment(item=f)
1550 progress.increment(item=f)
1551 f0, flags = args
1551 f0, flags = args
1552 repo.ui.note(_(b"getting %s to %s\n") % (f0, f))
1552 repo.ui.note(_(b"getting %s to %s\n") % (f0, f))
1553 wctx[f].write(mctx.filectx(f0).data(), flags)
1553 wctx[f].write(mctx.filectx(f0).data(), flags)
1554
1554
1555 # exec
1555 # exec
1556 for f, args, msg in mresult.getactions(
1556 for f, args, msg in mresult.getactions(
1557 (mergestatemod.ACTION_EXEC,), sort=True
1557 (mergestatemod.ACTION_EXEC,), sort=True
1558 ):
1558 ):
1559 repo.ui.debug(b" %s: %s -> e\n" % (f, msg))
1559 repo.ui.debug(b" %s: %s -> e\n" % (f, msg))
1560 progress.increment(item=f)
1560 progress.increment(item=f)
1561 (flags,) = args
1561 (flags,) = args
1562 wctx[f].audit()
1562 wctx[f].audit()
1563 wctx[f].setflags(b'l' in flags, b'x' in flags)
1563 wctx[f].setflags(b'l' in flags, b'x' in flags)
1564
1564
1565 # these actions updates the file
1565 # these actions updates the file
1566 updated = mresult.len(
1566 updated = mresult.len(
1567 (
1567 (
1568 mergestatemod.ACTION_GET,
1568 mergestatemod.ACTION_GET,
1569 mergestatemod.ACTION_EXEC,
1569 mergestatemod.ACTION_EXEC,
1570 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1570 mergestatemod.ACTION_LOCAL_DIR_RENAME_GET,
1571 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
1571 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
1572 )
1572 )
1573 )
1573 )
1574 # the ordering is important here -- ms.mergedriver will raise if the merge
1574 # the ordering is important here -- ms.mergedriver will raise if the merge
1575 # driver has changed, and we want to be able to bypass it when overwrite is
1575 # driver has changed, and we want to be able to bypass it when overwrite is
1576 # True
1576 # True
1577 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1577 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1578
1578
1579 if usemergedriver:
1579 if usemergedriver:
1580 if wctx.isinmemory():
1580 if wctx.isinmemory():
1581 raise error.InMemoryMergeConflictsError(
1581 raise error.InMemoryMergeConflictsError(
1582 b"in-memory merge does not support mergedriver"
1582 b"in-memory merge does not support mergedriver"
1583 )
1583 )
1584 ms.commit()
1584 ms.commit()
1585 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1585 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1586 # the driver might leave some files unresolved
1586 # the driver might leave some files unresolved
1587 unresolvedf = set(ms.unresolved())
1587 unresolvedf = set(ms.unresolved())
1588 if not proceed:
1588 if not proceed:
1589 # XXX setting unresolved to at least 1 is a hack to make sure we
1589 # XXX setting unresolved to at least 1 is a hack to make sure we
1590 # error out
1590 # error out
1591 return updateresult(
1591 return updateresult(
1592 updated, merged, removed, max(len(unresolvedf), 1)
1592 updated, merged, removed, max(len(unresolvedf), 1)
1593 )
1593 )
1594 newactions = []
1594 newactions = []
1595 for f, args, msg in mergeactions:
1595 for f, args, msg in mergeactions:
1596 if f in unresolvedf:
1596 if f in unresolvedf:
1597 newactions.append((f, args, msg))
1597 newactions.append((f, args, msg))
1598 mergeactions = newactions
1598 mergeactions = newactions
1599
1599
1600 try:
1600 try:
1601 # premerge
1601 # premerge
1602 tocomplete = []
1602 tocomplete = []
1603 for f, args, msg in mergeactions:
1603 for f, args, msg in mergeactions:
1604 repo.ui.debug(b" %s: %s -> m (premerge)\n" % (f, msg))
1604 repo.ui.debug(b" %s: %s -> m (premerge)\n" % (f, msg))
1605 progress.increment(item=f)
1605 progress.increment(item=f)
1606 if f == b'.hgsubstate': # subrepo states need updating
1606 if f == b'.hgsubstate': # subrepo states need updating
1607 subrepoutil.submerge(
1607 subrepoutil.submerge(
1608 repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
1608 repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
1609 )
1609 )
1610 continue
1610 continue
1611 wctx[f].audit()
1611 wctx[f].audit()
1612 complete, r = ms.preresolve(f, wctx)
1612 complete, r = ms.preresolve(f, wctx)
1613 if not complete:
1613 if not complete:
1614 numupdates += 1
1614 numupdates += 1
1615 tocomplete.append((f, args, msg))
1615 tocomplete.append((f, args, msg))
1616
1616
1617 # merge
1617 # merge
1618 for f, args, msg in tocomplete:
1618 for f, args, msg in tocomplete:
1619 repo.ui.debug(b" %s: %s -> m (merge)\n" % (f, msg))
1619 repo.ui.debug(b" %s: %s -> m (merge)\n" % (f, msg))
1620 progress.increment(item=f, total=numupdates)
1620 progress.increment(item=f, total=numupdates)
1621 ms.resolve(f, wctx)
1621 ms.resolve(f, wctx)
1622
1622
1623 finally:
1623 finally:
1624 ms.commit()
1624 ms.commit()
1625
1625
1626 unresolved = ms.unresolvedcount()
1626 unresolved = ms.unresolvedcount()
1627
1627
1628 if (
1628 if (
1629 usemergedriver
1629 usemergedriver
1630 and not unresolved
1630 and not unresolved
1631 and ms.mdstate() != mergestatemod.MERGE_DRIVER_STATE_SUCCESS
1631 and ms.mdstate() != mergestatemod.MERGE_DRIVER_STATE_SUCCESS
1632 ):
1632 ):
1633 if not driverconclude(repo, ms, wctx, labels=labels):
1633 if not driverconclude(repo, ms, wctx, labels=labels):
1634 # XXX setting unresolved to at least 1 is a hack to make sure we
1634 # XXX setting unresolved to at least 1 is a hack to make sure we
1635 # error out
1635 # error out
1636 unresolved = max(unresolved, 1)
1636 unresolved = max(unresolved, 1)
1637
1637
1638 ms.commit()
1638 ms.commit()
1639
1639
1640 msupdated, msmerged, msremoved = ms.counts()
1640 msupdated, msmerged, msremoved = ms.counts()
1641 updated += msupdated
1641 updated += msupdated
1642 merged += msmerged
1642 merged += msmerged
1643 removed += msremoved
1643 removed += msremoved
1644
1644
1645 extraactions = ms.actions()
1645 extraactions = ms.actions()
1646 if extraactions:
1646 if extraactions:
1647 mfiles = {
1647 mfiles = {
1648 a[0] for a in mresult.getactions((mergestatemod.ACTION_MERGE,))
1648 a[0] for a in mresult.getactions((mergestatemod.ACTION_MERGE,))
1649 }
1649 }
1650 for k, acts in pycompat.iteritems(extraactions):
1650 for k, acts in pycompat.iteritems(extraactions):
1651 for a in acts:
1651 for a in acts:
1652 mresult.addfile(a[0], k, *a[1:])
1652 mresult.addfile(a[0], k, *a[1:])
1653 if k == mergestatemod.ACTION_GET and wantfiledata:
1653 if k == mergestatemod.ACTION_GET and wantfiledata:
1654 # no filedata until mergestate is updated to provide it
1654 # no filedata until mergestate is updated to provide it
1655 for a in acts:
1655 for a in acts:
1656 getfiledata[a[0]] = None
1656 getfiledata[a[0]] = None
1657 # Remove these files from actions[ACTION_MERGE] as well. This is
1657 # Remove these files from actions[ACTION_MERGE] as well. This is
1658 # important because in recordupdates, files in actions[ACTION_MERGE]
1658 # important because in recordupdates, files in actions[ACTION_MERGE]
1659 # are processed after files in other actions, and the merge driver
1659 # are processed after files in other actions, and the merge driver
1660 # might add files to those actions via extraactions above. This can
1660 # might add files to those actions via extraactions above. This can
1661 # lead to a file being recorded twice, with poor results. This is
1661 # lead to a file being recorded twice, with poor results. This is
1662 # especially problematic for actions[ACTION_REMOVE] (currently only
1662 # especially problematic for actions[ACTION_REMOVE] (currently only
1663 # possible with the merge driver in the initial merge process;
1663 # possible with the merge driver in the initial merge process;
1664 # interrupted merges don't go through this flow).
1664 # interrupted merges don't go through this flow).
1665 #
1665 #
1666 # The real fix here is to have indexes by both file and action so
1666 # The real fix here is to have indexes by both file and action so
1667 # that when the action for a file is changed it is automatically
1667 # that when the action for a file is changed it is automatically
1668 # reflected in the other action lists. But that involves a more
1668 # reflected in the other action lists. But that involves a more
1669 # complex data structure, so this will do for now.
1669 # complex data structure, so this will do for now.
1670 #
1670 #
1671 # We don't need to do the same operation for 'dc' and 'cd' because
1671 # We don't need to do the same operation for 'dc' and 'cd' because
1672 # those lists aren't consulted again.
1672 # those lists aren't consulted again.
1673 mfiles.difference_update(a[0] for a in acts)
1673 mfiles.difference_update(a[0] for a in acts)
1674
1674
1675 for a in list(mresult.getactions((mergestatemod.ACTION_MERGE,))):
1675 for a in list(mresult.getactions((mergestatemod.ACTION_MERGE,))):
1676 if a[0] not in mfiles:
1676 if a[0] not in mfiles:
1677 mresult.removefile(a[0])
1677 mresult.removefile(a[0])
1678
1678
1679 progress.complete()
1679 progress.complete()
1680 assert len(getfiledata) == (
1680 assert len(getfiledata) == (
1681 mresult.len((mergestatemod.ACTION_GET,)) if wantfiledata else 0
1681 mresult.len((mergestatemod.ACTION_GET,)) if wantfiledata else 0
1682 )
1682 )
1683 return updateresult(updated, merged, removed, unresolved), getfiledata
1683 return updateresult(updated, merged, removed, unresolved), getfiledata
1684
1684
1685
1685
1686 def _advertisefsmonitor(repo, num_gets, p1node):
1686 def _advertisefsmonitor(repo, num_gets, p1node):
1687 # Advertise fsmonitor when its presence could be useful.
1687 # Advertise fsmonitor when its presence could be useful.
1688 #
1688 #
1689 # We only advertise when performing an update from an empty working
1689 # We only advertise when performing an update from an empty working
1690 # directory. This typically only occurs during initial clone.
1690 # directory. This typically only occurs during initial clone.
1691 #
1691 #
1692 # We give users a mechanism to disable the warning in case it is
1692 # We give users a mechanism to disable the warning in case it is
1693 # annoying.
1693 # annoying.
1694 #
1694 #
1695 # We only allow on Linux and MacOS because that's where fsmonitor is
1695 # We only allow on Linux and MacOS because that's where fsmonitor is
1696 # considered stable.
1696 # considered stable.
1697 fsmonitorwarning = repo.ui.configbool(b'fsmonitor', b'warn_when_unused')
1697 fsmonitorwarning = repo.ui.configbool(b'fsmonitor', b'warn_when_unused')
1698 fsmonitorthreshold = repo.ui.configint(
1698 fsmonitorthreshold = repo.ui.configint(
1699 b'fsmonitor', b'warn_update_file_count'
1699 b'fsmonitor', b'warn_update_file_count'
1700 )
1700 )
1701 # avoid cycle dirstate -> sparse -> merge -> dirstate
1702 from . import dirstate
1703
1704 if dirstate.rustmod is not None:
1705 # When using rust status, fsmonitor becomes necessary at higher sizes
1706 fsmonitorthreshold = repo.ui.configint(
1707 b'fsmonitor', b'warn_update_file_count_rust',
1708 )
1709
1701 try:
1710 try:
1702 # avoid cycle: extensions -> cmdutil -> merge
1711 # avoid cycle: extensions -> cmdutil -> merge
1703 from . import extensions
1712 from . import extensions
1704
1713
1705 extensions.find(b'fsmonitor')
1714 extensions.find(b'fsmonitor')
1706 fsmonitorenabled = repo.ui.config(b'fsmonitor', b'mode') != b'off'
1715 fsmonitorenabled = repo.ui.config(b'fsmonitor', b'mode') != b'off'
1707 # We intentionally don't look at whether fsmonitor has disabled
1716 # We intentionally don't look at whether fsmonitor has disabled
1708 # itself because a) fsmonitor may have already printed a warning
1717 # itself because a) fsmonitor may have already printed a warning
1709 # b) we only care about the config state here.
1718 # b) we only care about the config state here.
1710 except KeyError:
1719 except KeyError:
1711 fsmonitorenabled = False
1720 fsmonitorenabled = False
1712
1721
1713 if (
1722 if (
1714 fsmonitorwarning
1723 fsmonitorwarning
1715 and not fsmonitorenabled
1724 and not fsmonitorenabled
1716 and p1node == nullid
1725 and p1node == nullid
1717 and num_gets >= fsmonitorthreshold
1726 and num_gets >= fsmonitorthreshold
1718 and pycompat.sysplatform.startswith((b'linux', b'darwin'))
1727 and pycompat.sysplatform.startswith((b'linux', b'darwin'))
1719 ):
1728 ):
1720 repo.ui.warn(
1729 repo.ui.warn(
1721 _(
1730 _(
1722 b'(warning: large working directory being used without '
1731 b'(warning: large working directory being used without '
1723 b'fsmonitor enabled; enable fsmonitor to improve performance; '
1732 b'fsmonitor enabled; enable fsmonitor to improve performance; '
1724 b'see "hg help -e fsmonitor")\n'
1733 b'see "hg help -e fsmonitor")\n'
1725 )
1734 )
1726 )
1735 )
1727
1736
1728
1737
1729 UPDATECHECK_ABORT = b'abort' # handled at higher layers
1738 UPDATECHECK_ABORT = b'abort' # handled at higher layers
1730 UPDATECHECK_NONE = b'none'
1739 UPDATECHECK_NONE = b'none'
1731 UPDATECHECK_LINEAR = b'linear'
1740 UPDATECHECK_LINEAR = b'linear'
1732 UPDATECHECK_NO_CONFLICT = b'noconflict'
1741 UPDATECHECK_NO_CONFLICT = b'noconflict'
1733
1742
1734
1743
1735 def update(
1744 def update(
1736 repo,
1745 repo,
1737 node,
1746 node,
1738 branchmerge,
1747 branchmerge,
1739 force,
1748 force,
1740 ancestor=None,
1749 ancestor=None,
1741 mergeancestor=False,
1750 mergeancestor=False,
1742 labels=None,
1751 labels=None,
1743 matcher=None,
1752 matcher=None,
1744 mergeforce=False,
1753 mergeforce=False,
1745 updatedirstate=True,
1754 updatedirstate=True,
1746 updatecheck=None,
1755 updatecheck=None,
1747 wc=None,
1756 wc=None,
1748 ):
1757 ):
1749 """
1758 """
1750 Perform a merge between the working directory and the given node
1759 Perform a merge between the working directory and the given node
1751
1760
1752 node = the node to update to
1761 node = the node to update to
1753 branchmerge = whether to merge between branches
1762 branchmerge = whether to merge between branches
1754 force = whether to force branch merging or file overwriting
1763 force = whether to force branch merging or file overwriting
1755 matcher = a matcher to filter file lists (dirstate not updated)
1764 matcher = a matcher to filter file lists (dirstate not updated)
1756 mergeancestor = whether it is merging with an ancestor. If true,
1765 mergeancestor = whether it is merging with an ancestor. If true,
1757 we should accept the incoming changes for any prompts that occur.
1766 we should accept the incoming changes for any prompts that occur.
1758 If false, merging with an ancestor (fast-forward) is only allowed
1767 If false, merging with an ancestor (fast-forward) is only allowed
1759 between different named branches. This flag is used by rebase extension
1768 between different named branches. This flag is used by rebase extension
1760 as a temporary fix and should be avoided in general.
1769 as a temporary fix and should be avoided in general.
1761 labels = labels to use for base, local and other
1770 labels = labels to use for base, local and other
1762 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1771 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1763 this is True, then 'force' should be True as well.
1772 this is True, then 'force' should be True as well.
1764
1773
1765 The table below shows all the behaviors of the update command given the
1774 The table below shows all the behaviors of the update command given the
1766 -c/--check and -C/--clean or no options, whether the working directory is
1775 -c/--check and -C/--clean or no options, whether the working directory is
1767 dirty, whether a revision is specified, and the relationship of the parent
1776 dirty, whether a revision is specified, and the relationship of the parent
1768 rev to the target rev (linear or not). Match from top first. The -n
1777 rev to the target rev (linear or not). Match from top first. The -n
1769 option doesn't exist on the command line, but represents the
1778 option doesn't exist on the command line, but represents the
1770 experimental.updatecheck=noconflict option.
1779 experimental.updatecheck=noconflict option.
1771
1780
1772 This logic is tested by test-update-branches.t.
1781 This logic is tested by test-update-branches.t.
1773
1782
1774 -c -C -n -m dirty rev linear | result
1783 -c -C -n -m dirty rev linear | result
1775 y y * * * * * | (1)
1784 y y * * * * * | (1)
1776 y * y * * * * | (1)
1785 y * y * * * * | (1)
1777 y * * y * * * | (1)
1786 y * * y * * * | (1)
1778 * y y * * * * | (1)
1787 * y y * * * * | (1)
1779 * y * y * * * | (1)
1788 * y * y * * * | (1)
1780 * * y y * * * | (1)
1789 * * y y * * * | (1)
1781 * * * * * n n | x
1790 * * * * * n n | x
1782 * * * * n * * | ok
1791 * * * * n * * | ok
1783 n n n n y * y | merge
1792 n n n n y * y | merge
1784 n n n n y y n | (2)
1793 n n n n y y n | (2)
1785 n n n y y * * | merge
1794 n n n y y * * | merge
1786 n n y n y * * | merge if no conflict
1795 n n y n y * * | merge if no conflict
1787 n y n n y * * | discard
1796 n y n n y * * | discard
1788 y n n n y * * | (3)
1797 y n n n y * * | (3)
1789
1798
1790 x = can't happen
1799 x = can't happen
1791 * = don't-care
1800 * = don't-care
1792 1 = incompatible options (checked in commands.py)
1801 1 = incompatible options (checked in commands.py)
1793 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1802 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1794 3 = abort: uncommitted changes (checked in commands.py)
1803 3 = abort: uncommitted changes (checked in commands.py)
1795
1804
1796 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1805 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1797 to repo[None] if None is passed.
1806 to repo[None] if None is passed.
1798
1807
1799 Return the same tuple as applyupdates().
1808 Return the same tuple as applyupdates().
1800 """
1809 """
1801 # Avoid cycle.
1810 # Avoid cycle.
1802 from . import sparse
1811 from . import sparse
1803
1812
1804 # This function used to find the default destination if node was None, but
1813 # This function used to find the default destination if node was None, but
1805 # that's now in destutil.py.
1814 # that's now in destutil.py.
1806 assert node is not None
1815 assert node is not None
1807 if not branchmerge and not force:
1816 if not branchmerge and not force:
1808 # TODO: remove the default once all callers that pass branchmerge=False
1817 # TODO: remove the default once all callers that pass branchmerge=False
1809 # and force=False pass a value for updatecheck. We may want to allow
1818 # and force=False pass a value for updatecheck. We may want to allow
1810 # updatecheck='abort' to better suppport some of these callers.
1819 # updatecheck='abort' to better suppport some of these callers.
1811 if updatecheck is None:
1820 if updatecheck is None:
1812 updatecheck = UPDATECHECK_LINEAR
1821 updatecheck = UPDATECHECK_LINEAR
1813 if updatecheck not in (
1822 if updatecheck not in (
1814 UPDATECHECK_NONE,
1823 UPDATECHECK_NONE,
1815 UPDATECHECK_LINEAR,
1824 UPDATECHECK_LINEAR,
1816 UPDATECHECK_NO_CONFLICT,
1825 UPDATECHECK_NO_CONFLICT,
1817 ):
1826 ):
1818 raise ValueError(
1827 raise ValueError(
1819 r'Invalid updatecheck %r (can accept %r)'
1828 r'Invalid updatecheck %r (can accept %r)'
1820 % (
1829 % (
1821 updatecheck,
1830 updatecheck,
1822 (
1831 (
1823 UPDATECHECK_NONE,
1832 UPDATECHECK_NONE,
1824 UPDATECHECK_LINEAR,
1833 UPDATECHECK_LINEAR,
1825 UPDATECHECK_NO_CONFLICT,
1834 UPDATECHECK_NO_CONFLICT,
1826 ),
1835 ),
1827 )
1836 )
1828 )
1837 )
1829 if wc is not None and wc.isinmemory():
1838 if wc is not None and wc.isinmemory():
1830 maybe_wlock = util.nullcontextmanager()
1839 maybe_wlock = util.nullcontextmanager()
1831 else:
1840 else:
1832 maybe_wlock = repo.wlock()
1841 maybe_wlock = repo.wlock()
1833 with maybe_wlock:
1842 with maybe_wlock:
1834 if wc is None:
1843 if wc is None:
1835 wc = repo[None]
1844 wc = repo[None]
1836 pl = wc.parents()
1845 pl = wc.parents()
1837 p1 = pl[0]
1846 p1 = pl[0]
1838 p2 = repo[node]
1847 p2 = repo[node]
1839 if ancestor is not None:
1848 if ancestor is not None:
1840 pas = [repo[ancestor]]
1849 pas = [repo[ancestor]]
1841 else:
1850 else:
1842 if repo.ui.configlist(b'merge', b'preferancestor') == [b'*']:
1851 if repo.ui.configlist(b'merge', b'preferancestor') == [b'*']:
1843 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1852 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1844 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1853 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1845 else:
1854 else:
1846 pas = [p1.ancestor(p2, warn=branchmerge)]
1855 pas = [p1.ancestor(p2, warn=branchmerge)]
1847
1856
1848 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1857 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1849
1858
1850 overwrite = force and not branchmerge
1859 overwrite = force and not branchmerge
1851 ### check phase
1860 ### check phase
1852 if not overwrite:
1861 if not overwrite:
1853 if len(pl) > 1:
1862 if len(pl) > 1:
1854 raise error.Abort(_(b"outstanding uncommitted merge"))
1863 raise error.Abort(_(b"outstanding uncommitted merge"))
1855 ms = mergestatemod.mergestate.read(repo)
1864 ms = mergestatemod.mergestate.read(repo)
1856 if list(ms.unresolved()):
1865 if list(ms.unresolved()):
1857 raise error.Abort(
1866 raise error.Abort(
1858 _(b"outstanding merge conflicts"),
1867 _(b"outstanding merge conflicts"),
1859 hint=_(b"use 'hg resolve' to resolve"),
1868 hint=_(b"use 'hg resolve' to resolve"),
1860 )
1869 )
1861 if branchmerge:
1870 if branchmerge:
1862 if pas == [p2]:
1871 if pas == [p2]:
1863 raise error.Abort(
1872 raise error.Abort(
1864 _(
1873 _(
1865 b"merging with a working directory ancestor"
1874 b"merging with a working directory ancestor"
1866 b" has no effect"
1875 b" has no effect"
1867 )
1876 )
1868 )
1877 )
1869 elif pas == [p1]:
1878 elif pas == [p1]:
1870 if not mergeancestor and wc.branch() == p2.branch():
1879 if not mergeancestor and wc.branch() == p2.branch():
1871 raise error.Abort(
1880 raise error.Abort(
1872 _(b"nothing to merge"),
1881 _(b"nothing to merge"),
1873 hint=_(b"use 'hg update' or check 'hg heads'"),
1882 hint=_(b"use 'hg update' or check 'hg heads'"),
1874 )
1883 )
1875 if not force and (wc.files() or wc.deleted()):
1884 if not force and (wc.files() or wc.deleted()):
1876 raise error.Abort(
1885 raise error.Abort(
1877 _(b"uncommitted changes"),
1886 _(b"uncommitted changes"),
1878 hint=_(b"use 'hg status' to list changes"),
1887 hint=_(b"use 'hg status' to list changes"),
1879 )
1888 )
1880 if not wc.isinmemory():
1889 if not wc.isinmemory():
1881 for s in sorted(wc.substate):
1890 for s in sorted(wc.substate):
1882 wc.sub(s).bailifchanged()
1891 wc.sub(s).bailifchanged()
1883
1892
1884 elif not overwrite:
1893 elif not overwrite:
1885 if p1 == p2: # no-op update
1894 if p1 == p2: # no-op update
1886 # call the hooks and exit early
1895 # call the hooks and exit early
1887 repo.hook(b'preupdate', throw=True, parent1=xp2, parent2=b'')
1896 repo.hook(b'preupdate', throw=True, parent1=xp2, parent2=b'')
1888 repo.hook(b'update', parent1=xp2, parent2=b'', error=0)
1897 repo.hook(b'update', parent1=xp2, parent2=b'', error=0)
1889 return updateresult(0, 0, 0, 0)
1898 return updateresult(0, 0, 0, 0)
1890
1899
1891 if updatecheck == UPDATECHECK_LINEAR and pas not in (
1900 if updatecheck == UPDATECHECK_LINEAR and pas not in (
1892 [p1],
1901 [p1],
1893 [p2],
1902 [p2],
1894 ): # nonlinear
1903 ): # nonlinear
1895 dirty = wc.dirty(missing=True)
1904 dirty = wc.dirty(missing=True)
1896 if dirty:
1905 if dirty:
1897 # Branching is a bit strange to ensure we do the minimal
1906 # Branching is a bit strange to ensure we do the minimal
1898 # amount of call to obsutil.foreground.
1907 # amount of call to obsutil.foreground.
1899 foreground = obsutil.foreground(repo, [p1.node()])
1908 foreground = obsutil.foreground(repo, [p1.node()])
1900 # note: the <node> variable contains a random identifier
1909 # note: the <node> variable contains a random identifier
1901 if repo[node].node() in foreground:
1910 if repo[node].node() in foreground:
1902 pass # allow updating to successors
1911 pass # allow updating to successors
1903 else:
1912 else:
1904 msg = _(b"uncommitted changes")
1913 msg = _(b"uncommitted changes")
1905 hint = _(b"commit or update --clean to discard changes")
1914 hint = _(b"commit or update --clean to discard changes")
1906 raise error.UpdateAbort(msg, hint=hint)
1915 raise error.UpdateAbort(msg, hint=hint)
1907 else:
1916 else:
1908 # Allow jumping branches if clean and specific rev given
1917 # Allow jumping branches if clean and specific rev given
1909 pass
1918 pass
1910
1919
1911 if overwrite:
1920 if overwrite:
1912 pas = [wc]
1921 pas = [wc]
1913 elif not branchmerge:
1922 elif not branchmerge:
1914 pas = [p1]
1923 pas = [p1]
1915
1924
1916 # deprecated config: merge.followcopies
1925 # deprecated config: merge.followcopies
1917 followcopies = repo.ui.configbool(b'merge', b'followcopies')
1926 followcopies = repo.ui.configbool(b'merge', b'followcopies')
1918 if overwrite:
1927 if overwrite:
1919 followcopies = False
1928 followcopies = False
1920 elif not pas[0]:
1929 elif not pas[0]:
1921 followcopies = False
1930 followcopies = False
1922 if not branchmerge and not wc.dirty(missing=True):
1931 if not branchmerge and not wc.dirty(missing=True):
1923 followcopies = False
1932 followcopies = False
1924
1933
1925 ### calculate phase
1934 ### calculate phase
1926 mresult = calculateupdates(
1935 mresult = calculateupdates(
1927 repo,
1936 repo,
1928 wc,
1937 wc,
1929 p2,
1938 p2,
1930 pas,
1939 pas,
1931 branchmerge,
1940 branchmerge,
1932 force,
1941 force,
1933 mergeancestor,
1942 mergeancestor,
1934 followcopies,
1943 followcopies,
1935 matcher=matcher,
1944 matcher=matcher,
1936 mergeforce=mergeforce,
1945 mergeforce=mergeforce,
1937 )
1946 )
1938
1947
1939 if updatecheck == UPDATECHECK_NO_CONFLICT:
1948 if updatecheck == UPDATECHECK_NO_CONFLICT:
1940 if mresult.hasconflicts():
1949 if mresult.hasconflicts():
1941 msg = _(b"conflicting changes")
1950 msg = _(b"conflicting changes")
1942 hint = _(b"commit or update --clean to discard changes")
1951 hint = _(b"commit or update --clean to discard changes")
1943 raise error.Abort(msg, hint=hint)
1952 raise error.Abort(msg, hint=hint)
1944
1953
1945 # Prompt and create actions. Most of this is in the resolve phase
1954 # Prompt and create actions. Most of this is in the resolve phase
1946 # already, but we can't handle .hgsubstate in filemerge or
1955 # already, but we can't handle .hgsubstate in filemerge or
1947 # subrepoutil.submerge yet so we have to keep prompting for it.
1956 # subrepoutil.submerge yet so we have to keep prompting for it.
1948 vals = mresult.getfile(b'.hgsubstate')
1957 vals = mresult.getfile(b'.hgsubstate')
1949 if vals:
1958 if vals:
1950 f = b'.hgsubstate'
1959 f = b'.hgsubstate'
1951 m, args, msg = vals
1960 m, args, msg = vals
1952 prompts = filemerge.partextras(labels)
1961 prompts = filemerge.partextras(labels)
1953 prompts[b'f'] = f
1962 prompts[b'f'] = f
1954 if m == mergestatemod.ACTION_CHANGED_DELETED:
1963 if m == mergestatemod.ACTION_CHANGED_DELETED:
1955 if repo.ui.promptchoice(
1964 if repo.ui.promptchoice(
1956 _(
1965 _(
1957 b"local%(l)s changed %(f)s which other%(o)s deleted\n"
1966 b"local%(l)s changed %(f)s which other%(o)s deleted\n"
1958 b"use (c)hanged version or (d)elete?"
1967 b"use (c)hanged version or (d)elete?"
1959 b"$$ &Changed $$ &Delete"
1968 b"$$ &Changed $$ &Delete"
1960 )
1969 )
1961 % prompts,
1970 % prompts,
1962 0,
1971 0,
1963 ):
1972 ):
1964 mresult.addfile(
1973 mresult.addfile(
1965 f, mergestatemod.ACTION_REMOVE, None, b'prompt delete',
1974 f, mergestatemod.ACTION_REMOVE, None, b'prompt delete',
1966 )
1975 )
1967 elif f in p1:
1976 elif f in p1:
1968 mresult.addfile(
1977 mresult.addfile(
1969 f,
1978 f,
1970 mergestatemod.ACTION_ADD_MODIFIED,
1979 mergestatemod.ACTION_ADD_MODIFIED,
1971 None,
1980 None,
1972 b'prompt keep',
1981 b'prompt keep',
1973 )
1982 )
1974 else:
1983 else:
1975 mresult.addfile(
1984 mresult.addfile(
1976 f, mergestatemod.ACTION_ADD, None, b'prompt keep',
1985 f, mergestatemod.ACTION_ADD, None, b'prompt keep',
1977 )
1986 )
1978 elif m == mergestatemod.ACTION_DELETED_CHANGED:
1987 elif m == mergestatemod.ACTION_DELETED_CHANGED:
1979 f1, f2, fa, move, anc = args
1988 f1, f2, fa, move, anc = args
1980 flags = p2[f2].flags()
1989 flags = p2[f2].flags()
1981 if (
1990 if (
1982 repo.ui.promptchoice(
1991 repo.ui.promptchoice(
1983 _(
1992 _(
1984 b"other%(o)s changed %(f)s which local%(l)s deleted\n"
1993 b"other%(o)s changed %(f)s which local%(l)s deleted\n"
1985 b"use (c)hanged version or leave (d)eleted?"
1994 b"use (c)hanged version or leave (d)eleted?"
1986 b"$$ &Changed $$ &Deleted"
1995 b"$$ &Changed $$ &Deleted"
1987 )
1996 )
1988 % prompts,
1997 % prompts,
1989 0,
1998 0,
1990 )
1999 )
1991 == 0
2000 == 0
1992 ):
2001 ):
1993 mresult.addfile(
2002 mresult.addfile(
1994 f,
2003 f,
1995 mergestatemod.ACTION_GET,
2004 mergestatemod.ACTION_GET,
1996 (flags, False),
2005 (flags, False),
1997 b'prompt recreating',
2006 b'prompt recreating',
1998 )
2007 )
1999 else:
2008 else:
2000 mresult.removefile(f)
2009 mresult.removefile(f)
2001
2010
2002 if not util.fscasesensitive(repo.path):
2011 if not util.fscasesensitive(repo.path):
2003 # check collision between files only in p2 for clean update
2012 # check collision between files only in p2 for clean update
2004 if not branchmerge and (
2013 if not branchmerge and (
2005 force or not wc.dirty(missing=True, branch=False)
2014 force or not wc.dirty(missing=True, branch=False)
2006 ):
2015 ):
2007 _checkcollision(repo, p2.manifest(), None)
2016 _checkcollision(repo, p2.manifest(), None)
2008 else:
2017 else:
2009 _checkcollision(repo, wc.manifest(), mresult)
2018 _checkcollision(repo, wc.manifest(), mresult)
2010
2019
2011 # divergent renames
2020 # divergent renames
2012 for f, fl in sorted(pycompat.iteritems(mresult.diverge)):
2021 for f, fl in sorted(pycompat.iteritems(mresult.diverge)):
2013 repo.ui.warn(
2022 repo.ui.warn(
2014 _(
2023 _(
2015 b"note: possible conflict - %s was renamed "
2024 b"note: possible conflict - %s was renamed "
2016 b"multiple times to:\n"
2025 b"multiple times to:\n"
2017 )
2026 )
2018 % f
2027 % f
2019 )
2028 )
2020 for nf in sorted(fl):
2029 for nf in sorted(fl):
2021 repo.ui.warn(b" %s\n" % nf)
2030 repo.ui.warn(b" %s\n" % nf)
2022
2031
2023 # rename and delete
2032 # rename and delete
2024 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)):
2033 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)):
2025 repo.ui.warn(
2034 repo.ui.warn(
2026 _(
2035 _(
2027 b"note: possible conflict - %s was deleted "
2036 b"note: possible conflict - %s was deleted "
2028 b"and renamed to:\n"
2037 b"and renamed to:\n"
2029 )
2038 )
2030 % f
2039 % f
2031 )
2040 )
2032 for nf in sorted(fl):
2041 for nf in sorted(fl):
2033 repo.ui.warn(b" %s\n" % nf)
2042 repo.ui.warn(b" %s\n" % nf)
2034
2043
2035 ### apply phase
2044 ### apply phase
2036 if not branchmerge: # just jump to the new rev
2045 if not branchmerge: # just jump to the new rev
2037 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, b''
2046 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, b''
2038 # If we're doing a partial update, we need to skip updating
2047 # If we're doing a partial update, we need to skip updating
2039 # the dirstate.
2048 # the dirstate.
2040 always = matcher is None or matcher.always()
2049 always = matcher is None or matcher.always()
2041 updatedirstate = updatedirstate and always and not wc.isinmemory()
2050 updatedirstate = updatedirstate and always and not wc.isinmemory()
2042 if updatedirstate:
2051 if updatedirstate:
2043 repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2)
2052 repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2)
2044 # note that we're in the middle of an update
2053 # note that we're in the middle of an update
2045 repo.vfs.write(b'updatestate', p2.hex())
2054 repo.vfs.write(b'updatestate', p2.hex())
2046
2055
2047 _advertisefsmonitor(
2056 _advertisefsmonitor(
2048 repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
2057 repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
2049 )
2058 )
2050
2059
2051 wantfiledata = updatedirstate and not branchmerge
2060 wantfiledata = updatedirstate and not branchmerge
2052 stats, getfiledata = applyupdates(
2061 stats, getfiledata = applyupdates(
2053 repo, mresult, wc, p2, overwrite, wantfiledata, labels=labels,
2062 repo, mresult, wc, p2, overwrite, wantfiledata, labels=labels,
2054 )
2063 )
2055
2064
2056 if updatedirstate:
2065 if updatedirstate:
2057 with repo.dirstate.parentchange():
2066 with repo.dirstate.parentchange():
2058 repo.setparents(fp1, fp2)
2067 repo.setparents(fp1, fp2)
2059 mergestatemod.recordupdates(
2068 mergestatemod.recordupdates(
2060 repo, mresult.actionsdict, branchmerge, getfiledata
2069 repo, mresult.actionsdict, branchmerge, getfiledata
2061 )
2070 )
2062 # update completed, clear state
2071 # update completed, clear state
2063 util.unlink(repo.vfs.join(b'updatestate'))
2072 util.unlink(repo.vfs.join(b'updatestate'))
2064
2073
2065 if not branchmerge:
2074 if not branchmerge:
2066 repo.dirstate.setbranch(p2.branch())
2075 repo.dirstate.setbranch(p2.branch())
2067
2076
2068 # If we're updating to a location, clean up any stale temporary includes
2077 # If we're updating to a location, clean up any stale temporary includes
2069 # (ex: this happens during hg rebase --abort).
2078 # (ex: this happens during hg rebase --abort).
2070 if not branchmerge:
2079 if not branchmerge:
2071 sparse.prunetemporaryincludes(repo)
2080 sparse.prunetemporaryincludes(repo)
2072
2081
2073 if updatedirstate:
2082 if updatedirstate:
2074 repo.hook(
2083 repo.hook(
2075 b'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
2084 b'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
2076 )
2085 )
2077 return stats
2086 return stats
2078
2087
2079
2088
2080 def merge(ctx, labels=None, force=False, wc=None):
2089 def merge(ctx, labels=None, force=False, wc=None):
2081 """Merge another topological branch into the working copy.
2090 """Merge another topological branch into the working copy.
2082
2091
2083 force = whether the merge was run with 'merge --force' (deprecated)
2092 force = whether the merge was run with 'merge --force' (deprecated)
2084 """
2093 """
2085
2094
2086 return update(
2095 return update(
2087 ctx.repo(),
2096 ctx.repo(),
2088 ctx.rev(),
2097 ctx.rev(),
2089 labels=labels,
2098 labels=labels,
2090 branchmerge=True,
2099 branchmerge=True,
2091 force=force,
2100 force=force,
2092 mergeforce=force,
2101 mergeforce=force,
2093 wc=wc,
2102 wc=wc,
2094 )
2103 )
2095
2104
2096
2105
2097 def clean_update(ctx, wc=None):
2106 def clean_update(ctx, wc=None):
2098 """Do a clean update to the given commit.
2107 """Do a clean update to the given commit.
2099
2108
2100 This involves updating to the commit and discarding any changes in the
2109 This involves updating to the commit and discarding any changes in the
2101 working copy.
2110 working copy.
2102 """
2111 """
2103 return update(ctx.repo(), ctx.rev(), branchmerge=False, force=True, wc=wc)
2112 return update(ctx.repo(), ctx.rev(), branchmerge=False, force=True, wc=wc)
2104
2113
2105
2114
2106 def revert_to(ctx, matcher=None, wc=None):
2115 def revert_to(ctx, matcher=None, wc=None):
2107 """Revert the working copy to the given commit.
2116 """Revert the working copy to the given commit.
2108
2117
2109 The working copy will keep its current parent(s) but its content will
2118 The working copy will keep its current parent(s) but its content will
2110 be the same as in the given commit.
2119 be the same as in the given commit.
2111 """
2120 """
2112
2121
2113 return update(
2122 return update(
2114 ctx.repo(),
2123 ctx.repo(),
2115 ctx.rev(),
2124 ctx.rev(),
2116 branchmerge=False,
2125 branchmerge=False,
2117 force=True,
2126 force=True,
2118 updatedirstate=False,
2127 updatedirstate=False,
2119 matcher=matcher,
2128 matcher=matcher,
2120 wc=wc,
2129 wc=wc,
2121 )
2130 )
2122
2131
2123
2132
2124 def graft(
2133 def graft(
2125 repo,
2134 repo,
2126 ctx,
2135 ctx,
2127 base=None,
2136 base=None,
2128 labels=None,
2137 labels=None,
2129 keepparent=False,
2138 keepparent=False,
2130 keepconflictparent=False,
2139 keepconflictparent=False,
2131 wctx=None,
2140 wctx=None,
2132 ):
2141 ):
2133 """Do a graft-like merge.
2142 """Do a graft-like merge.
2134
2143
2135 This is a merge where the merge ancestor is chosen such that one
2144 This is a merge where the merge ancestor is chosen such that one
2136 or more changesets are grafted onto the current changeset. In
2145 or more changesets are grafted onto the current changeset. In
2137 addition to the merge, this fixes up the dirstate to include only
2146 addition to the merge, this fixes up the dirstate to include only
2138 a single parent (if keepparent is False) and tries to duplicate any
2147 a single parent (if keepparent is False) and tries to duplicate any
2139 renames/copies appropriately.
2148 renames/copies appropriately.
2140
2149
2141 ctx - changeset to rebase
2150 ctx - changeset to rebase
2142 base - merge base, or ctx.p1() if not specified
2151 base - merge base, or ctx.p1() if not specified
2143 labels - merge labels eg ['local', 'graft']
2152 labels - merge labels eg ['local', 'graft']
2144 keepparent - keep second parent if any
2153 keepparent - keep second parent if any
2145 keepconflictparent - if unresolved, keep parent used for the merge
2154 keepconflictparent - if unresolved, keep parent used for the merge
2146
2155
2147 """
2156 """
2148 # If we're grafting a descendant onto an ancestor, be sure to pass
2157 # If we're grafting a descendant onto an ancestor, be sure to pass
2149 # mergeancestor=True to update. This does two things: 1) allows the merge if
2158 # mergeancestor=True to update. This does two things: 1) allows the merge if
2150 # the destination is the same as the parent of the ctx (so we can use graft
2159 # the destination is the same as the parent of the ctx (so we can use graft
2151 # to copy commits), and 2) informs update that the incoming changes are
2160 # to copy commits), and 2) informs update that the incoming changes are
2152 # newer than the destination so it doesn't prompt about "remote changed foo
2161 # newer than the destination so it doesn't prompt about "remote changed foo
2153 # which local deleted".
2162 # which local deleted".
2154 # We also pass mergeancestor=True when base is the same revision as p1. 2)
2163 # We also pass mergeancestor=True when base is the same revision as p1. 2)
2155 # doesn't matter as there can't possibly be conflicts, but 1) is necessary.
2164 # doesn't matter as there can't possibly be conflicts, but 1) is necessary.
2156 wctx = wctx or repo[None]
2165 wctx = wctx or repo[None]
2157 pctx = wctx.p1()
2166 pctx = wctx.p1()
2158 base = base or ctx.p1()
2167 base = base or ctx.p1()
2159 mergeancestor = (
2168 mergeancestor = (
2160 repo.changelog.isancestor(pctx.node(), ctx.node())
2169 repo.changelog.isancestor(pctx.node(), ctx.node())
2161 or pctx.rev() == base.rev()
2170 or pctx.rev() == base.rev()
2162 )
2171 )
2163
2172
2164 stats = update(
2173 stats = update(
2165 repo,
2174 repo,
2166 ctx.node(),
2175 ctx.node(),
2167 True,
2176 True,
2168 True,
2177 True,
2169 base.node(),
2178 base.node(),
2170 mergeancestor=mergeancestor,
2179 mergeancestor=mergeancestor,
2171 labels=labels,
2180 labels=labels,
2172 wc=wctx,
2181 wc=wctx,
2173 )
2182 )
2174
2183
2175 if keepconflictparent and stats.unresolvedcount:
2184 if keepconflictparent and stats.unresolvedcount:
2176 pother = ctx.node()
2185 pother = ctx.node()
2177 else:
2186 else:
2178 pother = nullid
2187 pother = nullid
2179 parents = ctx.parents()
2188 parents = ctx.parents()
2180 if keepparent and len(parents) == 2 and base in parents:
2189 if keepparent and len(parents) == 2 and base in parents:
2181 parents.remove(base)
2190 parents.remove(base)
2182 pother = parents[0].node()
2191 pother = parents[0].node()
2183 # Never set both parents equal to each other
2192 # Never set both parents equal to each other
2184 if pother == pctx.node():
2193 if pother == pctx.node():
2185 pother = nullid
2194 pother = nullid
2186
2195
2187 if wctx.isinmemory():
2196 if wctx.isinmemory():
2188 wctx.setparents(pctx.node(), pother)
2197 wctx.setparents(pctx.node(), pother)
2189 # fix up dirstate for copies and renames
2198 # fix up dirstate for copies and renames
2190 copies.graftcopies(wctx, ctx, base)
2199 copies.graftcopies(wctx, ctx, base)
2191 else:
2200 else:
2192 with repo.dirstate.parentchange():
2201 with repo.dirstate.parentchange():
2193 repo.setparents(pctx.node(), pother)
2202 repo.setparents(pctx.node(), pother)
2194 repo.dirstate.write(repo.currenttransaction())
2203 repo.dirstate.write(repo.currenttransaction())
2195 # fix up dirstate for copies and renames
2204 # fix up dirstate for copies and renames
2196 copies.graftcopies(wctx, ctx, base)
2205 copies.graftcopies(wctx, ctx, base)
2197 return stats
2206 return stats
2198
2207
2199
2208
2200 def purge(
2209 def purge(
2201 repo,
2210 repo,
2202 matcher,
2211 matcher,
2203 unknown=True,
2212 unknown=True,
2204 ignored=False,
2213 ignored=False,
2205 removeemptydirs=True,
2214 removeemptydirs=True,
2206 removefiles=True,
2215 removefiles=True,
2207 abortonerror=False,
2216 abortonerror=False,
2208 noop=False,
2217 noop=False,
2209 ):
2218 ):
2210 """Purge the working directory of untracked files.
2219 """Purge the working directory of untracked files.
2211
2220
2212 ``matcher`` is a matcher configured to scan the working directory -
2221 ``matcher`` is a matcher configured to scan the working directory -
2213 potentially a subset.
2222 potentially a subset.
2214
2223
2215 ``unknown`` controls whether unknown files should be purged.
2224 ``unknown`` controls whether unknown files should be purged.
2216
2225
2217 ``ignored`` controls whether ignored files should be purged.
2226 ``ignored`` controls whether ignored files should be purged.
2218
2227
2219 ``removeemptydirs`` controls whether empty directories should be removed.
2228 ``removeemptydirs`` controls whether empty directories should be removed.
2220
2229
2221 ``removefiles`` controls whether files are removed.
2230 ``removefiles`` controls whether files are removed.
2222
2231
2223 ``abortonerror`` causes an exception to be raised if an error occurs
2232 ``abortonerror`` causes an exception to be raised if an error occurs
2224 deleting a file or directory.
2233 deleting a file or directory.
2225
2234
2226 ``noop`` controls whether to actually remove files. If not defined, actions
2235 ``noop`` controls whether to actually remove files. If not defined, actions
2227 will be taken.
2236 will be taken.
2228
2237
2229 Returns an iterable of relative paths in the working directory that were
2238 Returns an iterable of relative paths in the working directory that were
2230 or would be removed.
2239 or would be removed.
2231 """
2240 """
2232
2241
2233 def remove(removefn, path):
2242 def remove(removefn, path):
2234 try:
2243 try:
2235 removefn(path)
2244 removefn(path)
2236 except OSError:
2245 except OSError:
2237 m = _(b'%s cannot be removed') % path
2246 m = _(b'%s cannot be removed') % path
2238 if abortonerror:
2247 if abortonerror:
2239 raise error.Abort(m)
2248 raise error.Abort(m)
2240 else:
2249 else:
2241 repo.ui.warn(_(b'warning: %s\n') % m)
2250 repo.ui.warn(_(b'warning: %s\n') % m)
2242
2251
2243 # There's no API to copy a matcher. So mutate the passed matcher and
2252 # There's no API to copy a matcher. So mutate the passed matcher and
2244 # restore it when we're done.
2253 # restore it when we're done.
2245 oldtraversedir = matcher.traversedir
2254 oldtraversedir = matcher.traversedir
2246
2255
2247 res = []
2256 res = []
2248
2257
2249 try:
2258 try:
2250 if removeemptydirs:
2259 if removeemptydirs:
2251 directories = []
2260 directories = []
2252 matcher.traversedir = directories.append
2261 matcher.traversedir = directories.append
2253
2262
2254 status = repo.status(match=matcher, ignored=ignored, unknown=unknown)
2263 status = repo.status(match=matcher, ignored=ignored, unknown=unknown)
2255
2264
2256 if removefiles:
2265 if removefiles:
2257 for f in sorted(status.unknown + status.ignored):
2266 for f in sorted(status.unknown + status.ignored):
2258 if not noop:
2267 if not noop:
2259 repo.ui.note(_(b'removing file %s\n') % f)
2268 repo.ui.note(_(b'removing file %s\n') % f)
2260 remove(repo.wvfs.unlink, f)
2269 remove(repo.wvfs.unlink, f)
2261 res.append(f)
2270 res.append(f)
2262
2271
2263 if removeemptydirs:
2272 if removeemptydirs:
2264 for f in sorted(directories, reverse=True):
2273 for f in sorted(directories, reverse=True):
2265 if matcher(f) and not repo.wvfs.listdir(f):
2274 if matcher(f) and not repo.wvfs.listdir(f):
2266 if not noop:
2275 if not noop:
2267 repo.ui.note(_(b'removing directory %s\n') % f)
2276 repo.ui.note(_(b'removing directory %s\n') % f)
2268 remove(repo.wvfs.rmdir, f)
2277 remove(repo.wvfs.rmdir, f)
2269 res.append(f)
2278 res.append(f)
2270
2279
2271 return res
2280 return res
2272
2281
2273 finally:
2282 finally:
2274 matcher.traversedir = oldtraversedir
2283 matcher.traversedir = oldtraversedir
@@ -1,1308 +1,1309 b''
1 #testcases sshv1 sshv2
1 #testcases sshv1 sshv2
2
2
3 #if sshv2
3 #if sshv2
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [experimental]
5 > [experimental]
6 > sshpeer.advertise-v2 = true
6 > sshpeer.advertise-v2 = true
7 > sshserver.support-v2 = true
7 > sshserver.support-v2 = true
8 > EOF
8 > EOF
9 #endif
9 #endif
10
10
11 Prepare repo a:
11 Prepare repo a:
12
12
13 $ hg init a
13 $ hg init a
14 $ cd a
14 $ cd a
15 $ echo a > a
15 $ echo a > a
16 $ hg add a
16 $ hg add a
17 $ hg commit -m test
17 $ hg commit -m test
18 $ echo first line > b
18 $ echo first line > b
19 $ hg add b
19 $ hg add b
20
20
21 Create a non-inlined filelog:
21 Create a non-inlined filelog:
22
22
23 $ "$PYTHON" -c 'open("data1", "wb").write(b"".join(b"%d\n" % x for x in range(10000)))'
23 $ "$PYTHON" -c 'open("data1", "wb").write(b"".join(b"%d\n" % x for x in range(10000)))'
24 $ for j in 0 1 2 3 4 5 6 7 8 9; do
24 $ for j in 0 1 2 3 4 5 6 7 8 9; do
25 > cat data1 >> b
25 > cat data1 >> b
26 > hg commit -m test
26 > hg commit -m test
27 > done
27 > done
28
28
29 List files in store/data (should show a 'b.d'):
29 List files in store/data (should show a 'b.d'):
30
30
31 #if reporevlogstore
31 #if reporevlogstore
32 $ for i in .hg/store/data/*; do
32 $ for i in .hg/store/data/*; do
33 > echo $i
33 > echo $i
34 > done
34 > done
35 .hg/store/data/a.i
35 .hg/store/data/a.i
36 .hg/store/data/b.d
36 .hg/store/data/b.d
37 .hg/store/data/b.i
37 .hg/store/data/b.i
38 #endif
38 #endif
39
39
40 Trigger branchcache creation:
40 Trigger branchcache creation:
41
41
42 $ hg branches
42 $ hg branches
43 default 10:a7949464abda
43 default 10:a7949464abda
44 $ ls .hg/cache
44 $ ls .hg/cache
45 branch2-served
45 branch2-served
46 rbc-names-v1
46 rbc-names-v1
47 rbc-revs-v1
47 rbc-revs-v1
48
48
49 Default operation:
49 Default operation:
50
50
51 $ hg clone . ../b
51 $ hg clone . ../b
52 updating to branch default
52 updating to branch default
53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 $ cd ../b
54 $ cd ../b
55
55
56 Ensure branchcache got copied over:
56 Ensure branchcache got copied over:
57
57
58 $ ls .hg/cache
58 $ ls .hg/cache
59 branch2-served
59 branch2-served
60 rbc-names-v1
60 rbc-names-v1
61 rbc-revs-v1
61 rbc-revs-v1
62
62
63 $ cat a
63 $ cat a
64 a
64 a
65 $ hg verify
65 $ hg verify
66 checking changesets
66 checking changesets
67 checking manifests
67 checking manifests
68 crosschecking files in changesets and manifests
68 crosschecking files in changesets and manifests
69 checking files
69 checking files
70 checked 11 changesets with 11 changes to 2 files
70 checked 11 changesets with 11 changes to 2 files
71
71
72 Invalid dest '' must abort:
72 Invalid dest '' must abort:
73
73
74 $ hg clone . ''
74 $ hg clone . ''
75 abort: empty destination path is not valid
75 abort: empty destination path is not valid
76 [255]
76 [255]
77
77
78 No update, with debug option:
78 No update, with debug option:
79
79
80 #if hardlink
80 #if hardlink
81 $ hg --debug clone -U . ../c --config progress.debug=true
81 $ hg --debug clone -U . ../c --config progress.debug=true
82 linking: 1 files
82 linking: 1 files
83 linking: 2 files
83 linking: 2 files
84 linking: 3 files
84 linking: 3 files
85 linking: 4 files
85 linking: 4 files
86 linking: 5 files
86 linking: 5 files
87 linking: 6 files
87 linking: 6 files
88 linking: 7 files
88 linking: 7 files
89 linking: 8 files
89 linking: 8 files
90 linked 8 files (reporevlogstore !)
90 linked 8 files (reporevlogstore !)
91 linking: 9 files (reposimplestore !)
91 linking: 9 files (reposimplestore !)
92 linking: 10 files (reposimplestore !)
92 linking: 10 files (reposimplestore !)
93 linking: 11 files (reposimplestore !)
93 linking: 11 files (reposimplestore !)
94 linking: 12 files (reposimplestore !)
94 linking: 12 files (reposimplestore !)
95 linking: 13 files (reposimplestore !)
95 linking: 13 files (reposimplestore !)
96 linking: 14 files (reposimplestore !)
96 linking: 14 files (reposimplestore !)
97 linking: 15 files (reposimplestore !)
97 linking: 15 files (reposimplestore !)
98 linking: 16 files (reposimplestore !)
98 linking: 16 files (reposimplestore !)
99 linking: 17 files (reposimplestore !)
99 linking: 17 files (reposimplestore !)
100 linking: 18 files (reposimplestore !)
100 linking: 18 files (reposimplestore !)
101 linked 18 files (reposimplestore !)
101 linked 18 files (reposimplestore !)
102 #else
102 #else
103 $ hg --debug clone -U . ../c --config progress.debug=true
103 $ hg --debug clone -U . ../c --config progress.debug=true
104 linking: 1 files
104 linking: 1 files
105 copying: 2 files
105 copying: 2 files
106 copying: 3 files
106 copying: 3 files
107 copying: 4 files
107 copying: 4 files
108 copying: 5 files
108 copying: 5 files
109 copying: 6 files
109 copying: 6 files
110 copying: 7 files
110 copying: 7 files
111 copying: 8 files
111 copying: 8 files
112 copied 8 files (reporevlogstore !)
112 copied 8 files (reporevlogstore !)
113 copying: 9 files (reposimplestore !)
113 copying: 9 files (reposimplestore !)
114 copying: 10 files (reposimplestore !)
114 copying: 10 files (reposimplestore !)
115 copying: 11 files (reposimplestore !)
115 copying: 11 files (reposimplestore !)
116 copying: 12 files (reposimplestore !)
116 copying: 12 files (reposimplestore !)
117 copying: 13 files (reposimplestore !)
117 copying: 13 files (reposimplestore !)
118 copying: 14 files (reposimplestore !)
118 copying: 14 files (reposimplestore !)
119 copying: 15 files (reposimplestore !)
119 copying: 15 files (reposimplestore !)
120 copying: 16 files (reposimplestore !)
120 copying: 16 files (reposimplestore !)
121 copying: 17 files (reposimplestore !)
121 copying: 17 files (reposimplestore !)
122 copying: 18 files (reposimplestore !)
122 copying: 18 files (reposimplestore !)
123 copied 18 files (reposimplestore !)
123 copied 18 files (reposimplestore !)
124 #endif
124 #endif
125 $ cd ../c
125 $ cd ../c
126
126
127 Ensure branchcache got copied over:
127 Ensure branchcache got copied over:
128
128
129 $ ls .hg/cache
129 $ ls .hg/cache
130 branch2-served
130 branch2-served
131 rbc-names-v1
131 rbc-names-v1
132 rbc-revs-v1
132 rbc-revs-v1
133
133
134 $ cat a 2>/dev/null || echo "a not present"
134 $ cat a 2>/dev/null || echo "a not present"
135 a not present
135 a not present
136 $ hg verify
136 $ hg verify
137 checking changesets
137 checking changesets
138 checking manifests
138 checking manifests
139 crosschecking files in changesets and manifests
139 crosschecking files in changesets and manifests
140 checking files
140 checking files
141 checked 11 changesets with 11 changes to 2 files
141 checked 11 changesets with 11 changes to 2 files
142
142
143 Default destination:
143 Default destination:
144
144
145 $ mkdir ../d
145 $ mkdir ../d
146 $ cd ../d
146 $ cd ../d
147 $ hg clone ../a
147 $ hg clone ../a
148 destination directory: a
148 destination directory: a
149 updating to branch default
149 updating to branch default
150 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 $ cd a
151 $ cd a
152 $ hg cat a
152 $ hg cat a
153 a
153 a
154 $ cd ../..
154 $ cd ../..
155
155
156 Check that we drop the 'file:' from the path before writing the .hgrc:
156 Check that we drop the 'file:' from the path before writing the .hgrc:
157
157
158 $ hg clone file:a e
158 $ hg clone file:a e
159 updating to branch default
159 updating to branch default
160 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 $ grep 'file:' e/.hg/hgrc
161 $ grep 'file:' e/.hg/hgrc
162 [1]
162 [1]
163
163
164 Check that path aliases are expanded:
164 Check that path aliases are expanded:
165
165
166 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
166 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
167 $ hg -R f showconfig paths.default
167 $ hg -R f showconfig paths.default
168 $TESTTMP/a#0
168 $TESTTMP/a#0
169
169
170 Use --pull:
170 Use --pull:
171
171
172 $ hg clone --pull a g
172 $ hg clone --pull a g
173 requesting all changes
173 requesting all changes
174 adding changesets
174 adding changesets
175 adding manifests
175 adding manifests
176 adding file changes
176 adding file changes
177 added 11 changesets with 11 changes to 2 files
177 added 11 changesets with 11 changes to 2 files
178 new changesets acb14030fe0a:a7949464abda
178 new changesets acb14030fe0a:a7949464abda
179 updating to branch default
179 updating to branch default
180 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
180 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 $ hg -R g verify
181 $ hg -R g verify
182 checking changesets
182 checking changesets
183 checking manifests
183 checking manifests
184 crosschecking files in changesets and manifests
184 crosschecking files in changesets and manifests
185 checking files
185 checking files
186 checked 11 changesets with 11 changes to 2 files
186 checked 11 changesets with 11 changes to 2 files
187
187
188 Invalid dest '' with --pull must abort (issue2528):
188 Invalid dest '' with --pull must abort (issue2528):
189
189
190 $ hg clone --pull a ''
190 $ hg clone --pull a ''
191 abort: empty destination path is not valid
191 abort: empty destination path is not valid
192 [255]
192 [255]
193
193
194 Clone to '.':
194 Clone to '.':
195
195
196 $ mkdir h
196 $ mkdir h
197 $ cd h
197 $ cd h
198 $ hg clone ../a .
198 $ hg clone ../a .
199 updating to branch default
199 updating to branch default
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 $ cd ..
201 $ cd ..
202
202
203
203
204 *** Tests for option -u ***
204 *** Tests for option -u ***
205
205
206 Adding some more history to repo a:
206 Adding some more history to repo a:
207
207
208 $ cd a
208 $ cd a
209 $ hg tag ref1
209 $ hg tag ref1
210 $ echo the quick brown fox >a
210 $ echo the quick brown fox >a
211 $ hg ci -m "hacked default"
211 $ hg ci -m "hacked default"
212 $ hg up ref1
212 $ hg up ref1
213 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
213 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
214 $ hg branch stable
214 $ hg branch stable
215 marked working directory as branch stable
215 marked working directory as branch stable
216 (branches are permanent and global, did you want a bookmark?)
216 (branches are permanent and global, did you want a bookmark?)
217 $ echo some text >a
217 $ echo some text >a
218 $ hg ci -m "starting branch stable"
218 $ hg ci -m "starting branch stable"
219 $ hg tag ref2
219 $ hg tag ref2
220 $ echo some more text >a
220 $ echo some more text >a
221 $ hg ci -m "another change for branch stable"
221 $ hg ci -m "another change for branch stable"
222 $ hg up ref2
222 $ hg up ref2
223 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
223 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
224 $ hg parents
224 $ hg parents
225 changeset: 13:e8ece76546a6
225 changeset: 13:e8ece76546a6
226 branch: stable
226 branch: stable
227 tag: ref2
227 tag: ref2
228 parent: 10:a7949464abda
228 parent: 10:a7949464abda
229 user: test
229 user: test
230 date: Thu Jan 01 00:00:00 1970 +0000
230 date: Thu Jan 01 00:00:00 1970 +0000
231 summary: starting branch stable
231 summary: starting branch stable
232
232
233
233
234 Repo a has two heads:
234 Repo a has two heads:
235
235
236 $ hg heads
236 $ hg heads
237 changeset: 15:0aae7cf88f0d
237 changeset: 15:0aae7cf88f0d
238 branch: stable
238 branch: stable
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:00 1970 +0000
241 date: Thu Jan 01 00:00:00 1970 +0000
242 summary: another change for branch stable
242 summary: another change for branch stable
243
243
244 changeset: 12:f21241060d6a
244 changeset: 12:f21241060d6a
245 user: test
245 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
246 date: Thu Jan 01 00:00:00 1970 +0000
247 summary: hacked default
247 summary: hacked default
248
248
249
249
250 $ cd ..
250 $ cd ..
251
251
252
252
253 Testing --noupdate with --updaterev (must abort):
253 Testing --noupdate with --updaterev (must abort):
254
254
255 $ hg clone --noupdate --updaterev 1 a ua
255 $ hg clone --noupdate --updaterev 1 a ua
256 abort: cannot specify both --noupdate and --updaterev
256 abort: cannot specify both --noupdate and --updaterev
257 [255]
257 [255]
258
258
259
259
260 Testing clone -u:
260 Testing clone -u:
261
261
262 $ hg clone -u . a ua
262 $ hg clone -u . a ua
263 updating to branch stable
263 updating to branch stable
264 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
265
265
266 Repo ua has both heads:
266 Repo ua has both heads:
267
267
268 $ hg -R ua heads
268 $ hg -R ua heads
269 changeset: 15:0aae7cf88f0d
269 changeset: 15:0aae7cf88f0d
270 branch: stable
270 branch: stable
271 tag: tip
271 tag: tip
272 user: test
272 user: test
273 date: Thu Jan 01 00:00:00 1970 +0000
273 date: Thu Jan 01 00:00:00 1970 +0000
274 summary: another change for branch stable
274 summary: another change for branch stable
275
275
276 changeset: 12:f21241060d6a
276 changeset: 12:f21241060d6a
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:00 1970 +0000
278 date: Thu Jan 01 00:00:00 1970 +0000
279 summary: hacked default
279 summary: hacked default
280
280
281
281
282 Same revision checked out in repo a and ua:
282 Same revision checked out in repo a and ua:
283
283
284 $ hg -R a parents --template "{node|short}\n"
284 $ hg -R a parents --template "{node|short}\n"
285 e8ece76546a6
285 e8ece76546a6
286 $ hg -R ua parents --template "{node|short}\n"
286 $ hg -R ua parents --template "{node|short}\n"
287 e8ece76546a6
287 e8ece76546a6
288
288
289 $ rm -r ua
289 $ rm -r ua
290
290
291
291
292 Testing clone --pull -u:
292 Testing clone --pull -u:
293
293
294 $ hg clone --pull -u . a ua
294 $ hg clone --pull -u . a ua
295 requesting all changes
295 requesting all changes
296 adding changesets
296 adding changesets
297 adding manifests
297 adding manifests
298 adding file changes
298 adding file changes
299 added 16 changesets with 16 changes to 3 files (+1 heads)
299 added 16 changesets with 16 changes to 3 files (+1 heads)
300 new changesets acb14030fe0a:0aae7cf88f0d
300 new changesets acb14030fe0a:0aae7cf88f0d
301 updating to branch stable
301 updating to branch stable
302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
303
303
304 Repo ua has both heads:
304 Repo ua has both heads:
305
305
306 $ hg -R ua heads
306 $ hg -R ua heads
307 changeset: 15:0aae7cf88f0d
307 changeset: 15:0aae7cf88f0d
308 branch: stable
308 branch: stable
309 tag: tip
309 tag: tip
310 user: test
310 user: test
311 date: Thu Jan 01 00:00:00 1970 +0000
311 date: Thu Jan 01 00:00:00 1970 +0000
312 summary: another change for branch stable
312 summary: another change for branch stable
313
313
314 changeset: 12:f21241060d6a
314 changeset: 12:f21241060d6a
315 user: test
315 user: test
316 date: Thu Jan 01 00:00:00 1970 +0000
316 date: Thu Jan 01 00:00:00 1970 +0000
317 summary: hacked default
317 summary: hacked default
318
318
319
319
320 Same revision checked out in repo a and ua:
320 Same revision checked out in repo a and ua:
321
321
322 $ hg -R a parents --template "{node|short}\n"
322 $ hg -R a parents --template "{node|short}\n"
323 e8ece76546a6
323 e8ece76546a6
324 $ hg -R ua parents --template "{node|short}\n"
324 $ hg -R ua parents --template "{node|short}\n"
325 e8ece76546a6
325 e8ece76546a6
326
326
327 $ rm -r ua
327 $ rm -r ua
328
328
329
329
330 Testing clone -u <branch>:
330 Testing clone -u <branch>:
331
331
332 $ hg clone -u stable a ua
332 $ hg clone -u stable a ua
333 updating to branch stable
333 updating to branch stable
334 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
335
335
336 Repo ua has both heads:
336 Repo ua has both heads:
337
337
338 $ hg -R ua heads
338 $ hg -R ua heads
339 changeset: 15:0aae7cf88f0d
339 changeset: 15:0aae7cf88f0d
340 branch: stable
340 branch: stable
341 tag: tip
341 tag: tip
342 user: test
342 user: test
343 date: Thu Jan 01 00:00:00 1970 +0000
343 date: Thu Jan 01 00:00:00 1970 +0000
344 summary: another change for branch stable
344 summary: another change for branch stable
345
345
346 changeset: 12:f21241060d6a
346 changeset: 12:f21241060d6a
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:00 1970 +0000
348 date: Thu Jan 01 00:00:00 1970 +0000
349 summary: hacked default
349 summary: hacked default
350
350
351
351
352 Branch 'stable' is checked out:
352 Branch 'stable' is checked out:
353
353
354 $ hg -R ua parents
354 $ hg -R ua parents
355 changeset: 15:0aae7cf88f0d
355 changeset: 15:0aae7cf88f0d
356 branch: stable
356 branch: stable
357 tag: tip
357 tag: tip
358 user: test
358 user: test
359 date: Thu Jan 01 00:00:00 1970 +0000
359 date: Thu Jan 01 00:00:00 1970 +0000
360 summary: another change for branch stable
360 summary: another change for branch stable
361
361
362
362
363 $ rm -r ua
363 $ rm -r ua
364
364
365
365
366 Testing default checkout:
366 Testing default checkout:
367
367
368 $ hg clone a ua
368 $ hg clone a ua
369 updating to branch default
369 updating to branch default
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
371
371
372 Repo ua has both heads:
372 Repo ua has both heads:
373
373
374 $ hg -R ua heads
374 $ hg -R ua heads
375 changeset: 15:0aae7cf88f0d
375 changeset: 15:0aae7cf88f0d
376 branch: stable
376 branch: stable
377 tag: tip
377 tag: tip
378 user: test
378 user: test
379 date: Thu Jan 01 00:00:00 1970 +0000
379 date: Thu Jan 01 00:00:00 1970 +0000
380 summary: another change for branch stable
380 summary: another change for branch stable
381
381
382 changeset: 12:f21241060d6a
382 changeset: 12:f21241060d6a
383 user: test
383 user: test
384 date: Thu Jan 01 00:00:00 1970 +0000
384 date: Thu Jan 01 00:00:00 1970 +0000
385 summary: hacked default
385 summary: hacked default
386
386
387
387
388 Branch 'default' is checked out:
388 Branch 'default' is checked out:
389
389
390 $ hg -R ua parents
390 $ hg -R ua parents
391 changeset: 12:f21241060d6a
391 changeset: 12:f21241060d6a
392 user: test
392 user: test
393 date: Thu Jan 01 00:00:00 1970 +0000
393 date: Thu Jan 01 00:00:00 1970 +0000
394 summary: hacked default
394 summary: hacked default
395
395
396 Test clone with a branch named "@" (issue3677)
396 Test clone with a branch named "@" (issue3677)
397
397
398 $ hg -R ua branch @
398 $ hg -R ua branch @
399 marked working directory as branch @
399 marked working directory as branch @
400 $ hg -R ua commit -m 'created branch @'
400 $ hg -R ua commit -m 'created branch @'
401 $ hg clone ua atbranch
401 $ hg clone ua atbranch
402 updating to branch default
402 updating to branch default
403 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 $ hg -R atbranch heads
404 $ hg -R atbranch heads
405 changeset: 16:798b6d97153e
405 changeset: 16:798b6d97153e
406 branch: @
406 branch: @
407 tag: tip
407 tag: tip
408 parent: 12:f21241060d6a
408 parent: 12:f21241060d6a
409 user: test
409 user: test
410 date: Thu Jan 01 00:00:00 1970 +0000
410 date: Thu Jan 01 00:00:00 1970 +0000
411 summary: created branch @
411 summary: created branch @
412
412
413 changeset: 15:0aae7cf88f0d
413 changeset: 15:0aae7cf88f0d
414 branch: stable
414 branch: stable
415 user: test
415 user: test
416 date: Thu Jan 01 00:00:00 1970 +0000
416 date: Thu Jan 01 00:00:00 1970 +0000
417 summary: another change for branch stable
417 summary: another change for branch stable
418
418
419 changeset: 12:f21241060d6a
419 changeset: 12:f21241060d6a
420 user: test
420 user: test
421 date: Thu Jan 01 00:00:00 1970 +0000
421 date: Thu Jan 01 00:00:00 1970 +0000
422 summary: hacked default
422 summary: hacked default
423
423
424 $ hg -R atbranch parents
424 $ hg -R atbranch parents
425 changeset: 12:f21241060d6a
425 changeset: 12:f21241060d6a
426 user: test
426 user: test
427 date: Thu Jan 01 00:00:00 1970 +0000
427 date: Thu Jan 01 00:00:00 1970 +0000
428 summary: hacked default
428 summary: hacked default
429
429
430
430
431 $ rm -r ua atbranch
431 $ rm -r ua atbranch
432
432
433
433
434 Testing #<branch>:
434 Testing #<branch>:
435
435
436 $ hg clone -u . a#stable ua
436 $ hg clone -u . a#stable ua
437 adding changesets
437 adding changesets
438 adding manifests
438 adding manifests
439 adding file changes
439 adding file changes
440 added 14 changesets with 14 changes to 3 files
440 added 14 changesets with 14 changes to 3 files
441 new changesets acb14030fe0a:0aae7cf88f0d
441 new changesets acb14030fe0a:0aae7cf88f0d
442 updating to branch stable
442 updating to branch stable
443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
444
444
445 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
445 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
446
446
447 $ hg -R ua heads
447 $ hg -R ua heads
448 changeset: 13:0aae7cf88f0d
448 changeset: 13:0aae7cf88f0d
449 branch: stable
449 branch: stable
450 tag: tip
450 tag: tip
451 user: test
451 user: test
452 date: Thu Jan 01 00:00:00 1970 +0000
452 date: Thu Jan 01 00:00:00 1970 +0000
453 summary: another change for branch stable
453 summary: another change for branch stable
454
454
455 changeset: 10:a7949464abda
455 changeset: 10:a7949464abda
456 user: test
456 user: test
457 date: Thu Jan 01 00:00:00 1970 +0000
457 date: Thu Jan 01 00:00:00 1970 +0000
458 summary: test
458 summary: test
459
459
460
460
461 Same revision checked out in repo a and ua:
461 Same revision checked out in repo a and ua:
462
462
463 $ hg -R a parents --template "{node|short}\n"
463 $ hg -R a parents --template "{node|short}\n"
464 e8ece76546a6
464 e8ece76546a6
465 $ hg -R ua parents --template "{node|short}\n"
465 $ hg -R ua parents --template "{node|short}\n"
466 e8ece76546a6
466 e8ece76546a6
467
467
468 $ rm -r ua
468 $ rm -r ua
469
469
470
470
471 Testing -u -r <branch>:
471 Testing -u -r <branch>:
472
472
473 $ hg clone -u . -r stable a ua
473 $ hg clone -u . -r stable a ua
474 adding changesets
474 adding changesets
475 adding manifests
475 adding manifests
476 adding file changes
476 adding file changes
477 added 14 changesets with 14 changes to 3 files
477 added 14 changesets with 14 changes to 3 files
478 new changesets acb14030fe0a:0aae7cf88f0d
478 new changesets acb14030fe0a:0aae7cf88f0d
479 updating to branch stable
479 updating to branch stable
480 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
481
481
482 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
482 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
483
483
484 $ hg -R ua heads
484 $ hg -R ua heads
485 changeset: 13:0aae7cf88f0d
485 changeset: 13:0aae7cf88f0d
486 branch: stable
486 branch: stable
487 tag: tip
487 tag: tip
488 user: test
488 user: test
489 date: Thu Jan 01 00:00:00 1970 +0000
489 date: Thu Jan 01 00:00:00 1970 +0000
490 summary: another change for branch stable
490 summary: another change for branch stable
491
491
492 changeset: 10:a7949464abda
492 changeset: 10:a7949464abda
493 user: test
493 user: test
494 date: Thu Jan 01 00:00:00 1970 +0000
494 date: Thu Jan 01 00:00:00 1970 +0000
495 summary: test
495 summary: test
496
496
497
497
498 Same revision checked out in repo a and ua:
498 Same revision checked out in repo a and ua:
499
499
500 $ hg -R a parents --template "{node|short}\n"
500 $ hg -R a parents --template "{node|short}\n"
501 e8ece76546a6
501 e8ece76546a6
502 $ hg -R ua parents --template "{node|short}\n"
502 $ hg -R ua parents --template "{node|short}\n"
503 e8ece76546a6
503 e8ece76546a6
504
504
505 $ rm -r ua
505 $ rm -r ua
506
506
507
507
508 Testing -r <branch>:
508 Testing -r <branch>:
509
509
510 $ hg clone -r stable a ua
510 $ hg clone -r stable a ua
511 adding changesets
511 adding changesets
512 adding manifests
512 adding manifests
513 adding file changes
513 adding file changes
514 added 14 changesets with 14 changes to 3 files
514 added 14 changesets with 14 changes to 3 files
515 new changesets acb14030fe0a:0aae7cf88f0d
515 new changesets acb14030fe0a:0aae7cf88f0d
516 updating to branch stable
516 updating to branch stable
517 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
518
518
519 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
519 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
520
520
521 $ hg -R ua heads
521 $ hg -R ua heads
522 changeset: 13:0aae7cf88f0d
522 changeset: 13:0aae7cf88f0d
523 branch: stable
523 branch: stable
524 tag: tip
524 tag: tip
525 user: test
525 user: test
526 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
527 summary: another change for branch stable
527 summary: another change for branch stable
528
528
529 changeset: 10:a7949464abda
529 changeset: 10:a7949464abda
530 user: test
530 user: test
531 date: Thu Jan 01 00:00:00 1970 +0000
531 date: Thu Jan 01 00:00:00 1970 +0000
532 summary: test
532 summary: test
533
533
534
534
535 Branch 'stable' is checked out:
535 Branch 'stable' is checked out:
536
536
537 $ hg -R ua parents
537 $ hg -R ua parents
538 changeset: 13:0aae7cf88f0d
538 changeset: 13:0aae7cf88f0d
539 branch: stable
539 branch: stable
540 tag: tip
540 tag: tip
541 user: test
541 user: test
542 date: Thu Jan 01 00:00:00 1970 +0000
542 date: Thu Jan 01 00:00:00 1970 +0000
543 summary: another change for branch stable
543 summary: another change for branch stable
544
544
545
545
546 $ rm -r ua
546 $ rm -r ua
547
547
548
548
549 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
549 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
550 iterable in addbranchrevs()
550 iterable in addbranchrevs()
551
551
552 $ cat <<EOF > simpleclone.py
552 $ cat <<EOF > simpleclone.py
553 > from mercurial import hg, ui as uimod
553 > from mercurial import hg, ui as uimod
554 > myui = uimod.ui.load()
554 > myui = uimod.ui.load()
555 > repo = hg.repository(myui, b'a')
555 > repo = hg.repository(myui, b'a')
556 > hg.clone(myui, {}, repo, dest=b"ua")
556 > hg.clone(myui, {}, repo, dest=b"ua")
557 > EOF
557 > EOF
558
558
559 $ "$PYTHON" simpleclone.py
559 $ "$PYTHON" simpleclone.py
560 updating to branch default
560 updating to branch default
561 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
562
562
563 $ rm -r ua
563 $ rm -r ua
564
564
565 $ cat <<EOF > branchclone.py
565 $ cat <<EOF > branchclone.py
566 > from mercurial import extensions, hg, ui as uimod
566 > from mercurial import extensions, hg, ui as uimod
567 > myui = uimod.ui.load()
567 > myui = uimod.ui.load()
568 > extensions.loadall(myui)
568 > extensions.loadall(myui)
569 > extensions.populateui(myui)
569 > extensions.populateui(myui)
570 > repo = hg.repository(myui, b'a')
570 > repo = hg.repository(myui, b'a')
571 > hg.clone(myui, {}, repo, dest=b"ua", branch=[b"stable"])
571 > hg.clone(myui, {}, repo, dest=b"ua", branch=[b"stable"])
572 > EOF
572 > EOF
573
573
574 $ "$PYTHON" branchclone.py
574 $ "$PYTHON" branchclone.py
575 adding changesets
575 adding changesets
576 adding manifests
576 adding manifests
577 adding file changes
577 adding file changes
578 added 14 changesets with 14 changes to 3 files
578 added 14 changesets with 14 changes to 3 files
579 new changesets acb14030fe0a:0aae7cf88f0d
579 new changesets acb14030fe0a:0aae7cf88f0d
580 updating to branch stable
580 updating to branch stable
581 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
581 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 $ rm -r ua
582 $ rm -r ua
583
583
584
584
585 Test clone with special '@' bookmark:
585 Test clone with special '@' bookmark:
586 $ cd a
586 $ cd a
587 $ hg bookmark -r a7949464abda @ # branch point of stable from default
587 $ hg bookmark -r a7949464abda @ # branch point of stable from default
588 $ hg clone . ../i
588 $ hg clone . ../i
589 updating to bookmark @
589 updating to bookmark @
590 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
591 $ hg id -i ../i
591 $ hg id -i ../i
592 a7949464abda
592 a7949464abda
593 $ rm -r ../i
593 $ rm -r ../i
594
594
595 $ hg bookmark -f -r stable @
595 $ hg bookmark -f -r stable @
596 $ hg bookmarks
596 $ hg bookmarks
597 @ 15:0aae7cf88f0d
597 @ 15:0aae7cf88f0d
598 $ hg clone . ../i
598 $ hg clone . ../i
599 updating to bookmark @ on branch stable
599 updating to bookmark @ on branch stable
600 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
601 $ hg id -i ../i
601 $ hg id -i ../i
602 0aae7cf88f0d
602 0aae7cf88f0d
603 $ cd "$TESTTMP"
603 $ cd "$TESTTMP"
604
604
605
605
606 Testing failures:
606 Testing failures:
607
607
608 $ mkdir fail
608 $ mkdir fail
609 $ cd fail
609 $ cd fail
610
610
611 No local source
611 No local source
612
612
613 $ hg clone a b
613 $ hg clone a b
614 abort: repository a not found!
614 abort: repository a not found!
615 [255]
615 [255]
616
616
617 Invalid URL
617 Invalid URL
618
618
619 $ hg clone http://invalid:url/a b
619 $ hg clone http://invalid:url/a b
620 abort: error: nonnumeric port: 'url'
620 abort: error: nonnumeric port: 'url'
621 [255]
621 [255]
622
622
623 No remote source
623 No remote source
624
624
625 #if windows
625 #if windows
626 $ hg clone http://$LOCALIP:3121/a b
626 $ hg clone http://$LOCALIP:3121/a b
627 abort: error: * (glob)
627 abort: error: * (glob)
628 [255]
628 [255]
629 #else
629 #else
630 $ hg clone http://$LOCALIP:3121/a b
630 $ hg clone http://$LOCALIP:3121/a b
631 abort: error: *refused* (glob)
631 abort: error: *refused* (glob)
632 [255]
632 [255]
633 #endif
633 #endif
634 $ rm -rf b # work around bug with http clone
634 $ rm -rf b # work around bug with http clone
635
635
636
636
637 #if unix-permissions no-root
637 #if unix-permissions no-root
638
638
639 Inaccessible source
639 Inaccessible source
640
640
641 $ mkdir a
641 $ mkdir a
642 $ chmod 000 a
642 $ chmod 000 a
643 $ hg clone a b
643 $ hg clone a b
644 abort: Permission denied: *$TESTTMP/fail/a/.hg* (glob)
644 abort: Permission denied: *$TESTTMP/fail/a/.hg* (glob)
645 [255]
645 [255]
646
646
647 Inaccessible destination
647 Inaccessible destination
648
648
649 $ hg init b
649 $ hg init b
650 $ cd b
650 $ cd b
651 $ hg clone . ../a
651 $ hg clone . ../a
652 abort: Permission denied: *../a* (glob)
652 abort: Permission denied: *../a* (glob)
653 [255]
653 [255]
654 $ cd ..
654 $ cd ..
655 $ chmod 700 a
655 $ chmod 700 a
656 $ rm -r a b
656 $ rm -r a b
657
657
658 #endif
658 #endif
659
659
660
660
661 #if fifo
661 #if fifo
662
662
663 Source of wrong type
663 Source of wrong type
664
664
665 $ mkfifo a
665 $ mkfifo a
666 $ hg clone a b
666 $ hg clone a b
667 abort: $ENOTDIR$: *$TESTTMP/fail/a/.hg* (glob)
667 abort: $ENOTDIR$: *$TESTTMP/fail/a/.hg* (glob)
668 [255]
668 [255]
669 $ rm a
669 $ rm a
670
670
671 #endif
671 #endif
672
672
673 Default destination, same directory
673 Default destination, same directory
674
674
675 $ hg init q
675 $ hg init q
676 $ hg clone q
676 $ hg clone q
677 destination directory: q
677 destination directory: q
678 abort: destination 'q' is not empty
678 abort: destination 'q' is not empty
679 [255]
679 [255]
680
680
681 destination directory not empty
681 destination directory not empty
682
682
683 $ mkdir a
683 $ mkdir a
684 $ echo stuff > a/a
684 $ echo stuff > a/a
685 $ hg clone q a
685 $ hg clone q a
686 abort: destination 'a' is not empty
686 abort: destination 'a' is not empty
687 [255]
687 [255]
688
688
689
689
690 #if unix-permissions no-root
690 #if unix-permissions no-root
691
691
692 leave existing directory in place after clone failure
692 leave existing directory in place after clone failure
693
693
694 $ hg init c
694 $ hg init c
695 $ cd c
695 $ cd c
696 $ echo c > c
696 $ echo c > c
697 $ hg commit -A -m test
697 $ hg commit -A -m test
698 adding c
698 adding c
699 $ chmod -rx .hg/store/data
699 $ chmod -rx .hg/store/data
700 $ cd ..
700 $ cd ..
701 $ mkdir d
701 $ mkdir d
702 $ hg clone c d 2> err
702 $ hg clone c d 2> err
703 [255]
703 [255]
704 $ test -d d
704 $ test -d d
705 $ test -d d/.hg
705 $ test -d d/.hg
706 [1]
706 [1]
707
707
708 re-enable perm to allow deletion
708 re-enable perm to allow deletion
709
709
710 $ chmod +rx c/.hg/store/data
710 $ chmod +rx c/.hg/store/data
711
711
712 #endif
712 #endif
713
713
714 $ cd ..
714 $ cd ..
715
715
716 Test clone from the repository in (emulated) revlog format 0 (issue4203):
716 Test clone from the repository in (emulated) revlog format 0 (issue4203):
717
717
718 $ mkdir issue4203
718 $ mkdir issue4203
719 $ mkdir -p src/.hg
719 $ mkdir -p src/.hg
720 $ echo foo > src/foo
720 $ echo foo > src/foo
721 $ hg -R src add src/foo
721 $ hg -R src add src/foo
722 $ hg -R src commit -m '#0'
722 $ hg -R src commit -m '#0'
723 $ hg -R src log -q
723 $ hg -R src log -q
724 0:e1bab28bca43
724 0:e1bab28bca43
725 $ hg -R src debugrevlog -c | egrep 'format|flags'
725 $ hg -R src debugrevlog -c | egrep 'format|flags'
726 format : 0
726 format : 0
727 flags : (none)
727 flags : (none)
728 $ hg root -R src -T json | sed 's|\\\\|\\|g'
728 $ hg root -R src -T json | sed 's|\\\\|\\|g'
729 [
729 [
730 {
730 {
731 "hgpath": "$TESTTMP/src/.hg",
731 "hgpath": "$TESTTMP/src/.hg",
732 "reporoot": "$TESTTMP/src",
732 "reporoot": "$TESTTMP/src",
733 "storepath": "$TESTTMP/src/.hg"
733 "storepath": "$TESTTMP/src/.hg"
734 }
734 }
735 ]
735 ]
736 $ hg clone -U -q src dst
736 $ hg clone -U -q src dst
737 $ hg -R dst log -q
737 $ hg -R dst log -q
738 0:e1bab28bca43
738 0:e1bab28bca43
739
739
740 Create repositories to test auto sharing functionality
740 Create repositories to test auto sharing functionality
741
741
742 $ cat >> $HGRCPATH << EOF
742 $ cat >> $HGRCPATH << EOF
743 > [extensions]
743 > [extensions]
744 > share=
744 > share=
745 > EOF
745 > EOF
746
746
747 $ hg init empty
747 $ hg init empty
748 $ hg init source1a
748 $ hg init source1a
749 $ cd source1a
749 $ cd source1a
750 $ echo initial1 > foo
750 $ echo initial1 > foo
751 $ hg -q commit -A -m initial
751 $ hg -q commit -A -m initial
752 $ echo second > foo
752 $ echo second > foo
753 $ hg commit -m second
753 $ hg commit -m second
754 $ cd ..
754 $ cd ..
755
755
756 $ hg init filteredrev0
756 $ hg init filteredrev0
757 $ cd filteredrev0
757 $ cd filteredrev0
758 $ cat >> .hg/hgrc << EOF
758 $ cat >> .hg/hgrc << EOF
759 > [experimental]
759 > [experimental]
760 > evolution.createmarkers=True
760 > evolution.createmarkers=True
761 > EOF
761 > EOF
762 $ echo initial1 > foo
762 $ echo initial1 > foo
763 $ hg -q commit -A -m initial0
763 $ hg -q commit -A -m initial0
764 $ hg -q up -r null
764 $ hg -q up -r null
765 $ echo initial2 > foo
765 $ echo initial2 > foo
766 $ hg -q commit -A -m initial1
766 $ hg -q commit -A -m initial1
767 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
767 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
768 1 new obsolescence markers
768 1 new obsolescence markers
769 obsoleted 1 changesets
769 obsoleted 1 changesets
770 $ cd ..
770 $ cd ..
771
771
772 $ hg -q clone --pull source1a source1b
772 $ hg -q clone --pull source1a source1b
773 $ cd source1a
773 $ cd source1a
774 $ hg bookmark bookA
774 $ hg bookmark bookA
775 $ echo 1a > foo
775 $ echo 1a > foo
776 $ hg commit -m 1a
776 $ hg commit -m 1a
777 $ cd ../source1b
777 $ cd ../source1b
778 $ hg -q up -r 0
778 $ hg -q up -r 0
779 $ echo head1 > foo
779 $ echo head1 > foo
780 $ hg commit -m head1
780 $ hg commit -m head1
781 created new head
781 created new head
782 $ hg bookmark head1
782 $ hg bookmark head1
783 $ hg -q up -r 0
783 $ hg -q up -r 0
784 $ echo head2 > foo
784 $ echo head2 > foo
785 $ hg commit -m head2
785 $ hg commit -m head2
786 created new head
786 created new head
787 $ hg bookmark head2
787 $ hg bookmark head2
788 $ hg -q up -r 0
788 $ hg -q up -r 0
789 $ hg branch branch1
789 $ hg branch branch1
790 marked working directory as branch branch1
790 marked working directory as branch branch1
791 (branches are permanent and global, did you want a bookmark?)
791 (branches are permanent and global, did you want a bookmark?)
792 $ echo branch1 > foo
792 $ echo branch1 > foo
793 $ hg commit -m branch1
793 $ hg commit -m branch1
794 $ hg -q up -r 0
794 $ hg -q up -r 0
795 $ hg branch branch2
795 $ hg branch branch2
796 marked working directory as branch branch2
796 marked working directory as branch branch2
797 $ echo branch2 > foo
797 $ echo branch2 > foo
798 $ hg commit -m branch2
798 $ hg commit -m branch2
799 $ cd ..
799 $ cd ..
800 $ hg init source2
800 $ hg init source2
801 $ cd source2
801 $ cd source2
802 $ echo initial2 > foo
802 $ echo initial2 > foo
803 $ hg -q commit -A -m initial2
803 $ hg -q commit -A -m initial2
804 $ echo second > foo
804 $ echo second > foo
805 $ hg commit -m second
805 $ hg commit -m second
806 $ cd ..
806 $ cd ..
807
807
808 Clone with auto share from an empty repo should not result in share
808 Clone with auto share from an empty repo should not result in share
809
809
810 $ mkdir share
810 $ mkdir share
811 $ hg --config share.pool=share clone empty share-empty
811 $ hg --config share.pool=share clone empty share-empty
812 (not using pooled storage: remote appears to be empty)
812 (not using pooled storage: remote appears to be empty)
813 updating to branch default
813 updating to branch default
814 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
814 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
815 $ ls share
815 $ ls share
816 $ test -d share-empty/.hg/store
816 $ test -d share-empty/.hg/store
817 $ test -f share-empty/.hg/sharedpath
817 $ test -f share-empty/.hg/sharedpath
818 [1]
818 [1]
819
819
820 Clone with auto share from a repo with filtered revision 0 should not result in share
820 Clone with auto share from a repo with filtered revision 0 should not result in share
821
821
822 $ hg --config share.pool=share clone filteredrev0 share-filtered
822 $ hg --config share.pool=share clone filteredrev0 share-filtered
823 (not using pooled storage: unable to resolve identity of remote)
823 (not using pooled storage: unable to resolve identity of remote)
824 requesting all changes
824 requesting all changes
825 adding changesets
825 adding changesets
826 adding manifests
826 adding manifests
827 adding file changes
827 adding file changes
828 added 1 changesets with 1 changes to 1 files
828 added 1 changesets with 1 changes to 1 files
829 new changesets e082c1832e09
829 new changesets e082c1832e09
830 updating to branch default
830 updating to branch default
831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
832
832
833 Clone from repo with content should result in shared store being created
833 Clone from repo with content should result in shared store being created
834
834
835 $ hg --config share.pool=share clone source1a share-dest1a
835 $ hg --config share.pool=share clone source1a share-dest1a
836 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
836 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
837 requesting all changes
837 requesting all changes
838 adding changesets
838 adding changesets
839 adding manifests
839 adding manifests
840 adding file changes
840 adding file changes
841 added 3 changesets with 3 changes to 1 files
841 added 3 changesets with 3 changes to 1 files
842 new changesets b5f04eac9d8f:e5bfe23c0b47
842 new changesets b5f04eac9d8f:e5bfe23c0b47
843 searching for changes
843 searching for changes
844 no changes found
844 no changes found
845 adding remote bookmark bookA
845 adding remote bookmark bookA
846 updating working directory
846 updating working directory
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848
848
849 The shared repo should have been created
849 The shared repo should have been created
850
850
851 $ ls share
851 $ ls share
852 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
852 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
853
853
854 The destination should point to it
854 The destination should point to it
855
855
856 $ cat share-dest1a/.hg/sharedpath; echo
856 $ cat share-dest1a/.hg/sharedpath; echo
857 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
857 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
858
858
859 The destination should have bookmarks
859 The destination should have bookmarks
860
860
861 $ hg -R share-dest1a bookmarks
861 $ hg -R share-dest1a bookmarks
862 bookA 2:e5bfe23c0b47
862 bookA 2:e5bfe23c0b47
863
863
864 The default path should be the remote, not the share
864 The default path should be the remote, not the share
865
865
866 $ hg -R share-dest1a config paths.default
866 $ hg -R share-dest1a config paths.default
867 $TESTTMP/source1a
867 $TESTTMP/source1a
868
868
869 Clone with existing share dir should result in pull + share
869 Clone with existing share dir should result in pull + share
870
870
871 $ hg --config share.pool=share clone source1b share-dest1b
871 $ hg --config share.pool=share clone source1b share-dest1b
872 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
872 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
873 searching for changes
873 searching for changes
874 adding changesets
874 adding changesets
875 adding manifests
875 adding manifests
876 adding file changes
876 adding file changes
877 adding remote bookmark head1
877 adding remote bookmark head1
878 adding remote bookmark head2
878 adding remote bookmark head2
879 added 4 changesets with 4 changes to 1 files (+4 heads)
879 added 4 changesets with 4 changes to 1 files (+4 heads)
880 new changesets 4a8dc1ab4c13:6bacf4683960
880 new changesets 4a8dc1ab4c13:6bacf4683960
881 updating working directory
881 updating working directory
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
883
883
884 $ ls share
884 $ ls share
885 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
885 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
886
886
887 $ cat share-dest1b/.hg/sharedpath; echo
887 $ cat share-dest1b/.hg/sharedpath; echo
888 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
888 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
889
889
890 We only get bookmarks from the remote, not everything in the share
890 We only get bookmarks from the remote, not everything in the share
891
891
892 $ hg -R share-dest1b bookmarks
892 $ hg -R share-dest1b bookmarks
893 head1 3:4a8dc1ab4c13
893 head1 3:4a8dc1ab4c13
894 head2 4:99f71071f117
894 head2 4:99f71071f117
895
895
896 Default path should be source, not share.
896 Default path should be source, not share.
897
897
898 $ hg -R share-dest1b config paths.default
898 $ hg -R share-dest1b config paths.default
899 $TESTTMP/source1b
899 $TESTTMP/source1b
900
900
901 Checked out revision should be head of default branch
901 Checked out revision should be head of default branch
902
902
903 $ hg -R share-dest1b log -r .
903 $ hg -R share-dest1b log -r .
904 changeset: 4:99f71071f117
904 changeset: 4:99f71071f117
905 bookmark: head2
905 bookmark: head2
906 parent: 0:b5f04eac9d8f
906 parent: 0:b5f04eac9d8f
907 user: test
907 user: test
908 date: Thu Jan 01 00:00:00 1970 +0000
908 date: Thu Jan 01 00:00:00 1970 +0000
909 summary: head2
909 summary: head2
910
910
911
911
912 Clone from unrelated repo should result in new share
912 Clone from unrelated repo should result in new share
913
913
914 $ hg --config share.pool=share clone source2 share-dest2
914 $ hg --config share.pool=share clone source2 share-dest2
915 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
915 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
916 requesting all changes
916 requesting all changes
917 adding changesets
917 adding changesets
918 adding manifests
918 adding manifests
919 adding file changes
919 adding file changes
920 added 2 changesets with 2 changes to 1 files
920 added 2 changesets with 2 changes to 1 files
921 new changesets 22aeff664783:63cf6c3dba4a
921 new changesets 22aeff664783:63cf6c3dba4a
922 searching for changes
922 searching for changes
923 no changes found
923 no changes found
924 updating working directory
924 updating working directory
925 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
925 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
926
926
927 $ ls share
927 $ ls share
928 22aeff664783fd44c6d9b435618173c118c3448e
928 22aeff664783fd44c6d9b435618173c118c3448e
929 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
929 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
930
930
931 remote naming mode works as advertised
931 remote naming mode works as advertised
932
932
933 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
933 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
934 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
934 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
935 requesting all changes
935 requesting all changes
936 adding changesets
936 adding changesets
937 adding manifests
937 adding manifests
938 adding file changes
938 adding file changes
939 added 3 changesets with 3 changes to 1 files
939 added 3 changesets with 3 changes to 1 files
940 new changesets b5f04eac9d8f:e5bfe23c0b47
940 new changesets b5f04eac9d8f:e5bfe23c0b47
941 searching for changes
941 searching for changes
942 no changes found
942 no changes found
943 adding remote bookmark bookA
943 adding remote bookmark bookA
944 updating working directory
944 updating working directory
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
946
946
947 $ ls shareremote
947 $ ls shareremote
948 195bb1fcdb595c14a6c13e0269129ed78f6debde
948 195bb1fcdb595c14a6c13e0269129ed78f6debde
949
949
950 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
950 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
951 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
951 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
952 requesting all changes
952 requesting all changes
953 adding changesets
953 adding changesets
954 adding manifests
954 adding manifests
955 adding file changes
955 adding file changes
956 added 6 changesets with 6 changes to 1 files (+4 heads)
956 added 6 changesets with 6 changes to 1 files (+4 heads)
957 new changesets b5f04eac9d8f:6bacf4683960
957 new changesets b5f04eac9d8f:6bacf4683960
958 searching for changes
958 searching for changes
959 no changes found
959 no changes found
960 adding remote bookmark head1
960 adding remote bookmark head1
961 adding remote bookmark head2
961 adding remote bookmark head2
962 updating working directory
962 updating working directory
963 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
963 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
964
964
965 $ ls shareremote
965 $ ls shareremote
966 195bb1fcdb595c14a6c13e0269129ed78f6debde
966 195bb1fcdb595c14a6c13e0269129ed78f6debde
967 c0d4f83847ca2a873741feb7048a45085fd47c46
967 c0d4f83847ca2a873741feb7048a45085fd47c46
968
968
969 request to clone a single revision is respected in sharing mode
969 request to clone a single revision is respected in sharing mode
970
970
971 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
971 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
972 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
972 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
973 adding changesets
973 adding changesets
974 adding manifests
974 adding manifests
975 adding file changes
975 adding file changes
976 added 2 changesets with 2 changes to 1 files
976 added 2 changesets with 2 changes to 1 files
977 new changesets b5f04eac9d8f:4a8dc1ab4c13
977 new changesets b5f04eac9d8f:4a8dc1ab4c13
978 no changes found
978 no changes found
979 adding remote bookmark head1
979 adding remote bookmark head1
980 updating working directory
980 updating working directory
981 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
981 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
982
982
983 $ hg -R share-1arev log -G
983 $ hg -R share-1arev log -G
984 @ changeset: 1:4a8dc1ab4c13
984 @ changeset: 1:4a8dc1ab4c13
985 | bookmark: head1
985 | bookmark: head1
986 | tag: tip
986 | tag: tip
987 | user: test
987 | user: test
988 | date: Thu Jan 01 00:00:00 1970 +0000
988 | date: Thu Jan 01 00:00:00 1970 +0000
989 | summary: head1
989 | summary: head1
990 |
990 |
991 o changeset: 0:b5f04eac9d8f
991 o changeset: 0:b5f04eac9d8f
992 user: test
992 user: test
993 date: Thu Jan 01 00:00:00 1970 +0000
993 date: Thu Jan 01 00:00:00 1970 +0000
994 summary: initial
994 summary: initial
995
995
996
996
997 making another clone should only pull down requested rev
997 making another clone should only pull down requested rev
998
998
999 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
999 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
1000 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1000 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1001 searching for changes
1001 searching for changes
1002 adding changesets
1002 adding changesets
1003 adding manifests
1003 adding manifests
1004 adding file changes
1004 adding file changes
1005 adding remote bookmark head1
1005 adding remote bookmark head1
1006 adding remote bookmark head2
1006 adding remote bookmark head2
1007 added 1 changesets with 1 changes to 1 files (+1 heads)
1007 added 1 changesets with 1 changes to 1 files (+1 heads)
1008 new changesets 99f71071f117
1008 new changesets 99f71071f117
1009 updating working directory
1009 updating working directory
1010 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1010 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1011
1011
1012 $ hg -R share-1brev log -G
1012 $ hg -R share-1brev log -G
1013 @ changeset: 2:99f71071f117
1013 @ changeset: 2:99f71071f117
1014 | bookmark: head2
1014 | bookmark: head2
1015 | tag: tip
1015 | tag: tip
1016 | parent: 0:b5f04eac9d8f
1016 | parent: 0:b5f04eac9d8f
1017 | user: test
1017 | user: test
1018 | date: Thu Jan 01 00:00:00 1970 +0000
1018 | date: Thu Jan 01 00:00:00 1970 +0000
1019 | summary: head2
1019 | summary: head2
1020 |
1020 |
1021 | o changeset: 1:4a8dc1ab4c13
1021 | o changeset: 1:4a8dc1ab4c13
1022 |/ bookmark: head1
1022 |/ bookmark: head1
1023 | user: test
1023 | user: test
1024 | date: Thu Jan 01 00:00:00 1970 +0000
1024 | date: Thu Jan 01 00:00:00 1970 +0000
1025 | summary: head1
1025 | summary: head1
1026 |
1026 |
1027 o changeset: 0:b5f04eac9d8f
1027 o changeset: 0:b5f04eac9d8f
1028 user: test
1028 user: test
1029 date: Thu Jan 01 00:00:00 1970 +0000
1029 date: Thu Jan 01 00:00:00 1970 +0000
1030 summary: initial
1030 summary: initial
1031
1031
1032
1032
1033 Request to clone a single branch is respected in sharing mode
1033 Request to clone a single branch is respected in sharing mode
1034
1034
1035 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
1035 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
1036 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1036 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1037 adding changesets
1037 adding changesets
1038 adding manifests
1038 adding manifests
1039 adding file changes
1039 adding file changes
1040 added 2 changesets with 2 changes to 1 files
1040 added 2 changesets with 2 changes to 1 files
1041 new changesets b5f04eac9d8f:5f92a6c1a1b1
1041 new changesets b5f04eac9d8f:5f92a6c1a1b1
1042 no changes found
1042 no changes found
1043 updating working directory
1043 updating working directory
1044 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1044 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1045
1045
1046 $ hg -R share-1bbranch1 log -G
1046 $ hg -R share-1bbranch1 log -G
1047 o changeset: 1:5f92a6c1a1b1
1047 o changeset: 1:5f92a6c1a1b1
1048 | branch: branch1
1048 | branch: branch1
1049 | tag: tip
1049 | tag: tip
1050 | user: test
1050 | user: test
1051 | date: Thu Jan 01 00:00:00 1970 +0000
1051 | date: Thu Jan 01 00:00:00 1970 +0000
1052 | summary: branch1
1052 | summary: branch1
1053 |
1053 |
1054 @ changeset: 0:b5f04eac9d8f
1054 @ changeset: 0:b5f04eac9d8f
1055 user: test
1055 user: test
1056 date: Thu Jan 01 00:00:00 1970 +0000
1056 date: Thu Jan 01 00:00:00 1970 +0000
1057 summary: initial
1057 summary: initial
1058
1058
1059
1059
1060 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
1060 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
1061 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1061 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1062 searching for changes
1062 searching for changes
1063 adding changesets
1063 adding changesets
1064 adding manifests
1064 adding manifests
1065 adding file changes
1065 adding file changes
1066 added 1 changesets with 1 changes to 1 files (+1 heads)
1066 added 1 changesets with 1 changes to 1 files (+1 heads)
1067 new changesets 6bacf4683960
1067 new changesets 6bacf4683960
1068 updating working directory
1068 updating working directory
1069 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1069 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1070
1070
1071 $ hg -R share-1bbranch2 log -G
1071 $ hg -R share-1bbranch2 log -G
1072 o changeset: 2:6bacf4683960
1072 o changeset: 2:6bacf4683960
1073 | branch: branch2
1073 | branch: branch2
1074 | tag: tip
1074 | tag: tip
1075 | parent: 0:b5f04eac9d8f
1075 | parent: 0:b5f04eac9d8f
1076 | user: test
1076 | user: test
1077 | date: Thu Jan 01 00:00:00 1970 +0000
1077 | date: Thu Jan 01 00:00:00 1970 +0000
1078 | summary: branch2
1078 | summary: branch2
1079 |
1079 |
1080 | o changeset: 1:5f92a6c1a1b1
1080 | o changeset: 1:5f92a6c1a1b1
1081 |/ branch: branch1
1081 |/ branch: branch1
1082 | user: test
1082 | user: test
1083 | date: Thu Jan 01 00:00:00 1970 +0000
1083 | date: Thu Jan 01 00:00:00 1970 +0000
1084 | summary: branch1
1084 | summary: branch1
1085 |
1085 |
1086 @ changeset: 0:b5f04eac9d8f
1086 @ changeset: 0:b5f04eac9d8f
1087 user: test
1087 user: test
1088 date: Thu Jan 01 00:00:00 1970 +0000
1088 date: Thu Jan 01 00:00:00 1970 +0000
1089 summary: initial
1089 summary: initial
1090
1090
1091
1091
1092 -U is respected in share clone mode
1092 -U is respected in share clone mode
1093
1093
1094 $ hg --config share.pool=share clone -U source1a share-1anowc
1094 $ hg --config share.pool=share clone -U source1a share-1anowc
1095 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1095 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1096 searching for changes
1096 searching for changes
1097 no changes found
1097 no changes found
1098 adding remote bookmark bookA
1098 adding remote bookmark bookA
1099
1099
1100 $ ls -A share-1anowc
1100 $ ls -A share-1anowc
1101 .hg
1101 .hg
1102
1102
1103 Test that auto sharing doesn't cause failure of "hg clone local remote"
1103 Test that auto sharing doesn't cause failure of "hg clone local remote"
1104
1104
1105 $ cd $TESTTMP
1105 $ cd $TESTTMP
1106 $ hg -R a id -r 0
1106 $ hg -R a id -r 0
1107 acb14030fe0a
1107 acb14030fe0a
1108 $ hg id -R remote -r 0
1108 $ hg id -R remote -r 0
1109 abort: repository remote not found!
1109 abort: repository remote not found!
1110 [255]
1110 [255]
1111 $ hg --config share.pool=share -q clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1111 $ hg --config share.pool=share -q clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1112 $ hg -R remote id -r 0
1112 $ hg -R remote id -r 0
1113 acb14030fe0a
1113 acb14030fe0a
1114
1114
1115 Cloning into pooled storage doesn't race (issue5104)
1115 Cloning into pooled storage doesn't race (issue5104)
1116
1116
1117 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1117 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1118 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1118 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1119 $ wait
1119 $ wait
1120
1120
1121 $ hg -R share-destrace1 log -r tip
1121 $ hg -R share-destrace1 log -r tip
1122 changeset: 2:e5bfe23c0b47
1122 changeset: 2:e5bfe23c0b47
1123 bookmark: bookA
1123 bookmark: bookA
1124 tag: tip
1124 tag: tip
1125 user: test
1125 user: test
1126 date: Thu Jan 01 00:00:00 1970 +0000
1126 date: Thu Jan 01 00:00:00 1970 +0000
1127 summary: 1a
1127 summary: 1a
1128
1128
1129
1129
1130 $ hg -R share-destrace2 log -r tip
1130 $ hg -R share-destrace2 log -r tip
1131 changeset: 2:e5bfe23c0b47
1131 changeset: 2:e5bfe23c0b47
1132 bookmark: bookA
1132 bookmark: bookA
1133 tag: tip
1133 tag: tip
1134 user: test
1134 user: test
1135 date: Thu Jan 01 00:00:00 1970 +0000
1135 date: Thu Jan 01 00:00:00 1970 +0000
1136 summary: 1a
1136 summary: 1a
1137
1137
1138 One repo should be new, the other should be shared from the pool. We
1138 One repo should be new, the other should be shared from the pool. We
1139 don't care which is which, so we just make sure we always print the
1139 don't care which is which, so we just make sure we always print the
1140 one containing "new pooled" first, then one one containing "existing
1140 one containing "new pooled" first, then one one containing "existing
1141 pooled".
1141 pooled".
1142
1142
1143 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1143 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1144 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1144 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1145 requesting all changes
1145 requesting all changes
1146 adding changesets
1146 adding changesets
1147 adding manifests
1147 adding manifests
1148 adding file changes
1148 adding file changes
1149 added 3 changesets with 3 changes to 1 files
1149 added 3 changesets with 3 changes to 1 files
1150 new changesets b5f04eac9d8f:e5bfe23c0b47
1150 new changesets b5f04eac9d8f:e5bfe23c0b47
1151 searching for changes
1151 searching for changes
1152 no changes found
1152 no changes found
1153 adding remote bookmark bookA
1153 adding remote bookmark bookA
1154 updating working directory
1154 updating working directory
1155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1156
1156
1157 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1157 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1158 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1158 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1159 searching for changes
1159 searching for changes
1160 no changes found
1160 no changes found
1161 adding remote bookmark bookA
1161 adding remote bookmark bookA
1162 updating working directory
1162 updating working directory
1163 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1163 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1164
1164
1165 SEC: check for unsafe ssh url
1165 SEC: check for unsafe ssh url
1166
1166
1167 $ cat >> $HGRCPATH << EOF
1167 $ cat >> $HGRCPATH << EOF
1168 > [ui]
1168 > [ui]
1169 > ssh = sh -c "read l; read l; read l"
1169 > ssh = sh -c "read l; read l; read l"
1170 > EOF
1170 > EOF
1171
1171
1172 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1172 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1173 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1173 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1174 [255]
1174 [255]
1175 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1175 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1176 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1176 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1177 [255]
1177 [255]
1178 $ hg clone 'ssh://fakehost|touch%20owned/path'
1178 $ hg clone 'ssh://fakehost|touch%20owned/path'
1179 abort: no suitable response from remote hg!
1179 abort: no suitable response from remote hg!
1180 [255]
1180 [255]
1181 $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
1181 $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
1182 abort: no suitable response from remote hg!
1182 abort: no suitable response from remote hg!
1183 [255]
1183 [255]
1184
1184
1185 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1185 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1186 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1186 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1187 [255]
1187 [255]
1188
1188
1189 #if windows
1189 #if windows
1190 $ hg clone "ssh://%26touch%20owned%20/" --debug
1190 $ hg clone "ssh://%26touch%20owned%20/" --debug
1191 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1191 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1192 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1192 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1193 sending hello command
1193 sending hello command
1194 sending between command
1194 sending between command
1195 abort: no suitable response from remote hg!
1195 abort: no suitable response from remote hg!
1196 [255]
1196 [255]
1197 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1197 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1198 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1198 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1199 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1199 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1200 sending hello command
1200 sending hello command
1201 sending between command
1201 sending between command
1202 abort: no suitable response from remote hg!
1202 abort: no suitable response from remote hg!
1203 [255]
1203 [255]
1204 #else
1204 #else
1205 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1205 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1206 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1206 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1207 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1207 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1208 sending hello command
1208 sending hello command
1209 sending between command
1209 sending between command
1210 abort: no suitable response from remote hg!
1210 abort: no suitable response from remote hg!
1211 [255]
1211 [255]
1212 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1212 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1213 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1213 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1214 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1214 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1215 sending hello command
1215 sending hello command
1216 sending between command
1216 sending between command
1217 abort: no suitable response from remote hg!
1217 abort: no suitable response from remote hg!
1218 [255]
1218 [255]
1219 #endif
1219 #endif
1220
1220
1221 $ hg clone "ssh://v-alid.example.com/" --debug
1221 $ hg clone "ssh://v-alid.example.com/" --debug
1222 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1222 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1223 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1223 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1224 sending hello command
1224 sending hello command
1225 sending between command
1225 sending between command
1226 abort: no suitable response from remote hg!
1226 abort: no suitable response from remote hg!
1227 [255]
1227 [255]
1228
1228
1229 We should not have created a file named owned - if it exists, the
1229 We should not have created a file named owned - if it exists, the
1230 attack succeeded.
1230 attack succeeded.
1231 $ if test -f owned; then echo 'you got owned'; fi
1231 $ if test -f owned; then echo 'you got owned'; fi
1232
1232
1233 Cloning without fsmonitor enabled does not print a warning for small repos
1233 Cloning without fsmonitor enabled does not print a warning for small repos
1234
1234
1235 $ hg clone a fsmonitor-default
1235 $ hg clone a fsmonitor-default
1236 updating to bookmark @ on branch stable
1236 updating to bookmark @ on branch stable
1237 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1237 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1238
1238
1239 Lower the warning threshold to simulate a large repo
1239 Lower the warning threshold to simulate a large repo
1240
1240
1241 $ cat >> $HGRCPATH << EOF
1241 $ cat >> $HGRCPATH << EOF
1242 > [fsmonitor]
1242 > [fsmonitor]
1243 > warn_update_file_count = 2
1243 > warn_update_file_count = 2
1244 > warn_update_file_count_rust = 2
1244 > EOF
1245 > EOF
1245
1246
1246 We should see a warning about no fsmonitor on supported platforms
1247 We should see a warning about no fsmonitor on supported platforms
1247
1248
1248 #if linuxormacos no-fsmonitor
1249 #if linuxormacos no-fsmonitor
1249 $ hg clone a nofsmonitor
1250 $ hg clone a nofsmonitor
1250 updating to bookmark @ on branch stable
1251 updating to bookmark @ on branch stable
1251 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1252 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1252 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1253 #else
1254 #else
1254 $ hg clone a nofsmonitor
1255 $ hg clone a nofsmonitor
1255 updating to bookmark @ on branch stable
1256 updating to bookmark @ on branch stable
1256 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257 #endif
1258 #endif
1258
1259
1259 We should not see warning about fsmonitor when it is enabled
1260 We should not see warning about fsmonitor when it is enabled
1260
1261
1261 #if fsmonitor
1262 #if fsmonitor
1262 $ hg clone a fsmonitor-enabled
1263 $ hg clone a fsmonitor-enabled
1263 updating to bookmark @ on branch stable
1264 updating to bookmark @ on branch stable
1264 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1265 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1265 #endif
1266 #endif
1266
1267
1267 We can disable the fsmonitor warning
1268 We can disable the fsmonitor warning
1268
1269
1269 $ hg --config fsmonitor.warn_when_unused=false clone a fsmonitor-disable-warning
1270 $ hg --config fsmonitor.warn_when_unused=false clone a fsmonitor-disable-warning
1270 updating to bookmark @ on branch stable
1271 updating to bookmark @ on branch stable
1271 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1272 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1272
1273
1273 Loaded fsmonitor but disabled in config should still print warning
1274 Loaded fsmonitor but disabled in config should still print warning
1274
1275
1275 #if linuxormacos fsmonitor
1276 #if linuxormacos fsmonitor
1276 $ hg --config fsmonitor.mode=off clone a fsmonitor-mode-off
1277 $ hg --config fsmonitor.mode=off clone a fsmonitor-mode-off
1277 updating to bookmark @ on branch stable
1278 updating to bookmark @ on branch stable
1278 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (fsmonitor !)
1279 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (fsmonitor !)
1279 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 #endif
1281 #endif
1281
1282
1282 Warning not printed if working directory isn't empty
1283 Warning not printed if working directory isn't empty
1283
1284
1284 $ hg -q clone a fsmonitor-update
1285 $ hg -q clone a fsmonitor-update
1285 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (?)
1286 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (?)
1286 $ cd fsmonitor-update
1287 $ cd fsmonitor-update
1287 $ hg up acb14030fe0a
1288 $ hg up acb14030fe0a
1288 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1289 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1289 (leaving bookmark @)
1290 (leaving bookmark @)
1290 $ hg up cf0fe1914066
1291 $ hg up cf0fe1914066
1291 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1292
1293
1293 `hg update` from null revision also prints
1294 `hg update` from null revision also prints
1294
1295
1295 $ hg up null
1296 $ hg up null
1296 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1297 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1297
1298
1298 #if linuxormacos no-fsmonitor
1299 #if linuxormacos no-fsmonitor
1299 $ hg up cf0fe1914066
1300 $ hg up cf0fe1914066
1300 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1301 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1301 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1302 #else
1303 #else
1303 $ hg up cf0fe1914066
1304 $ hg up cf0fe1914066
1304 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1305 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1305 #endif
1306 #endif
1306
1307
1307 $ cd ..
1308 $ cd ..
1308
1309
General Comments 0
You need to be logged in to leave comments. Login now