##// END OF EJS Templates
errors: set detailed exit code to 30 for config errors...
Martin von Zweigbergk -
r46445:bff71952 default
parent child Browse files
Show More
@@ -1,2306 +1,2308
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import glob
11 import glob
12 import os
12 import os
13 import posixpath
13 import posixpath
14 import re
14 import re
15 import subprocess
15 import subprocess
16 import weakref
16 import weakref
17
17
18 from .i18n import _
18 from .i18n import _
19 from .node import (
19 from .node import (
20 bin,
20 bin,
21 hex,
21 hex,
22 nullid,
22 nullid,
23 nullrev,
23 nullrev,
24 short,
24 short,
25 wdirid,
25 wdirid,
26 wdirrev,
26 wdirrev,
27 )
27 )
28 from .pycompat import getattr
28 from .pycompat import getattr
29 from .thirdparty import attr
29 from .thirdparty import attr
30 from . import (
30 from . import (
31 copies as copiesmod,
31 copies as copiesmod,
32 encoding,
32 encoding,
33 error,
33 error,
34 match as matchmod,
34 match as matchmod,
35 obsolete,
35 obsolete,
36 obsutil,
36 obsutil,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 policy,
39 policy,
40 pycompat,
40 pycompat,
41 requirements as requirementsmod,
41 requirements as requirementsmod,
42 revsetlang,
42 revsetlang,
43 similar,
43 similar,
44 smartset,
44 smartset,
45 url,
45 url,
46 util,
46 util,
47 vfs,
47 vfs,
48 )
48 )
49
49
50 from .utils import (
50 from .utils import (
51 hashutil,
51 hashutil,
52 procutil,
52 procutil,
53 stringutil,
53 stringutil,
54 )
54 )
55
55
56 if pycompat.iswindows:
56 if pycompat.iswindows:
57 from . import scmwindows as scmplatform
57 from . import scmwindows as scmplatform
58 else:
58 else:
59 from . import scmposix as scmplatform
59 from . import scmposix as scmplatform
60
60
61 parsers = policy.importmod('parsers')
61 parsers = policy.importmod('parsers')
62 rustrevlog = policy.importrust('revlog')
62 rustrevlog = policy.importrust('revlog')
63
63
64 termsize = scmplatform.termsize
64 termsize = scmplatform.termsize
65
65
66
66
67 @attr.s(slots=True, repr=False)
67 @attr.s(slots=True, repr=False)
68 class status(object):
68 class status(object):
69 '''Struct with a list of files per status.
69 '''Struct with a list of files per status.
70
70
71 The 'deleted', 'unknown' and 'ignored' properties are only
71 The 'deleted', 'unknown' and 'ignored' properties are only
72 relevant to the working copy.
72 relevant to the working copy.
73 '''
73 '''
74
74
75 modified = attr.ib(default=attr.Factory(list))
75 modified = attr.ib(default=attr.Factory(list))
76 added = attr.ib(default=attr.Factory(list))
76 added = attr.ib(default=attr.Factory(list))
77 removed = attr.ib(default=attr.Factory(list))
77 removed = attr.ib(default=attr.Factory(list))
78 deleted = attr.ib(default=attr.Factory(list))
78 deleted = attr.ib(default=attr.Factory(list))
79 unknown = attr.ib(default=attr.Factory(list))
79 unknown = attr.ib(default=attr.Factory(list))
80 ignored = attr.ib(default=attr.Factory(list))
80 ignored = attr.ib(default=attr.Factory(list))
81 clean = attr.ib(default=attr.Factory(list))
81 clean = attr.ib(default=attr.Factory(list))
82
82
83 def __iter__(self):
83 def __iter__(self):
84 yield self.modified
84 yield self.modified
85 yield self.added
85 yield self.added
86 yield self.removed
86 yield self.removed
87 yield self.deleted
87 yield self.deleted
88 yield self.unknown
88 yield self.unknown
89 yield self.ignored
89 yield self.ignored
90 yield self.clean
90 yield self.clean
91
91
92 def __repr__(self):
92 def __repr__(self):
93 return (
93 return (
94 r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
94 r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
95 r'unknown=%s, ignored=%s, clean=%s>'
95 r'unknown=%s, ignored=%s, clean=%s>'
96 ) % tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)
96 ) % tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)
97
97
98
98
99 def itersubrepos(ctx1, ctx2):
99 def itersubrepos(ctx1, ctx2):
100 """find subrepos in ctx1 or ctx2"""
100 """find subrepos in ctx1 or ctx2"""
101 # Create a (subpath, ctx) mapping where we prefer subpaths from
101 # Create a (subpath, ctx) mapping where we prefer subpaths from
102 # ctx1. The subpaths from ctx2 are important when the .hgsub file
102 # ctx1. The subpaths from ctx2 are important when the .hgsub file
103 # has been modified (in ctx2) but not yet committed (in ctx1).
103 # has been modified (in ctx2) but not yet committed (in ctx1).
104 subpaths = dict.fromkeys(ctx2.substate, ctx2)
104 subpaths = dict.fromkeys(ctx2.substate, ctx2)
105 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
105 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
106
106
107 missing = set()
107 missing = set()
108
108
109 for subpath in ctx2.substate:
109 for subpath in ctx2.substate:
110 if subpath not in ctx1.substate:
110 if subpath not in ctx1.substate:
111 del subpaths[subpath]
111 del subpaths[subpath]
112 missing.add(subpath)
112 missing.add(subpath)
113
113
114 for subpath, ctx in sorted(pycompat.iteritems(subpaths)):
114 for subpath, ctx in sorted(pycompat.iteritems(subpaths)):
115 yield subpath, ctx.sub(subpath)
115 yield subpath, ctx.sub(subpath)
116
116
117 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
117 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
118 # status and diff will have an accurate result when it does
118 # status and diff will have an accurate result when it does
119 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
119 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
120 # against itself.
120 # against itself.
121 for subpath in missing:
121 for subpath in missing:
122 yield subpath, ctx2.nullsub(subpath, ctx1)
122 yield subpath, ctx2.nullsub(subpath, ctx1)
123
123
124
124
125 def nochangesfound(ui, repo, excluded=None):
125 def nochangesfound(ui, repo, excluded=None):
126 '''Report no changes for push/pull, excluded is None or a list of
126 '''Report no changes for push/pull, excluded is None or a list of
127 nodes excluded from the push/pull.
127 nodes excluded from the push/pull.
128 '''
128 '''
129 secretlist = []
129 secretlist = []
130 if excluded:
130 if excluded:
131 for n in excluded:
131 for n in excluded:
132 ctx = repo[n]
132 ctx = repo[n]
133 if ctx.phase() >= phases.secret and not ctx.extinct():
133 if ctx.phase() >= phases.secret and not ctx.extinct():
134 secretlist.append(n)
134 secretlist.append(n)
135
135
136 if secretlist:
136 if secretlist:
137 ui.status(
137 ui.status(
138 _(b"no changes found (ignored %d secret changesets)\n")
138 _(b"no changes found (ignored %d secret changesets)\n")
139 % len(secretlist)
139 % len(secretlist)
140 )
140 )
141 else:
141 else:
142 ui.status(_(b"no changes found\n"))
142 ui.status(_(b"no changes found\n"))
143
143
144
144
145 def callcatch(ui, func):
145 def callcatch(ui, func):
146 """call func() with global exception handling
146 """call func() with global exception handling
147
147
148 return func() if no exception happens. otherwise do some error handling
148 return func() if no exception happens. otherwise do some error handling
149 and return an exit code accordingly. does not handle all exceptions.
149 and return an exit code accordingly. does not handle all exceptions.
150 """
150 """
151 coarse_exit_code = -1
151 coarse_exit_code = -1
152 detailed_exit_code = -1
152 detailed_exit_code = -1
153 try:
153 try:
154 try:
154 try:
155 return func()
155 return func()
156 except: # re-raises
156 except: # re-raises
157 ui.traceback()
157 ui.traceback()
158 raise
158 raise
159 # Global exception handling, alphabetically
159 # Global exception handling, alphabetically
160 # Mercurial-specific first, followed by built-in and library exceptions
160 # Mercurial-specific first, followed by built-in and library exceptions
161 except error.LockHeld as inst:
161 except error.LockHeld as inst:
162 detailed_exit_code = 20
162 detailed_exit_code = 20
163 if inst.errno == errno.ETIMEDOUT:
163 if inst.errno == errno.ETIMEDOUT:
164 reason = _(b'timed out waiting for lock held by %r') % (
164 reason = _(b'timed out waiting for lock held by %r') % (
165 pycompat.bytestr(inst.locker)
165 pycompat.bytestr(inst.locker)
166 )
166 )
167 else:
167 else:
168 reason = _(b'lock held by %r') % inst.locker
168 reason = _(b'lock held by %r') % inst.locker
169 ui.error(
169 ui.error(
170 _(b"abort: %s: %s\n")
170 _(b"abort: %s: %s\n")
171 % (inst.desc or stringutil.forcebytestr(inst.filename), reason)
171 % (inst.desc or stringutil.forcebytestr(inst.filename), reason)
172 )
172 )
173 if not inst.locker:
173 if not inst.locker:
174 ui.error(_(b"(lock might be very busy)\n"))
174 ui.error(_(b"(lock might be very busy)\n"))
175 except error.LockUnavailable as inst:
175 except error.LockUnavailable as inst:
176 detailed_exit_code = 20
176 detailed_exit_code = 20
177 ui.error(
177 ui.error(
178 _(b"abort: could not lock %s: %s\n")
178 _(b"abort: could not lock %s: %s\n")
179 % (
179 % (
180 inst.desc or stringutil.forcebytestr(inst.filename),
180 inst.desc or stringutil.forcebytestr(inst.filename),
181 encoding.strtolocal(inst.strerror),
181 encoding.strtolocal(inst.strerror),
182 )
182 )
183 )
183 )
184 except error.OutOfBandError as inst:
184 except error.OutOfBandError as inst:
185 detailed_exit_code = 100
185 detailed_exit_code = 100
186 if inst.args:
186 if inst.args:
187 msg = _(b"abort: remote error:\n")
187 msg = _(b"abort: remote error:\n")
188 else:
188 else:
189 msg = _(b"abort: remote error\n")
189 msg = _(b"abort: remote error\n")
190 ui.error(msg)
190 ui.error(msg)
191 if inst.args:
191 if inst.args:
192 ui.error(b''.join(inst.args))
192 ui.error(b''.join(inst.args))
193 if inst.hint:
193 if inst.hint:
194 ui.error(b'(%s)\n' % inst.hint)
194 ui.error(b'(%s)\n' % inst.hint)
195 except error.RepoError as inst:
195 except error.RepoError as inst:
196 ui.error(_(b"abort: %s!\n") % inst)
196 ui.error(_(b"abort: %s!\n") % inst)
197 if inst.hint:
197 if inst.hint:
198 ui.error(_(b"(%s)\n") % inst.hint)
198 ui.error(_(b"(%s)\n") % inst.hint)
199 except error.ResponseError as inst:
199 except error.ResponseError as inst:
200 ui.error(_(b"abort: %s") % inst.args[0])
200 ui.error(_(b"abort: %s") % inst.args[0])
201 msg = inst.args[1]
201 msg = inst.args[1]
202 if isinstance(msg, type(u'')):
202 if isinstance(msg, type(u'')):
203 msg = pycompat.sysbytes(msg)
203 msg = pycompat.sysbytes(msg)
204 if not isinstance(msg, bytes):
204 if not isinstance(msg, bytes):
205 ui.error(b" %r\n" % (msg,))
205 ui.error(b" %r\n" % (msg,))
206 elif not msg:
206 elif not msg:
207 ui.error(_(b" empty string\n"))
207 ui.error(_(b" empty string\n"))
208 else:
208 else:
209 ui.error(b"\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
209 ui.error(b"\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
210 except error.CensoredNodeError as inst:
210 except error.CensoredNodeError as inst:
211 ui.error(_(b"abort: file censored %s!\n") % inst)
211 ui.error(_(b"abort: file censored %s!\n") % inst)
212 except error.StorageError as inst:
212 except error.StorageError as inst:
213 ui.error(_(b"abort: %s!\n") % inst)
213 ui.error(_(b"abort: %s!\n") % inst)
214 if inst.hint:
214 if inst.hint:
215 ui.error(_(b"(%s)\n") % inst.hint)
215 ui.error(_(b"(%s)\n") % inst.hint)
216 except error.InterventionRequired as inst:
216 except error.InterventionRequired as inst:
217 ui.error(b"%s\n" % inst)
217 ui.error(b"%s\n" % inst)
218 if inst.hint:
218 if inst.hint:
219 ui.error(_(b"(%s)\n") % inst.hint)
219 ui.error(_(b"(%s)\n") % inst.hint)
220 detailed_exit_code = 240
220 detailed_exit_code = 240
221 coarse_exit_code = 1
221 coarse_exit_code = 1
222 except error.WdirUnsupported:
222 except error.WdirUnsupported:
223 ui.error(_(b"abort: working directory revision cannot be specified\n"))
223 ui.error(_(b"abort: working directory revision cannot be specified\n"))
224 except error.Abort as inst:
224 except error.Abort as inst:
225 if isinstance(inst, error.InputError):
225 if isinstance(inst, error.InputError):
226 detailed_exit_code = 10
226 detailed_exit_code = 10
227 elif isinstance(inst, error.StateError):
227 elif isinstance(inst, error.StateError):
228 detailed_exit_code = 20
228 detailed_exit_code = 20
229 elif isinstance(inst, error.ConfigError):
230 detailed_exit_code = 30
229 ui.error(_(b"abort: %s\n") % inst.message)
231 ui.error(_(b"abort: %s\n") % inst.message)
230 if inst.hint:
232 if inst.hint:
231 ui.error(_(b"(%s)\n") % inst.hint)
233 ui.error(_(b"(%s)\n") % inst.hint)
232 except error.WorkerError as inst:
234 except error.WorkerError as inst:
233 # Don't print a message -- the worker already should have
235 # Don't print a message -- the worker already should have
234 return inst.status_code
236 return inst.status_code
235 except ImportError as inst:
237 except ImportError as inst:
236 ui.error(_(b"abort: %s!\n") % stringutil.forcebytestr(inst))
238 ui.error(_(b"abort: %s!\n") % stringutil.forcebytestr(inst))
237 m = stringutil.forcebytestr(inst).split()[-1]
239 m = stringutil.forcebytestr(inst).split()[-1]
238 if m in b"mpatch bdiff".split():
240 if m in b"mpatch bdiff".split():
239 ui.error(_(b"(did you forget to compile extensions?)\n"))
241 ui.error(_(b"(did you forget to compile extensions?)\n"))
240 elif m in b"zlib".split():
242 elif m in b"zlib".split():
241 ui.error(_(b"(is your Python install correct?)\n"))
243 ui.error(_(b"(is your Python install correct?)\n"))
242 except util.urlerr.httperror as inst:
244 except util.urlerr.httperror as inst:
243 detailed_exit_code = 100
245 detailed_exit_code = 100
244 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
246 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
245 except util.urlerr.urlerror as inst:
247 except util.urlerr.urlerror as inst:
246 detailed_exit_code = 100
248 detailed_exit_code = 100
247 try: # usually it is in the form (errno, strerror)
249 try: # usually it is in the form (errno, strerror)
248 reason = inst.reason.args[1]
250 reason = inst.reason.args[1]
249 except (AttributeError, IndexError):
251 except (AttributeError, IndexError):
250 # it might be anything, for example a string
252 # it might be anything, for example a string
251 reason = inst.reason
253 reason = inst.reason
252 if isinstance(reason, pycompat.unicode):
254 if isinstance(reason, pycompat.unicode):
253 # SSLError of Python 2.7.9 contains a unicode
255 # SSLError of Python 2.7.9 contains a unicode
254 reason = encoding.unitolocal(reason)
256 reason = encoding.unitolocal(reason)
255 ui.error(_(b"abort: error: %s\n") % stringutil.forcebytestr(reason))
257 ui.error(_(b"abort: error: %s\n") % stringutil.forcebytestr(reason))
256 except (IOError, OSError) as inst:
258 except (IOError, OSError) as inst:
257 if (
259 if (
258 util.safehasattr(inst, b"args")
260 util.safehasattr(inst, b"args")
259 and inst.args
261 and inst.args
260 and inst.args[0] == errno.EPIPE
262 and inst.args[0] == errno.EPIPE
261 ):
263 ):
262 pass
264 pass
263 elif getattr(inst, "strerror", None): # common IOError or OSError
265 elif getattr(inst, "strerror", None): # common IOError or OSError
264 if getattr(inst, "filename", None) is not None:
266 if getattr(inst, "filename", None) is not None:
265 ui.error(
267 ui.error(
266 _(b"abort: %s: '%s'\n")
268 _(b"abort: %s: '%s'\n")
267 % (
269 % (
268 encoding.strtolocal(inst.strerror),
270 encoding.strtolocal(inst.strerror),
269 stringutil.forcebytestr(inst.filename),
271 stringutil.forcebytestr(inst.filename),
270 )
272 )
271 )
273 )
272 else:
274 else:
273 ui.error(_(b"abort: %s\n") % encoding.strtolocal(inst.strerror))
275 ui.error(_(b"abort: %s\n") % encoding.strtolocal(inst.strerror))
274 else: # suspicious IOError
276 else: # suspicious IOError
275 raise
277 raise
276 except MemoryError:
278 except MemoryError:
277 ui.error(_(b"abort: out of memory\n"))
279 ui.error(_(b"abort: out of memory\n"))
278 except SystemExit as inst:
280 except SystemExit as inst:
279 # Commands shouldn't sys.exit directly, but give a return code.
281 # Commands shouldn't sys.exit directly, but give a return code.
280 # Just in case catch this and and pass exit code to caller.
282 # Just in case catch this and and pass exit code to caller.
281 detailed_exit_code = 254
283 detailed_exit_code = 254
282 coarse_exit_code = inst.code
284 coarse_exit_code = inst.code
283
285
284 if ui.configbool(b'ui', b'detailed-exit-code'):
286 if ui.configbool(b'ui', b'detailed-exit-code'):
285 return detailed_exit_code
287 return detailed_exit_code
286 else:
288 else:
287 return coarse_exit_code
289 return coarse_exit_code
288
290
289
291
290 def checknewlabel(repo, lbl, kind):
292 def checknewlabel(repo, lbl, kind):
291 # Do not use the "kind" parameter in ui output.
293 # Do not use the "kind" parameter in ui output.
292 # It makes strings difficult to translate.
294 # It makes strings difficult to translate.
293 if lbl in [b'tip', b'.', b'null']:
295 if lbl in [b'tip', b'.', b'null']:
294 raise error.Abort(_(b"the name '%s' is reserved") % lbl)
296 raise error.Abort(_(b"the name '%s' is reserved") % lbl)
295 for c in (b':', b'\0', b'\n', b'\r'):
297 for c in (b':', b'\0', b'\n', b'\r'):
296 if c in lbl:
298 if c in lbl:
297 raise error.Abort(
299 raise error.Abort(
298 _(b"%r cannot be used in a name") % pycompat.bytestr(c)
300 _(b"%r cannot be used in a name") % pycompat.bytestr(c)
299 )
301 )
300 try:
302 try:
301 int(lbl)
303 int(lbl)
302 raise error.Abort(_(b"cannot use an integer as a name"))
304 raise error.Abort(_(b"cannot use an integer as a name"))
303 except ValueError:
305 except ValueError:
304 pass
306 pass
305 if lbl.strip() != lbl:
307 if lbl.strip() != lbl:
306 raise error.Abort(_(b"leading or trailing whitespace in name %r") % lbl)
308 raise error.Abort(_(b"leading or trailing whitespace in name %r") % lbl)
307
309
308
310
309 def checkfilename(f):
311 def checkfilename(f):
310 '''Check that the filename f is an acceptable filename for a tracked file'''
312 '''Check that the filename f is an acceptable filename for a tracked file'''
311 if b'\r' in f or b'\n' in f:
313 if b'\r' in f or b'\n' in f:
312 raise error.Abort(
314 raise error.Abort(
313 _(b"'\\n' and '\\r' disallowed in filenames: %r")
315 _(b"'\\n' and '\\r' disallowed in filenames: %r")
314 % pycompat.bytestr(f)
316 % pycompat.bytestr(f)
315 )
317 )
316
318
317
319
318 def checkportable(ui, f):
320 def checkportable(ui, f):
319 '''Check if filename f is portable and warn or abort depending on config'''
321 '''Check if filename f is portable and warn or abort depending on config'''
320 checkfilename(f)
322 checkfilename(f)
321 abort, warn = checkportabilityalert(ui)
323 abort, warn = checkportabilityalert(ui)
322 if abort or warn:
324 if abort or warn:
323 msg = util.checkwinfilename(f)
325 msg = util.checkwinfilename(f)
324 if msg:
326 if msg:
325 msg = b"%s: %s" % (msg, procutil.shellquote(f))
327 msg = b"%s: %s" % (msg, procutil.shellquote(f))
326 if abort:
328 if abort:
327 raise error.Abort(msg)
329 raise error.Abort(msg)
328 ui.warn(_(b"warning: %s\n") % msg)
330 ui.warn(_(b"warning: %s\n") % msg)
329
331
330
332
331 def checkportabilityalert(ui):
333 def checkportabilityalert(ui):
332 '''check if the user's config requests nothing, a warning, or abort for
334 '''check if the user's config requests nothing, a warning, or abort for
333 non-portable filenames'''
335 non-portable filenames'''
334 val = ui.config(b'ui', b'portablefilenames')
336 val = ui.config(b'ui', b'portablefilenames')
335 lval = val.lower()
337 lval = val.lower()
336 bval = stringutil.parsebool(val)
338 bval = stringutil.parsebool(val)
337 abort = pycompat.iswindows or lval == b'abort'
339 abort = pycompat.iswindows or lval == b'abort'
338 warn = bval or lval == b'warn'
340 warn = bval or lval == b'warn'
339 if bval is None and not (warn or abort or lval == b'ignore'):
341 if bval is None and not (warn or abort or lval == b'ignore'):
340 raise error.ConfigError(
342 raise error.ConfigError(
341 _(b"ui.portablefilenames value is invalid ('%s')") % val
343 _(b"ui.portablefilenames value is invalid ('%s')") % val
342 )
344 )
343 return abort, warn
345 return abort, warn
344
346
345
347
346 class casecollisionauditor(object):
348 class casecollisionauditor(object):
347 def __init__(self, ui, abort, dirstate):
349 def __init__(self, ui, abort, dirstate):
348 self._ui = ui
350 self._ui = ui
349 self._abort = abort
351 self._abort = abort
350 allfiles = b'\0'.join(dirstate)
352 allfiles = b'\0'.join(dirstate)
351 self._loweredfiles = set(encoding.lower(allfiles).split(b'\0'))
353 self._loweredfiles = set(encoding.lower(allfiles).split(b'\0'))
352 self._dirstate = dirstate
354 self._dirstate = dirstate
353 # The purpose of _newfiles is so that we don't complain about
355 # The purpose of _newfiles is so that we don't complain about
354 # case collisions if someone were to call this object with the
356 # case collisions if someone were to call this object with the
355 # same filename twice.
357 # same filename twice.
356 self._newfiles = set()
358 self._newfiles = set()
357
359
358 def __call__(self, f):
360 def __call__(self, f):
359 if f in self._newfiles:
361 if f in self._newfiles:
360 return
362 return
361 fl = encoding.lower(f)
363 fl = encoding.lower(f)
362 if fl in self._loweredfiles and f not in self._dirstate:
364 if fl in self._loweredfiles and f not in self._dirstate:
363 msg = _(b'possible case-folding collision for %s') % f
365 msg = _(b'possible case-folding collision for %s') % f
364 if self._abort:
366 if self._abort:
365 raise error.Abort(msg)
367 raise error.Abort(msg)
366 self._ui.warn(_(b"warning: %s\n") % msg)
368 self._ui.warn(_(b"warning: %s\n") % msg)
367 self._loweredfiles.add(fl)
369 self._loweredfiles.add(fl)
368 self._newfiles.add(f)
370 self._newfiles.add(f)
369
371
370
372
371 def filteredhash(repo, maxrev):
373 def filteredhash(repo, maxrev):
372 """build hash of filtered revisions in the current repoview.
374 """build hash of filtered revisions in the current repoview.
373
375
374 Multiple caches perform up-to-date validation by checking that the
376 Multiple caches perform up-to-date validation by checking that the
375 tiprev and tipnode stored in the cache file match the current repository.
377 tiprev and tipnode stored in the cache file match the current repository.
376 However, this is not sufficient for validating repoviews because the set
378 However, this is not sufficient for validating repoviews because the set
377 of revisions in the view may change without the repository tiprev and
379 of revisions in the view may change without the repository tiprev and
378 tipnode changing.
380 tipnode changing.
379
381
380 This function hashes all the revs filtered from the view and returns
382 This function hashes all the revs filtered from the view and returns
381 that SHA-1 digest.
383 that SHA-1 digest.
382 """
384 """
383 cl = repo.changelog
385 cl = repo.changelog
384 if not cl.filteredrevs:
386 if not cl.filteredrevs:
385 return None
387 return None
386 key = cl._filteredrevs_hashcache.get(maxrev)
388 key = cl._filteredrevs_hashcache.get(maxrev)
387 if not key:
389 if not key:
388 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
390 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
389 if revs:
391 if revs:
390 s = hashutil.sha1()
392 s = hashutil.sha1()
391 for rev in revs:
393 for rev in revs:
392 s.update(b'%d;' % rev)
394 s.update(b'%d;' % rev)
393 key = s.digest()
395 key = s.digest()
394 cl._filteredrevs_hashcache[maxrev] = key
396 cl._filteredrevs_hashcache[maxrev] = key
395 return key
397 return key
396
398
397
399
398 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
400 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
399 '''yield every hg repository under path, always recursively.
401 '''yield every hg repository under path, always recursively.
400 The recurse flag will only control recursion into repo working dirs'''
402 The recurse flag will only control recursion into repo working dirs'''
401
403
402 def errhandler(err):
404 def errhandler(err):
403 if err.filename == path:
405 if err.filename == path:
404 raise err
406 raise err
405
407
406 samestat = getattr(os.path, 'samestat', None)
408 samestat = getattr(os.path, 'samestat', None)
407 if followsym and samestat is not None:
409 if followsym and samestat is not None:
408
410
409 def adddir(dirlst, dirname):
411 def adddir(dirlst, dirname):
410 dirstat = os.stat(dirname)
412 dirstat = os.stat(dirname)
411 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
413 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
412 if not match:
414 if not match:
413 dirlst.append(dirstat)
415 dirlst.append(dirstat)
414 return not match
416 return not match
415
417
416 else:
418 else:
417 followsym = False
419 followsym = False
418
420
419 if (seen_dirs is None) and followsym:
421 if (seen_dirs is None) and followsym:
420 seen_dirs = []
422 seen_dirs = []
421 adddir(seen_dirs, path)
423 adddir(seen_dirs, path)
422 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
424 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
423 dirs.sort()
425 dirs.sort()
424 if b'.hg' in dirs:
426 if b'.hg' in dirs:
425 yield root # found a repository
427 yield root # found a repository
426 qroot = os.path.join(root, b'.hg', b'patches')
428 qroot = os.path.join(root, b'.hg', b'patches')
427 if os.path.isdir(os.path.join(qroot, b'.hg')):
429 if os.path.isdir(os.path.join(qroot, b'.hg')):
428 yield qroot # we have a patch queue repo here
430 yield qroot # we have a patch queue repo here
429 if recurse:
431 if recurse:
430 # avoid recursing inside the .hg directory
432 # avoid recursing inside the .hg directory
431 dirs.remove(b'.hg')
433 dirs.remove(b'.hg')
432 else:
434 else:
433 dirs[:] = [] # don't descend further
435 dirs[:] = [] # don't descend further
434 elif followsym:
436 elif followsym:
435 newdirs = []
437 newdirs = []
436 for d in dirs:
438 for d in dirs:
437 fname = os.path.join(root, d)
439 fname = os.path.join(root, d)
438 if adddir(seen_dirs, fname):
440 if adddir(seen_dirs, fname):
439 if os.path.islink(fname):
441 if os.path.islink(fname):
440 for hgname in walkrepos(fname, True, seen_dirs):
442 for hgname in walkrepos(fname, True, seen_dirs):
441 yield hgname
443 yield hgname
442 else:
444 else:
443 newdirs.append(d)
445 newdirs.append(d)
444 dirs[:] = newdirs
446 dirs[:] = newdirs
445
447
446
448
447 def binnode(ctx):
449 def binnode(ctx):
448 """Return binary node id for a given basectx"""
450 """Return binary node id for a given basectx"""
449 node = ctx.node()
451 node = ctx.node()
450 if node is None:
452 if node is None:
451 return wdirid
453 return wdirid
452 return node
454 return node
453
455
454
456
455 def intrev(ctx):
457 def intrev(ctx):
456 """Return integer for a given basectx that can be used in comparison or
458 """Return integer for a given basectx that can be used in comparison or
457 arithmetic operation"""
459 arithmetic operation"""
458 rev = ctx.rev()
460 rev = ctx.rev()
459 if rev is None:
461 if rev is None:
460 return wdirrev
462 return wdirrev
461 return rev
463 return rev
462
464
463
465
464 def formatchangeid(ctx):
466 def formatchangeid(ctx):
465 """Format changectx as '{rev}:{node|formatnode}', which is the default
467 """Format changectx as '{rev}:{node|formatnode}', which is the default
466 template provided by logcmdutil.changesettemplater"""
468 template provided by logcmdutil.changesettemplater"""
467 repo = ctx.repo()
469 repo = ctx.repo()
468 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
470 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
469
471
470
472
471 def formatrevnode(ui, rev, node):
473 def formatrevnode(ui, rev, node):
472 """Format given revision and node depending on the current verbosity"""
474 """Format given revision and node depending on the current verbosity"""
473 if ui.debugflag:
475 if ui.debugflag:
474 hexfunc = hex
476 hexfunc = hex
475 else:
477 else:
476 hexfunc = short
478 hexfunc = short
477 return b'%d:%s' % (rev, hexfunc(node))
479 return b'%d:%s' % (rev, hexfunc(node))
478
480
479
481
480 def resolvehexnodeidprefix(repo, prefix):
482 def resolvehexnodeidprefix(repo, prefix):
481 if prefix.startswith(b'x'):
483 if prefix.startswith(b'x'):
482 prefix = prefix[1:]
484 prefix = prefix[1:]
483 try:
485 try:
484 # Uses unfiltered repo because it's faster when prefix is ambiguous/
486 # Uses unfiltered repo because it's faster when prefix is ambiguous/
485 # This matches the shortesthexnodeidprefix() function below.
487 # This matches the shortesthexnodeidprefix() function below.
486 node = repo.unfiltered().changelog._partialmatch(prefix)
488 node = repo.unfiltered().changelog._partialmatch(prefix)
487 except error.AmbiguousPrefixLookupError:
489 except error.AmbiguousPrefixLookupError:
488 revset = repo.ui.config(
490 revset = repo.ui.config(
489 b'experimental', b'revisions.disambiguatewithin'
491 b'experimental', b'revisions.disambiguatewithin'
490 )
492 )
491 if revset:
493 if revset:
492 # Clear config to avoid infinite recursion
494 # Clear config to avoid infinite recursion
493 configoverrides = {
495 configoverrides = {
494 (b'experimental', b'revisions.disambiguatewithin'): None
496 (b'experimental', b'revisions.disambiguatewithin'): None
495 }
497 }
496 with repo.ui.configoverride(configoverrides):
498 with repo.ui.configoverride(configoverrides):
497 revs = repo.anyrevs([revset], user=True)
499 revs = repo.anyrevs([revset], user=True)
498 matches = []
500 matches = []
499 for rev in revs:
501 for rev in revs:
500 node = repo.changelog.node(rev)
502 node = repo.changelog.node(rev)
501 if hex(node).startswith(prefix):
503 if hex(node).startswith(prefix):
502 matches.append(node)
504 matches.append(node)
503 if len(matches) == 1:
505 if len(matches) == 1:
504 return matches[0]
506 return matches[0]
505 raise
507 raise
506 if node is None:
508 if node is None:
507 return
509 return
508 repo.changelog.rev(node) # make sure node isn't filtered
510 repo.changelog.rev(node) # make sure node isn't filtered
509 return node
511 return node
510
512
511
513
512 def mayberevnum(repo, prefix):
514 def mayberevnum(repo, prefix):
513 """Checks if the given prefix may be mistaken for a revision number"""
515 """Checks if the given prefix may be mistaken for a revision number"""
514 try:
516 try:
515 i = int(prefix)
517 i = int(prefix)
516 # if we are a pure int, then starting with zero will not be
518 # if we are a pure int, then starting with zero will not be
517 # confused as a rev; or, obviously, if the int is larger
519 # confused as a rev; or, obviously, if the int is larger
518 # than the value of the tip rev. We still need to disambiguate if
520 # than the value of the tip rev. We still need to disambiguate if
519 # prefix == '0', since that *is* a valid revnum.
521 # prefix == '0', since that *is* a valid revnum.
520 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
522 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
521 return False
523 return False
522 return True
524 return True
523 except ValueError:
525 except ValueError:
524 return False
526 return False
525
527
526
528
527 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
529 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
528 """Find the shortest unambiguous prefix that matches hexnode.
530 """Find the shortest unambiguous prefix that matches hexnode.
529
531
530 If "cache" is not None, it must be a dictionary that can be used for
532 If "cache" is not None, it must be a dictionary that can be used for
531 caching between calls to this method.
533 caching between calls to this method.
532 """
534 """
533 # _partialmatch() of filtered changelog could take O(len(repo)) time,
535 # _partialmatch() of filtered changelog could take O(len(repo)) time,
534 # which would be unacceptably slow. so we look for hash collision in
536 # which would be unacceptably slow. so we look for hash collision in
535 # unfiltered space, which means some hashes may be slightly longer.
537 # unfiltered space, which means some hashes may be slightly longer.
536
538
537 minlength = max(minlength, 1)
539 minlength = max(minlength, 1)
538
540
539 def disambiguate(prefix):
541 def disambiguate(prefix):
540 """Disambiguate against revnums."""
542 """Disambiguate against revnums."""
541 if repo.ui.configbool(b'experimental', b'revisions.prefixhexnode'):
543 if repo.ui.configbool(b'experimental', b'revisions.prefixhexnode'):
542 if mayberevnum(repo, prefix):
544 if mayberevnum(repo, prefix):
543 return b'x' + prefix
545 return b'x' + prefix
544 else:
546 else:
545 return prefix
547 return prefix
546
548
547 hexnode = hex(node)
549 hexnode = hex(node)
548 for length in range(len(prefix), len(hexnode) + 1):
550 for length in range(len(prefix), len(hexnode) + 1):
549 prefix = hexnode[:length]
551 prefix = hexnode[:length]
550 if not mayberevnum(repo, prefix):
552 if not mayberevnum(repo, prefix):
551 return prefix
553 return prefix
552
554
553 cl = repo.unfiltered().changelog
555 cl = repo.unfiltered().changelog
554 revset = repo.ui.config(b'experimental', b'revisions.disambiguatewithin')
556 revset = repo.ui.config(b'experimental', b'revisions.disambiguatewithin')
555 if revset:
557 if revset:
556 revs = None
558 revs = None
557 if cache is not None:
559 if cache is not None:
558 revs = cache.get(b'disambiguationrevset')
560 revs = cache.get(b'disambiguationrevset')
559 if revs is None:
561 if revs is None:
560 revs = repo.anyrevs([revset], user=True)
562 revs = repo.anyrevs([revset], user=True)
561 if cache is not None:
563 if cache is not None:
562 cache[b'disambiguationrevset'] = revs
564 cache[b'disambiguationrevset'] = revs
563 if cl.rev(node) in revs:
565 if cl.rev(node) in revs:
564 hexnode = hex(node)
566 hexnode = hex(node)
565 nodetree = None
567 nodetree = None
566 if cache is not None:
568 if cache is not None:
567 nodetree = cache.get(b'disambiguationnodetree')
569 nodetree = cache.get(b'disambiguationnodetree')
568 if not nodetree:
570 if not nodetree:
569 if util.safehasattr(parsers, 'nodetree'):
571 if util.safehasattr(parsers, 'nodetree'):
570 # The CExt is the only implementation to provide a nodetree
572 # The CExt is the only implementation to provide a nodetree
571 # class so far.
573 # class so far.
572 index = cl.index
574 index = cl.index
573 if util.safehasattr(index, 'get_cindex'):
575 if util.safehasattr(index, 'get_cindex'):
574 # the rust wrapped need to give access to its internal index
576 # the rust wrapped need to give access to its internal index
575 index = index.get_cindex()
577 index = index.get_cindex()
576 nodetree = parsers.nodetree(index, len(revs))
578 nodetree = parsers.nodetree(index, len(revs))
577 for r in revs:
579 for r in revs:
578 nodetree.insert(r)
580 nodetree.insert(r)
579 if cache is not None:
581 if cache is not None:
580 cache[b'disambiguationnodetree'] = nodetree
582 cache[b'disambiguationnodetree'] = nodetree
581 if nodetree is not None:
583 if nodetree is not None:
582 length = max(nodetree.shortest(node), minlength)
584 length = max(nodetree.shortest(node), minlength)
583 prefix = hexnode[:length]
585 prefix = hexnode[:length]
584 return disambiguate(prefix)
586 return disambiguate(prefix)
585 for length in range(minlength, len(hexnode) + 1):
587 for length in range(minlength, len(hexnode) + 1):
586 matches = []
588 matches = []
587 prefix = hexnode[:length]
589 prefix = hexnode[:length]
588 for rev in revs:
590 for rev in revs:
589 otherhexnode = repo[rev].hex()
591 otherhexnode = repo[rev].hex()
590 if prefix == otherhexnode[:length]:
592 if prefix == otherhexnode[:length]:
591 matches.append(otherhexnode)
593 matches.append(otherhexnode)
592 if len(matches) == 1:
594 if len(matches) == 1:
593 return disambiguate(prefix)
595 return disambiguate(prefix)
594
596
595 try:
597 try:
596 return disambiguate(cl.shortest(node, minlength))
598 return disambiguate(cl.shortest(node, minlength))
597 except error.LookupError:
599 except error.LookupError:
598 raise error.RepoLookupError()
600 raise error.RepoLookupError()
599
601
600
602
601 def isrevsymbol(repo, symbol):
603 def isrevsymbol(repo, symbol):
602 """Checks if a symbol exists in the repo.
604 """Checks if a symbol exists in the repo.
603
605
604 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
606 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
605 symbol is an ambiguous nodeid prefix.
607 symbol is an ambiguous nodeid prefix.
606 """
608 """
607 try:
609 try:
608 revsymbol(repo, symbol)
610 revsymbol(repo, symbol)
609 return True
611 return True
610 except error.RepoLookupError:
612 except error.RepoLookupError:
611 return False
613 return False
612
614
613
615
614 def revsymbol(repo, symbol):
616 def revsymbol(repo, symbol):
615 """Returns a context given a single revision symbol (as string).
617 """Returns a context given a single revision symbol (as string).
616
618
617 This is similar to revsingle(), but accepts only a single revision symbol,
619 This is similar to revsingle(), but accepts only a single revision symbol,
618 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
620 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
619 not "max(public())".
621 not "max(public())".
620 """
622 """
621 if not isinstance(symbol, bytes):
623 if not isinstance(symbol, bytes):
622 msg = (
624 msg = (
623 b"symbol (%s of type %s) was not a string, did you mean "
625 b"symbol (%s of type %s) was not a string, did you mean "
624 b"repo[symbol]?" % (symbol, type(symbol))
626 b"repo[symbol]?" % (symbol, type(symbol))
625 )
627 )
626 raise error.ProgrammingError(msg)
628 raise error.ProgrammingError(msg)
627 try:
629 try:
628 if symbol in (b'.', b'tip', b'null'):
630 if symbol in (b'.', b'tip', b'null'):
629 return repo[symbol]
631 return repo[symbol]
630
632
631 try:
633 try:
632 r = int(symbol)
634 r = int(symbol)
633 if b'%d' % r != symbol:
635 if b'%d' % r != symbol:
634 raise ValueError
636 raise ValueError
635 l = len(repo.changelog)
637 l = len(repo.changelog)
636 if r < 0:
638 if r < 0:
637 r += l
639 r += l
638 if r < 0 or r >= l and r != wdirrev:
640 if r < 0 or r >= l and r != wdirrev:
639 raise ValueError
641 raise ValueError
640 return repo[r]
642 return repo[r]
641 except error.FilteredIndexError:
643 except error.FilteredIndexError:
642 raise
644 raise
643 except (ValueError, OverflowError, IndexError):
645 except (ValueError, OverflowError, IndexError):
644 pass
646 pass
645
647
646 if len(symbol) == 40:
648 if len(symbol) == 40:
647 try:
649 try:
648 node = bin(symbol)
650 node = bin(symbol)
649 rev = repo.changelog.rev(node)
651 rev = repo.changelog.rev(node)
650 return repo[rev]
652 return repo[rev]
651 except error.FilteredLookupError:
653 except error.FilteredLookupError:
652 raise
654 raise
653 except (TypeError, LookupError):
655 except (TypeError, LookupError):
654 pass
656 pass
655
657
656 # look up bookmarks through the name interface
658 # look up bookmarks through the name interface
657 try:
659 try:
658 node = repo.names.singlenode(repo, symbol)
660 node = repo.names.singlenode(repo, symbol)
659 rev = repo.changelog.rev(node)
661 rev = repo.changelog.rev(node)
660 return repo[rev]
662 return repo[rev]
661 except KeyError:
663 except KeyError:
662 pass
664 pass
663
665
664 node = resolvehexnodeidprefix(repo, symbol)
666 node = resolvehexnodeidprefix(repo, symbol)
665 if node is not None:
667 if node is not None:
666 rev = repo.changelog.rev(node)
668 rev = repo.changelog.rev(node)
667 return repo[rev]
669 return repo[rev]
668
670
669 raise error.RepoLookupError(_(b"unknown revision '%s'") % symbol)
671 raise error.RepoLookupError(_(b"unknown revision '%s'") % symbol)
670
672
671 except error.WdirUnsupported:
673 except error.WdirUnsupported:
672 return repo[None]
674 return repo[None]
673 except (
675 except (
674 error.FilteredIndexError,
676 error.FilteredIndexError,
675 error.FilteredLookupError,
677 error.FilteredLookupError,
676 error.FilteredRepoLookupError,
678 error.FilteredRepoLookupError,
677 ):
679 ):
678 raise _filterederror(repo, symbol)
680 raise _filterederror(repo, symbol)
679
681
680
682
681 def _filterederror(repo, changeid):
683 def _filterederror(repo, changeid):
682 """build an exception to be raised about a filtered changeid
684 """build an exception to be raised about a filtered changeid
683
685
684 This is extracted in a function to help extensions (eg: evolve) to
686 This is extracted in a function to help extensions (eg: evolve) to
685 experiment with various message variants."""
687 experiment with various message variants."""
686 if repo.filtername.startswith(b'visible'):
688 if repo.filtername.startswith(b'visible'):
687
689
688 # Check if the changeset is obsolete
690 # Check if the changeset is obsolete
689 unfilteredrepo = repo.unfiltered()
691 unfilteredrepo = repo.unfiltered()
690 ctx = revsymbol(unfilteredrepo, changeid)
692 ctx = revsymbol(unfilteredrepo, changeid)
691
693
692 # If the changeset is obsolete, enrich the message with the reason
694 # If the changeset is obsolete, enrich the message with the reason
693 # that made this changeset not visible
695 # that made this changeset not visible
694 if ctx.obsolete():
696 if ctx.obsolete():
695 msg = obsutil._getfilteredreason(repo, changeid, ctx)
697 msg = obsutil._getfilteredreason(repo, changeid, ctx)
696 else:
698 else:
697 msg = _(b"hidden revision '%s'") % changeid
699 msg = _(b"hidden revision '%s'") % changeid
698
700
699 hint = _(b'use --hidden to access hidden revisions')
701 hint = _(b'use --hidden to access hidden revisions')
700
702
701 return error.FilteredRepoLookupError(msg, hint=hint)
703 return error.FilteredRepoLookupError(msg, hint=hint)
702 msg = _(b"filtered revision '%s' (not in '%s' subset)")
704 msg = _(b"filtered revision '%s' (not in '%s' subset)")
703 msg %= (changeid, repo.filtername)
705 msg %= (changeid, repo.filtername)
704 return error.FilteredRepoLookupError(msg)
706 return error.FilteredRepoLookupError(msg)
705
707
706
708
707 def revsingle(repo, revspec, default=b'.', localalias=None):
709 def revsingle(repo, revspec, default=b'.', localalias=None):
708 if not revspec and revspec != 0:
710 if not revspec and revspec != 0:
709 return repo[default]
711 return repo[default]
710
712
711 l = revrange(repo, [revspec], localalias=localalias)
713 l = revrange(repo, [revspec], localalias=localalias)
712 if not l:
714 if not l:
713 raise error.Abort(_(b'empty revision set'))
715 raise error.Abort(_(b'empty revision set'))
714 return repo[l.last()]
716 return repo[l.last()]
715
717
716
718
717 def _pairspec(revspec):
719 def _pairspec(revspec):
718 tree = revsetlang.parse(revspec)
720 tree = revsetlang.parse(revspec)
719 return tree and tree[0] in (
721 return tree and tree[0] in (
720 b'range',
722 b'range',
721 b'rangepre',
723 b'rangepre',
722 b'rangepost',
724 b'rangepost',
723 b'rangeall',
725 b'rangeall',
724 )
726 )
725
727
726
728
727 def revpair(repo, revs):
729 def revpair(repo, revs):
728 if not revs:
730 if not revs:
729 return repo[b'.'], repo[None]
731 return repo[b'.'], repo[None]
730
732
731 l = revrange(repo, revs)
733 l = revrange(repo, revs)
732
734
733 if not l:
735 if not l:
734 raise error.Abort(_(b'empty revision range'))
736 raise error.Abort(_(b'empty revision range'))
735
737
736 first = l.first()
738 first = l.first()
737 second = l.last()
739 second = l.last()
738
740
739 if (
741 if (
740 first == second
742 first == second
741 and len(revs) >= 2
743 and len(revs) >= 2
742 and not all(revrange(repo, [r]) for r in revs)
744 and not all(revrange(repo, [r]) for r in revs)
743 ):
745 ):
744 raise error.Abort(_(b'empty revision on one side of range'))
746 raise error.Abort(_(b'empty revision on one side of range'))
745
747
746 # if top-level is range expression, the result must always be a pair
748 # if top-level is range expression, the result must always be a pair
747 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
749 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
748 return repo[first], repo[None]
750 return repo[first], repo[None]
749
751
750 return repo[first], repo[second]
752 return repo[first], repo[second]
751
753
752
754
753 def revrange(repo, specs, localalias=None):
755 def revrange(repo, specs, localalias=None):
754 """Execute 1 to many revsets and return the union.
756 """Execute 1 to many revsets and return the union.
755
757
756 This is the preferred mechanism for executing revsets using user-specified
758 This is the preferred mechanism for executing revsets using user-specified
757 config options, such as revset aliases.
759 config options, such as revset aliases.
758
760
759 The revsets specified by ``specs`` will be executed via a chained ``OR``
761 The revsets specified by ``specs`` will be executed via a chained ``OR``
760 expression. If ``specs`` is empty, an empty result is returned.
762 expression. If ``specs`` is empty, an empty result is returned.
761
763
762 ``specs`` can contain integers, in which case they are assumed to be
764 ``specs`` can contain integers, in which case they are assumed to be
763 revision numbers.
765 revision numbers.
764
766
765 It is assumed the revsets are already formatted. If you have arguments
767 It is assumed the revsets are already formatted. If you have arguments
766 that need to be expanded in the revset, call ``revsetlang.formatspec()``
768 that need to be expanded in the revset, call ``revsetlang.formatspec()``
767 and pass the result as an element of ``specs``.
769 and pass the result as an element of ``specs``.
768
770
769 Specifying a single revset is allowed.
771 Specifying a single revset is allowed.
770
772
771 Returns a ``smartset.abstractsmartset`` which is a list-like interface over
773 Returns a ``smartset.abstractsmartset`` which is a list-like interface over
772 integer revisions.
774 integer revisions.
773 """
775 """
774 allspecs = []
776 allspecs = []
775 for spec in specs:
777 for spec in specs:
776 if isinstance(spec, int):
778 if isinstance(spec, int):
777 spec = revsetlang.formatspec(b'%d', spec)
779 spec = revsetlang.formatspec(b'%d', spec)
778 allspecs.append(spec)
780 allspecs.append(spec)
779 return repo.anyrevs(allspecs, user=True, localalias=localalias)
781 return repo.anyrevs(allspecs, user=True, localalias=localalias)
780
782
781
783
782 def increasingwindows(windowsize=8, sizelimit=512):
784 def increasingwindows(windowsize=8, sizelimit=512):
783 while True:
785 while True:
784 yield windowsize
786 yield windowsize
785 if windowsize < sizelimit:
787 if windowsize < sizelimit:
786 windowsize *= 2
788 windowsize *= 2
787
789
788
790
789 def walkchangerevs(repo, revs, makefilematcher, prepare):
791 def walkchangerevs(repo, revs, makefilematcher, prepare):
790 '''Iterate over files and the revs in a "windowed" way.
792 '''Iterate over files and the revs in a "windowed" way.
791
793
792 Callers most commonly need to iterate backwards over the history
794 Callers most commonly need to iterate backwards over the history
793 in which they are interested. Doing so has awful (quadratic-looking)
795 in which they are interested. Doing so has awful (quadratic-looking)
794 performance, so we use iterators in a "windowed" way.
796 performance, so we use iterators in a "windowed" way.
795
797
796 We walk a window of revisions in the desired order. Within the
798 We walk a window of revisions in the desired order. Within the
797 window, we first walk forwards to gather data, then in the desired
799 window, we first walk forwards to gather data, then in the desired
798 order (usually backwards) to display it.
800 order (usually backwards) to display it.
799
801
800 This function returns an iterator yielding contexts. Before
802 This function returns an iterator yielding contexts. Before
801 yielding each context, the iterator will first call the prepare
803 yielding each context, the iterator will first call the prepare
802 function on each context in the window in forward order.'''
804 function on each context in the window in forward order.'''
803
805
804 if not revs:
806 if not revs:
805 return []
807 return []
806 change = repo.__getitem__
808 change = repo.__getitem__
807
809
808 def iterate():
810 def iterate():
809 it = iter(revs)
811 it = iter(revs)
810 stopiteration = False
812 stopiteration = False
811 for windowsize in increasingwindows():
813 for windowsize in increasingwindows():
812 nrevs = []
814 nrevs = []
813 for i in pycompat.xrange(windowsize):
815 for i in pycompat.xrange(windowsize):
814 rev = next(it, None)
816 rev = next(it, None)
815 if rev is None:
817 if rev is None:
816 stopiteration = True
818 stopiteration = True
817 break
819 break
818 nrevs.append(rev)
820 nrevs.append(rev)
819 for rev in sorted(nrevs):
821 for rev in sorted(nrevs):
820 ctx = change(rev)
822 ctx = change(rev)
821 prepare(ctx, makefilematcher(ctx))
823 prepare(ctx, makefilematcher(ctx))
822 for rev in nrevs:
824 for rev in nrevs:
823 yield change(rev)
825 yield change(rev)
824
826
825 if stopiteration:
827 if stopiteration:
826 break
828 break
827
829
828 return iterate()
830 return iterate()
829
831
830
832
831 def meaningfulparents(repo, ctx):
833 def meaningfulparents(repo, ctx):
832 """Return list of meaningful (or all if debug) parentrevs for rev.
834 """Return list of meaningful (or all if debug) parentrevs for rev.
833
835
834 For merges (two non-nullrev revisions) both parents are meaningful.
836 For merges (two non-nullrev revisions) both parents are meaningful.
835 Otherwise the first parent revision is considered meaningful if it
837 Otherwise the first parent revision is considered meaningful if it
836 is not the preceding revision.
838 is not the preceding revision.
837 """
839 """
838 parents = ctx.parents()
840 parents = ctx.parents()
839 if len(parents) > 1:
841 if len(parents) > 1:
840 return parents
842 return parents
841 if repo.ui.debugflag:
843 if repo.ui.debugflag:
842 return [parents[0], repo[nullrev]]
844 return [parents[0], repo[nullrev]]
843 if parents[0].rev() >= intrev(ctx) - 1:
845 if parents[0].rev() >= intrev(ctx) - 1:
844 return []
846 return []
845 return parents
847 return parents
846
848
847
849
848 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
850 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
849 """Return a function that produced paths for presenting to the user.
851 """Return a function that produced paths for presenting to the user.
850
852
851 The returned function takes a repo-relative path and produces a path
853 The returned function takes a repo-relative path and produces a path
852 that can be presented in the UI.
854 that can be presented in the UI.
853
855
854 Depending on the value of ui.relative-paths, either a repo-relative or
856 Depending on the value of ui.relative-paths, either a repo-relative or
855 cwd-relative path will be produced.
857 cwd-relative path will be produced.
856
858
857 legacyrelativevalue is the value to use if ui.relative-paths=legacy
859 legacyrelativevalue is the value to use if ui.relative-paths=legacy
858
860
859 If forcerelativevalue is not None, then that value will be used regardless
861 If forcerelativevalue is not None, then that value will be used regardless
860 of what ui.relative-paths is set to.
862 of what ui.relative-paths is set to.
861 """
863 """
862 if forcerelativevalue is not None:
864 if forcerelativevalue is not None:
863 relative = forcerelativevalue
865 relative = forcerelativevalue
864 else:
866 else:
865 config = repo.ui.config(b'ui', b'relative-paths')
867 config = repo.ui.config(b'ui', b'relative-paths')
866 if config == b'legacy':
868 if config == b'legacy':
867 relative = legacyrelativevalue
869 relative = legacyrelativevalue
868 else:
870 else:
869 relative = stringutil.parsebool(config)
871 relative = stringutil.parsebool(config)
870 if relative is None:
872 if relative is None:
871 raise error.ConfigError(
873 raise error.ConfigError(
872 _(b"ui.relative-paths is not a boolean ('%s')") % config
874 _(b"ui.relative-paths is not a boolean ('%s')") % config
873 )
875 )
874
876
875 if relative:
877 if relative:
876 cwd = repo.getcwd()
878 cwd = repo.getcwd()
877 if cwd != b'':
879 if cwd != b'':
878 # this branch would work even if cwd == b'' (ie cwd = repo
880 # this branch would work even if cwd == b'' (ie cwd = repo
879 # root), but its generality makes the returned function slower
881 # root), but its generality makes the returned function slower
880 pathto = repo.pathto
882 pathto = repo.pathto
881 return lambda f: pathto(f, cwd)
883 return lambda f: pathto(f, cwd)
882 if repo.ui.configbool(b'ui', b'slash'):
884 if repo.ui.configbool(b'ui', b'slash'):
883 return lambda f: f
885 return lambda f: f
884 else:
886 else:
885 return util.localpath
887 return util.localpath
886
888
887
889
888 def subdiruipathfn(subpath, uipathfn):
890 def subdiruipathfn(subpath, uipathfn):
889 '''Create a new uipathfn that treats the file as relative to subpath.'''
891 '''Create a new uipathfn that treats the file as relative to subpath.'''
890 return lambda f: uipathfn(posixpath.join(subpath, f))
892 return lambda f: uipathfn(posixpath.join(subpath, f))
891
893
892
894
893 def anypats(pats, opts):
895 def anypats(pats, opts):
894 '''Checks if any patterns, including --include and --exclude were given.
896 '''Checks if any patterns, including --include and --exclude were given.
895
897
896 Some commands (e.g. addremove) use this condition for deciding whether to
898 Some commands (e.g. addremove) use this condition for deciding whether to
897 print absolute or relative paths.
899 print absolute or relative paths.
898 '''
900 '''
899 return bool(pats or opts.get(b'include') or opts.get(b'exclude'))
901 return bool(pats or opts.get(b'include') or opts.get(b'exclude'))
900
902
901
903
902 def expandpats(pats):
904 def expandpats(pats):
903 '''Expand bare globs when running on windows.
905 '''Expand bare globs when running on windows.
904 On posix we assume it already has already been done by sh.'''
906 On posix we assume it already has already been done by sh.'''
905 if not util.expandglobs:
907 if not util.expandglobs:
906 return list(pats)
908 return list(pats)
907 ret = []
909 ret = []
908 for kindpat in pats:
910 for kindpat in pats:
909 kind, pat = matchmod._patsplit(kindpat, None)
911 kind, pat = matchmod._patsplit(kindpat, None)
910 if kind is None:
912 if kind is None:
911 try:
913 try:
912 globbed = glob.glob(pat)
914 globbed = glob.glob(pat)
913 except re.error:
915 except re.error:
914 globbed = [pat]
916 globbed = [pat]
915 if globbed:
917 if globbed:
916 ret.extend(globbed)
918 ret.extend(globbed)
917 continue
919 continue
918 ret.append(kindpat)
920 ret.append(kindpat)
919 return ret
921 return ret
920
922
921
923
922 def matchandpats(
924 def matchandpats(
923 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
925 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
924 ):
926 ):
925 '''Return a matcher and the patterns that were used.
927 '''Return a matcher and the patterns that were used.
926 The matcher will warn about bad matches, unless an alternate badfn callback
928 The matcher will warn about bad matches, unless an alternate badfn callback
927 is provided.'''
929 is provided.'''
928 if opts is None:
930 if opts is None:
929 opts = {}
931 opts = {}
930 if not globbed and default == b'relpath':
932 if not globbed and default == b'relpath':
931 pats = expandpats(pats or [])
933 pats = expandpats(pats or [])
932
934
933 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
935 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
934
936
935 def bad(f, msg):
937 def bad(f, msg):
936 ctx.repo().ui.warn(b"%s: %s\n" % (uipathfn(f), msg))
938 ctx.repo().ui.warn(b"%s: %s\n" % (uipathfn(f), msg))
937
939
938 if badfn is None:
940 if badfn is None:
939 badfn = bad
941 badfn = bad
940
942
941 m = ctx.match(
943 m = ctx.match(
942 pats,
944 pats,
943 opts.get(b'include'),
945 opts.get(b'include'),
944 opts.get(b'exclude'),
946 opts.get(b'exclude'),
945 default,
947 default,
946 listsubrepos=opts.get(b'subrepos'),
948 listsubrepos=opts.get(b'subrepos'),
947 badfn=badfn,
949 badfn=badfn,
948 )
950 )
949
951
950 if m.always():
952 if m.always():
951 pats = []
953 pats = []
952 return m, pats
954 return m, pats
953
955
954
956
955 def match(
957 def match(
956 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
958 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
957 ):
959 ):
958 '''Return a matcher that will warn about bad matches.'''
960 '''Return a matcher that will warn about bad matches.'''
959 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
961 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
960
962
961
963
962 def matchall(repo):
964 def matchall(repo):
963 '''Return a matcher that will efficiently match everything.'''
965 '''Return a matcher that will efficiently match everything.'''
964 return matchmod.always()
966 return matchmod.always()
965
967
966
968
967 def matchfiles(repo, files, badfn=None):
969 def matchfiles(repo, files, badfn=None):
968 '''Return a matcher that will efficiently match exactly these files.'''
970 '''Return a matcher that will efficiently match exactly these files.'''
969 return matchmod.exact(files, badfn=badfn)
971 return matchmod.exact(files, badfn=badfn)
970
972
971
973
972 def parsefollowlinespattern(repo, rev, pat, msg):
974 def parsefollowlinespattern(repo, rev, pat, msg):
973 """Return a file name from `pat` pattern suitable for usage in followlines
975 """Return a file name from `pat` pattern suitable for usage in followlines
974 logic.
976 logic.
975 """
977 """
976 if not matchmod.patkind(pat):
978 if not matchmod.patkind(pat):
977 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
979 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
978 else:
980 else:
979 ctx = repo[rev]
981 ctx = repo[rev]
980 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
982 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
981 files = [f for f in ctx if m(f)]
983 files = [f for f in ctx if m(f)]
982 if len(files) != 1:
984 if len(files) != 1:
983 raise error.ParseError(msg)
985 raise error.ParseError(msg)
984 return files[0]
986 return files[0]
985
987
986
988
987 def getorigvfs(ui, repo):
989 def getorigvfs(ui, repo):
988 """return a vfs suitable to save 'orig' file
990 """return a vfs suitable to save 'orig' file
989
991
990 return None if no special directory is configured"""
992 return None if no special directory is configured"""
991 origbackuppath = ui.config(b'ui', b'origbackuppath')
993 origbackuppath = ui.config(b'ui', b'origbackuppath')
992 if not origbackuppath:
994 if not origbackuppath:
993 return None
995 return None
994 return vfs.vfs(repo.wvfs.join(origbackuppath))
996 return vfs.vfs(repo.wvfs.join(origbackuppath))
995
997
996
998
997 def backuppath(ui, repo, filepath):
999 def backuppath(ui, repo, filepath):
998 '''customize where working copy backup files (.orig files) are created
1000 '''customize where working copy backup files (.orig files) are created
999
1001
1000 Fetch user defined path from config file: [ui] origbackuppath = <path>
1002 Fetch user defined path from config file: [ui] origbackuppath = <path>
1001 Fall back to default (filepath with .orig suffix) if not specified
1003 Fall back to default (filepath with .orig suffix) if not specified
1002
1004
1003 filepath is repo-relative
1005 filepath is repo-relative
1004
1006
1005 Returns an absolute path
1007 Returns an absolute path
1006 '''
1008 '''
1007 origvfs = getorigvfs(ui, repo)
1009 origvfs = getorigvfs(ui, repo)
1008 if origvfs is None:
1010 if origvfs is None:
1009 return repo.wjoin(filepath + b".orig")
1011 return repo.wjoin(filepath + b".orig")
1010
1012
1011 origbackupdir = origvfs.dirname(filepath)
1013 origbackupdir = origvfs.dirname(filepath)
1012 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
1014 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
1013 ui.note(_(b'creating directory: %s\n') % origvfs.join(origbackupdir))
1015 ui.note(_(b'creating directory: %s\n') % origvfs.join(origbackupdir))
1014
1016
1015 # Remove any files that conflict with the backup file's path
1017 # Remove any files that conflict with the backup file's path
1016 for f in reversed(list(pathutil.finddirs(filepath))):
1018 for f in reversed(list(pathutil.finddirs(filepath))):
1017 if origvfs.isfileorlink(f):
1019 if origvfs.isfileorlink(f):
1018 ui.note(_(b'removing conflicting file: %s\n') % origvfs.join(f))
1020 ui.note(_(b'removing conflicting file: %s\n') % origvfs.join(f))
1019 origvfs.unlink(f)
1021 origvfs.unlink(f)
1020 break
1022 break
1021
1023
1022 origvfs.makedirs(origbackupdir)
1024 origvfs.makedirs(origbackupdir)
1023
1025
1024 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
1026 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
1025 ui.note(
1027 ui.note(
1026 _(b'removing conflicting directory: %s\n') % origvfs.join(filepath)
1028 _(b'removing conflicting directory: %s\n') % origvfs.join(filepath)
1027 )
1029 )
1028 origvfs.rmtree(filepath, forcibly=True)
1030 origvfs.rmtree(filepath, forcibly=True)
1029
1031
1030 return origvfs.join(filepath)
1032 return origvfs.join(filepath)
1031
1033
1032
1034
1033 class _containsnode(object):
1035 class _containsnode(object):
1034 """proxy __contains__(node) to container.__contains__ which accepts revs"""
1036 """proxy __contains__(node) to container.__contains__ which accepts revs"""
1035
1037
1036 def __init__(self, repo, revcontainer):
1038 def __init__(self, repo, revcontainer):
1037 self._torev = repo.changelog.rev
1039 self._torev = repo.changelog.rev
1038 self._revcontains = revcontainer.__contains__
1040 self._revcontains = revcontainer.__contains__
1039
1041
1040 def __contains__(self, node):
1042 def __contains__(self, node):
1041 return self._revcontains(self._torev(node))
1043 return self._revcontains(self._torev(node))
1042
1044
1043
1045
1044 def cleanupnodes(
1046 def cleanupnodes(
1045 repo,
1047 repo,
1046 replacements,
1048 replacements,
1047 operation,
1049 operation,
1048 moves=None,
1050 moves=None,
1049 metadata=None,
1051 metadata=None,
1050 fixphase=False,
1052 fixphase=False,
1051 targetphase=None,
1053 targetphase=None,
1052 backup=True,
1054 backup=True,
1053 ):
1055 ):
1054 """do common cleanups when old nodes are replaced by new nodes
1056 """do common cleanups when old nodes are replaced by new nodes
1055
1057
1056 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
1058 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
1057 (we might also want to move working directory parent in the future)
1059 (we might also want to move working directory parent in the future)
1058
1060
1059 By default, bookmark moves are calculated automatically from 'replacements',
1061 By default, bookmark moves are calculated automatically from 'replacements',
1060 but 'moves' can be used to override that. Also, 'moves' may include
1062 but 'moves' can be used to override that. Also, 'moves' may include
1061 additional bookmark moves that should not have associated obsmarkers.
1063 additional bookmark moves that should not have associated obsmarkers.
1062
1064
1063 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
1065 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
1064 have replacements. operation is a string, like "rebase".
1066 have replacements. operation is a string, like "rebase".
1065
1067
1066 metadata is dictionary containing metadata to be stored in obsmarker if
1068 metadata is dictionary containing metadata to be stored in obsmarker if
1067 obsolescence is enabled.
1069 obsolescence is enabled.
1068 """
1070 """
1069 assert fixphase or targetphase is None
1071 assert fixphase or targetphase is None
1070 if not replacements and not moves:
1072 if not replacements and not moves:
1071 return
1073 return
1072
1074
1073 # translate mapping's other forms
1075 # translate mapping's other forms
1074 if not util.safehasattr(replacements, b'items'):
1076 if not util.safehasattr(replacements, b'items'):
1075 replacements = {(n,): () for n in replacements}
1077 replacements = {(n,): () for n in replacements}
1076 else:
1078 else:
1077 # upgrading non tuple "source" to tuple ones for BC
1079 # upgrading non tuple "source" to tuple ones for BC
1078 repls = {}
1080 repls = {}
1079 for key, value in replacements.items():
1081 for key, value in replacements.items():
1080 if not isinstance(key, tuple):
1082 if not isinstance(key, tuple):
1081 key = (key,)
1083 key = (key,)
1082 repls[key] = value
1084 repls[key] = value
1083 replacements = repls
1085 replacements = repls
1084
1086
1085 # Unfiltered repo is needed since nodes in replacements might be hidden.
1087 # Unfiltered repo is needed since nodes in replacements might be hidden.
1086 unfi = repo.unfiltered()
1088 unfi = repo.unfiltered()
1087
1089
1088 # Calculate bookmark movements
1090 # Calculate bookmark movements
1089 if moves is None:
1091 if moves is None:
1090 moves = {}
1092 moves = {}
1091 for oldnodes, newnodes in replacements.items():
1093 for oldnodes, newnodes in replacements.items():
1092 for oldnode in oldnodes:
1094 for oldnode in oldnodes:
1093 if oldnode in moves:
1095 if oldnode in moves:
1094 continue
1096 continue
1095 if len(newnodes) > 1:
1097 if len(newnodes) > 1:
1096 # usually a split, take the one with biggest rev number
1098 # usually a split, take the one with biggest rev number
1097 newnode = next(unfi.set(b'max(%ln)', newnodes)).node()
1099 newnode = next(unfi.set(b'max(%ln)', newnodes)).node()
1098 elif len(newnodes) == 0:
1100 elif len(newnodes) == 0:
1099 # move bookmark backwards
1101 # move bookmark backwards
1100 allreplaced = []
1102 allreplaced = []
1101 for rep in replacements:
1103 for rep in replacements:
1102 allreplaced.extend(rep)
1104 allreplaced.extend(rep)
1103 roots = list(
1105 roots = list(
1104 unfi.set(b'max((::%n) - %ln)', oldnode, allreplaced)
1106 unfi.set(b'max((::%n) - %ln)', oldnode, allreplaced)
1105 )
1107 )
1106 if roots:
1108 if roots:
1107 newnode = roots[0].node()
1109 newnode = roots[0].node()
1108 else:
1110 else:
1109 newnode = nullid
1111 newnode = nullid
1110 else:
1112 else:
1111 newnode = newnodes[0]
1113 newnode = newnodes[0]
1112 moves[oldnode] = newnode
1114 moves[oldnode] = newnode
1113
1115
1114 allnewnodes = [n for ns in replacements.values() for n in ns]
1116 allnewnodes = [n for ns in replacements.values() for n in ns]
1115 toretract = {}
1117 toretract = {}
1116 toadvance = {}
1118 toadvance = {}
1117 if fixphase:
1119 if fixphase:
1118 precursors = {}
1120 precursors = {}
1119 for oldnodes, newnodes in replacements.items():
1121 for oldnodes, newnodes in replacements.items():
1120 for oldnode in oldnodes:
1122 for oldnode in oldnodes:
1121 for newnode in newnodes:
1123 for newnode in newnodes:
1122 precursors.setdefault(newnode, []).append(oldnode)
1124 precursors.setdefault(newnode, []).append(oldnode)
1123
1125
1124 allnewnodes.sort(key=lambda n: unfi[n].rev())
1126 allnewnodes.sort(key=lambda n: unfi[n].rev())
1125 newphases = {}
1127 newphases = {}
1126
1128
1127 def phase(ctx):
1129 def phase(ctx):
1128 return newphases.get(ctx.node(), ctx.phase())
1130 return newphases.get(ctx.node(), ctx.phase())
1129
1131
1130 for newnode in allnewnodes:
1132 for newnode in allnewnodes:
1131 ctx = unfi[newnode]
1133 ctx = unfi[newnode]
1132 parentphase = max(phase(p) for p in ctx.parents())
1134 parentphase = max(phase(p) for p in ctx.parents())
1133 if targetphase is None:
1135 if targetphase is None:
1134 oldphase = max(
1136 oldphase = max(
1135 unfi[oldnode].phase() for oldnode in precursors[newnode]
1137 unfi[oldnode].phase() for oldnode in precursors[newnode]
1136 )
1138 )
1137 newphase = max(oldphase, parentphase)
1139 newphase = max(oldphase, parentphase)
1138 else:
1140 else:
1139 newphase = max(targetphase, parentphase)
1141 newphase = max(targetphase, parentphase)
1140 newphases[newnode] = newphase
1142 newphases[newnode] = newphase
1141 if newphase > ctx.phase():
1143 if newphase > ctx.phase():
1142 toretract.setdefault(newphase, []).append(newnode)
1144 toretract.setdefault(newphase, []).append(newnode)
1143 elif newphase < ctx.phase():
1145 elif newphase < ctx.phase():
1144 toadvance.setdefault(newphase, []).append(newnode)
1146 toadvance.setdefault(newphase, []).append(newnode)
1145
1147
1146 with repo.transaction(b'cleanup') as tr:
1148 with repo.transaction(b'cleanup') as tr:
1147 # Move bookmarks
1149 # Move bookmarks
1148 bmarks = repo._bookmarks
1150 bmarks = repo._bookmarks
1149 bmarkchanges = []
1151 bmarkchanges = []
1150 for oldnode, newnode in moves.items():
1152 for oldnode, newnode in moves.items():
1151 oldbmarks = repo.nodebookmarks(oldnode)
1153 oldbmarks = repo.nodebookmarks(oldnode)
1152 if not oldbmarks:
1154 if not oldbmarks:
1153 continue
1155 continue
1154 from . import bookmarks # avoid import cycle
1156 from . import bookmarks # avoid import cycle
1155
1157
1156 repo.ui.debug(
1158 repo.ui.debug(
1157 b'moving bookmarks %r from %s to %s\n'
1159 b'moving bookmarks %r from %s to %s\n'
1158 % (
1160 % (
1159 pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1161 pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1160 hex(oldnode),
1162 hex(oldnode),
1161 hex(newnode),
1163 hex(newnode),
1162 )
1164 )
1163 )
1165 )
1164 # Delete divergent bookmarks being parents of related newnodes
1166 # Delete divergent bookmarks being parents of related newnodes
1165 deleterevs = repo.revs(
1167 deleterevs = repo.revs(
1166 b'parents(roots(%ln & (::%n))) - parents(%n)',
1168 b'parents(roots(%ln & (::%n))) - parents(%n)',
1167 allnewnodes,
1169 allnewnodes,
1168 newnode,
1170 newnode,
1169 oldnode,
1171 oldnode,
1170 )
1172 )
1171 deletenodes = _containsnode(repo, deleterevs)
1173 deletenodes = _containsnode(repo, deleterevs)
1172 for name in oldbmarks:
1174 for name in oldbmarks:
1173 bmarkchanges.append((name, newnode))
1175 bmarkchanges.append((name, newnode))
1174 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1176 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1175 bmarkchanges.append((b, None))
1177 bmarkchanges.append((b, None))
1176
1178
1177 if bmarkchanges:
1179 if bmarkchanges:
1178 bmarks.applychanges(repo, tr, bmarkchanges)
1180 bmarks.applychanges(repo, tr, bmarkchanges)
1179
1181
1180 for phase, nodes in toretract.items():
1182 for phase, nodes in toretract.items():
1181 phases.retractboundary(repo, tr, phase, nodes)
1183 phases.retractboundary(repo, tr, phase, nodes)
1182 for phase, nodes in toadvance.items():
1184 for phase, nodes in toadvance.items():
1183 phases.advanceboundary(repo, tr, phase, nodes)
1185 phases.advanceboundary(repo, tr, phase, nodes)
1184
1186
1185 mayusearchived = repo.ui.config(b'experimental', b'cleanup-as-archived')
1187 mayusearchived = repo.ui.config(b'experimental', b'cleanup-as-archived')
1186 # Obsolete or strip nodes
1188 # Obsolete or strip nodes
1187 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1189 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1188 # If a node is already obsoleted, and we want to obsolete it
1190 # If a node is already obsoleted, and we want to obsolete it
1189 # without a successor, skip that obssolete request since it's
1191 # without a successor, skip that obssolete request since it's
1190 # unnecessary. That's the "if s or not isobs(n)" check below.
1192 # unnecessary. That's the "if s or not isobs(n)" check below.
1191 # Also sort the node in topology order, that might be useful for
1193 # Also sort the node in topology order, that might be useful for
1192 # some obsstore logic.
1194 # some obsstore logic.
1193 # NOTE: the sorting might belong to createmarkers.
1195 # NOTE: the sorting might belong to createmarkers.
1194 torev = unfi.changelog.rev
1196 torev = unfi.changelog.rev
1195 sortfunc = lambda ns: torev(ns[0][0])
1197 sortfunc = lambda ns: torev(ns[0][0])
1196 rels = []
1198 rels = []
1197 for ns, s in sorted(replacements.items(), key=sortfunc):
1199 for ns, s in sorted(replacements.items(), key=sortfunc):
1198 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1200 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1199 rels.append(rel)
1201 rels.append(rel)
1200 if rels:
1202 if rels:
1201 obsolete.createmarkers(
1203 obsolete.createmarkers(
1202 repo, rels, operation=operation, metadata=metadata
1204 repo, rels, operation=operation, metadata=metadata
1203 )
1205 )
1204 elif phases.supportinternal(repo) and mayusearchived:
1206 elif phases.supportinternal(repo) and mayusearchived:
1205 # this assume we do not have "unstable" nodes above the cleaned ones
1207 # this assume we do not have "unstable" nodes above the cleaned ones
1206 allreplaced = set()
1208 allreplaced = set()
1207 for ns in replacements.keys():
1209 for ns in replacements.keys():
1208 allreplaced.update(ns)
1210 allreplaced.update(ns)
1209 if backup:
1211 if backup:
1210 from . import repair # avoid import cycle
1212 from . import repair # avoid import cycle
1211
1213
1212 node = min(allreplaced, key=repo.changelog.rev)
1214 node = min(allreplaced, key=repo.changelog.rev)
1213 repair.backupbundle(
1215 repair.backupbundle(
1214 repo, allreplaced, allreplaced, node, operation
1216 repo, allreplaced, allreplaced, node, operation
1215 )
1217 )
1216 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1218 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1217 else:
1219 else:
1218 from . import repair # avoid import cycle
1220 from . import repair # avoid import cycle
1219
1221
1220 tostrip = list(n for ns in replacements for n in ns)
1222 tostrip = list(n for ns in replacements for n in ns)
1221 if tostrip:
1223 if tostrip:
1222 repair.delayedstrip(
1224 repair.delayedstrip(
1223 repo.ui, repo, tostrip, operation, backup=backup
1225 repo.ui, repo, tostrip, operation, backup=backup
1224 )
1226 )
1225
1227
1226
1228
1227 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1229 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1228 if opts is None:
1230 if opts is None:
1229 opts = {}
1231 opts = {}
1230 m = matcher
1232 m = matcher
1231 dry_run = opts.get(b'dry_run')
1233 dry_run = opts.get(b'dry_run')
1232 try:
1234 try:
1233 similarity = float(opts.get(b'similarity') or 0)
1235 similarity = float(opts.get(b'similarity') or 0)
1234 except ValueError:
1236 except ValueError:
1235 raise error.Abort(_(b'similarity must be a number'))
1237 raise error.Abort(_(b'similarity must be a number'))
1236 if similarity < 0 or similarity > 100:
1238 if similarity < 0 or similarity > 100:
1237 raise error.Abort(_(b'similarity must be between 0 and 100'))
1239 raise error.Abort(_(b'similarity must be between 0 and 100'))
1238 similarity /= 100.0
1240 similarity /= 100.0
1239
1241
1240 ret = 0
1242 ret = 0
1241
1243
1242 wctx = repo[None]
1244 wctx = repo[None]
1243 for subpath in sorted(wctx.substate):
1245 for subpath in sorted(wctx.substate):
1244 submatch = matchmod.subdirmatcher(subpath, m)
1246 submatch = matchmod.subdirmatcher(subpath, m)
1245 if opts.get(b'subrepos') or m.exact(subpath) or any(submatch.files()):
1247 if opts.get(b'subrepos') or m.exact(subpath) or any(submatch.files()):
1246 sub = wctx.sub(subpath)
1248 sub = wctx.sub(subpath)
1247 subprefix = repo.wvfs.reljoin(prefix, subpath)
1249 subprefix = repo.wvfs.reljoin(prefix, subpath)
1248 subuipathfn = subdiruipathfn(subpath, uipathfn)
1250 subuipathfn = subdiruipathfn(subpath, uipathfn)
1249 try:
1251 try:
1250 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1252 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1251 ret = 1
1253 ret = 1
1252 except error.LookupError:
1254 except error.LookupError:
1253 repo.ui.status(
1255 repo.ui.status(
1254 _(b"skipping missing subrepository: %s\n")
1256 _(b"skipping missing subrepository: %s\n")
1255 % uipathfn(subpath)
1257 % uipathfn(subpath)
1256 )
1258 )
1257
1259
1258 rejected = []
1260 rejected = []
1259
1261
1260 def badfn(f, msg):
1262 def badfn(f, msg):
1261 if f in m.files():
1263 if f in m.files():
1262 m.bad(f, msg)
1264 m.bad(f, msg)
1263 rejected.append(f)
1265 rejected.append(f)
1264
1266
1265 badmatch = matchmod.badmatch(m, badfn)
1267 badmatch = matchmod.badmatch(m, badfn)
1266 added, unknown, deleted, removed, forgotten = _interestingfiles(
1268 added, unknown, deleted, removed, forgotten = _interestingfiles(
1267 repo, badmatch
1269 repo, badmatch
1268 )
1270 )
1269
1271
1270 unknownset = set(unknown + forgotten)
1272 unknownset = set(unknown + forgotten)
1271 toprint = unknownset.copy()
1273 toprint = unknownset.copy()
1272 toprint.update(deleted)
1274 toprint.update(deleted)
1273 for abs in sorted(toprint):
1275 for abs in sorted(toprint):
1274 if repo.ui.verbose or not m.exact(abs):
1276 if repo.ui.verbose or not m.exact(abs):
1275 if abs in unknownset:
1277 if abs in unknownset:
1276 status = _(b'adding %s\n') % uipathfn(abs)
1278 status = _(b'adding %s\n') % uipathfn(abs)
1277 label = b'ui.addremove.added'
1279 label = b'ui.addremove.added'
1278 else:
1280 else:
1279 status = _(b'removing %s\n') % uipathfn(abs)
1281 status = _(b'removing %s\n') % uipathfn(abs)
1280 label = b'ui.addremove.removed'
1282 label = b'ui.addremove.removed'
1281 repo.ui.status(status, label=label)
1283 repo.ui.status(status, label=label)
1282
1284
1283 renames = _findrenames(
1285 renames = _findrenames(
1284 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1286 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1285 )
1287 )
1286
1288
1287 if not dry_run:
1289 if not dry_run:
1288 _markchanges(repo, unknown + forgotten, deleted, renames)
1290 _markchanges(repo, unknown + forgotten, deleted, renames)
1289
1291
1290 for f in rejected:
1292 for f in rejected:
1291 if f in m.files():
1293 if f in m.files():
1292 return 1
1294 return 1
1293 return ret
1295 return ret
1294
1296
1295
1297
1296 def marktouched(repo, files, similarity=0.0):
1298 def marktouched(repo, files, similarity=0.0):
1297 '''Assert that files have somehow been operated upon. files are relative to
1299 '''Assert that files have somehow been operated upon. files are relative to
1298 the repo root.'''
1300 the repo root.'''
1299 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1301 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1300 rejected = []
1302 rejected = []
1301
1303
1302 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1304 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1303
1305
1304 if repo.ui.verbose:
1306 if repo.ui.verbose:
1305 unknownset = set(unknown + forgotten)
1307 unknownset = set(unknown + forgotten)
1306 toprint = unknownset.copy()
1308 toprint = unknownset.copy()
1307 toprint.update(deleted)
1309 toprint.update(deleted)
1308 for abs in sorted(toprint):
1310 for abs in sorted(toprint):
1309 if abs in unknownset:
1311 if abs in unknownset:
1310 status = _(b'adding %s\n') % abs
1312 status = _(b'adding %s\n') % abs
1311 else:
1313 else:
1312 status = _(b'removing %s\n') % abs
1314 status = _(b'removing %s\n') % abs
1313 repo.ui.status(status)
1315 repo.ui.status(status)
1314
1316
1315 # TODO: We should probably have the caller pass in uipathfn and apply it to
1317 # TODO: We should probably have the caller pass in uipathfn and apply it to
1316 # the messages above too. legacyrelativevalue=True is consistent with how
1318 # the messages above too. legacyrelativevalue=True is consistent with how
1317 # it used to work.
1319 # it used to work.
1318 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1320 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1319 renames = _findrenames(
1321 renames = _findrenames(
1320 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1322 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1321 )
1323 )
1322
1324
1323 _markchanges(repo, unknown + forgotten, deleted, renames)
1325 _markchanges(repo, unknown + forgotten, deleted, renames)
1324
1326
1325 for f in rejected:
1327 for f in rejected:
1326 if f in m.files():
1328 if f in m.files():
1327 return 1
1329 return 1
1328 return 0
1330 return 0
1329
1331
1330
1332
1331 def _interestingfiles(repo, matcher):
1333 def _interestingfiles(repo, matcher):
1332 '''Walk dirstate with matcher, looking for files that addremove would care
1334 '''Walk dirstate with matcher, looking for files that addremove would care
1333 about.
1335 about.
1334
1336
1335 This is different from dirstate.status because it doesn't care about
1337 This is different from dirstate.status because it doesn't care about
1336 whether files are modified or clean.'''
1338 whether files are modified or clean.'''
1337 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1339 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1338 audit_path = pathutil.pathauditor(repo.root, cached=True)
1340 audit_path = pathutil.pathauditor(repo.root, cached=True)
1339
1341
1340 ctx = repo[None]
1342 ctx = repo[None]
1341 dirstate = repo.dirstate
1343 dirstate = repo.dirstate
1342 matcher = repo.narrowmatch(matcher, includeexact=True)
1344 matcher = repo.narrowmatch(matcher, includeexact=True)
1343 walkresults = dirstate.walk(
1345 walkresults = dirstate.walk(
1344 matcher,
1346 matcher,
1345 subrepos=sorted(ctx.substate),
1347 subrepos=sorted(ctx.substate),
1346 unknown=True,
1348 unknown=True,
1347 ignored=False,
1349 ignored=False,
1348 full=False,
1350 full=False,
1349 )
1351 )
1350 for abs, st in pycompat.iteritems(walkresults):
1352 for abs, st in pycompat.iteritems(walkresults):
1351 dstate = dirstate[abs]
1353 dstate = dirstate[abs]
1352 if dstate == b'?' and audit_path.check(abs):
1354 if dstate == b'?' and audit_path.check(abs):
1353 unknown.append(abs)
1355 unknown.append(abs)
1354 elif dstate != b'r' and not st:
1356 elif dstate != b'r' and not st:
1355 deleted.append(abs)
1357 deleted.append(abs)
1356 elif dstate == b'r' and st:
1358 elif dstate == b'r' and st:
1357 forgotten.append(abs)
1359 forgotten.append(abs)
1358 # for finding renames
1360 # for finding renames
1359 elif dstate == b'r' and not st:
1361 elif dstate == b'r' and not st:
1360 removed.append(abs)
1362 removed.append(abs)
1361 elif dstate == b'a':
1363 elif dstate == b'a':
1362 added.append(abs)
1364 added.append(abs)
1363
1365
1364 return added, unknown, deleted, removed, forgotten
1366 return added, unknown, deleted, removed, forgotten
1365
1367
1366
1368
1367 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1369 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1368 '''Find renames from removed files to added ones.'''
1370 '''Find renames from removed files to added ones.'''
1369 renames = {}
1371 renames = {}
1370 if similarity > 0:
1372 if similarity > 0:
1371 for old, new, score in similar.findrenames(
1373 for old, new, score in similar.findrenames(
1372 repo, added, removed, similarity
1374 repo, added, removed, similarity
1373 ):
1375 ):
1374 if (
1376 if (
1375 repo.ui.verbose
1377 repo.ui.verbose
1376 or not matcher.exact(old)
1378 or not matcher.exact(old)
1377 or not matcher.exact(new)
1379 or not matcher.exact(new)
1378 ):
1380 ):
1379 repo.ui.status(
1381 repo.ui.status(
1380 _(
1382 _(
1381 b'recording removal of %s as rename to %s '
1383 b'recording removal of %s as rename to %s '
1382 b'(%d%% similar)\n'
1384 b'(%d%% similar)\n'
1383 )
1385 )
1384 % (uipathfn(old), uipathfn(new), score * 100)
1386 % (uipathfn(old), uipathfn(new), score * 100)
1385 )
1387 )
1386 renames[new] = old
1388 renames[new] = old
1387 return renames
1389 return renames
1388
1390
1389
1391
1390 def _markchanges(repo, unknown, deleted, renames):
1392 def _markchanges(repo, unknown, deleted, renames):
1391 '''Marks the files in unknown as added, the files in deleted as removed,
1393 '''Marks the files in unknown as added, the files in deleted as removed,
1392 and the files in renames as copied.'''
1394 and the files in renames as copied.'''
1393 wctx = repo[None]
1395 wctx = repo[None]
1394 with repo.wlock():
1396 with repo.wlock():
1395 wctx.forget(deleted)
1397 wctx.forget(deleted)
1396 wctx.add(unknown)
1398 wctx.add(unknown)
1397 for new, old in pycompat.iteritems(renames):
1399 for new, old in pycompat.iteritems(renames):
1398 wctx.copy(old, new)
1400 wctx.copy(old, new)
1399
1401
1400
1402
1401 def getrenamedfn(repo, endrev=None):
1403 def getrenamedfn(repo, endrev=None):
1402 if copiesmod.usechangesetcentricalgo(repo):
1404 if copiesmod.usechangesetcentricalgo(repo):
1403
1405
1404 def getrenamed(fn, rev):
1406 def getrenamed(fn, rev):
1405 ctx = repo[rev]
1407 ctx = repo[rev]
1406 p1copies = ctx.p1copies()
1408 p1copies = ctx.p1copies()
1407 if fn in p1copies:
1409 if fn in p1copies:
1408 return p1copies[fn]
1410 return p1copies[fn]
1409 p2copies = ctx.p2copies()
1411 p2copies = ctx.p2copies()
1410 if fn in p2copies:
1412 if fn in p2copies:
1411 return p2copies[fn]
1413 return p2copies[fn]
1412 return None
1414 return None
1413
1415
1414 return getrenamed
1416 return getrenamed
1415
1417
1416 rcache = {}
1418 rcache = {}
1417 if endrev is None:
1419 if endrev is None:
1418 endrev = len(repo)
1420 endrev = len(repo)
1419
1421
1420 def getrenamed(fn, rev):
1422 def getrenamed(fn, rev):
1421 '''looks up all renames for a file (up to endrev) the first
1423 '''looks up all renames for a file (up to endrev) the first
1422 time the file is given. It indexes on the changerev and only
1424 time the file is given. It indexes on the changerev and only
1423 parses the manifest if linkrev != changerev.
1425 parses the manifest if linkrev != changerev.
1424 Returns rename info for fn at changerev rev.'''
1426 Returns rename info for fn at changerev rev.'''
1425 if fn not in rcache:
1427 if fn not in rcache:
1426 rcache[fn] = {}
1428 rcache[fn] = {}
1427 fl = repo.file(fn)
1429 fl = repo.file(fn)
1428 for i in fl:
1430 for i in fl:
1429 lr = fl.linkrev(i)
1431 lr = fl.linkrev(i)
1430 renamed = fl.renamed(fl.node(i))
1432 renamed = fl.renamed(fl.node(i))
1431 rcache[fn][lr] = renamed and renamed[0]
1433 rcache[fn][lr] = renamed and renamed[0]
1432 if lr >= endrev:
1434 if lr >= endrev:
1433 break
1435 break
1434 if rev in rcache[fn]:
1436 if rev in rcache[fn]:
1435 return rcache[fn][rev]
1437 return rcache[fn][rev]
1436
1438
1437 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1439 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1438 # filectx logic.
1440 # filectx logic.
1439 try:
1441 try:
1440 return repo[rev][fn].copysource()
1442 return repo[rev][fn].copysource()
1441 except error.LookupError:
1443 except error.LookupError:
1442 return None
1444 return None
1443
1445
1444 return getrenamed
1446 return getrenamed
1445
1447
1446
1448
1447 def getcopiesfn(repo, endrev=None):
1449 def getcopiesfn(repo, endrev=None):
1448 if copiesmod.usechangesetcentricalgo(repo):
1450 if copiesmod.usechangesetcentricalgo(repo):
1449
1451
1450 def copiesfn(ctx):
1452 def copiesfn(ctx):
1451 if ctx.p2copies():
1453 if ctx.p2copies():
1452 allcopies = ctx.p1copies().copy()
1454 allcopies = ctx.p1copies().copy()
1453 # There should be no overlap
1455 # There should be no overlap
1454 allcopies.update(ctx.p2copies())
1456 allcopies.update(ctx.p2copies())
1455 return sorted(allcopies.items())
1457 return sorted(allcopies.items())
1456 else:
1458 else:
1457 return sorted(ctx.p1copies().items())
1459 return sorted(ctx.p1copies().items())
1458
1460
1459 else:
1461 else:
1460 getrenamed = getrenamedfn(repo, endrev)
1462 getrenamed = getrenamedfn(repo, endrev)
1461
1463
1462 def copiesfn(ctx):
1464 def copiesfn(ctx):
1463 copies = []
1465 copies = []
1464 for fn in ctx.files():
1466 for fn in ctx.files():
1465 rename = getrenamed(fn, ctx.rev())
1467 rename = getrenamed(fn, ctx.rev())
1466 if rename:
1468 if rename:
1467 copies.append((fn, rename))
1469 copies.append((fn, rename))
1468 return copies
1470 return copies
1469
1471
1470 return copiesfn
1472 return copiesfn
1471
1473
1472
1474
1473 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1475 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1474 """Update the dirstate to reflect the intent of copying src to dst. For
1476 """Update the dirstate to reflect the intent of copying src to dst. For
1475 different reasons it might not end with dst being marked as copied from src.
1477 different reasons it might not end with dst being marked as copied from src.
1476 """
1478 """
1477 origsrc = repo.dirstate.copied(src) or src
1479 origsrc = repo.dirstate.copied(src) or src
1478 if dst == origsrc: # copying back a copy?
1480 if dst == origsrc: # copying back a copy?
1479 if repo.dirstate[dst] not in b'mn' and not dryrun:
1481 if repo.dirstate[dst] not in b'mn' and not dryrun:
1480 repo.dirstate.normallookup(dst)
1482 repo.dirstate.normallookup(dst)
1481 else:
1483 else:
1482 if repo.dirstate[origsrc] == b'a' and origsrc == src:
1484 if repo.dirstate[origsrc] == b'a' and origsrc == src:
1483 if not ui.quiet:
1485 if not ui.quiet:
1484 ui.warn(
1486 ui.warn(
1485 _(
1487 _(
1486 b"%s has not been committed yet, so no copy "
1488 b"%s has not been committed yet, so no copy "
1487 b"data will be stored for %s.\n"
1489 b"data will be stored for %s.\n"
1488 )
1490 )
1489 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd))
1491 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd))
1490 )
1492 )
1491 if repo.dirstate[dst] in b'?r' and not dryrun:
1493 if repo.dirstate[dst] in b'?r' and not dryrun:
1492 wctx.add([dst])
1494 wctx.add([dst])
1493 elif not dryrun:
1495 elif not dryrun:
1494 wctx.copy(origsrc, dst)
1496 wctx.copy(origsrc, dst)
1495
1497
1496
1498
1497 def movedirstate(repo, newctx, match=None):
1499 def movedirstate(repo, newctx, match=None):
1498 """Move the dirstate to newctx and adjust it as necessary.
1500 """Move the dirstate to newctx and adjust it as necessary.
1499
1501
1500 A matcher can be provided as an optimization. It is probably a bug to pass
1502 A matcher can be provided as an optimization. It is probably a bug to pass
1501 a matcher that doesn't match all the differences between the parent of the
1503 a matcher that doesn't match all the differences between the parent of the
1502 working copy and newctx.
1504 working copy and newctx.
1503 """
1505 """
1504 oldctx = repo[b'.']
1506 oldctx = repo[b'.']
1505 ds = repo.dirstate
1507 ds = repo.dirstate
1506 copies = dict(ds.copies())
1508 copies = dict(ds.copies())
1507 ds.setparents(newctx.node(), nullid)
1509 ds.setparents(newctx.node(), nullid)
1508 s = newctx.status(oldctx, match=match)
1510 s = newctx.status(oldctx, match=match)
1509 for f in s.modified:
1511 for f in s.modified:
1510 if ds[f] == b'r':
1512 if ds[f] == b'r':
1511 # modified + removed -> removed
1513 # modified + removed -> removed
1512 continue
1514 continue
1513 ds.normallookup(f)
1515 ds.normallookup(f)
1514
1516
1515 for f in s.added:
1517 for f in s.added:
1516 if ds[f] == b'r':
1518 if ds[f] == b'r':
1517 # added + removed -> unknown
1519 # added + removed -> unknown
1518 ds.drop(f)
1520 ds.drop(f)
1519 elif ds[f] != b'a':
1521 elif ds[f] != b'a':
1520 ds.add(f)
1522 ds.add(f)
1521
1523
1522 for f in s.removed:
1524 for f in s.removed:
1523 if ds[f] == b'a':
1525 if ds[f] == b'a':
1524 # removed + added -> normal
1526 # removed + added -> normal
1525 ds.normallookup(f)
1527 ds.normallookup(f)
1526 elif ds[f] != b'r':
1528 elif ds[f] != b'r':
1527 ds.remove(f)
1529 ds.remove(f)
1528
1530
1529 # Merge old parent and old working dir copies
1531 # Merge old parent and old working dir copies
1530 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1532 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1531 oldcopies.update(copies)
1533 oldcopies.update(copies)
1532 copies = {
1534 copies = {
1533 dst: oldcopies.get(src, src)
1535 dst: oldcopies.get(src, src)
1534 for dst, src in pycompat.iteritems(oldcopies)
1536 for dst, src in pycompat.iteritems(oldcopies)
1535 }
1537 }
1536 # Adjust the dirstate copies
1538 # Adjust the dirstate copies
1537 for dst, src in pycompat.iteritems(copies):
1539 for dst, src in pycompat.iteritems(copies):
1538 if src not in newctx or dst in newctx or ds[dst] != b'a':
1540 if src not in newctx or dst in newctx or ds[dst] != b'a':
1539 src = None
1541 src = None
1540 ds.copy(src, dst)
1542 ds.copy(src, dst)
1541 repo._quick_access_changeid_invalidate()
1543 repo._quick_access_changeid_invalidate()
1542
1544
1543
1545
1544 def filterrequirements(requirements):
1546 def filterrequirements(requirements):
1545 """ filters the requirements into two sets:
1547 """ filters the requirements into two sets:
1546
1548
1547 wcreq: requirements which should be written in .hg/requires
1549 wcreq: requirements which should be written in .hg/requires
1548 storereq: which should be written in .hg/store/requires
1550 storereq: which should be written in .hg/store/requires
1549
1551
1550 Returns (wcreq, storereq)
1552 Returns (wcreq, storereq)
1551 """
1553 """
1552 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
1554 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
1553 wc, store = set(), set()
1555 wc, store = set(), set()
1554 for r in requirements:
1556 for r in requirements:
1555 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
1557 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
1556 wc.add(r)
1558 wc.add(r)
1557 else:
1559 else:
1558 store.add(r)
1560 store.add(r)
1559 return wc, store
1561 return wc, store
1560 return requirements, None
1562 return requirements, None
1561
1563
1562
1564
1563 def istreemanifest(repo):
1565 def istreemanifest(repo):
1564 """ returns whether the repository is using treemanifest or not """
1566 """ returns whether the repository is using treemanifest or not """
1565 return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements
1567 return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements
1566
1568
1567
1569
1568 def writereporequirements(repo, requirements=None):
1570 def writereporequirements(repo, requirements=None):
1569 """ writes requirements for the repo to .hg/requires """
1571 """ writes requirements for the repo to .hg/requires """
1570 if requirements:
1572 if requirements:
1571 repo.requirements = requirements
1573 repo.requirements = requirements
1572 wcreq, storereq = filterrequirements(repo.requirements)
1574 wcreq, storereq = filterrequirements(repo.requirements)
1573 if wcreq is not None:
1575 if wcreq is not None:
1574 writerequires(repo.vfs, wcreq)
1576 writerequires(repo.vfs, wcreq)
1575 if storereq is not None:
1577 if storereq is not None:
1576 writerequires(repo.svfs, storereq)
1578 writerequires(repo.svfs, storereq)
1577
1579
1578
1580
1579 def writerequires(opener, requirements):
1581 def writerequires(opener, requirements):
1580 with opener(b'requires', b'w', atomictemp=True) as fp:
1582 with opener(b'requires', b'w', atomictemp=True) as fp:
1581 for r in sorted(requirements):
1583 for r in sorted(requirements):
1582 fp.write(b"%s\n" % r)
1584 fp.write(b"%s\n" % r)
1583
1585
1584
1586
1585 class filecachesubentry(object):
1587 class filecachesubentry(object):
1586 def __init__(self, path, stat):
1588 def __init__(self, path, stat):
1587 self.path = path
1589 self.path = path
1588 self.cachestat = None
1590 self.cachestat = None
1589 self._cacheable = None
1591 self._cacheable = None
1590
1592
1591 if stat:
1593 if stat:
1592 self.cachestat = filecachesubentry.stat(self.path)
1594 self.cachestat = filecachesubentry.stat(self.path)
1593
1595
1594 if self.cachestat:
1596 if self.cachestat:
1595 self._cacheable = self.cachestat.cacheable()
1597 self._cacheable = self.cachestat.cacheable()
1596 else:
1598 else:
1597 # None means we don't know yet
1599 # None means we don't know yet
1598 self._cacheable = None
1600 self._cacheable = None
1599
1601
1600 def refresh(self):
1602 def refresh(self):
1601 if self.cacheable():
1603 if self.cacheable():
1602 self.cachestat = filecachesubentry.stat(self.path)
1604 self.cachestat = filecachesubentry.stat(self.path)
1603
1605
1604 def cacheable(self):
1606 def cacheable(self):
1605 if self._cacheable is not None:
1607 if self._cacheable is not None:
1606 return self._cacheable
1608 return self._cacheable
1607
1609
1608 # we don't know yet, assume it is for now
1610 # we don't know yet, assume it is for now
1609 return True
1611 return True
1610
1612
1611 def changed(self):
1613 def changed(self):
1612 # no point in going further if we can't cache it
1614 # no point in going further if we can't cache it
1613 if not self.cacheable():
1615 if not self.cacheable():
1614 return True
1616 return True
1615
1617
1616 newstat = filecachesubentry.stat(self.path)
1618 newstat = filecachesubentry.stat(self.path)
1617
1619
1618 # we may not know if it's cacheable yet, check again now
1620 # we may not know if it's cacheable yet, check again now
1619 if newstat and self._cacheable is None:
1621 if newstat and self._cacheable is None:
1620 self._cacheable = newstat.cacheable()
1622 self._cacheable = newstat.cacheable()
1621
1623
1622 # check again
1624 # check again
1623 if not self._cacheable:
1625 if not self._cacheable:
1624 return True
1626 return True
1625
1627
1626 if self.cachestat != newstat:
1628 if self.cachestat != newstat:
1627 self.cachestat = newstat
1629 self.cachestat = newstat
1628 return True
1630 return True
1629 else:
1631 else:
1630 return False
1632 return False
1631
1633
1632 @staticmethod
1634 @staticmethod
1633 def stat(path):
1635 def stat(path):
1634 try:
1636 try:
1635 return util.cachestat(path)
1637 return util.cachestat(path)
1636 except OSError as e:
1638 except OSError as e:
1637 if e.errno != errno.ENOENT:
1639 if e.errno != errno.ENOENT:
1638 raise
1640 raise
1639
1641
1640
1642
1641 class filecacheentry(object):
1643 class filecacheentry(object):
1642 def __init__(self, paths, stat=True):
1644 def __init__(self, paths, stat=True):
1643 self._entries = []
1645 self._entries = []
1644 for path in paths:
1646 for path in paths:
1645 self._entries.append(filecachesubentry(path, stat))
1647 self._entries.append(filecachesubentry(path, stat))
1646
1648
1647 def changed(self):
1649 def changed(self):
1648 '''true if any entry has changed'''
1650 '''true if any entry has changed'''
1649 for entry in self._entries:
1651 for entry in self._entries:
1650 if entry.changed():
1652 if entry.changed():
1651 return True
1653 return True
1652 return False
1654 return False
1653
1655
1654 def refresh(self):
1656 def refresh(self):
1655 for entry in self._entries:
1657 for entry in self._entries:
1656 entry.refresh()
1658 entry.refresh()
1657
1659
1658
1660
1659 class filecache(object):
1661 class filecache(object):
1660 """A property like decorator that tracks files under .hg/ for updates.
1662 """A property like decorator that tracks files under .hg/ for updates.
1661
1663
1662 On first access, the files defined as arguments are stat()ed and the
1664 On first access, the files defined as arguments are stat()ed and the
1663 results cached. The decorated function is called. The results are stashed
1665 results cached. The decorated function is called. The results are stashed
1664 away in a ``_filecache`` dict on the object whose method is decorated.
1666 away in a ``_filecache`` dict on the object whose method is decorated.
1665
1667
1666 On subsequent access, the cached result is used as it is set to the
1668 On subsequent access, the cached result is used as it is set to the
1667 instance dictionary.
1669 instance dictionary.
1668
1670
1669 On external property set/delete operations, the caller must update the
1671 On external property set/delete operations, the caller must update the
1670 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1672 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1671 instead of directly setting <attr>.
1673 instead of directly setting <attr>.
1672
1674
1673 When using the property API, the cached data is always used if available.
1675 When using the property API, the cached data is always used if available.
1674 No stat() is performed to check if the file has changed.
1676 No stat() is performed to check if the file has changed.
1675
1677
1676 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1678 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1677 can populate an entry before the property's getter is called. In this case,
1679 can populate an entry before the property's getter is called. In this case,
1678 entries in ``_filecache`` will be used during property operations,
1680 entries in ``_filecache`` will be used during property operations,
1679 if available. If the underlying file changes, it is up to external callers
1681 if available. If the underlying file changes, it is up to external callers
1680 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1682 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1681 method result as well as possibly calling ``del obj._filecache[attr]`` to
1683 method result as well as possibly calling ``del obj._filecache[attr]`` to
1682 remove the ``filecacheentry``.
1684 remove the ``filecacheentry``.
1683 """
1685 """
1684
1686
1685 def __init__(self, *paths):
1687 def __init__(self, *paths):
1686 self.paths = paths
1688 self.paths = paths
1687
1689
1688 def join(self, obj, fname):
1690 def join(self, obj, fname):
1689 """Used to compute the runtime path of a cached file.
1691 """Used to compute the runtime path of a cached file.
1690
1692
1691 Users should subclass filecache and provide their own version of this
1693 Users should subclass filecache and provide their own version of this
1692 function to call the appropriate join function on 'obj' (an instance
1694 function to call the appropriate join function on 'obj' (an instance
1693 of the class that its member function was decorated).
1695 of the class that its member function was decorated).
1694 """
1696 """
1695 raise NotImplementedError
1697 raise NotImplementedError
1696
1698
1697 def __call__(self, func):
1699 def __call__(self, func):
1698 self.func = func
1700 self.func = func
1699 self.sname = func.__name__
1701 self.sname = func.__name__
1700 self.name = pycompat.sysbytes(self.sname)
1702 self.name = pycompat.sysbytes(self.sname)
1701 return self
1703 return self
1702
1704
1703 def __get__(self, obj, type=None):
1705 def __get__(self, obj, type=None):
1704 # if accessed on the class, return the descriptor itself.
1706 # if accessed on the class, return the descriptor itself.
1705 if obj is None:
1707 if obj is None:
1706 return self
1708 return self
1707
1709
1708 assert self.sname not in obj.__dict__
1710 assert self.sname not in obj.__dict__
1709
1711
1710 entry = obj._filecache.get(self.name)
1712 entry = obj._filecache.get(self.name)
1711
1713
1712 if entry:
1714 if entry:
1713 if entry.changed():
1715 if entry.changed():
1714 entry.obj = self.func(obj)
1716 entry.obj = self.func(obj)
1715 else:
1717 else:
1716 paths = [self.join(obj, path) for path in self.paths]
1718 paths = [self.join(obj, path) for path in self.paths]
1717
1719
1718 # We stat -before- creating the object so our cache doesn't lie if
1720 # We stat -before- creating the object so our cache doesn't lie if
1719 # a writer modified between the time we read and stat
1721 # a writer modified between the time we read and stat
1720 entry = filecacheentry(paths, True)
1722 entry = filecacheentry(paths, True)
1721 entry.obj = self.func(obj)
1723 entry.obj = self.func(obj)
1722
1724
1723 obj._filecache[self.name] = entry
1725 obj._filecache[self.name] = entry
1724
1726
1725 obj.__dict__[self.sname] = entry.obj
1727 obj.__dict__[self.sname] = entry.obj
1726 return entry.obj
1728 return entry.obj
1727
1729
1728 # don't implement __set__(), which would make __dict__ lookup as slow as
1730 # don't implement __set__(), which would make __dict__ lookup as slow as
1729 # function call.
1731 # function call.
1730
1732
1731 def set(self, obj, value):
1733 def set(self, obj, value):
1732 if self.name not in obj._filecache:
1734 if self.name not in obj._filecache:
1733 # we add an entry for the missing value because X in __dict__
1735 # we add an entry for the missing value because X in __dict__
1734 # implies X in _filecache
1736 # implies X in _filecache
1735 paths = [self.join(obj, path) for path in self.paths]
1737 paths = [self.join(obj, path) for path in self.paths]
1736 ce = filecacheentry(paths, False)
1738 ce = filecacheentry(paths, False)
1737 obj._filecache[self.name] = ce
1739 obj._filecache[self.name] = ce
1738 else:
1740 else:
1739 ce = obj._filecache[self.name]
1741 ce = obj._filecache[self.name]
1740
1742
1741 ce.obj = value # update cached copy
1743 ce.obj = value # update cached copy
1742 obj.__dict__[self.sname] = value # update copy returned by obj.x
1744 obj.__dict__[self.sname] = value # update copy returned by obj.x
1743
1745
1744
1746
1745 def extdatasource(repo, source):
1747 def extdatasource(repo, source):
1746 """Gather a map of rev -> value dict from the specified source
1748 """Gather a map of rev -> value dict from the specified source
1747
1749
1748 A source spec is treated as a URL, with a special case shell: type
1750 A source spec is treated as a URL, with a special case shell: type
1749 for parsing the output from a shell command.
1751 for parsing the output from a shell command.
1750
1752
1751 The data is parsed as a series of newline-separated records where
1753 The data is parsed as a series of newline-separated records where
1752 each record is a revision specifier optionally followed by a space
1754 each record is a revision specifier optionally followed by a space
1753 and a freeform string value. If the revision is known locally, it
1755 and a freeform string value. If the revision is known locally, it
1754 is converted to a rev, otherwise the record is skipped.
1756 is converted to a rev, otherwise the record is skipped.
1755
1757
1756 Note that both key and value are treated as UTF-8 and converted to
1758 Note that both key and value are treated as UTF-8 and converted to
1757 the local encoding. This allows uniformity between local and
1759 the local encoding. This allows uniformity between local and
1758 remote data sources.
1760 remote data sources.
1759 """
1761 """
1760
1762
1761 spec = repo.ui.config(b"extdata", source)
1763 spec = repo.ui.config(b"extdata", source)
1762 if not spec:
1764 if not spec:
1763 raise error.Abort(_(b"unknown extdata source '%s'") % source)
1765 raise error.Abort(_(b"unknown extdata source '%s'") % source)
1764
1766
1765 data = {}
1767 data = {}
1766 src = proc = None
1768 src = proc = None
1767 try:
1769 try:
1768 if spec.startswith(b"shell:"):
1770 if spec.startswith(b"shell:"):
1769 # external commands should be run relative to the repo root
1771 # external commands should be run relative to the repo root
1770 cmd = spec[6:]
1772 cmd = spec[6:]
1771 proc = subprocess.Popen(
1773 proc = subprocess.Popen(
1772 procutil.tonativestr(cmd),
1774 procutil.tonativestr(cmd),
1773 shell=True,
1775 shell=True,
1774 bufsize=-1,
1776 bufsize=-1,
1775 close_fds=procutil.closefds,
1777 close_fds=procutil.closefds,
1776 stdout=subprocess.PIPE,
1778 stdout=subprocess.PIPE,
1777 cwd=procutil.tonativestr(repo.root),
1779 cwd=procutil.tonativestr(repo.root),
1778 )
1780 )
1779 src = proc.stdout
1781 src = proc.stdout
1780 else:
1782 else:
1781 # treat as a URL or file
1783 # treat as a URL or file
1782 src = url.open(repo.ui, spec)
1784 src = url.open(repo.ui, spec)
1783 for l in src:
1785 for l in src:
1784 if b" " in l:
1786 if b" " in l:
1785 k, v = l.strip().split(b" ", 1)
1787 k, v = l.strip().split(b" ", 1)
1786 else:
1788 else:
1787 k, v = l.strip(), b""
1789 k, v = l.strip(), b""
1788
1790
1789 k = encoding.tolocal(k)
1791 k = encoding.tolocal(k)
1790 try:
1792 try:
1791 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1793 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1792 except (error.LookupError, error.RepoLookupError):
1794 except (error.LookupError, error.RepoLookupError):
1793 pass # we ignore data for nodes that don't exist locally
1795 pass # we ignore data for nodes that don't exist locally
1794 finally:
1796 finally:
1795 if proc:
1797 if proc:
1796 try:
1798 try:
1797 proc.communicate()
1799 proc.communicate()
1798 except ValueError:
1800 except ValueError:
1799 # This happens if we started iterating src and then
1801 # This happens if we started iterating src and then
1800 # get a parse error on a line. It should be safe to ignore.
1802 # get a parse error on a line. It should be safe to ignore.
1801 pass
1803 pass
1802 if src:
1804 if src:
1803 src.close()
1805 src.close()
1804 if proc and proc.returncode != 0:
1806 if proc and proc.returncode != 0:
1805 raise error.Abort(
1807 raise error.Abort(
1806 _(b"extdata command '%s' failed: %s")
1808 _(b"extdata command '%s' failed: %s")
1807 % (cmd, procutil.explainexit(proc.returncode))
1809 % (cmd, procutil.explainexit(proc.returncode))
1808 )
1810 )
1809
1811
1810 return data
1812 return data
1811
1813
1812
1814
1813 class progress(object):
1815 class progress(object):
1814 def __init__(self, ui, updatebar, topic, unit=b"", total=None):
1816 def __init__(self, ui, updatebar, topic, unit=b"", total=None):
1815 self.ui = ui
1817 self.ui = ui
1816 self.pos = 0
1818 self.pos = 0
1817 self.topic = topic
1819 self.topic = topic
1818 self.unit = unit
1820 self.unit = unit
1819 self.total = total
1821 self.total = total
1820 self.debug = ui.configbool(b'progress', b'debug')
1822 self.debug = ui.configbool(b'progress', b'debug')
1821 self._updatebar = updatebar
1823 self._updatebar = updatebar
1822
1824
1823 def __enter__(self):
1825 def __enter__(self):
1824 return self
1826 return self
1825
1827
1826 def __exit__(self, exc_type, exc_value, exc_tb):
1828 def __exit__(self, exc_type, exc_value, exc_tb):
1827 self.complete()
1829 self.complete()
1828
1830
1829 def update(self, pos, item=b"", total=None):
1831 def update(self, pos, item=b"", total=None):
1830 assert pos is not None
1832 assert pos is not None
1831 if total:
1833 if total:
1832 self.total = total
1834 self.total = total
1833 self.pos = pos
1835 self.pos = pos
1834 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1836 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1835 if self.debug:
1837 if self.debug:
1836 self._printdebug(item)
1838 self._printdebug(item)
1837
1839
1838 def increment(self, step=1, item=b"", total=None):
1840 def increment(self, step=1, item=b"", total=None):
1839 self.update(self.pos + step, item, total)
1841 self.update(self.pos + step, item, total)
1840
1842
1841 def complete(self):
1843 def complete(self):
1842 self.pos = None
1844 self.pos = None
1843 self.unit = b""
1845 self.unit = b""
1844 self.total = None
1846 self.total = None
1845 self._updatebar(self.topic, self.pos, b"", self.unit, self.total)
1847 self._updatebar(self.topic, self.pos, b"", self.unit, self.total)
1846
1848
1847 def _printdebug(self, item):
1849 def _printdebug(self, item):
1848 unit = b''
1850 unit = b''
1849 if self.unit:
1851 if self.unit:
1850 unit = b' ' + self.unit
1852 unit = b' ' + self.unit
1851 if item:
1853 if item:
1852 item = b' ' + item
1854 item = b' ' + item
1853
1855
1854 if self.total:
1856 if self.total:
1855 pct = 100.0 * self.pos / self.total
1857 pct = 100.0 * self.pos / self.total
1856 self.ui.debug(
1858 self.ui.debug(
1857 b'%s:%s %d/%d%s (%4.2f%%)\n'
1859 b'%s:%s %d/%d%s (%4.2f%%)\n'
1858 % (self.topic, item, self.pos, self.total, unit, pct)
1860 % (self.topic, item, self.pos, self.total, unit, pct)
1859 )
1861 )
1860 else:
1862 else:
1861 self.ui.debug(b'%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1863 self.ui.debug(b'%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1862
1864
1863
1865
1864 def gdinitconfig(ui):
1866 def gdinitconfig(ui):
1865 """helper function to know if a repo should be created as general delta
1867 """helper function to know if a repo should be created as general delta
1866 """
1868 """
1867 # experimental config: format.generaldelta
1869 # experimental config: format.generaldelta
1868 return ui.configbool(b'format', b'generaldelta') or ui.configbool(
1870 return ui.configbool(b'format', b'generaldelta') or ui.configbool(
1869 b'format', b'usegeneraldelta'
1871 b'format', b'usegeneraldelta'
1870 )
1872 )
1871
1873
1872
1874
1873 def gddeltaconfig(ui):
1875 def gddeltaconfig(ui):
1874 """helper function to know if incoming delta should be optimised
1876 """helper function to know if incoming delta should be optimised
1875 """
1877 """
1876 # experimental config: format.generaldelta
1878 # experimental config: format.generaldelta
1877 return ui.configbool(b'format', b'generaldelta')
1879 return ui.configbool(b'format', b'generaldelta')
1878
1880
1879
1881
1880 class simplekeyvaluefile(object):
1882 class simplekeyvaluefile(object):
1881 """A simple file with key=value lines
1883 """A simple file with key=value lines
1882
1884
1883 Keys must be alphanumerics and start with a letter, values must not
1885 Keys must be alphanumerics and start with a letter, values must not
1884 contain '\n' characters"""
1886 contain '\n' characters"""
1885
1887
1886 firstlinekey = b'__firstline'
1888 firstlinekey = b'__firstline'
1887
1889
1888 def __init__(self, vfs, path, keys=None):
1890 def __init__(self, vfs, path, keys=None):
1889 self.vfs = vfs
1891 self.vfs = vfs
1890 self.path = path
1892 self.path = path
1891
1893
1892 def read(self, firstlinenonkeyval=False):
1894 def read(self, firstlinenonkeyval=False):
1893 """Read the contents of a simple key-value file
1895 """Read the contents of a simple key-value file
1894
1896
1895 'firstlinenonkeyval' indicates whether the first line of file should
1897 'firstlinenonkeyval' indicates whether the first line of file should
1896 be treated as a key-value pair or reuturned fully under the
1898 be treated as a key-value pair or reuturned fully under the
1897 __firstline key."""
1899 __firstline key."""
1898 lines = self.vfs.readlines(self.path)
1900 lines = self.vfs.readlines(self.path)
1899 d = {}
1901 d = {}
1900 if firstlinenonkeyval:
1902 if firstlinenonkeyval:
1901 if not lines:
1903 if not lines:
1902 e = _(b"empty simplekeyvalue file")
1904 e = _(b"empty simplekeyvalue file")
1903 raise error.CorruptedState(e)
1905 raise error.CorruptedState(e)
1904 # we don't want to include '\n' in the __firstline
1906 # we don't want to include '\n' in the __firstline
1905 d[self.firstlinekey] = lines[0][:-1]
1907 d[self.firstlinekey] = lines[0][:-1]
1906 del lines[0]
1908 del lines[0]
1907
1909
1908 try:
1910 try:
1909 # the 'if line.strip()' part prevents us from failing on empty
1911 # the 'if line.strip()' part prevents us from failing on empty
1910 # lines which only contain '\n' therefore are not skipped
1912 # lines which only contain '\n' therefore are not skipped
1911 # by 'if line'
1913 # by 'if line'
1912 updatedict = dict(
1914 updatedict = dict(
1913 line[:-1].split(b'=', 1) for line in lines if line.strip()
1915 line[:-1].split(b'=', 1) for line in lines if line.strip()
1914 )
1916 )
1915 if self.firstlinekey in updatedict:
1917 if self.firstlinekey in updatedict:
1916 e = _(b"%r can't be used as a key")
1918 e = _(b"%r can't be used as a key")
1917 raise error.CorruptedState(e % self.firstlinekey)
1919 raise error.CorruptedState(e % self.firstlinekey)
1918 d.update(updatedict)
1920 d.update(updatedict)
1919 except ValueError as e:
1921 except ValueError as e:
1920 raise error.CorruptedState(stringutil.forcebytestr(e))
1922 raise error.CorruptedState(stringutil.forcebytestr(e))
1921 return d
1923 return d
1922
1924
1923 def write(self, data, firstline=None):
1925 def write(self, data, firstline=None):
1924 """Write key=>value mapping to a file
1926 """Write key=>value mapping to a file
1925 data is a dict. Keys must be alphanumerical and start with a letter.
1927 data is a dict. Keys must be alphanumerical and start with a letter.
1926 Values must not contain newline characters.
1928 Values must not contain newline characters.
1927
1929
1928 If 'firstline' is not None, it is written to file before
1930 If 'firstline' is not None, it is written to file before
1929 everything else, as it is, not in a key=value form"""
1931 everything else, as it is, not in a key=value form"""
1930 lines = []
1932 lines = []
1931 if firstline is not None:
1933 if firstline is not None:
1932 lines.append(b'%s\n' % firstline)
1934 lines.append(b'%s\n' % firstline)
1933
1935
1934 for k, v in data.items():
1936 for k, v in data.items():
1935 if k == self.firstlinekey:
1937 if k == self.firstlinekey:
1936 e = b"key name '%s' is reserved" % self.firstlinekey
1938 e = b"key name '%s' is reserved" % self.firstlinekey
1937 raise error.ProgrammingError(e)
1939 raise error.ProgrammingError(e)
1938 if not k[0:1].isalpha():
1940 if not k[0:1].isalpha():
1939 e = b"keys must start with a letter in a key-value file"
1941 e = b"keys must start with a letter in a key-value file"
1940 raise error.ProgrammingError(e)
1942 raise error.ProgrammingError(e)
1941 if not k.isalnum():
1943 if not k.isalnum():
1942 e = b"invalid key name in a simple key-value file"
1944 e = b"invalid key name in a simple key-value file"
1943 raise error.ProgrammingError(e)
1945 raise error.ProgrammingError(e)
1944 if b'\n' in v:
1946 if b'\n' in v:
1945 e = b"invalid value in a simple key-value file"
1947 e = b"invalid value in a simple key-value file"
1946 raise error.ProgrammingError(e)
1948 raise error.ProgrammingError(e)
1947 lines.append(b"%s=%s\n" % (k, v))
1949 lines.append(b"%s=%s\n" % (k, v))
1948 with self.vfs(self.path, mode=b'wb', atomictemp=True) as fp:
1950 with self.vfs(self.path, mode=b'wb', atomictemp=True) as fp:
1949 fp.write(b''.join(lines))
1951 fp.write(b''.join(lines))
1950
1952
1951
1953
1952 _reportobsoletedsource = [
1954 _reportobsoletedsource = [
1953 b'debugobsolete',
1955 b'debugobsolete',
1954 b'pull',
1956 b'pull',
1955 b'push',
1957 b'push',
1956 b'serve',
1958 b'serve',
1957 b'unbundle',
1959 b'unbundle',
1958 ]
1960 ]
1959
1961
1960 _reportnewcssource = [
1962 _reportnewcssource = [
1961 b'pull',
1963 b'pull',
1962 b'unbundle',
1964 b'unbundle',
1963 ]
1965 ]
1964
1966
1965
1967
1966 def prefetchfiles(repo, revmatches):
1968 def prefetchfiles(repo, revmatches):
1967 """Invokes the registered file prefetch functions, allowing extensions to
1969 """Invokes the registered file prefetch functions, allowing extensions to
1968 ensure the corresponding files are available locally, before the command
1970 ensure the corresponding files are available locally, before the command
1969 uses them.
1971 uses them.
1970
1972
1971 Args:
1973 Args:
1972 revmatches: a list of (revision, match) tuples to indicate the files to
1974 revmatches: a list of (revision, match) tuples to indicate the files to
1973 fetch at each revision. If any of the match elements is None, it matches
1975 fetch at each revision. If any of the match elements is None, it matches
1974 all files.
1976 all files.
1975 """
1977 """
1976
1978
1977 def _matcher(m):
1979 def _matcher(m):
1978 if m:
1980 if m:
1979 assert isinstance(m, matchmod.basematcher)
1981 assert isinstance(m, matchmod.basematcher)
1980 # The command itself will complain about files that don't exist, so
1982 # The command itself will complain about files that don't exist, so
1981 # don't duplicate the message.
1983 # don't duplicate the message.
1982 return matchmod.badmatch(m, lambda fn, msg: None)
1984 return matchmod.badmatch(m, lambda fn, msg: None)
1983 else:
1985 else:
1984 return matchall(repo)
1986 return matchall(repo)
1985
1987
1986 revbadmatches = [(rev, _matcher(match)) for (rev, match) in revmatches]
1988 revbadmatches = [(rev, _matcher(match)) for (rev, match) in revmatches]
1987
1989
1988 fileprefetchhooks(repo, revbadmatches)
1990 fileprefetchhooks(repo, revbadmatches)
1989
1991
1990
1992
1991 # a list of (repo, revs, match) prefetch functions
1993 # a list of (repo, revs, match) prefetch functions
1992 fileprefetchhooks = util.hooks()
1994 fileprefetchhooks = util.hooks()
1993
1995
1994 # A marker that tells the evolve extension to suppress its own reporting
1996 # A marker that tells the evolve extension to suppress its own reporting
1995 _reportstroubledchangesets = True
1997 _reportstroubledchangesets = True
1996
1998
1997
1999
1998 def registersummarycallback(repo, otr, txnname=b'', as_validator=False):
2000 def registersummarycallback(repo, otr, txnname=b'', as_validator=False):
1999 """register a callback to issue a summary after the transaction is closed
2001 """register a callback to issue a summary after the transaction is closed
2000
2002
2001 If as_validator is true, then the callbacks are registered as transaction
2003 If as_validator is true, then the callbacks are registered as transaction
2002 validators instead
2004 validators instead
2003 """
2005 """
2004
2006
2005 def txmatch(sources):
2007 def txmatch(sources):
2006 return any(txnname.startswith(source) for source in sources)
2008 return any(txnname.startswith(source) for source in sources)
2007
2009
2008 categories = []
2010 categories = []
2009
2011
2010 def reportsummary(func):
2012 def reportsummary(func):
2011 """decorator for report callbacks."""
2013 """decorator for report callbacks."""
2012 # The repoview life cycle is shorter than the one of the actual
2014 # The repoview life cycle is shorter than the one of the actual
2013 # underlying repository. So the filtered object can die before the
2015 # underlying repository. So the filtered object can die before the
2014 # weakref is used leading to troubles. We keep a reference to the
2016 # weakref is used leading to troubles. We keep a reference to the
2015 # unfiltered object and restore the filtering when retrieving the
2017 # unfiltered object and restore the filtering when retrieving the
2016 # repository through the weakref.
2018 # repository through the weakref.
2017 filtername = repo.filtername
2019 filtername = repo.filtername
2018 reporef = weakref.ref(repo.unfiltered())
2020 reporef = weakref.ref(repo.unfiltered())
2019
2021
2020 def wrapped(tr):
2022 def wrapped(tr):
2021 repo = reporef()
2023 repo = reporef()
2022 if filtername:
2024 if filtername:
2023 assert repo is not None # help pytype
2025 assert repo is not None # help pytype
2024 repo = repo.filtered(filtername)
2026 repo = repo.filtered(filtername)
2025 func(repo, tr)
2027 func(repo, tr)
2026
2028
2027 newcat = b'%02i-txnreport' % len(categories)
2029 newcat = b'%02i-txnreport' % len(categories)
2028 if as_validator:
2030 if as_validator:
2029 otr.addvalidator(newcat, wrapped)
2031 otr.addvalidator(newcat, wrapped)
2030 else:
2032 else:
2031 otr.addpostclose(newcat, wrapped)
2033 otr.addpostclose(newcat, wrapped)
2032 categories.append(newcat)
2034 categories.append(newcat)
2033 return wrapped
2035 return wrapped
2034
2036
2035 @reportsummary
2037 @reportsummary
2036 def reportchangegroup(repo, tr):
2038 def reportchangegroup(repo, tr):
2037 cgchangesets = tr.changes.get(b'changegroup-count-changesets', 0)
2039 cgchangesets = tr.changes.get(b'changegroup-count-changesets', 0)
2038 cgrevisions = tr.changes.get(b'changegroup-count-revisions', 0)
2040 cgrevisions = tr.changes.get(b'changegroup-count-revisions', 0)
2039 cgfiles = tr.changes.get(b'changegroup-count-files', 0)
2041 cgfiles = tr.changes.get(b'changegroup-count-files', 0)
2040 cgheads = tr.changes.get(b'changegroup-count-heads', 0)
2042 cgheads = tr.changes.get(b'changegroup-count-heads', 0)
2041 if cgchangesets or cgrevisions or cgfiles:
2043 if cgchangesets or cgrevisions or cgfiles:
2042 htext = b""
2044 htext = b""
2043 if cgheads:
2045 if cgheads:
2044 htext = _(b" (%+d heads)") % cgheads
2046 htext = _(b" (%+d heads)") % cgheads
2045 msg = _(b"added %d changesets with %d changes to %d files%s\n")
2047 msg = _(b"added %d changesets with %d changes to %d files%s\n")
2046 if as_validator:
2048 if as_validator:
2047 msg = _(b"adding %d changesets with %d changes to %d files%s\n")
2049 msg = _(b"adding %d changesets with %d changes to %d files%s\n")
2048 assert repo is not None # help pytype
2050 assert repo is not None # help pytype
2049 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
2051 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
2050
2052
2051 if txmatch(_reportobsoletedsource):
2053 if txmatch(_reportobsoletedsource):
2052
2054
2053 @reportsummary
2055 @reportsummary
2054 def reportobsoleted(repo, tr):
2056 def reportobsoleted(repo, tr):
2055 obsoleted = obsutil.getobsoleted(repo, tr)
2057 obsoleted = obsutil.getobsoleted(repo, tr)
2056 newmarkers = len(tr.changes.get(b'obsmarkers', ()))
2058 newmarkers = len(tr.changes.get(b'obsmarkers', ()))
2057 if newmarkers:
2059 if newmarkers:
2058 repo.ui.status(_(b'%i new obsolescence markers\n') % newmarkers)
2060 repo.ui.status(_(b'%i new obsolescence markers\n') % newmarkers)
2059 if obsoleted:
2061 if obsoleted:
2060 msg = _(b'obsoleted %i changesets\n')
2062 msg = _(b'obsoleted %i changesets\n')
2061 if as_validator:
2063 if as_validator:
2062 msg = _(b'obsoleting %i changesets\n')
2064 msg = _(b'obsoleting %i changesets\n')
2063 repo.ui.status(msg % len(obsoleted))
2065 repo.ui.status(msg % len(obsoleted))
2064
2066
2065 if obsolete.isenabled(
2067 if obsolete.isenabled(
2066 repo, obsolete.createmarkersopt
2068 repo, obsolete.createmarkersopt
2067 ) and repo.ui.configbool(
2069 ) and repo.ui.configbool(
2068 b'experimental', b'evolution.report-instabilities'
2070 b'experimental', b'evolution.report-instabilities'
2069 ):
2071 ):
2070 instabilitytypes = [
2072 instabilitytypes = [
2071 (b'orphan', b'orphan'),
2073 (b'orphan', b'orphan'),
2072 (b'phase-divergent', b'phasedivergent'),
2074 (b'phase-divergent', b'phasedivergent'),
2073 (b'content-divergent', b'contentdivergent'),
2075 (b'content-divergent', b'contentdivergent'),
2074 ]
2076 ]
2075
2077
2076 def getinstabilitycounts(repo):
2078 def getinstabilitycounts(repo):
2077 filtered = repo.changelog.filteredrevs
2079 filtered = repo.changelog.filteredrevs
2078 counts = {}
2080 counts = {}
2079 for instability, revset in instabilitytypes:
2081 for instability, revset in instabilitytypes:
2080 counts[instability] = len(
2082 counts[instability] = len(
2081 set(obsolete.getrevs(repo, revset)) - filtered
2083 set(obsolete.getrevs(repo, revset)) - filtered
2082 )
2084 )
2083 return counts
2085 return counts
2084
2086
2085 oldinstabilitycounts = getinstabilitycounts(repo)
2087 oldinstabilitycounts = getinstabilitycounts(repo)
2086
2088
2087 @reportsummary
2089 @reportsummary
2088 def reportnewinstabilities(repo, tr):
2090 def reportnewinstabilities(repo, tr):
2089 newinstabilitycounts = getinstabilitycounts(repo)
2091 newinstabilitycounts = getinstabilitycounts(repo)
2090 for instability, revset in instabilitytypes:
2092 for instability, revset in instabilitytypes:
2091 delta = (
2093 delta = (
2092 newinstabilitycounts[instability]
2094 newinstabilitycounts[instability]
2093 - oldinstabilitycounts[instability]
2095 - oldinstabilitycounts[instability]
2094 )
2096 )
2095 msg = getinstabilitymessage(delta, instability)
2097 msg = getinstabilitymessage(delta, instability)
2096 if msg:
2098 if msg:
2097 repo.ui.warn(msg)
2099 repo.ui.warn(msg)
2098
2100
2099 if txmatch(_reportnewcssource):
2101 if txmatch(_reportnewcssource):
2100
2102
2101 @reportsummary
2103 @reportsummary
2102 def reportnewcs(repo, tr):
2104 def reportnewcs(repo, tr):
2103 """Report the range of new revisions pulled/unbundled."""
2105 """Report the range of new revisions pulled/unbundled."""
2104 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2106 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2105 unfi = repo.unfiltered()
2107 unfi = repo.unfiltered()
2106 if origrepolen >= len(unfi):
2108 if origrepolen >= len(unfi):
2107 return
2109 return
2108
2110
2109 # Compute the bounds of new visible revisions' range.
2111 # Compute the bounds of new visible revisions' range.
2110 revs = smartset.spanset(repo, start=origrepolen)
2112 revs = smartset.spanset(repo, start=origrepolen)
2111 if revs:
2113 if revs:
2112 minrev, maxrev = repo[revs.min()], repo[revs.max()]
2114 minrev, maxrev = repo[revs.min()], repo[revs.max()]
2113
2115
2114 if minrev == maxrev:
2116 if minrev == maxrev:
2115 revrange = minrev
2117 revrange = minrev
2116 else:
2118 else:
2117 revrange = b'%s:%s' % (minrev, maxrev)
2119 revrange = b'%s:%s' % (minrev, maxrev)
2118 draft = len(repo.revs(b'%ld and draft()', revs))
2120 draft = len(repo.revs(b'%ld and draft()', revs))
2119 secret = len(repo.revs(b'%ld and secret()', revs))
2121 secret = len(repo.revs(b'%ld and secret()', revs))
2120 if not (draft or secret):
2122 if not (draft or secret):
2121 msg = _(b'new changesets %s\n') % revrange
2123 msg = _(b'new changesets %s\n') % revrange
2122 elif draft and secret:
2124 elif draft and secret:
2123 msg = _(b'new changesets %s (%d drafts, %d secrets)\n')
2125 msg = _(b'new changesets %s (%d drafts, %d secrets)\n')
2124 msg %= (revrange, draft, secret)
2126 msg %= (revrange, draft, secret)
2125 elif draft:
2127 elif draft:
2126 msg = _(b'new changesets %s (%d drafts)\n')
2128 msg = _(b'new changesets %s (%d drafts)\n')
2127 msg %= (revrange, draft)
2129 msg %= (revrange, draft)
2128 elif secret:
2130 elif secret:
2129 msg = _(b'new changesets %s (%d secrets)\n')
2131 msg = _(b'new changesets %s (%d secrets)\n')
2130 msg %= (revrange, secret)
2132 msg %= (revrange, secret)
2131 else:
2133 else:
2132 errormsg = b'entered unreachable condition'
2134 errormsg = b'entered unreachable condition'
2133 raise error.ProgrammingError(errormsg)
2135 raise error.ProgrammingError(errormsg)
2134 repo.ui.status(msg)
2136 repo.ui.status(msg)
2135
2137
2136 # search new changesets directly pulled as obsolete
2138 # search new changesets directly pulled as obsolete
2137 duplicates = tr.changes.get(b'revduplicates', ())
2139 duplicates = tr.changes.get(b'revduplicates', ())
2138 obsadded = unfi.revs(
2140 obsadded = unfi.revs(
2139 b'(%d: + %ld) and obsolete()', origrepolen, duplicates
2141 b'(%d: + %ld) and obsolete()', origrepolen, duplicates
2140 )
2142 )
2141 cl = repo.changelog
2143 cl = repo.changelog
2142 extinctadded = [r for r in obsadded if r not in cl]
2144 extinctadded = [r for r in obsadded if r not in cl]
2143 if extinctadded:
2145 if extinctadded:
2144 # They are not just obsolete, but obsolete and invisible
2146 # They are not just obsolete, but obsolete and invisible
2145 # we call them "extinct" internally but the terms have not been
2147 # we call them "extinct" internally but the terms have not been
2146 # exposed to users.
2148 # exposed to users.
2147 msg = b'(%d other changesets obsolete on arrival)\n'
2149 msg = b'(%d other changesets obsolete on arrival)\n'
2148 repo.ui.status(msg % len(extinctadded))
2150 repo.ui.status(msg % len(extinctadded))
2149
2151
2150 @reportsummary
2152 @reportsummary
2151 def reportphasechanges(repo, tr):
2153 def reportphasechanges(repo, tr):
2152 """Report statistics of phase changes for changesets pre-existing
2154 """Report statistics of phase changes for changesets pre-existing
2153 pull/unbundle.
2155 pull/unbundle.
2154 """
2156 """
2155 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2157 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2156 published = []
2158 published = []
2157 for revs, (old, new) in tr.changes.get(b'phases', []):
2159 for revs, (old, new) in tr.changes.get(b'phases', []):
2158 if new != phases.public:
2160 if new != phases.public:
2159 continue
2161 continue
2160 published.extend(rev for rev in revs if rev < origrepolen)
2162 published.extend(rev for rev in revs if rev < origrepolen)
2161 if not published:
2163 if not published:
2162 return
2164 return
2163 msg = _(b'%d local changesets published\n')
2165 msg = _(b'%d local changesets published\n')
2164 if as_validator:
2166 if as_validator:
2165 msg = _(b'%d local changesets will be published\n')
2167 msg = _(b'%d local changesets will be published\n')
2166 repo.ui.status(msg % len(published))
2168 repo.ui.status(msg % len(published))
2167
2169
2168
2170
2169 def getinstabilitymessage(delta, instability):
2171 def getinstabilitymessage(delta, instability):
2170 """function to return the message to show warning about new instabilities
2172 """function to return the message to show warning about new instabilities
2171
2173
2172 exists as a separate function so that extension can wrap to show more
2174 exists as a separate function so that extension can wrap to show more
2173 information like how to fix instabilities"""
2175 information like how to fix instabilities"""
2174 if delta > 0:
2176 if delta > 0:
2175 return _(b'%i new %s changesets\n') % (delta, instability)
2177 return _(b'%i new %s changesets\n') % (delta, instability)
2176
2178
2177
2179
2178 def nodesummaries(repo, nodes, maxnumnodes=4):
2180 def nodesummaries(repo, nodes, maxnumnodes=4):
2179 if len(nodes) <= maxnumnodes or repo.ui.verbose:
2181 if len(nodes) <= maxnumnodes or repo.ui.verbose:
2180 return b' '.join(short(h) for h in nodes)
2182 return b' '.join(short(h) for h in nodes)
2181 first = b' '.join(short(h) for h in nodes[:maxnumnodes])
2183 first = b' '.join(short(h) for h in nodes[:maxnumnodes])
2182 return _(b"%s and %d others") % (first, len(nodes) - maxnumnodes)
2184 return _(b"%s and %d others") % (first, len(nodes) - maxnumnodes)
2183
2185
2184
2186
2185 def enforcesinglehead(repo, tr, desc, accountclosed=False):
2187 def enforcesinglehead(repo, tr, desc, accountclosed=False):
2186 """check that no named branch has multiple heads"""
2188 """check that no named branch has multiple heads"""
2187 if desc in (b'strip', b'repair'):
2189 if desc in (b'strip', b'repair'):
2188 # skip the logic during strip
2190 # skip the logic during strip
2189 return
2191 return
2190 visible = repo.filtered(b'visible')
2192 visible = repo.filtered(b'visible')
2191 # possible improvement: we could restrict the check to affected branch
2193 # possible improvement: we could restrict the check to affected branch
2192 bm = visible.branchmap()
2194 bm = visible.branchmap()
2193 for name in bm:
2195 for name in bm:
2194 heads = bm.branchheads(name, closed=accountclosed)
2196 heads = bm.branchheads(name, closed=accountclosed)
2195 if len(heads) > 1:
2197 if len(heads) > 1:
2196 msg = _(b'rejecting multiple heads on branch "%s"')
2198 msg = _(b'rejecting multiple heads on branch "%s"')
2197 msg %= name
2199 msg %= name
2198 hint = _(b'%d heads: %s')
2200 hint = _(b'%d heads: %s')
2199 hint %= (len(heads), nodesummaries(repo, heads))
2201 hint %= (len(heads), nodesummaries(repo, heads))
2200 raise error.Abort(msg, hint=hint)
2202 raise error.Abort(msg, hint=hint)
2201
2203
2202
2204
2203 def wrapconvertsink(sink):
2205 def wrapconvertsink(sink):
2204 """Allow extensions to wrap the sink returned by convcmd.convertsink()
2206 """Allow extensions to wrap the sink returned by convcmd.convertsink()
2205 before it is used, whether or not the convert extension was formally loaded.
2207 before it is used, whether or not the convert extension was formally loaded.
2206 """
2208 """
2207 return sink
2209 return sink
2208
2210
2209
2211
2210 def unhidehashlikerevs(repo, specs, hiddentype):
2212 def unhidehashlikerevs(repo, specs, hiddentype):
2211 """parse the user specs and unhide changesets whose hash or revision number
2213 """parse the user specs and unhide changesets whose hash or revision number
2212 is passed.
2214 is passed.
2213
2215
2214 hiddentype can be: 1) 'warn': warn while unhiding changesets
2216 hiddentype can be: 1) 'warn': warn while unhiding changesets
2215 2) 'nowarn': don't warn while unhiding changesets
2217 2) 'nowarn': don't warn while unhiding changesets
2216
2218
2217 returns a repo object with the required changesets unhidden
2219 returns a repo object with the required changesets unhidden
2218 """
2220 """
2219 if not repo.filtername or not repo.ui.configbool(
2221 if not repo.filtername or not repo.ui.configbool(
2220 b'experimental', b'directaccess'
2222 b'experimental', b'directaccess'
2221 ):
2223 ):
2222 return repo
2224 return repo
2223
2225
2224 if repo.filtername not in (b'visible', b'visible-hidden'):
2226 if repo.filtername not in (b'visible', b'visible-hidden'):
2225 return repo
2227 return repo
2226
2228
2227 symbols = set()
2229 symbols = set()
2228 for spec in specs:
2230 for spec in specs:
2229 try:
2231 try:
2230 tree = revsetlang.parse(spec)
2232 tree = revsetlang.parse(spec)
2231 except error.ParseError: # will be reported by scmutil.revrange()
2233 except error.ParseError: # will be reported by scmutil.revrange()
2232 continue
2234 continue
2233
2235
2234 symbols.update(revsetlang.gethashlikesymbols(tree))
2236 symbols.update(revsetlang.gethashlikesymbols(tree))
2235
2237
2236 if not symbols:
2238 if not symbols:
2237 return repo
2239 return repo
2238
2240
2239 revs = _getrevsfromsymbols(repo, symbols)
2241 revs = _getrevsfromsymbols(repo, symbols)
2240
2242
2241 if not revs:
2243 if not revs:
2242 return repo
2244 return repo
2243
2245
2244 if hiddentype == b'warn':
2246 if hiddentype == b'warn':
2245 unfi = repo.unfiltered()
2247 unfi = repo.unfiltered()
2246 revstr = b", ".join([pycompat.bytestr(unfi[l]) for l in revs])
2248 revstr = b", ".join([pycompat.bytestr(unfi[l]) for l in revs])
2247 repo.ui.warn(
2249 repo.ui.warn(
2248 _(
2250 _(
2249 b"warning: accessing hidden changesets for write "
2251 b"warning: accessing hidden changesets for write "
2250 b"operation: %s\n"
2252 b"operation: %s\n"
2251 )
2253 )
2252 % revstr
2254 % revstr
2253 )
2255 )
2254
2256
2255 # we have to use new filtername to separate branch/tags cache until we can
2257 # we have to use new filtername to separate branch/tags cache until we can
2256 # disbale these cache when revisions are dynamically pinned.
2258 # disbale these cache when revisions are dynamically pinned.
2257 return repo.filtered(b'visible-hidden', revs)
2259 return repo.filtered(b'visible-hidden', revs)
2258
2260
2259
2261
2260 def _getrevsfromsymbols(repo, symbols):
2262 def _getrevsfromsymbols(repo, symbols):
2261 """parse the list of symbols and returns a set of revision numbers of hidden
2263 """parse the list of symbols and returns a set of revision numbers of hidden
2262 changesets present in symbols"""
2264 changesets present in symbols"""
2263 revs = set()
2265 revs = set()
2264 unfi = repo.unfiltered()
2266 unfi = repo.unfiltered()
2265 unficl = unfi.changelog
2267 unficl = unfi.changelog
2266 cl = repo.changelog
2268 cl = repo.changelog
2267 tiprev = len(unficl)
2269 tiprev = len(unficl)
2268 allowrevnums = repo.ui.configbool(b'experimental', b'directaccess.revnums')
2270 allowrevnums = repo.ui.configbool(b'experimental', b'directaccess.revnums')
2269 for s in symbols:
2271 for s in symbols:
2270 try:
2272 try:
2271 n = int(s)
2273 n = int(s)
2272 if n <= tiprev:
2274 if n <= tiprev:
2273 if not allowrevnums:
2275 if not allowrevnums:
2274 continue
2276 continue
2275 else:
2277 else:
2276 if n not in cl:
2278 if n not in cl:
2277 revs.add(n)
2279 revs.add(n)
2278 continue
2280 continue
2279 except ValueError:
2281 except ValueError:
2280 pass
2282 pass
2281
2283
2282 try:
2284 try:
2283 s = resolvehexnodeidprefix(unfi, s)
2285 s = resolvehexnodeidprefix(unfi, s)
2284 except (error.LookupError, error.WdirUnsupported):
2286 except (error.LookupError, error.WdirUnsupported):
2285 s = None
2287 s = None
2286
2288
2287 if s is not None:
2289 if s is not None:
2288 rev = unficl.rev(s)
2290 rev = unficl.rev(s)
2289 if rev not in cl:
2291 if rev not in cl:
2290 revs.add(rev)
2292 revs.add(rev)
2291
2293
2292 return revs
2294 return revs
2293
2295
2294
2296
2295 def bookmarkrevs(repo, mark):
2297 def bookmarkrevs(repo, mark):
2296 """
2298 """
2297 Select revisions reachable by a given bookmark
2299 Select revisions reachable by a given bookmark
2298 """
2300 """
2299 return repo.revs(
2301 return repo.revs(
2300 b"ancestors(bookmark(%s)) - "
2302 b"ancestors(bookmark(%s)) - "
2301 b"ancestors(head() and not bookmark(%s)) - "
2303 b"ancestors(head() and not bookmark(%s)) - "
2302 b"ancestors(bookmark() and not bookmark(%s))",
2304 b"ancestors(bookmark() and not bookmark(%s))",
2303 mark,
2305 mark,
2304 mark,
2306 mark,
2305 mark,
2307 mark,
2306 )
2308 )
@@ -1,332 +1,332
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add -n
4 $ hg add -n
5 adding a
5 adding a
6 $ hg st
6 $ hg st
7 ? a
7 ? a
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 $ hg st
10 $ hg st
11 A a
11 A a
12 $ hg forget a
12 $ hg forget a
13 $ hg add
13 $ hg add
14 adding a
14 adding a
15 $ hg forget a
15 $ hg forget a
16 $ hg add --color debug
16 $ hg add --color debug
17 [ui.addremove.added ui.status|adding a]
17 [ui.addremove.added ui.status|adding a]
18 $ hg st
18 $ hg st
19 A a
19 A a
20 $ mkdir dir
20 $ mkdir dir
21 $ cd dir
21 $ cd dir
22 $ hg add ../a
22 $ hg add ../a
23 ../a already tracked!
23 ../a already tracked!
24 $ cd ..
24 $ cd ..
25
25
26 $ echo b > b
26 $ echo b > b
27 $ hg add -n b
27 $ hg add -n b
28 $ hg st
28 $ hg st
29 A a
29 A a
30 ? b
30 ? b
31 $ hg add b
31 $ hg add b
32 $ hg st
32 $ hg st
33 A a
33 A a
34 A b
34 A b
35
35
36 should fail
36 should fail
37
37
38 $ hg add b
38 $ hg add b
39 b already tracked!
39 b already tracked!
40 $ hg st
40 $ hg st
41 A a
41 A a
42 A b
42 A b
43
43
44 #if no-windows
44 #if no-windows
45 $ echo foo > con.xml
45 $ echo foo > con.xml
46 $ hg --config ui.portablefilenames=jump add con.xml
46 $ hg --config ui.portablefilenames=jump add con.xml
47 abort: ui.portablefilenames value is invalid ('jump')
47 abort: ui.portablefilenames value is invalid ('jump')
48 [255]
48 [30]
49 $ hg --config ui.portablefilenames=abort add con.xml
49 $ hg --config ui.portablefilenames=abort add con.xml
50 abort: filename contains 'con', which is reserved on Windows: con.xml
50 abort: filename contains 'con', which is reserved on Windows: con.xml
51 [255]
51 [255]
52 $ hg st
52 $ hg st
53 A a
53 A a
54 A b
54 A b
55 ? con.xml
55 ? con.xml
56 $ hg add con.xml
56 $ hg add con.xml
57 warning: filename contains 'con', which is reserved on Windows: con.xml
57 warning: filename contains 'con', which is reserved on Windows: con.xml
58 $ hg st
58 $ hg st
59 A a
59 A a
60 A b
60 A b
61 A con.xml
61 A con.xml
62 $ hg forget con.xml
62 $ hg forget con.xml
63 $ rm con.xml
63 $ rm con.xml
64 #endif
64 #endif
65
65
66 #if eol-in-paths
66 #if eol-in-paths
67 $ echo bla > 'hello:world'
67 $ echo bla > 'hello:world'
68 $ hg --config ui.portablefilenames=abort add
68 $ hg --config ui.portablefilenames=abort add
69 adding hello:world
69 adding hello:world
70 abort: filename contains ':', which is reserved on Windows: 'hello:world'
70 abort: filename contains ':', which is reserved on Windows: 'hello:world'
71 [255]
71 [255]
72 $ hg st
72 $ hg st
73 A a
73 A a
74 A b
74 A b
75 ? hello:world
75 ? hello:world
76 $ hg --config ui.portablefilenames=ignore add
76 $ hg --config ui.portablefilenames=ignore add
77 adding hello:world
77 adding hello:world
78 $ hg st
78 $ hg st
79 A a
79 A a
80 A b
80 A b
81 A hello:world
81 A hello:world
82 #endif
82 #endif
83
83
84 $ hg ci -m 0 --traceback
84 $ hg ci -m 0 --traceback
85
85
86 $ hg log -r "heads(. or wdir() & file('**'))"
86 $ hg log -r "heads(. or wdir() & file('**'))"
87 changeset: 0:* (glob)
87 changeset: 0:* (glob)
88 tag: tip
88 tag: tip
89 user: test
89 user: test
90 date: Thu Jan 01 00:00:00 1970 +0000
90 date: Thu Jan 01 00:00:00 1970 +0000
91 summary: 0
91 summary: 0
92
92
93 should fail
93 should fail
94
94
95 $ hg add a
95 $ hg add a
96 a already tracked!
96 a already tracked!
97
97
98 $ echo aa > a
98 $ echo aa > a
99 $ hg ci -m 1
99 $ hg ci -m 1
100 $ hg up 0
100 $ hg up 0
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ echo aaa > a
102 $ echo aaa > a
103 $ hg ci -m 2
103 $ hg ci -m 2
104 created new head
104 created new head
105
105
106 $ hg merge
106 $ hg merge
107 merging a
107 merging a
108 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
108 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
109 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
109 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
110 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
110 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
111 [1]
111 [1]
112 $ hg st
112 $ hg st
113 M a
113 M a
114 ? a.orig
114 ? a.orig
115
115
116 wdir doesn't cause a crash, and can be dynamically selected if dirty
116 wdir doesn't cause a crash, and can be dynamically selected if dirty
117
117
118 $ hg log -r "heads(. or wdir() & file('**'))"
118 $ hg log -r "heads(. or wdir() & file('**'))"
119 changeset: 2147483647:ffffffffffff
119 changeset: 2147483647:ffffffffffff
120 parent: 2:* (glob)
120 parent: 2:* (glob)
121 parent: 1:* (glob)
121 parent: 1:* (glob)
122 user: test
122 user: test
123 date: * (glob)
123 date: * (glob)
124
124
125 should fail
125 should fail
126
126
127 $ hg add a
127 $ hg add a
128 a already tracked!
128 a already tracked!
129 $ hg st
129 $ hg st
130 M a
130 M a
131 ? a.orig
131 ? a.orig
132 $ hg resolve -m a
132 $ hg resolve -m a
133 (no more unresolved files)
133 (no more unresolved files)
134 $ hg ci -m merge
134 $ hg ci -m merge
135
135
136 Issue683: peculiarity with hg revert of an removed then added file
136 Issue683: peculiarity with hg revert of an removed then added file
137
137
138 $ hg forget a
138 $ hg forget a
139 $ hg add a
139 $ hg add a
140 $ hg st
140 $ hg st
141 ? a.orig
141 ? a.orig
142 $ hg rm a
142 $ hg rm a
143 $ hg st
143 $ hg st
144 R a
144 R a
145 ? a.orig
145 ? a.orig
146 $ echo a > a
146 $ echo a > a
147 $ hg add a
147 $ hg add a
148 $ hg st
148 $ hg st
149 M a
149 M a
150 ? a.orig
150 ? a.orig
151
151
152 excluded file shouldn't be added even if it is explicitly specified
152 excluded file shouldn't be added even if it is explicitly specified
153
153
154 $ hg add a.orig -X '*.orig'
154 $ hg add a.orig -X '*.orig'
155 $ hg st
155 $ hg st
156 M a
156 M a
157 ? a.orig
157 ? a.orig
158
158
159 Forgotten file can be added back (as either clean or modified)
159 Forgotten file can be added back (as either clean or modified)
160
160
161 $ hg forget b
161 $ hg forget b
162 $ hg add b
162 $ hg add b
163 $ hg st -A b
163 $ hg st -A b
164 C b
164 C b
165 $ hg forget b
165 $ hg forget b
166 $ echo modified > b
166 $ echo modified > b
167 $ hg add b
167 $ hg add b
168 $ hg st -A b
168 $ hg st -A b
169 M b
169 M b
170 $ hg revert -qC b
170 $ hg revert -qC b
171
171
172 $ hg add c && echo "unexpected addition of missing file"
172 $ hg add c && echo "unexpected addition of missing file"
173 c: * (glob)
173 c: * (glob)
174 [1]
174 [1]
175 $ echo c > c
175 $ echo c > c
176 $ hg add d c && echo "unexpected addition of missing file"
176 $ hg add d c && echo "unexpected addition of missing file"
177 d: * (glob)
177 d: * (glob)
178 [1]
178 [1]
179 $ hg st
179 $ hg st
180 M a
180 M a
181 A c
181 A c
182 ? a.orig
182 ? a.orig
183 $ hg up -C
183 $ hg up -C
184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185
185
186 forget and get should have the right order: added but missing dir should be
186 forget and get should have the right order: added but missing dir should be
187 forgotten before file with same name is added
187 forgotten before file with same name is added
188
188
189 $ echo file d > d
189 $ echo file d > d
190 $ hg add d
190 $ hg add d
191 $ hg ci -md
191 $ hg ci -md
192 $ hg rm d
192 $ hg rm d
193 $ mkdir d
193 $ mkdir d
194 $ echo a > d/a
194 $ echo a > d/a
195 $ hg add d/a
195 $ hg add d/a
196 $ rm -r d
196 $ rm -r d
197 $ hg up -C
197 $ hg up -C
198 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
199 $ cat d
199 $ cat d
200 file d
200 file d
201
201
202 Test that adding a directory doesn't require case matching (issue4578)
202 Test that adding a directory doesn't require case matching (issue4578)
203 #if icasefs
203 #if icasefs
204 $ mkdir -p CapsDir1/CapsDir
204 $ mkdir -p CapsDir1/CapsDir
205 $ echo abc > CapsDir1/CapsDir/AbC.txt
205 $ echo abc > CapsDir1/CapsDir/AbC.txt
206 $ mkdir CapsDir1/CapsDir/SubDir
206 $ mkdir CapsDir1/CapsDir/SubDir
207 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
207 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
208
208
209 $ hg add capsdir1/capsdir
209 $ hg add capsdir1/capsdir
210 adding CapsDir1/CapsDir/AbC.txt
210 adding CapsDir1/CapsDir/AbC.txt
211 adding CapsDir1/CapsDir/SubDir/Def.txt
211 adding CapsDir1/CapsDir/SubDir/Def.txt
212
212
213 $ hg forget capsdir1/capsdir/abc.txt
213 $ hg forget capsdir1/capsdir/abc.txt
214
214
215 $ hg forget capsdir1/capsdir
215 $ hg forget capsdir1/capsdir
216 removing CapsDir1/CapsDir/SubDir/Def.txt
216 removing CapsDir1/CapsDir/SubDir/Def.txt
217
217
218 $ hg add capsdir1
218 $ hg add capsdir1
219 adding CapsDir1/CapsDir/AbC.txt
219 adding CapsDir1/CapsDir/AbC.txt
220 adding CapsDir1/CapsDir/SubDir/Def.txt
220 adding CapsDir1/CapsDir/SubDir/Def.txt
221
221
222 $ hg ci -m "AbCDef" capsdir1/capsdir
222 $ hg ci -m "AbCDef" capsdir1/capsdir
223
223
224 $ hg status -A capsdir1/capsdir
224 $ hg status -A capsdir1/capsdir
225 C CapsDir1/CapsDir/AbC.txt
225 C CapsDir1/CapsDir/AbC.txt
226 C CapsDir1/CapsDir/SubDir/Def.txt
226 C CapsDir1/CapsDir/SubDir/Def.txt
227
227
228 $ hg files capsdir1/capsdir
228 $ hg files capsdir1/capsdir
229 CapsDir1/CapsDir/AbC.txt
229 CapsDir1/CapsDir/AbC.txt
230 CapsDir1/CapsDir/SubDir/Def.txt
230 CapsDir1/CapsDir/SubDir/Def.txt
231
231
232 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
232 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
233 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
233 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
234
234
235 $ hg revert -r '.^' capsdir1/capsdir
235 $ hg revert -r '.^' capsdir1/capsdir
236 reverting CapsDir1/CapsDir/SubDir/Def.txt
236 reverting CapsDir1/CapsDir/SubDir/Def.txt
237
237
238 The conditional tests above mean the hash on the diff line differs on Windows
238 The conditional tests above mean the hash on the diff line differs on Windows
239 and OS X
239 and OS X
240 $ hg diff capsdir1/capsdir
240 $ hg diff capsdir1/capsdir
241 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
241 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
242 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
242 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
243 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
243 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
244 @@ -1,1 +1,1 @@
244 @@ -1,1 +1,1 @@
245 -xyz
245 -xyz
246 +def
246 +def
247
247
248 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
248 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
249 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
249 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
250
250
251 $ hg status -A capsdir1/capsdir
251 $ hg status -A capsdir1/capsdir
252 M CapsDir1/CapsDir/SubDir/Def.txt
252 M CapsDir1/CapsDir/SubDir/Def.txt
253 C CapsDir1/CapsDir/ABC.txt
253 C CapsDir1/CapsDir/ABC.txt
254
254
255 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
255 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
256 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
256 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
257 removing CapsDir1/CapsDir/ABC.txt
257 removing CapsDir1/CapsDir/ABC.txt
258 removing CapsDir1/CapsDir/SubDir/Def.txt
258 removing CapsDir1/CapsDir/SubDir/Def.txt
259 #endif
259 #endif
260
260
261 $ cd ..
261 $ cd ..
262
262
263 test --dry-run mode in forget
263 test --dry-run mode in forget
264
264
265 $ hg init testdir_forget
265 $ hg init testdir_forget
266 $ cd testdir_forget
266 $ cd testdir_forget
267 $ echo foo > foo
267 $ echo foo > foo
268 $ hg add foo
268 $ hg add foo
269 $ hg commit -m "foo"
269 $ hg commit -m "foo"
270 $ hg forget foo --dry-run -v
270 $ hg forget foo --dry-run -v
271 removing foo
271 removing foo
272 $ hg diff
272 $ hg diff
273 $ hg forget not_exist -n
273 $ hg forget not_exist -n
274 not_exist: $ENOENT$
274 not_exist: $ENOENT$
275 [1]
275 [1]
276
276
277 $ cd ..
277 $ cd ..
278
278
279 test --interactive mode in forget
279 test --interactive mode in forget
280
280
281 $ hg init interactiveforget
281 $ hg init interactiveforget
282 $ cd interactiveforget
282 $ cd interactiveforget
283 $ echo foo > foo
283 $ echo foo > foo
284 $ hg commit -qAm "foo"
284 $ hg commit -qAm "foo"
285 $ echo bar > bar
285 $ echo bar > bar
286 $ hg commit -qAm "bar"
286 $ hg commit -qAm "bar"
287 $ hg forget foo --dry-run -i
287 $ hg forget foo --dry-run -i
288 abort: cannot specify both --dry-run and --interactive
288 abort: cannot specify both --dry-run and --interactive
289 [10]
289 [10]
290
290
291 $ hg forget foo --config ui.interactive=True -i << EOF
291 $ hg forget foo --config ui.interactive=True -i << EOF
292 > ?
292 > ?
293 > n
293 > n
294 > EOF
294 > EOF
295 forget foo [Ynsa?] ?
295 forget foo [Ynsa?] ?
296 y - yes, forget this file
296 y - yes, forget this file
297 n - no, skip this file
297 n - no, skip this file
298 s - skip remaining files
298 s - skip remaining files
299 a - include all remaining files
299 a - include all remaining files
300 ? - ? (display help)
300 ? - ? (display help)
301 forget foo [Ynsa?] n
301 forget foo [Ynsa?] n
302
302
303 $ hg forget foo bar --config ui.interactive=True -i << EOF
303 $ hg forget foo bar --config ui.interactive=True -i << EOF
304 > y
304 > y
305 > n
305 > n
306 > EOF
306 > EOF
307 forget bar [Ynsa?] y
307 forget bar [Ynsa?] y
308 forget foo [Ynsa?] n
308 forget foo [Ynsa?] n
309 removing bar
309 removing bar
310 $ hg status
310 $ hg status
311 R bar
311 R bar
312 $ hg up -qC .
312 $ hg up -qC .
313
313
314 $ hg forget foo bar --config ui.interactive=True -i << EOF
314 $ hg forget foo bar --config ui.interactive=True -i << EOF
315 > s
315 > s
316 > EOF
316 > EOF
317 forget bar [Ynsa?] s
317 forget bar [Ynsa?] s
318 $ hg st
318 $ hg st
319 $ hg up -qC .
319 $ hg up -qC .
320
320
321 $ hg forget foo bar --config ui.interactive=True -i << EOF
321 $ hg forget foo bar --config ui.interactive=True -i << EOF
322 > a
322 > a
323 > EOF
323 > EOF
324 forget bar [Ynsa?] a
324 forget bar [Ynsa?] a
325 removing bar
325 removing bar
326 removing foo
326 removing foo
327 $ hg status
327 $ hg status
328 R bar
328 R bar
329 R foo
329 R foo
330 $ hg up -qC .
330 $ hg up -qC .
331
331
332 $ cd ..
332 $ cd ..
@@ -1,1169 +1,1169
1 #require git
1 #require git
2
2
3 $ git config -f $HOME/.gitconfig init.defaultBranch master
3 $ git config -f $HOME/.gitconfig init.defaultBranch master
4 $ git config -f $HOME/.gitconfig core.autocrlf false
4 $ git config -f $HOME/.gitconfig core.autocrlf false
5 $ echo "[extensions]" >> $HGRCPATH
5 $ echo "[extensions]" >> $HGRCPATH
6 $ echo "convert=" >> $HGRCPATH
6 $ echo "convert=" >> $HGRCPATH
7 $ cat >> $HGRCPATH <<EOF
7 $ cat >> $HGRCPATH <<EOF
8 > [subrepos]
8 > [subrepos]
9 > git:allowed = true
9 > git:allowed = true
10 > EOF
10 > EOF
11 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
11 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
12 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
12 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
13 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
13 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
14 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
14 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
15 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
15 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
16 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
16 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
17 $ INVALIDID1=afd12345af
17 $ INVALIDID1=afd12345af
18 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
18 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
19 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
19 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
20 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
20 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
21 $ count=10
21 $ count=10
22 $ commit()
22 $ commit()
23 > {
23 > {
24 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
24 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
25 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
25 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
26 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
26 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
27 > count=`expr $count + 1`
27 > count=`expr $count + 1`
28 > }
28 > }
29 $ mkdir git-repo
29 $ mkdir git-repo
30 $ cd git-repo
30 $ cd git-repo
31 $ git init >/dev/null 2>/dev/null
31 $ git init >/dev/null 2>/dev/null
32 $ echo a > a
32 $ echo a > a
33 $ mkdir d
33 $ mkdir d
34 $ echo b > d/b
34 $ echo b > d/b
35 $ git add a d
35 $ git add a d
36 $ commit -a -m t1
36 $ commit -a -m t1
37
37
38 Remove the directory, then try to replace it with a file (issue754)
38 Remove the directory, then try to replace it with a file (issue754)
39
39
40 $ git rm -f d/b
40 $ git rm -f d/b
41 rm 'd/b'
41 rm 'd/b'
42 $ commit -m t2
42 $ commit -m t2
43 $ echo d > d
43 $ echo d > d
44 $ git add d
44 $ git add d
45 $ commit -m t3
45 $ commit -m t3
46 $ echo b >> a
46 $ echo b >> a
47 $ commit -a -m t4.1
47 $ commit -a -m t4.1
48 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
48 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
49 $ echo c > a
49 $ echo c > a
50 $ echo a >> a
50 $ echo a >> a
51 $ commit -a -m t4.2
51 $ commit -a -m t4.2
52 $ git checkout master >/dev/null 2>/dev/null
52 $ git checkout master >/dev/null 2>/dev/null
53 $ git pull --no-commit . other > /dev/null 2>/dev/null
53 $ git pull --no-commit . other > /dev/null 2>/dev/null
54 $ commit -m 'Merge branch other'
54 $ commit -m 'Merge branch other'
55 $ cd ..
55 $ cd ..
56 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
56 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
57 > --config progress.delay=0 --config progress.changedelay=0 \
57 > --config progress.delay=0 --config progress.changedelay=0 \
58 > --config progress.refresh=0 --config progress.width=60 \
58 > --config progress.refresh=0 --config progress.width=60 \
59 > --config progress.format='topic, bar, number' --datesort git-repo
59 > --config progress.format='topic, bar, number' --datesort git-repo
60 \r (no-eol) (esc)
60 \r (no-eol) (esc)
61 scanning [======> ] 1/6\r (no-eol) (esc)
61 scanning [======> ] 1/6\r (no-eol) (esc)
62 scanning [=============> ] 2/6\r (no-eol) (esc)
62 scanning [=============> ] 2/6\r (no-eol) (esc)
63 scanning [=====================> ] 3/6\r (no-eol) (esc)
63 scanning [=====================> ] 3/6\r (no-eol) (esc)
64 scanning [============================> ] 4/6\r (no-eol) (esc)
64 scanning [============================> ] 4/6\r (no-eol) (esc)
65 scanning [===================================> ] 5/6\r (no-eol) (esc)
65 scanning [===================================> ] 5/6\r (no-eol) (esc)
66 scanning [===========================================>] 6/6\r (no-eol) (esc)
66 scanning [===========================================>] 6/6\r (no-eol) (esc)
67 \r (no-eol) (esc)
67 \r (no-eol) (esc)
68 \r (no-eol) (esc)
68 \r (no-eol) (esc)
69 converting [ ] 0/6\r (no-eol) (esc)
69 converting [ ] 0/6\r (no-eol) (esc)
70 getting files [==================> ] 1/2\r (no-eol) (esc)
70 getting files [==================> ] 1/2\r (no-eol) (esc)
71 getting files [======================================>] 2/2\r (no-eol) (esc)
71 getting files [======================================>] 2/2\r (no-eol) (esc)
72 \r (no-eol) (esc)
72 \r (no-eol) (esc)
73 \r (no-eol) (esc)
73 \r (no-eol) (esc)
74 converting [======> ] 1/6\r (no-eol) (esc)
74 converting [======> ] 1/6\r (no-eol) (esc)
75 getting files [======================================>] 1/1\r (no-eol) (esc)
75 getting files [======================================>] 1/1\r (no-eol) (esc)
76 \r (no-eol) (esc)
76 \r (no-eol) (esc)
77 \r (no-eol) (esc)
77 \r (no-eol) (esc)
78 converting [=============> ] 2/6\r (no-eol) (esc)
78 converting [=============> ] 2/6\r (no-eol) (esc)
79 getting files [======================================>] 1/1\r (no-eol) (esc)
79 getting files [======================================>] 1/1\r (no-eol) (esc)
80 \r (no-eol) (esc)
80 \r (no-eol) (esc)
81 \r (no-eol) (esc)
81 \r (no-eol) (esc)
82 converting [====================> ] 3/6\r (no-eol) (esc)
82 converting [====================> ] 3/6\r (no-eol) (esc)
83 getting files [======================================>] 1/1\r (no-eol) (esc)
83 getting files [======================================>] 1/1\r (no-eol) (esc)
84 \r (no-eol) (esc)
84 \r (no-eol) (esc)
85 \r (no-eol) (esc)
85 \r (no-eol) (esc)
86 converting [===========================> ] 4/6\r (no-eol) (esc)
86 converting [===========================> ] 4/6\r (no-eol) (esc)
87 getting files [======================================>] 1/1\r (no-eol) (esc)
87 getting files [======================================>] 1/1\r (no-eol) (esc)
88 \r (no-eol) (esc)
88 \r (no-eol) (esc)
89 \r (no-eol) (esc)
89 \r (no-eol) (esc)
90 converting [==================================> ] 5/6\r (no-eol) (esc)
90 converting [==================================> ] 5/6\r (no-eol) (esc)
91 getting files [======================================>] 1/1\r (no-eol) (esc)
91 getting files [======================================>] 1/1\r (no-eol) (esc)
92 \r (no-eol) (esc)
92 \r (no-eol) (esc)
93 assuming destination git-repo-hg
93 assuming destination git-repo-hg
94 initializing destination git-repo-hg repository
94 initializing destination git-repo-hg repository
95 scanning source...
95 scanning source...
96 sorting...
96 sorting...
97 converting...
97 converting...
98 5 t1
98 5 t1
99 4 t2
99 4 t2
100 3 t3
100 3 t3
101 2 t4.1
101 2 t4.1
102 1 t4.2
102 1 t4.2
103 0 Merge branch other
103 0 Merge branch other
104 updating bookmarks
104 updating bookmarks
105 $ hg up -q -R git-repo-hg
105 $ hg up -q -R git-repo-hg
106 $ hg -R git-repo-hg tip -v
106 $ hg -R git-repo-hg tip -v
107 changeset: 5:c78094926be2
107 changeset: 5:c78094926be2
108 bookmark: master
108 bookmark: master
109 tag: tip
109 tag: tip
110 parent: 3:f5f5cb45432b
110 parent: 3:f5f5cb45432b
111 parent: 4:4e174f80c67c
111 parent: 4:4e174f80c67c
112 user: test <test@example.org>
112 user: test <test@example.org>
113 date: Mon Jan 01 00:00:15 2007 +0000
113 date: Mon Jan 01 00:00:15 2007 +0000
114 files: a
114 files: a
115 description:
115 description:
116 Merge branch other
116 Merge branch other
117
117
118
118
119 $ count=10
119 $ count=10
120 $ mkdir git-repo2
120 $ mkdir git-repo2
121 $ cd git-repo2
121 $ cd git-repo2
122 $ git init >/dev/null 2>/dev/null
122 $ git init >/dev/null 2>/dev/null
123 $ echo foo > foo
123 $ echo foo > foo
124 $ git add foo
124 $ git add foo
125 $ commit -a -m 'add foo'
125 $ commit -a -m 'add foo'
126 $ echo >> foo
126 $ echo >> foo
127 $ commit -a -m 'change foo'
127 $ commit -a -m 'change foo'
128 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
128 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
129 $ echo quux >> quux
129 $ echo quux >> quux
130 $ git add quux
130 $ git add quux
131 $ commit -a -m 'add quux'
131 $ commit -a -m 'add quux'
132 $ echo bar > bar
132 $ echo bar > bar
133 $ git add bar
133 $ git add bar
134 $ commit -a -m 'add bar'
134 $ commit -a -m 'add bar'
135 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
135 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
136 $ echo baz > baz
136 $ echo baz > baz
137 $ git add baz
137 $ git add baz
138 $ commit -a -m 'add baz'
138 $ commit -a -m 'add baz'
139 $ git checkout master >/dev/null 2>/dev/null
139 $ git checkout master >/dev/null 2>/dev/null
140 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
140 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
141 $ commit -m 'Octopus merge'
141 $ commit -m 'Octopus merge'
142 $ echo bar >> bar
142 $ echo bar >> bar
143 $ commit -a -m 'change bar'
143 $ commit -a -m 'change bar'
144 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
144 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
145 $ echo >> foo
145 $ echo >> foo
146 $ commit -a -m 'change foo'
146 $ commit -a -m 'change foo'
147 $ git checkout master >/dev/null 2>/dev/null
147 $ git checkout master >/dev/null 2>/dev/null
148 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
148 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
149 $ commit -m 'Discard change to foo'
149 $ commit -m 'Discard change to foo'
150 $ cd ..
150 $ cd ..
151 $ glog()
151 $ glog()
152 > {
152 > {
153 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
153 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
154 > }
154 > }
155 $ splitrepo()
155 $ splitrepo()
156 > {
156 > {
157 > msg="$1"
157 > msg="$1"
158 > files="$2"
158 > files="$2"
159 > opts=$3
159 > opts=$3
160 > echo "% $files: $msg"
160 > echo "% $files: $msg"
161 > prefix=`echo "$files" | sed -e 's/ /-/g'`
161 > prefix=`echo "$files" | sed -e 's/ /-/g'`
162 > fmap="$prefix.fmap"
162 > fmap="$prefix.fmap"
163 > repo="$prefix.repo"
163 > repo="$prefix.repo"
164 > for i in $files; do
164 > for i in $files; do
165 > echo "include $i" >> "$fmap"
165 > echo "include $i" >> "$fmap"
166 > done
166 > done
167 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
167 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
168 > hg up -q -R "$repo"
168 > hg up -q -R "$repo"
169 > glog -R "$repo"
169 > glog -R "$repo"
170 > hg -R "$repo" manifest --debug
170 > hg -R "$repo" manifest --debug
171 > }
171 > }
172
172
173 full conversion
173 full conversion
174
174
175 $ hg convert --datesort git-repo2 fullrepo \
175 $ hg convert --datesort git-repo2 fullrepo \
176 > --config extensions.progress= --config progress.assume-tty=1 \
176 > --config extensions.progress= --config progress.assume-tty=1 \
177 > --config progress.delay=0 --config progress.changedelay=0 \
177 > --config progress.delay=0 --config progress.changedelay=0 \
178 > --config progress.refresh=0 --config progress.width=60 \
178 > --config progress.refresh=0 --config progress.width=60 \
179 > --config progress.format='topic, bar, number'
179 > --config progress.format='topic, bar, number'
180 \r (no-eol) (esc)
180 \r (no-eol) (esc)
181 scanning [===> ] 1/9\r (no-eol) (esc)
181 scanning [===> ] 1/9\r (no-eol) (esc)
182 scanning [========> ] 2/9\r (no-eol) (esc)
182 scanning [========> ] 2/9\r (no-eol) (esc)
183 scanning [=============> ] 3/9\r (no-eol) (esc)
183 scanning [=============> ] 3/9\r (no-eol) (esc)
184 scanning [==================> ] 4/9\r (no-eol) (esc)
184 scanning [==================> ] 4/9\r (no-eol) (esc)
185 scanning [=======================> ] 5/9\r (no-eol) (esc)
185 scanning [=======================> ] 5/9\r (no-eol) (esc)
186 scanning [============================> ] 6/9\r (no-eol) (esc)
186 scanning [============================> ] 6/9\r (no-eol) (esc)
187 scanning [=================================> ] 7/9\r (no-eol) (esc)
187 scanning [=================================> ] 7/9\r (no-eol) (esc)
188 scanning [======================================> ] 8/9\r (no-eol) (esc)
188 scanning [======================================> ] 8/9\r (no-eol) (esc)
189 scanning [===========================================>] 9/9\r (no-eol) (esc)
189 scanning [===========================================>] 9/9\r (no-eol) (esc)
190 \r (no-eol) (esc)
190 \r (no-eol) (esc)
191 \r (no-eol) (esc)
191 \r (no-eol) (esc)
192 converting [ ] 0/9\r (no-eol) (esc)
192 converting [ ] 0/9\r (no-eol) (esc)
193 getting files [======================================>] 1/1\r (no-eol) (esc)
193 getting files [======================================>] 1/1\r (no-eol) (esc)
194 \r (no-eol) (esc)
194 \r (no-eol) (esc)
195 \r (no-eol) (esc)
195 \r (no-eol) (esc)
196 converting [===> ] 1/9\r (no-eol) (esc)
196 converting [===> ] 1/9\r (no-eol) (esc)
197 getting files [======================================>] 1/1\r (no-eol) (esc)
197 getting files [======================================>] 1/1\r (no-eol) (esc)
198 \r (no-eol) (esc)
198 \r (no-eol) (esc)
199 \r (no-eol) (esc)
199 \r (no-eol) (esc)
200 converting [========> ] 2/9\r (no-eol) (esc)
200 converting [========> ] 2/9\r (no-eol) (esc)
201 getting files [======================================>] 1/1\r (no-eol) (esc)
201 getting files [======================================>] 1/1\r (no-eol) (esc)
202 \r (no-eol) (esc)
202 \r (no-eol) (esc)
203 \r (no-eol) (esc)
203 \r (no-eol) (esc)
204 converting [=============> ] 3/9\r (no-eol) (esc)
204 converting [=============> ] 3/9\r (no-eol) (esc)
205 getting files [======================================>] 1/1\r (no-eol) (esc)
205 getting files [======================================>] 1/1\r (no-eol) (esc)
206 \r (no-eol) (esc)
206 \r (no-eol) (esc)
207 \r (no-eol) (esc)
207 \r (no-eol) (esc)
208 converting [=================> ] 4/9\r (no-eol) (esc)
208 converting [=================> ] 4/9\r (no-eol) (esc)
209 getting files [======================================>] 1/1\r (no-eol) (esc)
209 getting files [======================================>] 1/1\r (no-eol) (esc)
210 \r (no-eol) (esc)
210 \r (no-eol) (esc)
211 \r (no-eol) (esc)
211 \r (no-eol) (esc)
212 converting [======================> ] 5/9\r (no-eol) (esc)
212 converting [======================> ] 5/9\r (no-eol) (esc)
213 getting files [===> ] 1/8\r (no-eol) (esc)
213 getting files [===> ] 1/8\r (no-eol) (esc)
214 getting files [========> ] 2/8\r (no-eol) (esc)
214 getting files [========> ] 2/8\r (no-eol) (esc)
215 getting files [=============> ] 3/8\r (no-eol) (esc)
215 getting files [=============> ] 3/8\r (no-eol) (esc)
216 getting files [==================> ] 4/8\r (no-eol) (esc)
216 getting files [==================> ] 4/8\r (no-eol) (esc)
217 getting files [=======================> ] 5/8\r (no-eol) (esc)
217 getting files [=======================> ] 5/8\r (no-eol) (esc)
218 getting files [============================> ] 6/8\r (no-eol) (esc)
218 getting files [============================> ] 6/8\r (no-eol) (esc)
219 getting files [=================================> ] 7/8\r (no-eol) (esc)
219 getting files [=================================> ] 7/8\r (no-eol) (esc)
220 getting files [======================================>] 8/8\r (no-eol) (esc)
220 getting files [======================================>] 8/8\r (no-eol) (esc)
221 \r (no-eol) (esc)
221 \r (no-eol) (esc)
222 \r (no-eol) (esc)
222 \r (no-eol) (esc)
223 converting [===========================> ] 6/9\r (no-eol) (esc)
223 converting [===========================> ] 6/9\r (no-eol) (esc)
224 getting files [======================================>] 1/1\r (no-eol) (esc)
224 getting files [======================================>] 1/1\r (no-eol) (esc)
225 \r (no-eol) (esc)
225 \r (no-eol) (esc)
226 \r (no-eol) (esc)
226 \r (no-eol) (esc)
227 converting [===============================> ] 7/9\r (no-eol) (esc)
227 converting [===============================> ] 7/9\r (no-eol) (esc)
228 getting files [======================================>] 1/1\r (no-eol) (esc)
228 getting files [======================================>] 1/1\r (no-eol) (esc)
229 \r (no-eol) (esc)
229 \r (no-eol) (esc)
230 \r (no-eol) (esc)
230 \r (no-eol) (esc)
231 converting [====================================> ] 8/9\r (no-eol) (esc)
231 converting [====================================> ] 8/9\r (no-eol) (esc)
232 getting files [==================> ] 1/2\r (no-eol) (esc)
232 getting files [==================> ] 1/2\r (no-eol) (esc)
233 getting files [======================================>] 2/2\r (no-eol) (esc)
233 getting files [======================================>] 2/2\r (no-eol) (esc)
234 \r (no-eol) (esc)
234 \r (no-eol) (esc)
235 initializing destination fullrepo repository
235 initializing destination fullrepo repository
236 scanning source...
236 scanning source...
237 sorting...
237 sorting...
238 converting...
238 converting...
239 8 add foo
239 8 add foo
240 7 change foo
240 7 change foo
241 6 add quux
241 6 add quux
242 5 add bar
242 5 add bar
243 4 add baz
243 4 add baz
244 3 Octopus merge
244 3 Octopus merge
245 2 change bar
245 2 change bar
246 1 change foo
246 1 change foo
247 0 Discard change to foo
247 0 Discard change to foo
248 updating bookmarks
248 updating bookmarks
249 $ hg up -q -R fullrepo
249 $ hg up -q -R fullrepo
250 $ glog -R fullrepo
250 $ glog -R fullrepo
251 @ 9 "Discard change to foo" files: foo
251 @ 9 "Discard change to foo" files: foo
252 |\
252 |\
253 | o 8 "change foo" files: foo
253 | o 8 "change foo" files: foo
254 | |
254 | |
255 o | 7 "change bar" files: bar
255 o | 7 "change bar" files: bar
256 |/
256 |/
257 o 6 "(octopus merge fixup)" files:
257 o 6 "(octopus merge fixup)" files:
258 |\
258 |\
259 | o 5 "Octopus merge" files: baz
259 | o 5 "Octopus merge" files: baz
260 | |\
260 | |\
261 o | | 4 "add baz" files: baz
261 o | | 4 "add baz" files: baz
262 | | |
262 | | |
263 +---o 3 "add bar" files: bar
263 +---o 3 "add bar" files: bar
264 | |
264 | |
265 o | 2 "add quux" files: quux
265 o | 2 "add quux" files: quux
266 | |
266 | |
267 | o 1 "change foo" files: foo
267 | o 1 "change foo" files: foo
268 |/
268 |/
269 o 0 "add foo" files: foo
269 o 0 "add foo" files: foo
270
270
271 $ hg -R fullrepo manifest --debug
271 $ hg -R fullrepo manifest --debug
272 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
272 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
273 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
273 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
274 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
274 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
275 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
275 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
276 $ splitrepo 'octopus merge' 'foo bar baz'
276 $ splitrepo 'octopus merge' 'foo bar baz'
277 % foo bar baz: octopus merge
277 % foo bar baz: octopus merge
278 @ 8 "Discard change to foo" files: foo
278 @ 8 "Discard change to foo" files: foo
279 |\
279 |\
280 | o 7 "change foo" files: foo
280 | o 7 "change foo" files: foo
281 | |
281 | |
282 o | 6 "change bar" files: bar
282 o | 6 "change bar" files: bar
283 |/
283 |/
284 o 5 "(octopus merge fixup)" files:
284 o 5 "(octopus merge fixup)" files:
285 |\
285 |\
286 | o 4 "Octopus merge" files: baz
286 | o 4 "Octopus merge" files: baz
287 | |\
287 | |\
288 o | | 3 "add baz" files: baz
288 o | | 3 "add baz" files: baz
289 | | |
289 | | |
290 +---o 2 "add bar" files: bar
290 +---o 2 "add bar" files: bar
291 | |
291 | |
292 | o 1 "change foo" files: foo
292 | o 1 "change foo" files: foo
293 |/
293 |/
294 o 0 "add foo" files: foo
294 o 0 "add foo" files: foo
295
295
296 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
296 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
297 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
297 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
298 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
298 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
299 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
299 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
300 % foo baz quux: only some parents of an octopus merge; "discard" a head
300 % foo baz quux: only some parents of an octopus merge; "discard" a head
301 @ 6 "Discard change to foo" files: foo
301 @ 6 "Discard change to foo" files: foo
302 |
302 |
303 o 5 "change foo" files: foo
303 o 5 "change foo" files: foo
304 |
304 |
305 o 4 "Octopus merge" files:
305 o 4 "Octopus merge" files:
306 |\
306 |\
307 | o 3 "add baz" files: baz
307 | o 3 "add baz" files: baz
308 | |
308 | |
309 | o 2 "add quux" files: quux
309 | o 2 "add quux" files: quux
310 | |
310 | |
311 o | 1 "change foo" files: foo
311 o | 1 "change foo" files: foo
312 |/
312 |/
313 o 0 "add foo" files: foo
313 o 0 "add foo" files: foo
314
314
315 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
315 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
316 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
316 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
317 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
317 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
318
318
319 test importing git renames and copies
319 test importing git renames and copies
320
320
321 $ cd git-repo2
321 $ cd git-repo2
322 $ git mv foo foo-renamed
322 $ git mv foo foo-renamed
323 since bar is not touched in this commit, this copy will not be detected
323 since bar is not touched in this commit, this copy will not be detected
324 $ cp bar bar-copied
324 $ cp bar bar-copied
325 $ cp baz baz-copied
325 $ cp baz baz-copied
326 $ cp baz baz-copied2
326 $ cp baz baz-copied2
327 $ cp baz ba-copy
327 $ cp baz ba-copy
328 $ echo baz2 >> baz
328 $ echo baz2 >> baz
329 $ git add bar-copied baz-copied baz-copied2 ba-copy
329 $ git add bar-copied baz-copied baz-copied2 ba-copy
330 $ commit -a -m 'rename and copy'
330 $ commit -a -m 'rename and copy'
331 $ cd ..
331 $ cd ..
332
332
333 input validation
333 input validation
334 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
334 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
335 abort: convert.git.similarity is not a valid integer ('foo')
335 abort: convert.git.similarity is not a valid integer ('foo')
336 [255]
336 [30]
337 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
337 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
338 abort: similarity must be between 0 and 100
338 abort: similarity must be between 0 and 100
339 [255]
339 [255]
340 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
340 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
341 abort: similarity must be between 0 and 100
341 abort: similarity must be between 0 and 100
342 [255]
342 [255]
343
343
344 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
344 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
345 $ hg -R fullrepo status -C --change master
345 $ hg -R fullrepo status -C --change master
346 M baz
346 M baz
347 A ba-copy
347 A ba-copy
348 baz
348 baz
349 A bar-copied
349 A bar-copied
350 A baz-copied
350 A baz-copied
351 baz
351 baz
352 A baz-copied2
352 A baz-copied2
353 baz
353 baz
354 A foo-renamed
354 A foo-renamed
355 foo
355 foo
356 R foo
356 R foo
357
357
358 Ensure that the modification to the copy source was preserved
358 Ensure that the modification to the copy source was preserved
359 (there was a bug where if the copy dest was alphabetically prior to the copy
359 (there was a bug where if the copy dest was alphabetically prior to the copy
360 source, the copy source took the contents of the copy dest)
360 source, the copy source took the contents of the copy dest)
361 $ hg cat -r tip fullrepo/baz
361 $ hg cat -r tip fullrepo/baz
362 baz
362 baz
363 baz2
363 baz2
364
364
365 $ cd git-repo2
365 $ cd git-repo2
366 $ echo bar2 >> bar
366 $ echo bar2 >> bar
367 $ commit -a -m 'change bar'
367 $ commit -a -m 'change bar'
368 $ cp bar bar-copied2
368 $ cp bar bar-copied2
369 $ git add bar-copied2
369 $ git add bar-copied2
370 $ commit -a -m 'copy with no changes'
370 $ commit -a -m 'copy with no changes'
371 $ cd ..
371 $ cd ..
372
372
373 $ hg -q convert --config convert.git.similarity=100 \
373 $ hg -q convert --config convert.git.similarity=100 \
374 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
374 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
375 $ hg -R fullrepo status -C --change master
375 $ hg -R fullrepo status -C --change master
376 A bar-copied2
376 A bar-copied2
377 bar
377 bar
378
378
379 renamelimit config option works
379 renamelimit config option works
380
380
381 $ cd git-repo2
381 $ cd git-repo2
382 $ cat >> copy-source << EOF
382 $ cat >> copy-source << EOF
383 > sc0
383 > sc0
384 > sc1
384 > sc1
385 > sc2
385 > sc2
386 > sc3
386 > sc3
387 > sc4
387 > sc4
388 > sc5
388 > sc5
389 > sc6
389 > sc6
390 > EOF
390 > EOF
391 $ git add copy-source
391 $ git add copy-source
392 $ commit -m 'add copy-source'
392 $ commit -m 'add copy-source'
393 $ cp copy-source source-copy0
393 $ cp copy-source source-copy0
394 $ echo 0 >> source-copy0
394 $ echo 0 >> source-copy0
395 $ cp copy-source source-copy1
395 $ cp copy-source source-copy1
396 $ echo 1 >> source-copy1
396 $ echo 1 >> source-copy1
397 $ git add source-copy0 source-copy1
397 $ git add source-copy0 source-copy1
398 $ commit -a -m 'copy copy-source 2 times'
398 $ commit -a -m 'copy copy-source 2 times'
399 $ cd ..
399 $ cd ..
400
400
401 $ hg -q convert --config convert.git.renamelimit=1 \
401 $ hg -q convert --config convert.git.renamelimit=1 \
402 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo2
402 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo2
403 $ hg -R fullrepo2 status -C --change master
403 $ hg -R fullrepo2 status -C --change master
404 A source-copy0
404 A source-copy0
405 A source-copy1
405 A source-copy1
406
406
407 $ hg -q convert --config convert.git.renamelimit=100 \
407 $ hg -q convert --config convert.git.renamelimit=100 \
408 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo3
408 > --config convert.git.findcopiesharder=true --datesort git-repo2 fullrepo3
409 $ hg -R fullrepo3 status -C --change master
409 $ hg -R fullrepo3 status -C --change master
410 A source-copy0
410 A source-copy0
411 copy-source
411 copy-source
412 A source-copy1
412 A source-copy1
413 copy-source
413 copy-source
414
414
415 test binary conversion (issue1359)
415 test binary conversion (issue1359)
416
416
417 $ count=19
417 $ count=19
418 $ mkdir git-repo3
418 $ mkdir git-repo3
419 $ cd git-repo3
419 $ cd git-repo3
420 $ git init >/dev/null 2>/dev/null
420 $ git init >/dev/null 2>/dev/null
421 $ "$PYTHON" -c 'import struct; open("b", "wb").write(b"".join([struct.Struct(">B").pack(i) for i in range(256)])*16)'
421 $ "$PYTHON" -c 'import struct; open("b", "wb").write(b"".join([struct.Struct(">B").pack(i) for i in range(256)])*16)'
422 $ git add b
422 $ git add b
423 $ commit -a -m addbinary
423 $ commit -a -m addbinary
424 $ cd ..
424 $ cd ..
425
425
426 convert binary file
426 convert binary file
427
427
428 $ hg convert git-repo3 git-repo3-hg
428 $ hg convert git-repo3 git-repo3-hg
429 initializing destination git-repo3-hg repository
429 initializing destination git-repo3-hg repository
430 scanning source...
430 scanning source...
431 sorting...
431 sorting...
432 converting...
432 converting...
433 0 addbinary
433 0 addbinary
434 updating bookmarks
434 updating bookmarks
435 $ cd git-repo3-hg
435 $ cd git-repo3-hg
436 $ hg up -C
436 $ hg up -C
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 $ "$PYTHON" -c 'from __future__ import print_function; print(len(open("b", "rb").read()))'
438 $ "$PYTHON" -c 'from __future__ import print_function; print(len(open("b", "rb").read()))'
439 4096
439 4096
440 $ cd ..
440 $ cd ..
441
441
442 test author vs committer
442 test author vs committer
443
443
444 $ mkdir git-repo4
444 $ mkdir git-repo4
445 $ cd git-repo4
445 $ cd git-repo4
446 $ git init >/dev/null 2>/dev/null
446 $ git init >/dev/null 2>/dev/null
447 $ echo >> foo
447 $ echo >> foo
448 $ git add foo
448 $ git add foo
449 $ commit -a -m addfoo
449 $ commit -a -m addfoo
450 $ echo >> foo
450 $ echo >> foo
451 $ GIT_AUTHOR_NAME="nottest"
451 $ GIT_AUTHOR_NAME="nottest"
452 $ commit -a -m addfoo2
452 $ commit -a -m addfoo2
453 $ cd ..
453 $ cd ..
454
454
455 convert author committer
455 convert author committer
456
456
457 $ hg convert git-repo4 git-repo4-hg
457 $ hg convert git-repo4 git-repo4-hg
458 initializing destination git-repo4-hg repository
458 initializing destination git-repo4-hg repository
459 scanning source...
459 scanning source...
460 sorting...
460 sorting...
461 converting...
461 converting...
462 1 addfoo
462 1 addfoo
463 0 addfoo2
463 0 addfoo2
464 updating bookmarks
464 updating bookmarks
465 $ hg -R git-repo4-hg log -v
465 $ hg -R git-repo4-hg log -v
466 changeset: 1:d63e967f93da
466 changeset: 1:d63e967f93da
467 bookmark: master
467 bookmark: master
468 tag: tip
468 tag: tip
469 user: nottest <test@example.org>
469 user: nottest <test@example.org>
470 date: Mon Jan 01 00:00:21 2007 +0000
470 date: Mon Jan 01 00:00:21 2007 +0000
471 files: foo
471 files: foo
472 description:
472 description:
473 addfoo2
473 addfoo2
474
474
475 committer: test <test@example.org>
475 committer: test <test@example.org>
476
476
477
477
478 changeset: 0:0735477b0224
478 changeset: 0:0735477b0224
479 user: test <test@example.org>
479 user: test <test@example.org>
480 date: Mon Jan 01 00:00:20 2007 +0000
480 date: Mon Jan 01 00:00:20 2007 +0000
481 files: foo
481 files: foo
482 description:
482 description:
483 addfoo
483 addfoo
484
484
485
485
486
486
487 Various combinations of committeractions fail
487 Various combinations of committeractions fail
488
488
489 $ hg --config convert.git.committeractions=messagedifferent,messagealways convert git-repo4 bad-committer
489 $ hg --config convert.git.committeractions=messagedifferent,messagealways convert git-repo4 bad-committer
490 initializing destination bad-committer repository
490 initializing destination bad-committer repository
491 abort: committeractions cannot define both messagedifferent and messagealways
491 abort: committeractions cannot define both messagedifferent and messagealways
492 [255]
492 [255]
493
493
494 $ hg --config convert.git.committeractions=dropcommitter,replaceauthor convert git-repo4 bad-committer
494 $ hg --config convert.git.committeractions=dropcommitter,replaceauthor convert git-repo4 bad-committer
495 initializing destination bad-committer repository
495 initializing destination bad-committer repository
496 abort: committeractions cannot define both dropcommitter and replaceauthor
496 abort: committeractions cannot define both dropcommitter and replaceauthor
497 [255]
497 [255]
498
498
499 $ hg --config convert.git.committeractions=dropcommitter,messagealways convert git-repo4 bad-committer
499 $ hg --config convert.git.committeractions=dropcommitter,messagealways convert git-repo4 bad-committer
500 initializing destination bad-committer repository
500 initializing destination bad-committer repository
501 abort: committeractions cannot define both dropcommitter and messagealways
501 abort: committeractions cannot define both dropcommitter and messagealways
502 [255]
502 [255]
503
503
504 custom prefix on messagedifferent works
504 custom prefix on messagedifferent works
505
505
506 $ hg --config convert.git.committeractions=messagedifferent=different: convert git-repo4 git-repo4-hg-messagedifferentprefix
506 $ hg --config convert.git.committeractions=messagedifferent=different: convert git-repo4 git-repo4-hg-messagedifferentprefix
507 initializing destination git-repo4-hg-messagedifferentprefix repository
507 initializing destination git-repo4-hg-messagedifferentprefix repository
508 scanning source...
508 scanning source...
509 sorting...
509 sorting...
510 converting...
510 converting...
511 1 addfoo
511 1 addfoo
512 0 addfoo2
512 0 addfoo2
513 updating bookmarks
513 updating bookmarks
514
514
515 $ hg -R git-repo4-hg-messagedifferentprefix log -v
515 $ hg -R git-repo4-hg-messagedifferentprefix log -v
516 changeset: 1:2fe0c98a109d
516 changeset: 1:2fe0c98a109d
517 bookmark: master
517 bookmark: master
518 tag: tip
518 tag: tip
519 user: nottest <test@example.org>
519 user: nottest <test@example.org>
520 date: Mon Jan 01 00:00:21 2007 +0000
520 date: Mon Jan 01 00:00:21 2007 +0000
521 files: foo
521 files: foo
522 description:
522 description:
523 addfoo2
523 addfoo2
524
524
525 different: test <test@example.org>
525 different: test <test@example.org>
526
526
527
527
528 changeset: 0:0735477b0224
528 changeset: 0:0735477b0224
529 user: test <test@example.org>
529 user: test <test@example.org>
530 date: Mon Jan 01 00:00:20 2007 +0000
530 date: Mon Jan 01 00:00:20 2007 +0000
531 files: foo
531 files: foo
532 description:
532 description:
533 addfoo
533 addfoo
534
534
535
535
536
536
537 messagealways will always add the "committer: " line even if committer identical
537 messagealways will always add the "committer: " line even if committer identical
538
538
539 $ hg --config convert.git.committeractions=messagealways convert git-repo4 git-repo4-hg-messagealways
539 $ hg --config convert.git.committeractions=messagealways convert git-repo4 git-repo4-hg-messagealways
540 initializing destination git-repo4-hg-messagealways repository
540 initializing destination git-repo4-hg-messagealways repository
541 scanning source...
541 scanning source...
542 sorting...
542 sorting...
543 converting...
543 converting...
544 1 addfoo
544 1 addfoo
545 0 addfoo2
545 0 addfoo2
546 updating bookmarks
546 updating bookmarks
547
547
548 $ hg -R git-repo4-hg-messagealways log -v
548 $ hg -R git-repo4-hg-messagealways log -v
549 changeset: 1:8db057d8cd37
549 changeset: 1:8db057d8cd37
550 bookmark: master
550 bookmark: master
551 tag: tip
551 tag: tip
552 user: nottest <test@example.org>
552 user: nottest <test@example.org>
553 date: Mon Jan 01 00:00:21 2007 +0000
553 date: Mon Jan 01 00:00:21 2007 +0000
554 files: foo
554 files: foo
555 description:
555 description:
556 addfoo2
556 addfoo2
557
557
558 committer: test <test@example.org>
558 committer: test <test@example.org>
559
559
560
560
561 changeset: 0:8f71fe9c98be
561 changeset: 0:8f71fe9c98be
562 user: test <test@example.org>
562 user: test <test@example.org>
563 date: Mon Jan 01 00:00:20 2007 +0000
563 date: Mon Jan 01 00:00:20 2007 +0000
564 files: foo
564 files: foo
565 description:
565 description:
566 addfoo
566 addfoo
567
567
568 committer: test <test@example.org>
568 committer: test <test@example.org>
569
569
570
570
571
571
572 custom prefix on messagealways works
572 custom prefix on messagealways works
573
573
574 $ hg --config convert.git.committeractions=messagealways=always: convert git-repo4 git-repo4-hg-messagealwaysprefix
574 $ hg --config convert.git.committeractions=messagealways=always: convert git-repo4 git-repo4-hg-messagealwaysprefix
575 initializing destination git-repo4-hg-messagealwaysprefix repository
575 initializing destination git-repo4-hg-messagealwaysprefix repository
576 scanning source...
576 scanning source...
577 sorting...
577 sorting...
578 converting...
578 converting...
579 1 addfoo
579 1 addfoo
580 0 addfoo2
580 0 addfoo2
581 updating bookmarks
581 updating bookmarks
582
582
583 $ hg -R git-repo4-hg-messagealwaysprefix log -v
583 $ hg -R git-repo4-hg-messagealwaysprefix log -v
584 changeset: 1:83c17174de79
584 changeset: 1:83c17174de79
585 bookmark: master
585 bookmark: master
586 tag: tip
586 tag: tip
587 user: nottest <test@example.org>
587 user: nottest <test@example.org>
588 date: Mon Jan 01 00:00:21 2007 +0000
588 date: Mon Jan 01 00:00:21 2007 +0000
589 files: foo
589 files: foo
590 description:
590 description:
591 addfoo2
591 addfoo2
592
592
593 always: test <test@example.org>
593 always: test <test@example.org>
594
594
595
595
596 changeset: 0:2ac9bcb3534a
596 changeset: 0:2ac9bcb3534a
597 user: test <test@example.org>
597 user: test <test@example.org>
598 date: Mon Jan 01 00:00:20 2007 +0000
598 date: Mon Jan 01 00:00:20 2007 +0000
599 files: foo
599 files: foo
600 description:
600 description:
601 addfoo
601 addfoo
602
602
603 always: test <test@example.org>
603 always: test <test@example.org>
604
604
605
605
606
606
607 replaceauthor replaces author with committer
607 replaceauthor replaces author with committer
608
608
609 $ hg --config convert.git.committeractions=replaceauthor convert git-repo4 git-repo4-hg-replaceauthor
609 $ hg --config convert.git.committeractions=replaceauthor convert git-repo4 git-repo4-hg-replaceauthor
610 initializing destination git-repo4-hg-replaceauthor repository
610 initializing destination git-repo4-hg-replaceauthor repository
611 scanning source...
611 scanning source...
612 sorting...
612 sorting...
613 converting...
613 converting...
614 1 addfoo
614 1 addfoo
615 0 addfoo2
615 0 addfoo2
616 updating bookmarks
616 updating bookmarks
617
617
618 $ hg -R git-repo4-hg-replaceauthor log -v
618 $ hg -R git-repo4-hg-replaceauthor log -v
619 changeset: 1:122c1d8999ea
619 changeset: 1:122c1d8999ea
620 bookmark: master
620 bookmark: master
621 tag: tip
621 tag: tip
622 user: test <test@example.org>
622 user: test <test@example.org>
623 date: Mon Jan 01 00:00:21 2007 +0000
623 date: Mon Jan 01 00:00:21 2007 +0000
624 files: foo
624 files: foo
625 description:
625 description:
626 addfoo2
626 addfoo2
627
627
628
628
629 changeset: 0:0735477b0224
629 changeset: 0:0735477b0224
630 user: test <test@example.org>
630 user: test <test@example.org>
631 date: Mon Jan 01 00:00:20 2007 +0000
631 date: Mon Jan 01 00:00:20 2007 +0000
632 files: foo
632 files: foo
633 description:
633 description:
634 addfoo
634 addfoo
635
635
636
636
637
637
638 dropcommitter removes the committer
638 dropcommitter removes the committer
639
639
640 $ hg --config convert.git.committeractions=dropcommitter convert git-repo4 git-repo4-hg-dropcommitter
640 $ hg --config convert.git.committeractions=dropcommitter convert git-repo4 git-repo4-hg-dropcommitter
641 initializing destination git-repo4-hg-dropcommitter repository
641 initializing destination git-repo4-hg-dropcommitter repository
642 scanning source...
642 scanning source...
643 sorting...
643 sorting...
644 converting...
644 converting...
645 1 addfoo
645 1 addfoo
646 0 addfoo2
646 0 addfoo2
647 updating bookmarks
647 updating bookmarks
648
648
649 $ hg -R git-repo4-hg-dropcommitter log -v
649 $ hg -R git-repo4-hg-dropcommitter log -v
650 changeset: 1:190b2da396cc
650 changeset: 1:190b2da396cc
651 bookmark: master
651 bookmark: master
652 tag: tip
652 tag: tip
653 user: nottest <test@example.org>
653 user: nottest <test@example.org>
654 date: Mon Jan 01 00:00:21 2007 +0000
654 date: Mon Jan 01 00:00:21 2007 +0000
655 files: foo
655 files: foo
656 description:
656 description:
657 addfoo2
657 addfoo2
658
658
659
659
660 changeset: 0:0735477b0224
660 changeset: 0:0735477b0224
661 user: test <test@example.org>
661 user: test <test@example.org>
662 date: Mon Jan 01 00:00:20 2007 +0000
662 date: Mon Jan 01 00:00:20 2007 +0000
663 files: foo
663 files: foo
664 description:
664 description:
665 addfoo
665 addfoo
666
666
667
667
668
668
669 --sourceorder should fail
669 --sourceorder should fail
670
670
671 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
671 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
672 initializing destination git-repo4-sourcesort-hg repository
672 initializing destination git-repo4-sourcesort-hg repository
673 abort: --sourcesort is not supported by this data source
673 abort: --sourcesort is not supported by this data source
674 [255]
674 [255]
675
675
676 test converting certain branches
676 test converting certain branches
677
677
678 $ mkdir git-testrevs
678 $ mkdir git-testrevs
679 $ cd git-testrevs
679 $ cd git-testrevs
680 $ git init
680 $ git init
681 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
681 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
682 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
682 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
683 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
683 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
684 $ git checkout -b goodbranch 'HEAD^'
684 $ git checkout -b goodbranch 'HEAD^'
685 Switched to a new branch 'goodbranch'
685 Switched to a new branch 'goodbranch'
686 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
686 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
687 $ git checkout -b badbranch 'HEAD^'
687 $ git checkout -b badbranch 'HEAD^'
688 Switched to a new branch 'badbranch'
688 Switched to a new branch 'badbranch'
689 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
689 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
690 $ cd ..
690 $ cd ..
691 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
691 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
692 initializing destination hg-testrevs repository
692 initializing destination hg-testrevs repository
693 scanning source...
693 scanning source...
694 sorting...
694 sorting...
695 converting...
695 converting...
696 2 first
696 2 first
697 1 good branch commit
697 1 good branch commit
698 0 master commit
698 0 master commit
699 updating bookmarks
699 updating bookmarks
700 $ cd hg-testrevs
700 $ cd hg-testrevs
701 $ hg log -G -T '{rev} {bookmarks}'
701 $ hg log -G -T '{rev} {bookmarks}'
702 o 2 master
702 o 2 master
703 |
703 |
704 | o 1 goodbranch
704 | o 1 goodbranch
705 |/
705 |/
706 o 0
706 o 0
707
707
708 $ cd ..
708 $ cd ..
709
709
710 test sub modules
710 test sub modules
711
711
712 $ mkdir git-repo5
712 $ mkdir git-repo5
713 $ cd git-repo5
713 $ cd git-repo5
714 $ git init >/dev/null 2>/dev/null
714 $ git init >/dev/null 2>/dev/null
715 $ echo 'sub' >> foo
715 $ echo 'sub' >> foo
716 $ git add foo
716 $ git add foo
717 $ commit -a -m 'addfoo'
717 $ commit -a -m 'addfoo'
718 $ BASE=`pwd`
718 $ BASE=`pwd`
719 $ cd ..
719 $ cd ..
720 $ mkdir git-repo6
720 $ mkdir git-repo6
721 $ cd git-repo6
721 $ cd git-repo6
722 $ git init >/dev/null 2>/dev/null
722 $ git init >/dev/null 2>/dev/null
723 $ git submodule add ${BASE} >/dev/null 2>/dev/null
723 $ git submodule add ${BASE} >/dev/null 2>/dev/null
724 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
724 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
725
725
726 test non-tab whitespace .gitmodules
726 test non-tab whitespace .gitmodules
727
727
728 $ cat >> .gitmodules <<EOF
728 $ cat >> .gitmodules <<EOF
729 > [submodule "git-repo5"]
729 > [submodule "git-repo5"]
730 > path = git-repo5
730 > path = git-repo5
731 > url = git-repo5
731 > url = git-repo5
732 > EOF
732 > EOF
733 $ git commit -q -a -m "weird white space submodule"
733 $ git commit -q -a -m "weird white space submodule"
734 $ cd ..
734 $ cd ..
735 $ hg convert git-repo6 hg-repo6
735 $ hg convert git-repo6 hg-repo6
736 initializing destination hg-repo6 repository
736 initializing destination hg-repo6 repository
737 scanning source...
737 scanning source...
738 sorting...
738 sorting...
739 converting...
739 converting...
740 1 addsubmodule
740 1 addsubmodule
741 0 weird white space submodule
741 0 weird white space submodule
742 updating bookmarks
742 updating bookmarks
743
743
744 $ rm -rf hg-repo6
744 $ rm -rf hg-repo6
745 $ cd git-repo6
745 $ cd git-repo6
746 $ git reset --hard 'HEAD^' > /dev/null
746 $ git reset --hard 'HEAD^' > /dev/null
747
747
748 test missing .gitmodules
748 test missing .gitmodules
749
749
750 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
750 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
751 $ git checkout HEAD -- .gitmodules
751 $ git checkout HEAD -- .gitmodules
752 $ git rm .gitmodules
752 $ git rm .gitmodules
753 rm '.gitmodules'
753 rm '.gitmodules'
754 $ git commit -q -m "remove .gitmodules" .gitmodules
754 $ git commit -q -m "remove .gitmodules" .gitmodules
755 $ git commit -q -m "missing .gitmodules"
755 $ git commit -q -m "missing .gitmodules"
756 $ cd ..
756 $ cd ..
757 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
757 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
758 initializing destination hg-repo6 repository
758 initializing destination hg-repo6 repository
759 scanning source...
759 scanning source...
760 sorting...
760 sorting...
761 converting...
761 converting...
762 2 addsubmodule
762 2 addsubmodule
763 1 remove .gitmodules
763 1 remove .gitmodules
764 0 missing .gitmodules
764 0 missing .gitmodules
765 warning: cannot read submodules config file in * (glob)
765 warning: cannot read submodules config file in * (glob)
766 updating bookmarks
766 updating bookmarks
767 $ rm -rf hg-repo6
767 $ rm -rf hg-repo6
768 $ cd git-repo6
768 $ cd git-repo6
769 $ rm -rf git-repo4
769 $ rm -rf git-repo4
770 $ git reset --hard 'HEAD^^' > /dev/null
770 $ git reset --hard 'HEAD^^' > /dev/null
771 $ cd ..
771 $ cd ..
772
772
773 test invalid splicemap1
773 test invalid splicemap1
774
774
775 $ cat > splicemap <<EOF
775 $ cat > splicemap <<EOF
776 > $VALIDID1
776 > $VALIDID1
777 > EOF
777 > EOF
778 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
778 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
779 initializing destination git-repo2-splicemap1-hg repository
779 initializing destination git-repo2-splicemap1-hg repository
780 abort: syntax error in splicemap(1): child parent1[,parent2] expected
780 abort: syntax error in splicemap(1): child parent1[,parent2] expected
781 [255]
781 [255]
782
782
783 test invalid splicemap2
783 test invalid splicemap2
784
784
785 $ cat > splicemap <<EOF
785 $ cat > splicemap <<EOF
786 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
786 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
787 > EOF
787 > EOF
788 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
788 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
789 initializing destination git-repo2-splicemap2-hg repository
789 initializing destination git-repo2-splicemap2-hg repository
790 abort: syntax error in splicemap(1): child parent1[,parent2] expected
790 abort: syntax error in splicemap(1): child parent1[,parent2] expected
791 [255]
791 [255]
792
792
793 test invalid splicemap3
793 test invalid splicemap3
794
794
795 $ cat > splicemap <<EOF
795 $ cat > splicemap <<EOF
796 > $INVALIDID1 $INVALIDID2
796 > $INVALIDID1 $INVALIDID2
797 > EOF
797 > EOF
798 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
798 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
799 initializing destination git-repo2-splicemap3-hg repository
799 initializing destination git-repo2-splicemap3-hg repository
800 abort: splicemap entry afd12345af is not a valid revision identifier
800 abort: splicemap entry afd12345af is not a valid revision identifier
801 [255]
801 [255]
802
802
803 convert sub modules
803 convert sub modules
804 $ hg convert git-repo6 git-repo6-hg
804 $ hg convert git-repo6 git-repo6-hg
805 initializing destination git-repo6-hg repository
805 initializing destination git-repo6-hg repository
806 scanning source...
806 scanning source...
807 sorting...
807 sorting...
808 converting...
808 converting...
809 0 addsubmodule
809 0 addsubmodule
810 updating bookmarks
810 updating bookmarks
811 $ hg -R git-repo6-hg log -v
811 $ hg -R git-repo6-hg log -v
812 changeset: 0:* (glob)
812 changeset: 0:* (glob)
813 bookmark: master
813 bookmark: master
814 tag: tip
814 tag: tip
815 user: nottest <test@example.org>
815 user: nottest <test@example.org>
816 date: Mon Jan 01 00:00:23 2007 +0000
816 date: Mon Jan 01 00:00:23 2007 +0000
817 files: .hgsub .hgsubstate
817 files: .hgsub .hgsubstate
818 description:
818 description:
819 addsubmodule
819 addsubmodule
820
820
821 committer: test <test@example.org>
821 committer: test <test@example.org>
822
822
823
823
824
824
825 $ cd git-repo6-hg
825 $ cd git-repo6-hg
826 $ hg up >/dev/null 2>/dev/null
826 $ hg up >/dev/null 2>/dev/null
827 $ cat .hgsubstate
827 $ cat .hgsubstate
828 * git-repo5 (glob)
828 * git-repo5 (glob)
829 $ cd git-repo5
829 $ cd git-repo5
830 $ cat foo
830 $ cat foo
831 sub
831 sub
832
832
833 $ cd ../..
833 $ cd ../..
834
834
835 make sure rename detection doesn't break removing and adding gitmodules
835 make sure rename detection doesn't break removing and adding gitmodules
836
836
837 $ cd git-repo6
837 $ cd git-repo6
838 $ git mv .gitmodules .gitmodules-renamed
838 $ git mv .gitmodules .gitmodules-renamed
839 $ commit -a -m 'rename .gitmodules'
839 $ commit -a -m 'rename .gitmodules'
840 $ git mv .gitmodules-renamed .gitmodules
840 $ git mv .gitmodules-renamed .gitmodules
841 $ commit -a -m 'rename .gitmodules back'
841 $ commit -a -m 'rename .gitmodules back'
842 $ cd ..
842 $ cd ..
843
843
844 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
844 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
845 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
845 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
846 rename .gitmodules
846 rename .gitmodules
847 $ hg -R git-repo6-hg status -C --change 'tip^'
847 $ hg -R git-repo6-hg status -C --change 'tip^'
848 A .gitmodules-renamed
848 A .gitmodules-renamed
849 R .hgsub
849 R .hgsub
850 R .hgsubstate
850 R .hgsubstate
851 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
851 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
852 rename .gitmodules back
852 rename .gitmodules back
853 $ hg -R git-repo6-hg status -C --change tip
853 $ hg -R git-repo6-hg status -C --change tip
854 A .hgsub
854 A .hgsub
855 A .hgsubstate
855 A .hgsubstate
856 R .gitmodules-renamed
856 R .gitmodules-renamed
857
857
858 convert the revision removing '.gitmodules' itself (and related
858 convert the revision removing '.gitmodules' itself (and related
859 submodules)
859 submodules)
860
860
861 $ cd git-repo6
861 $ cd git-repo6
862 $ git rm .gitmodules
862 $ git rm .gitmodules
863 rm '.gitmodules'
863 rm '.gitmodules'
864 $ git rm --cached git-repo5
864 $ git rm --cached git-repo5
865 rm 'git-repo5'
865 rm 'git-repo5'
866 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
866 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
867 $ cd ..
867 $ cd ..
868
868
869 $ hg convert -q git-repo6 git-repo6-hg
869 $ hg convert -q git-repo6 git-repo6-hg
870 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
870 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
871 remove .gitmodules and submodule git-repo5
871 remove .gitmodules and submodule git-repo5
872 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
872 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
873 .hgsub .hgsubstate
873 .hgsub .hgsubstate
874
874
875 skip submodules in the conversion
875 skip submodules in the conversion
876
876
877 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
877 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
878 $ hg -R no-submodules manifest --all
878 $ hg -R no-submodules manifest --all
879 .gitmodules-renamed
879 .gitmodules-renamed
880
880
881 convert using a different remote prefix
881 convert using a different remote prefix
882 $ git init git-repo7
882 $ git init git-repo7
883 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
883 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
884 $ cd git-repo7
884 $ cd git-repo7
885 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
885 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
886 git output variance, but that doesn't currently work in the middle of
886 git output variance, but that doesn't currently work in the middle of
887 a block, so do this for now.
887 a block, so do this for now.
888 $ touch a && git add a && git commit -am "commit a" | grep -v changed
888 $ touch a && git add a && git commit -am "commit a" | grep -v changed
889 [master (root-commit) 8ae5f69] commit a
889 [master (root-commit) 8ae5f69] commit a
890 Author: nottest <test@example.org>
890 Author: nottest <test@example.org>
891 create mode 100644 a
891 create mode 100644 a
892 $ cd ..
892 $ cd ..
893 $ git clone git-repo7 git-repo7-client
893 $ git clone git-repo7 git-repo7-client
894 Cloning into 'git-repo7-client'...
894 Cloning into 'git-repo7-client'...
895 done.
895 done.
896 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
896 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
897 initializing destination hg-repo7 repository
897 initializing destination hg-repo7 repository
898 scanning source...
898 scanning source...
899 sorting...
899 sorting...
900 converting...
900 converting...
901 0 commit a
901 0 commit a
902 updating bookmarks
902 updating bookmarks
903 $ hg -R hg-repo7 bookmarks
903 $ hg -R hg-repo7 bookmarks
904 master 0:03bf38caa4c6
904 master 0:03bf38caa4c6
905 origin/master 0:03bf38caa4c6
905 origin/master 0:03bf38caa4c6
906
906
907 Run convert when the remote branches have changed
907 Run convert when the remote branches have changed
908 (there was an old bug where the local convert read branches from the server)
908 (there was an old bug where the local convert read branches from the server)
909
909
910 $ cd git-repo7
910 $ cd git-repo7
911 $ echo a >> a
911 $ echo a >> a
912 $ git commit -q -am "move master forward"
912 $ git commit -q -am "move master forward"
913 $ cd ..
913 $ cd ..
914 $ rm -rf hg-repo7
914 $ rm -rf hg-repo7
915 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
915 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
916 initializing destination hg-repo7 repository
916 initializing destination hg-repo7 repository
917 scanning source...
917 scanning source...
918 sorting...
918 sorting...
919 converting...
919 converting...
920 0 commit a
920 0 commit a
921 updating bookmarks
921 updating bookmarks
922 $ hg -R hg-repo7 bookmarks
922 $ hg -R hg-repo7 bookmarks
923 master 0:03bf38caa4c6
923 master 0:03bf38caa4c6
924 origin/master 0:03bf38caa4c6
924 origin/master 0:03bf38caa4c6
925
925
926 damaged git repository tests:
926 damaged git repository tests:
927 In case the hard-coded hashes change, the following commands can be used to
927 In case the hard-coded hashes change, the following commands can be used to
928 list the hashes and their corresponding types in the repository:
928 list the hashes and their corresponding types in the repository:
929 cd git-repo4/.git/objects
929 cd git-repo4/.git/objects
930 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
930 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
931 cd ../../..
931 cd ../../..
932
932
933 damage git repository by renaming a commit object
933 damage git repository by renaming a commit object
934 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
934 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
935 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
935 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
936 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
936 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
937 abort: cannot retrieve number of commits in $TESTTMP/git-repo4/.git
937 abort: cannot retrieve number of commits in $TESTTMP/git-repo4/.git
938 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
938 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
939 damage git repository by renaming a blob object
939 damage git repository by renaming a blob object
940
940
941 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
941 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
942 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
942 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
943 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
943 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
944 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
944 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
945 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
945 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
946 damage git repository by renaming a tree object
946 damage git repository by renaming a tree object
947
947
948 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
948 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
949 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
949 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
950 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
950 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
951 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
951 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
952
952
953 #if no-windows git19
953 #if no-windows git19
954
954
955 test for escaping the repo name (CVE-2016-3069)
955 test for escaping the repo name (CVE-2016-3069)
956
956
957 $ git init '`echo pwned >COMMAND-INJECTION`'
957 $ git init '`echo pwned >COMMAND-INJECTION`'
958 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
958 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
959 $ cd '`echo pwned >COMMAND-INJECTION`'
959 $ cd '`echo pwned >COMMAND-INJECTION`'
960 $ git commit -q --allow-empty -m 'empty'
960 $ git commit -q --allow-empty -m 'empty'
961 $ cd ..
961 $ cd ..
962 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
962 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
963 initializing destination converted repository
963 initializing destination converted repository
964 scanning source...
964 scanning source...
965 sorting...
965 sorting...
966 converting...
966 converting...
967 0 empty
967 0 empty
968 updating bookmarks
968 updating bookmarks
969 $ test -f COMMAND-INJECTION
969 $ test -f COMMAND-INJECTION
970 [1]
970 [1]
971
971
972 test for safely passing paths to git (CVE-2016-3105)
972 test for safely passing paths to git (CVE-2016-3105)
973
973
974 $ git init 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
974 $ git init 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
975 Initialized empty Git repository in $TESTTMP/ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #/.git/
975 Initialized empty Git repository in $TESTTMP/ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #/.git/
976 $ cd 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
976 $ cd 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
977 $ git commit -q --allow-empty -m 'empty'
977 $ git commit -q --allow-empty -m 'empty'
978 $ cd ..
978 $ cd ..
979 $ hg convert 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #' 'converted-git-ext'
979 $ hg convert 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #' 'converted-git-ext'
980 initializing destination converted-git-ext repository
980 initializing destination converted-git-ext repository
981 scanning source...
981 scanning source...
982 sorting...
982 sorting...
983 converting...
983 converting...
984 0 empty
984 0 empty
985 updating bookmarks
985 updating bookmarks
986 $ test -f GIT-EXT-COMMAND-INJECTION
986 $ test -f GIT-EXT-COMMAND-INJECTION
987 [1]
987 [1]
988
988
989 #endif
989 #endif
990
990
991 Conversion of extra commit metadata to extras works
991 Conversion of extra commit metadata to extras works
992
992
993 $ git init gitextras >/dev/null 2>/dev/null
993 $ git init gitextras >/dev/null 2>/dev/null
994 $ cd gitextras
994 $ cd gitextras
995 $ touch foo
995 $ touch foo
996 $ git add foo
996 $ git add foo
997 $ commit -m initial
997 $ commit -m initial
998 $ echo 1 > foo
998 $ echo 1 > foo
999 $ tree=`git write-tree`
999 $ tree=`git write-tree`
1000
1000
1001 Git doesn't provider a user-facing API to write extra metadata into the
1001 Git doesn't provider a user-facing API to write extra metadata into the
1002 commit, so create the commit object by hand
1002 commit, so create the commit object by hand
1003
1003
1004 $ git hash-object -t commit -w --stdin << EOF
1004 $ git hash-object -t commit -w --stdin << EOF
1005 > tree ${tree}
1005 > tree ${tree}
1006 > parent ba6b1344e977ece9e00958dbbf17f1f09384b2c1
1006 > parent ba6b1344e977ece9e00958dbbf17f1f09384b2c1
1007 > author test <test@example.com> 1000000000 +0000
1007 > author test <test@example.com> 1000000000 +0000
1008 > committer test <test@example.com> 1000000000 +0000
1008 > committer test <test@example.com> 1000000000 +0000
1009 > extra-1 extra-1
1009 > extra-1 extra-1
1010 > extra-2 extra-2 with space
1010 > extra-2 extra-2 with space
1011 > convert_revision 0000aaaabbbbccccddddeeee
1011 > convert_revision 0000aaaabbbbccccddddeeee
1012 >
1012 >
1013 > message with extras
1013 > message with extras
1014 > EOF
1014 > EOF
1015 8123727c8361a4117d1a2d80e0c4e7d70c757f18
1015 8123727c8361a4117d1a2d80e0c4e7d70c757f18
1016
1016
1017 $ git reset --hard 8123727c8361a4117d1a2d80e0c4e7d70c757f18 > /dev/null
1017 $ git reset --hard 8123727c8361a4117d1a2d80e0c4e7d70c757f18 > /dev/null
1018
1018
1019 $ cd ..
1019 $ cd ..
1020
1020
1021 convert will not retain custom metadata keys by default
1021 convert will not retain custom metadata keys by default
1022
1022
1023 $ hg convert gitextras hgextras1
1023 $ hg convert gitextras hgextras1
1024 initializing destination hgextras1 repository
1024 initializing destination hgextras1 repository
1025 scanning source...
1025 scanning source...
1026 sorting...
1026 sorting...
1027 converting...
1027 converting...
1028 1 initial
1028 1 initial
1029 0 message with extras
1029 0 message with extras
1030 updating bookmarks
1030 updating bookmarks
1031
1031
1032 $ hg -R hgextras1 log --debug -r 1
1032 $ hg -R hgextras1 log --debug -r 1
1033 changeset: 1:e13a39880f68479127b2a80fa0b448cc8524aa09
1033 changeset: 1:e13a39880f68479127b2a80fa0b448cc8524aa09
1034 bookmark: master
1034 bookmark: master
1035 tag: tip
1035 tag: tip
1036 phase: draft
1036 phase: draft
1037 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1037 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1038 parent: -1:0000000000000000000000000000000000000000
1038 parent: -1:0000000000000000000000000000000000000000
1039 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1039 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1040 user: test <test@example.com>
1040 user: test <test@example.com>
1041 date: Sun Sep 09 01:46:40 2001 +0000
1041 date: Sun Sep 09 01:46:40 2001 +0000
1042 extra: branch=default
1042 extra: branch=default
1043 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1043 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1044 description:
1044 description:
1045 message with extras
1045 message with extras
1046
1046
1047
1047
1048
1048
1049 Attempting to convert a banned extra is disallowed
1049 Attempting to convert a banned extra is disallowed
1050
1050
1051 $ hg convert --config convert.git.extrakeys=tree,parent gitextras hgextras-banned
1051 $ hg convert --config convert.git.extrakeys=tree,parent gitextras hgextras-banned
1052 initializing destination hgextras-banned repository
1052 initializing destination hgextras-banned repository
1053 abort: copying of extra key is forbidden: parent, tree
1053 abort: copying of extra key is forbidden: parent, tree
1054 [255]
1054 [255]
1055
1055
1056 Converting a specific extra works
1056 Converting a specific extra works
1057
1057
1058 $ hg convert --config convert.git.extrakeys=extra-1 gitextras hgextras2
1058 $ hg convert --config convert.git.extrakeys=extra-1 gitextras hgextras2
1059 initializing destination hgextras2 repository
1059 initializing destination hgextras2 repository
1060 scanning source...
1060 scanning source...
1061 sorting...
1061 sorting...
1062 converting...
1062 converting...
1063 1 initial
1063 1 initial
1064 0 message with extras
1064 0 message with extras
1065 updating bookmarks
1065 updating bookmarks
1066
1066
1067 $ hg -R hgextras2 log --debug -r 1
1067 $ hg -R hgextras2 log --debug -r 1
1068 changeset: 1:d40fb205d58597e6ecfd55b16f198be5bf436391
1068 changeset: 1:d40fb205d58597e6ecfd55b16f198be5bf436391
1069 bookmark: master
1069 bookmark: master
1070 tag: tip
1070 tag: tip
1071 phase: draft
1071 phase: draft
1072 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1072 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1073 parent: -1:0000000000000000000000000000000000000000
1073 parent: -1:0000000000000000000000000000000000000000
1074 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1074 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1075 user: test <test@example.com>
1075 user: test <test@example.com>
1076 date: Sun Sep 09 01:46:40 2001 +0000
1076 date: Sun Sep 09 01:46:40 2001 +0000
1077 extra: branch=default
1077 extra: branch=default
1078 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1078 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1079 extra: extra-1=extra-1
1079 extra: extra-1=extra-1
1080 description:
1080 description:
1081 message with extras
1081 message with extras
1082
1082
1083
1083
1084
1084
1085 Converting multiple extras works
1085 Converting multiple extras works
1086
1086
1087 $ hg convert --config convert.git.extrakeys=extra-1,extra-2 gitextras hgextras3
1087 $ hg convert --config convert.git.extrakeys=extra-1,extra-2 gitextras hgextras3
1088 initializing destination hgextras3 repository
1088 initializing destination hgextras3 repository
1089 scanning source...
1089 scanning source...
1090 sorting...
1090 sorting...
1091 converting...
1091 converting...
1092 1 initial
1092 1 initial
1093 0 message with extras
1093 0 message with extras
1094 updating bookmarks
1094 updating bookmarks
1095
1095
1096 $ hg -R hgextras3 log --debug -r 1
1096 $ hg -R hgextras3 log --debug -r 1
1097 changeset: 1:0105af33379e7b6491501fd34141b7af700fe125
1097 changeset: 1:0105af33379e7b6491501fd34141b7af700fe125
1098 bookmark: master
1098 bookmark: master
1099 tag: tip
1099 tag: tip
1100 phase: draft
1100 phase: draft
1101 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1101 parent: 0:dcb68977c55cd02cbd13b901df65c4b6e7b9c4b9
1102 parent: -1:0000000000000000000000000000000000000000
1102 parent: -1:0000000000000000000000000000000000000000
1103 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1103 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1104 user: test <test@example.com>
1104 user: test <test@example.com>
1105 date: Sun Sep 09 01:46:40 2001 +0000
1105 date: Sun Sep 09 01:46:40 2001 +0000
1106 extra: branch=default
1106 extra: branch=default
1107 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1107 extra: convert_revision=8123727c8361a4117d1a2d80e0c4e7d70c757f18
1108 extra: extra-1=extra-1
1108 extra: extra-1=extra-1
1109 extra: extra-2=extra-2 with space
1109 extra: extra-2=extra-2 with space
1110 description:
1110 description:
1111 message with extras
1111 message with extras
1112
1112
1113
1113
1114
1114
1115 convert.git.saverev can be disabled to prevent convert_revision from being written
1115 convert.git.saverev can be disabled to prevent convert_revision from being written
1116
1116
1117 $ hg convert --config convert.git.saverev=false gitextras hgextras4
1117 $ hg convert --config convert.git.saverev=false gitextras hgextras4
1118 initializing destination hgextras4 repository
1118 initializing destination hgextras4 repository
1119 scanning source...
1119 scanning source...
1120 sorting...
1120 sorting...
1121 converting...
1121 converting...
1122 1 initial
1122 1 initial
1123 0 message with extras
1123 0 message with extras
1124 updating bookmarks
1124 updating bookmarks
1125
1125
1126 $ hg -R hgextras4 log --debug -r 1
1126 $ hg -R hgextras4 log --debug -r 1
1127 changeset: 1:1dcaf4ffe5bee43fa86db2800821f6f0af212c5c
1127 changeset: 1:1dcaf4ffe5bee43fa86db2800821f6f0af212c5c
1128 bookmark: master
1128 bookmark: master
1129 tag: tip
1129 tag: tip
1130 phase: draft
1130 phase: draft
1131 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1131 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1132 parent: -1:0000000000000000000000000000000000000000
1132 parent: -1:0000000000000000000000000000000000000000
1133 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1133 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1134 user: test <test@example.com>
1134 user: test <test@example.com>
1135 date: Sun Sep 09 01:46:40 2001 +0000
1135 date: Sun Sep 09 01:46:40 2001 +0000
1136 extra: branch=default
1136 extra: branch=default
1137 description:
1137 description:
1138 message with extras
1138 message with extras
1139
1139
1140
1140
1141
1141
1142 convert.git.saverev and convert.git.extrakeys can be combined to preserve
1142 convert.git.saverev and convert.git.extrakeys can be combined to preserve
1143 convert_revision from source
1143 convert_revision from source
1144
1144
1145 $ hg convert --config convert.git.saverev=false --config convert.git.extrakeys=convert_revision gitextras hgextras5
1145 $ hg convert --config convert.git.saverev=false --config convert.git.extrakeys=convert_revision gitextras hgextras5
1146 initializing destination hgextras5 repository
1146 initializing destination hgextras5 repository
1147 scanning source...
1147 scanning source...
1148 sorting...
1148 sorting...
1149 converting...
1149 converting...
1150 1 initial
1150 1 initial
1151 0 message with extras
1151 0 message with extras
1152 updating bookmarks
1152 updating bookmarks
1153
1153
1154 $ hg -R hgextras5 log --debug -r 1
1154 $ hg -R hgextras5 log --debug -r 1
1155 changeset: 1:574d85931544d4542007664fee3747360e85ee28
1155 changeset: 1:574d85931544d4542007664fee3747360e85ee28
1156 bookmark: master
1156 bookmark: master
1157 tag: tip
1157 tag: tip
1158 phase: draft
1158 phase: draft
1159 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1159 parent: 0:a13935fec4daf06a5a87a7307ccb0fc94f98d06d
1160 parent: -1:0000000000000000000000000000000000000000
1160 parent: -1:0000000000000000000000000000000000000000
1161 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1161 manifest: 0:6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50
1162 user: test <test@example.com>
1162 user: test <test@example.com>
1163 date: Sun Sep 09 01:46:40 2001 +0000
1163 date: Sun Sep 09 01:46:40 2001 +0000
1164 extra: branch=default
1164 extra: branch=default
1165 extra: convert_revision=0000aaaabbbbccccddddeeee
1165 extra: convert_revision=0000aaaabbbbccccddddeeee
1166 description:
1166 description:
1167 message with extras
1167 message with extras
1168
1168
1169
1169
@@ -1,439 +1,439
1 $ cat <<EOF > merge
1 $ cat <<EOF > merge
2 > from __future__ import print_function
2 > from __future__ import print_function
3 > import sys, os
3 > import sys, os
4 >
4 >
5 > try:
5 > try:
6 > import msvcrt
6 > import msvcrt
7 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
7 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
8 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
8 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
9 > except ImportError:
9 > except ImportError:
10 > pass
10 > pass
11 >
11 >
12 > print("merging for", os.path.basename(sys.argv[1]))
12 > print("merging for", os.path.basename(sys.argv[1]))
13 > EOF
13 > EOF
14 $ HGMERGE="\"$PYTHON\" ../merge"; export HGMERGE
14 $ HGMERGE="\"$PYTHON\" ../merge"; export HGMERGE
15
15
16 $ hg init t
16 $ hg init t
17 $ cd t
17 $ cd t
18 $ echo This is file a1 > a
18 $ echo This is file a1 > a
19 $ hg add a
19 $ hg add a
20 $ hg commit -m "commit #0"
20 $ hg commit -m "commit #0"
21 $ echo This is file b1 > b
21 $ echo This is file b1 > b
22 $ hg add b
22 $ hg add b
23 $ hg commit -m "commit #1"
23 $ hg commit -m "commit #1"
24
24
25 $ hg update 0
25 $ hg update 0
26 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
26 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
27
27
28 Test interrupted updates by having a non-empty dir with the same name as one
28 Test interrupted updates by having a non-empty dir with the same name as one
29 of the files in a commit we're updating to
29 of the files in a commit we're updating to
30
30
31 $ mkdir b && touch b/nonempty
31 $ mkdir b && touch b/nonempty
32 $ hg up
32 $ hg up
33 abort: Unlinking directory not permitted: *$TESTTMP/t/b* (glob) (windows !)
33 abort: Unlinking directory not permitted: *$TESTTMP/t/b* (glob) (windows !)
34 abort: Directory not empty: '?\$TESTTMP/t/b'? (re) (no-windows !)
34 abort: Directory not empty: '?\$TESTTMP/t/b'? (re) (no-windows !)
35 [255]
35 [255]
36 $ hg ci
36 $ hg ci
37 abort: last update was interrupted
37 abort: last update was interrupted
38 (use 'hg update' to get a consistent checkout)
38 (use 'hg update' to get a consistent checkout)
39 [20]
39 [20]
40 $ hg sum
40 $ hg sum
41 parent: 0:538afb845929
41 parent: 0:538afb845929
42 commit #0
42 commit #0
43 branch: default
43 branch: default
44 commit: 1 unknown (interrupted update)
44 commit: 1 unknown (interrupted update)
45 update: 1 new changesets (update)
45 update: 1 new changesets (update)
46 phases: 2 draft
46 phases: 2 draft
47 Detect interrupted update by hg status --verbose
47 Detect interrupted update by hg status --verbose
48 $ hg status -v
48 $ hg status -v
49 ? b/nonempty
49 ? b/nonempty
50 # The repository is in an unfinished *update* state.
50 # The repository is in an unfinished *update* state.
51
51
52 # To continue: hg update .
52 # To continue: hg update .
53
53
54
54
55 $ rm b/nonempty
55 $ rm b/nonempty
56
56
57 $ hg up
57 $ hg up
58 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 $ hg sum
59 $ hg sum
60 parent: 1:b8bb4a988f25 tip
60 parent: 1:b8bb4a988f25 tip
61 commit #1
61 commit #1
62 branch: default
62 branch: default
63 commit: (clean)
63 commit: (clean)
64 update: (current)
64 update: (current)
65 phases: 2 draft
65 phases: 2 draft
66
66
67 Prepare a basic merge
67 Prepare a basic merge
68
68
69 $ hg up 0
69 $ hg up 0
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 $ echo This is file c1 > c
71 $ echo This is file c1 > c
72 $ hg add c
72 $ hg add c
73 $ hg commit -m "commit #2"
73 $ hg commit -m "commit #2"
74 created new head
74 created new head
75 $ echo This is file b1 > b
75 $ echo This is file b1 > b
76 no merges expected
76 no merges expected
77 $ hg merge -P 1
77 $ hg merge -P 1
78 changeset: 1:b8bb4a988f25
78 changeset: 1:b8bb4a988f25
79 user: test
79 user: test
80 date: Thu Jan 01 00:00:00 1970 +0000
80 date: Thu Jan 01 00:00:00 1970 +0000
81 summary: commit #1
81 summary: commit #1
82
82
83 $ hg merge 1
83 $ hg merge 1
84 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 (branch merge, don't forget to commit)
85 (branch merge, don't forget to commit)
86 $ hg diff --nodates
86 $ hg diff --nodates
87 diff -r 49035e18a8e6 b
87 diff -r 49035e18a8e6 b
88 --- /dev/null
88 --- /dev/null
89 +++ b/b
89 +++ b/b
90 @@ -0,0 +1,1 @@
90 @@ -0,0 +1,1 @@
91 +This is file b1
91 +This is file b1
92 $ hg status
92 $ hg status
93 M b
93 M b
94 $ cd ..; rm -r t
94 $ cd ..; rm -r t
95
95
96 $ hg init t
96 $ hg init t
97 $ cd t
97 $ cd t
98 $ echo This is file a1 > a
98 $ echo This is file a1 > a
99 $ hg add a
99 $ hg add a
100 $ hg commit -m "commit #0"
100 $ hg commit -m "commit #0"
101 $ echo This is file b1 > b
101 $ echo This is file b1 > b
102 $ hg add b
102 $ hg add b
103 $ hg commit -m "commit #1"
103 $ hg commit -m "commit #1"
104
104
105 $ hg update 0
105 $ hg update 0
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 $ echo This is file c1 > c
107 $ echo This is file c1 > c
108 $ hg add c
108 $ hg add c
109 $ hg commit -m "commit #2"
109 $ hg commit -m "commit #2"
110 created new head
110 created new head
111 $ echo This is file b2 > b
111 $ echo This is file b2 > b
112 merge should fail
112 merge should fail
113 $ hg merge 1
113 $ hg merge 1
114 b: untracked file differs
114 b: untracked file differs
115 abort: untracked files in working directory differ from files in requested revision
115 abort: untracked files in working directory differ from files in requested revision
116 [255]
116 [255]
117
117
118 #if symlink
118 #if symlink
119 symlinks to directories should be treated as regular files (issue5027)
119 symlinks to directories should be treated as regular files (issue5027)
120 $ rm b
120 $ rm b
121 $ ln -s 'This is file b2' b
121 $ ln -s 'This is file b2' b
122 $ hg merge 1
122 $ hg merge 1
123 b: untracked file differs
123 b: untracked file differs
124 abort: untracked files in working directory differ from files in requested revision
124 abort: untracked files in working directory differ from files in requested revision
125 [255]
125 [255]
126 symlinks shouldn't be followed
126 symlinks shouldn't be followed
127 $ rm b
127 $ rm b
128 $ echo This is file b1 > .hg/b
128 $ echo This is file b1 > .hg/b
129 $ ln -s .hg/b b
129 $ ln -s .hg/b b
130 $ hg merge 1
130 $ hg merge 1
131 b: untracked file differs
131 b: untracked file differs
132 abort: untracked files in working directory differ from files in requested revision
132 abort: untracked files in working directory differ from files in requested revision
133 [255]
133 [255]
134
134
135 $ rm b
135 $ rm b
136 $ echo This is file b2 > b
136 $ echo This is file b2 > b
137 #endif
137 #endif
138
138
139 bad config
139 bad config
140 $ hg merge 1 --config merge.checkunknown=x
140 $ hg merge 1 --config merge.checkunknown=x
141 abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
141 abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
142 [255]
142 [30]
143 this merge should fail
143 this merge should fail
144 $ hg merge 1 --config merge.checkunknown=abort
144 $ hg merge 1 --config merge.checkunknown=abort
145 b: untracked file differs
145 b: untracked file differs
146 abort: untracked files in working directory differ from files in requested revision
146 abort: untracked files in working directory differ from files in requested revision
147 [255]
147 [255]
148
148
149 this merge should warn
149 this merge should warn
150 $ hg merge 1 --config merge.checkunknown=warn
150 $ hg merge 1 --config merge.checkunknown=warn
151 b: replacing untracked file
151 b: replacing untracked file
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 (branch merge, don't forget to commit)
153 (branch merge, don't forget to commit)
154 $ cat b.orig
154 $ cat b.orig
155 This is file b2
155 This is file b2
156 $ hg up --clean 2
156 $ hg up --clean 2
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
158 $ mv b.orig b
158 $ mv b.orig b
159
159
160 this merge should silently ignore
160 this merge should silently ignore
161 $ cat b
161 $ cat b
162 This is file b2
162 This is file b2
163 $ hg merge 1 --config merge.checkunknown=ignore
163 $ hg merge 1 --config merge.checkunknown=ignore
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 (branch merge, don't forget to commit)
165 (branch merge, don't forget to commit)
166
166
167 merge.checkignored
167 merge.checkignored
168 $ hg up --clean 1
168 $ hg up --clean 1
169 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
169 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
170 $ cat >> .hgignore << EOF
170 $ cat >> .hgignore << EOF
171 > remoteignored
171 > remoteignored
172 > EOF
172 > EOF
173 $ echo This is file localignored3 > localignored
173 $ echo This is file localignored3 > localignored
174 $ echo This is file remoteignored3 > remoteignored
174 $ echo This is file remoteignored3 > remoteignored
175 $ hg add .hgignore localignored remoteignored
175 $ hg add .hgignore localignored remoteignored
176 $ hg commit -m "commit #3"
176 $ hg commit -m "commit #3"
177
177
178 $ hg up 2
178 $ hg up 2
179 1 files updated, 0 files merged, 4 files removed, 0 files unresolved
179 1 files updated, 0 files merged, 4 files removed, 0 files unresolved
180 $ cat >> .hgignore << EOF
180 $ cat >> .hgignore << EOF
181 > localignored
181 > localignored
182 > EOF
182 > EOF
183 $ hg add .hgignore
183 $ hg add .hgignore
184 $ hg commit -m "commit #4"
184 $ hg commit -m "commit #4"
185
185
186 remote .hgignore shouldn't be used for determining whether a file is ignored
186 remote .hgignore shouldn't be used for determining whether a file is ignored
187 $ echo This is file remoteignored4 > remoteignored
187 $ echo This is file remoteignored4 > remoteignored
188 $ hg merge 3 --config merge.checkignored=ignore --config merge.checkunknown=abort
188 $ hg merge 3 --config merge.checkignored=ignore --config merge.checkunknown=abort
189 remoteignored: untracked file differs
189 remoteignored: untracked file differs
190 abort: untracked files in working directory differ from files in requested revision
190 abort: untracked files in working directory differ from files in requested revision
191 [255]
191 [255]
192 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
192 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
193 merging .hgignore
193 merging .hgignore
194 merging for .hgignore
194 merging for .hgignore
195 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
195 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
196 (branch merge, don't forget to commit)
196 (branch merge, don't forget to commit)
197 $ cat remoteignored
197 $ cat remoteignored
198 This is file remoteignored3
198 This is file remoteignored3
199 $ cat remoteignored.orig
199 $ cat remoteignored.orig
200 This is file remoteignored4
200 This is file remoteignored4
201 $ rm remoteignored.orig
201 $ rm remoteignored.orig
202
202
203 local .hgignore should be used for that
203 local .hgignore should be used for that
204 $ hg up --clean 4
204 $ hg up --clean 4
205 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
205 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
206 $ echo This is file localignored4 > localignored
206 $ echo This is file localignored4 > localignored
207 also test other conflicting files to see we output the full set of warnings
207 also test other conflicting files to see we output the full set of warnings
208 $ echo This is file b2 > b
208 $ echo This is file b2 > b
209 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=abort
209 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=abort
210 b: untracked file differs
210 b: untracked file differs
211 localignored: untracked file differs
211 localignored: untracked file differs
212 abort: untracked files in working directory differ from files in requested revision
212 abort: untracked files in working directory differ from files in requested revision
213 [255]
213 [255]
214 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
214 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
215 localignored: untracked file differs
215 localignored: untracked file differs
216 abort: untracked files in working directory differ from files in requested revision
216 abort: untracked files in working directory differ from files in requested revision
217 [255]
217 [255]
218 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=abort
218 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=abort
219 b: untracked file differs
219 b: untracked file differs
220 abort: untracked files in working directory differ from files in requested revision
220 abort: untracked files in working directory differ from files in requested revision
221 [255]
221 [255]
222 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=warn
222 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=warn
223 b: replacing untracked file
223 b: replacing untracked file
224 localignored: replacing untracked file
224 localignored: replacing untracked file
225 merging .hgignore
225 merging .hgignore
226 merging for .hgignore
226 merging for .hgignore
227 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
227 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
228 (branch merge, don't forget to commit)
228 (branch merge, don't forget to commit)
229 $ cat localignored
229 $ cat localignored
230 This is file localignored3
230 This is file localignored3
231 $ cat localignored.orig
231 $ cat localignored.orig
232 This is file localignored4
232 This is file localignored4
233 $ rm localignored.orig
233 $ rm localignored.orig
234
234
235 $ cat b.orig
235 $ cat b.orig
236 This is file b2
236 This is file b2
237 $ hg up --clean 2
237 $ hg up --clean 2
238 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
238 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
239 $ mv b.orig b
239 $ mv b.orig b
240
240
241 this merge of b should work
241 this merge of b should work
242 $ cat b
242 $ cat b
243 This is file b2
243 This is file b2
244 $ hg merge -f 1
244 $ hg merge -f 1
245 merging b
245 merging b
246 merging for b
246 merging for b
247 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
247 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
248 (branch merge, don't forget to commit)
248 (branch merge, don't forget to commit)
249 $ hg diff --nodates
249 $ hg diff --nodates
250 diff -r 49035e18a8e6 b
250 diff -r 49035e18a8e6 b
251 --- /dev/null
251 --- /dev/null
252 +++ b/b
252 +++ b/b
253 @@ -0,0 +1,1 @@
253 @@ -0,0 +1,1 @@
254 +This is file b2
254 +This is file b2
255 $ hg status
255 $ hg status
256 M b
256 M b
257 $ cd ..; rm -r t
257 $ cd ..; rm -r t
258
258
259 $ hg init t
259 $ hg init t
260 $ cd t
260 $ cd t
261 $ echo This is file a1 > a
261 $ echo This is file a1 > a
262 $ hg add a
262 $ hg add a
263 $ hg commit -m "commit #0"
263 $ hg commit -m "commit #0"
264 $ echo This is file b1 > b
264 $ echo This is file b1 > b
265 $ hg add b
265 $ hg add b
266 $ hg commit -m "commit #1"
266 $ hg commit -m "commit #1"
267 $ echo This is file b22 > b
267 $ echo This is file b22 > b
268 $ hg commit -m "commit #2"
268 $ hg commit -m "commit #2"
269 $ hg update 1
269 $ hg update 1
270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 $ echo This is file c1 > c
271 $ echo This is file c1 > c
272 $ hg add c
272 $ hg add c
273 $ hg commit -m "commit #3"
273 $ hg commit -m "commit #3"
274 created new head
274 created new head
275
275
276 Contents of b should be "this is file b1"
276 Contents of b should be "this is file b1"
277 $ cat b
277 $ cat b
278 This is file b1
278 This is file b1
279
279
280 $ echo This is file b22 > b
280 $ echo This is file b22 > b
281 merge fails
281 merge fails
282 $ hg merge 2
282 $ hg merge 2
283 abort: uncommitted changes
283 abort: uncommitted changes
284 (use 'hg status' to list changes)
284 (use 'hg status' to list changes)
285 [255]
285 [255]
286 merge expected!
286 merge expected!
287 $ hg merge -f 2
287 $ hg merge -f 2
288 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 (branch merge, don't forget to commit)
289 (branch merge, don't forget to commit)
290 $ hg diff --nodates
290 $ hg diff --nodates
291 diff -r 85de557015a8 b
291 diff -r 85de557015a8 b
292 --- a/b
292 --- a/b
293 +++ b/b
293 +++ b/b
294 @@ -1,1 +1,1 @@
294 @@ -1,1 +1,1 @@
295 -This is file b1
295 -This is file b1
296 +This is file b22
296 +This is file b22
297 $ hg status
297 $ hg status
298 M b
298 M b
299 $ cd ..; rm -r t
299 $ cd ..; rm -r t
300
300
301 $ hg init t
301 $ hg init t
302 $ cd t
302 $ cd t
303 $ echo This is file a1 > a
303 $ echo This is file a1 > a
304 $ hg add a
304 $ hg add a
305 $ hg commit -m "commit #0"
305 $ hg commit -m "commit #0"
306 $ echo This is file b1 > b
306 $ echo This is file b1 > b
307 $ hg add b
307 $ hg add b
308 $ hg commit -m "commit #1"
308 $ hg commit -m "commit #1"
309 $ echo This is file b22 > b
309 $ echo This is file b22 > b
310 $ hg commit -m "commit #2"
310 $ hg commit -m "commit #2"
311 $ hg update 1
311 $ hg update 1
312 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 $ echo This is file c1 > c
313 $ echo This is file c1 > c
314 $ hg add c
314 $ hg add c
315 $ hg commit -m "commit #3"
315 $ hg commit -m "commit #3"
316 created new head
316 created new head
317 $ echo This is file b33 > b
317 $ echo This is file b33 > b
318 merge of b should fail
318 merge of b should fail
319 $ hg merge 2
319 $ hg merge 2
320 abort: uncommitted changes
320 abort: uncommitted changes
321 (use 'hg status' to list changes)
321 (use 'hg status' to list changes)
322 [255]
322 [255]
323 merge of b expected
323 merge of b expected
324 $ hg merge -f 2
324 $ hg merge -f 2
325 merging b
325 merging b
326 merging for b
326 merging for b
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 (branch merge, don't forget to commit)
328 (branch merge, don't forget to commit)
329 $ hg diff --nodates
329 $ hg diff --nodates
330 diff -r 85de557015a8 b
330 diff -r 85de557015a8 b
331 --- a/b
331 --- a/b
332 +++ b/b
332 +++ b/b
333 @@ -1,1 +1,1 @@
333 @@ -1,1 +1,1 @@
334 -This is file b1
334 -This is file b1
335 +This is file b33
335 +This is file b33
336 $ hg status
336 $ hg status
337 M b
337 M b
338
338
339 Test for issue2364
339 Test for issue2364
340
340
341 $ hg up -qC .
341 $ hg up -qC .
342 $ hg rm b
342 $ hg rm b
343 $ hg ci -md
343 $ hg ci -md
344 $ hg revert -r -2 b
344 $ hg revert -r -2 b
345 $ hg up -q -- -2
345 $ hg up -q -- -2
346
346
347 Test that updated files are treated as "modified", when
347 Test that updated files are treated as "modified", when
348 'merge.update()' is aborted before 'merge.recordupdates()' (= parents
348 'merge.update()' is aborted before 'merge.recordupdates()' (= parents
349 aren't changed), even if none of mode, size and timestamp of them
349 aren't changed), even if none of mode, size and timestamp of them
350 isn't changed on the filesystem (see also issue4583).
350 isn't changed on the filesystem (see also issue4583).
351
351
352 $ cat > $TESTTMP/abort.py <<EOF
352 $ cat > $TESTTMP/abort.py <<EOF
353 > from __future__ import absolute_import
353 > from __future__ import absolute_import
354 > # emulate aborting before "recordupdates()". in this case, files
354 > # emulate aborting before "recordupdates()". in this case, files
355 > # are changed without updating dirstate
355 > # are changed without updating dirstate
356 > from mercurial import (
356 > from mercurial import (
357 > error,
357 > error,
358 > extensions,
358 > extensions,
359 > merge,
359 > merge,
360 > )
360 > )
361 > def applyupdates(orig, *args, **kwargs):
361 > def applyupdates(orig, *args, **kwargs):
362 > orig(*args, **kwargs)
362 > orig(*args, **kwargs)
363 > raise error.Abort(b'intentional aborting')
363 > raise error.Abort(b'intentional aborting')
364 > def extsetup(ui):
364 > def extsetup(ui):
365 > extensions.wrapfunction(merge, "applyupdates", applyupdates)
365 > extensions.wrapfunction(merge, "applyupdates", applyupdates)
366 > EOF
366 > EOF
367
367
368 $ cat >> .hg/hgrc <<EOF
368 $ cat >> .hg/hgrc <<EOF
369 > [fakedirstatewritetime]
369 > [fakedirstatewritetime]
370 > # emulate invoking dirstate.write() via repo.status()
370 > # emulate invoking dirstate.write() via repo.status()
371 > # at 2000-01-01 00:00
371 > # at 2000-01-01 00:00
372 > fakenow = 200001010000
372 > fakenow = 200001010000
373 > EOF
373 > EOF
374
374
375 (file gotten from other revision)
375 (file gotten from other revision)
376
376
377 $ hg update -q -C 2
377 $ hg update -q -C 2
378 $ echo 'THIS IS FILE B5' > b
378 $ echo 'THIS IS FILE B5' > b
379 $ hg commit -m 'commit #5'
379 $ hg commit -m 'commit #5'
380
380
381 $ hg update -q -C 3
381 $ hg update -q -C 3
382 $ cat b
382 $ cat b
383 This is file b1
383 This is file b1
384 $ touch -t 200001010000 b
384 $ touch -t 200001010000 b
385 $ hg debugrebuildstate
385 $ hg debugrebuildstate
386
386
387 $ cat >> .hg/hgrc <<EOF
387 $ cat >> .hg/hgrc <<EOF
388 > [extensions]
388 > [extensions]
389 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
389 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
390 > abort = $TESTTMP/abort.py
390 > abort = $TESTTMP/abort.py
391 > EOF
391 > EOF
392 $ hg merge 5
392 $ hg merge 5
393 abort: intentional aborting
393 abort: intentional aborting
394 [255]
394 [255]
395 $ cat >> .hg/hgrc <<EOF
395 $ cat >> .hg/hgrc <<EOF
396 > [extensions]
396 > [extensions]
397 > fakedirstatewritetime = !
397 > fakedirstatewritetime = !
398 > abort = !
398 > abort = !
399 > EOF
399 > EOF
400
400
401 $ cat b
401 $ cat b
402 THIS IS FILE B5
402 THIS IS FILE B5
403 $ touch -t 200001010000 b
403 $ touch -t 200001010000 b
404 $ hg status -A b
404 $ hg status -A b
405 M b
405 M b
406
406
407 (file merged from other revision)
407 (file merged from other revision)
408
408
409 $ hg update -q -C 3
409 $ hg update -q -C 3
410 $ echo 'this is file b6' > b
410 $ echo 'this is file b6' > b
411 $ hg commit -m 'commit #6'
411 $ hg commit -m 'commit #6'
412 created new head
412 created new head
413
413
414 $ cat b
414 $ cat b
415 this is file b6
415 this is file b6
416 $ touch -t 200001010000 b
416 $ touch -t 200001010000 b
417 $ hg debugrebuildstate
417 $ hg debugrebuildstate
418
418
419 $ cat >> .hg/hgrc <<EOF
419 $ cat >> .hg/hgrc <<EOF
420 > [extensions]
420 > [extensions]
421 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
421 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
422 > abort = $TESTTMP/abort.py
422 > abort = $TESTTMP/abort.py
423 > EOF
423 > EOF
424 $ hg merge --tool internal:other 5
424 $ hg merge --tool internal:other 5
425 abort: intentional aborting
425 abort: intentional aborting
426 [255]
426 [255]
427 $ cat >> .hg/hgrc <<EOF
427 $ cat >> .hg/hgrc <<EOF
428 > [extensions]
428 > [extensions]
429 > fakedirstatewritetime = !
429 > fakedirstatewritetime = !
430 > abort = !
430 > abort = !
431 > EOF
431 > EOF
432
432
433 $ cat b
433 $ cat b
434 THIS IS FILE B5
434 THIS IS FILE B5
435 $ touch -t 200001010000 b
435 $ touch -t 200001010000 b
436 $ hg status -A b
436 $ hg status -A b
437 M b
437 M b
438
438
439 $ cd ..
439 $ cd ..
@@ -1,1054 +1,1054
1 $ cat > $TESTTMP/hook.sh << 'EOF'
1 $ cat > $TESTTMP/hook.sh << 'EOF'
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
2 > echo "test-hook-close-phase: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
3 > EOF
3 > EOF
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [extensions]
6 > [extensions]
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
7 > phasereport=$TESTDIR/testlib/ext-phase-report.py
8 > [hooks]
8 > [hooks]
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
9 > txnclose-phase.test = sh $TESTTMP/hook.sh
10 > EOF
10 > EOF
11
11
12 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
12 $ hglog() { hg log --template "{rev} {phaseidx} {desc}\n" $*; }
13 $ mkcommit() {
13 $ mkcommit() {
14 > echo "$1" > "$1"
14 > echo "$1" > "$1"
15 > hg add "$1"
15 > hg add "$1"
16 > message="$1"
16 > message="$1"
17 > shift
17 > shift
18 > hg ci -m "$message" $*
18 > hg ci -m "$message" $*
19 > }
19 > }
20
20
21 $ hg init initialrepo
21 $ hg init initialrepo
22 $ cd initialrepo
22 $ cd initialrepo
23
23
24 Cannot change null revision phase
24 Cannot change null revision phase
25
25
26 $ hg phase --force --secret null
26 $ hg phase --force --secret null
27 abort: cannot change null revision phase
27 abort: cannot change null revision phase
28 [255]
28 [255]
29 $ hg phase null
29 $ hg phase null
30 -1: public
30 -1: public
31
31
32 $ mkcommit A
32 $ mkcommit A
33 test-debug-phase: new rev 0: x -> 1
33 test-debug-phase: new rev 0: x -> 1
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
34 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
35
35
36 New commit are draft by default
36 New commit are draft by default
37
37
38 $ hglog
38 $ hglog
39 0 1 A
39 0 1 A
40
40
41 Following commit are draft too
41 Following commit are draft too
42
42
43 $ mkcommit B
43 $ mkcommit B
44 test-debug-phase: new rev 1: x -> 1
44 test-debug-phase: new rev 1: x -> 1
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
45 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> draft
46
46
47 $ hglog
47 $ hglog
48 1 1 B
48 1 1 B
49 0 1 A
49 0 1 A
50
50
51 Working directory phase is secret when its parent is secret.
51 Working directory phase is secret when its parent is secret.
52
52
53 $ hg phase --force --secret .
53 $ hg phase --force --secret .
54 test-debug-phase: move rev 0: 1 -> 2
54 test-debug-phase: move rev 0: 1 -> 2
55 test-debug-phase: move rev 1: 1 -> 2
55 test-debug-phase: move rev 1: 1 -> 2
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
56 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> secret
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
57 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> secret
58 $ hg log -r 'wdir()' -T '{phase}\n'
58 $ hg log -r 'wdir()' -T '{phase}\n'
59 secret
59 secret
60 $ hg log -r 'wdir() and public()' -T '{phase}\n'
60 $ hg log -r 'wdir() and public()' -T '{phase}\n'
61 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
61 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
62 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
62 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
63 secret
63 secret
64
64
65 Working directory phase is draft when its parent is draft.
65 Working directory phase is draft when its parent is draft.
66
66
67 $ hg phase --draft .
67 $ hg phase --draft .
68 test-debug-phase: move rev 1: 2 -> 1
68 test-debug-phase: move rev 1: 2 -> 1
69 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
69 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: secret -> draft
70 $ hg log -r 'wdir()' -T '{phase}\n'
70 $ hg log -r 'wdir()' -T '{phase}\n'
71 draft
71 draft
72 $ hg log -r 'wdir() and public()' -T '{phase}\n'
72 $ hg log -r 'wdir() and public()' -T '{phase}\n'
73 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
73 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
74 draft
74 draft
75 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
75 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
76
76
77 Working directory phase is secret when a new commit will be created as secret,
77 Working directory phase is secret when a new commit will be created as secret,
78 even if the parent is draft.
78 even if the parent is draft.
79
79
80 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
80 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
81 > --config phases.new-commit='secret'
81 > --config phases.new-commit='secret'
82 secret
82 secret
83
83
84 Working directory phase is draft when its parent is public.
84 Working directory phase is draft when its parent is public.
85
85
86 $ hg phase --public .
86 $ hg phase --public .
87 test-debug-phase: move rev 0: 1 -> 0
87 test-debug-phase: move rev 0: 1 -> 0
88 test-debug-phase: move rev 1: 1 -> 0
88 test-debug-phase: move rev 1: 1 -> 0
89 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
89 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: draft -> public
90 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
90 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
91 $ hg log -r 'wdir()' -T '{phase}\n'
91 $ hg log -r 'wdir()' -T '{phase}\n'
92 draft
92 draft
93 $ hg log -r 'wdir() and public()' -T '{phase}\n'
93 $ hg log -r 'wdir() and public()' -T '{phase}\n'
94 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
94 $ hg log -r 'wdir() and draft()' -T '{phase}\n'
95 draft
95 draft
96 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
96 $ hg log -r 'wdir() and secret()' -T '{phase}\n'
97 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
97 $ hg log -r 'wdir() and secret()' -T '{phase}\n' \
98 > --config phases.new-commit='secret'
98 > --config phases.new-commit='secret'
99 secret
99 secret
100
100
101 Draft commit are properly created over public one:
101 Draft commit are properly created over public one:
102
102
103 $ hg phase
103 $ hg phase
104 1: public
104 1: public
105 $ hglog
105 $ hglog
106 1 0 B
106 1 0 B
107 0 0 A
107 0 0 A
108
108
109 $ mkcommit C
109 $ mkcommit C
110 test-debug-phase: new rev 2: x -> 1
110 test-debug-phase: new rev 2: x -> 1
111 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
111 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
112 $ mkcommit D
112 $ mkcommit D
113 test-debug-phase: new rev 3: x -> 1
113 test-debug-phase: new rev 3: x -> 1
114 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
114 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
115
115
116 $ hglog
116 $ hglog
117 3 1 D
117 3 1 D
118 2 1 C
118 2 1 C
119 1 0 B
119 1 0 B
120 0 0 A
120 0 0 A
121
121
122 Test creating changeset as secret
122 Test creating changeset as secret
123
123
124 $ mkcommit E --config phases.new-commit='secret'
124 $ mkcommit E --config phases.new-commit='secret'
125 test-debug-phase: new rev 4: x -> 2
125 test-debug-phase: new rev 4: x -> 2
126 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
126 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> secret
127 $ hglog
127 $ hglog
128 4 2 E
128 4 2 E
129 3 1 D
129 3 1 D
130 2 1 C
130 2 1 C
131 1 0 B
131 1 0 B
132 0 0 A
132 0 0 A
133
133
134 Test the secret property is inherited
134 Test the secret property is inherited
135
135
136 $ mkcommit H
136 $ mkcommit H
137 test-debug-phase: new rev 5: x -> 2
137 test-debug-phase: new rev 5: x -> 2
138 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
138 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
139 $ hglog
139 $ hglog
140 5 2 H
140 5 2 H
141 4 2 E
141 4 2 E
142 3 1 D
142 3 1 D
143 2 1 C
143 2 1 C
144 1 0 B
144 1 0 B
145 0 0 A
145 0 0 A
146
146
147 Even on merge
147 Even on merge
148
148
149 $ hg up -q 1
149 $ hg up -q 1
150 $ mkcommit "B'"
150 $ mkcommit "B'"
151 test-debug-phase: new rev 6: x -> 1
151 test-debug-phase: new rev 6: x -> 1
152 created new head
152 created new head
153 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
153 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
154 $ hglog
154 $ hglog
155 6 1 B'
155 6 1 B'
156 5 2 H
156 5 2 H
157 4 2 E
157 4 2 E
158 3 1 D
158 3 1 D
159 2 1 C
159 2 1 C
160 1 0 B
160 1 0 B
161 0 0 A
161 0 0 A
162 $ hg merge 4 # E
162 $ hg merge 4 # E
163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 (branch merge, don't forget to commit)
164 (branch merge, don't forget to commit)
165 $ hg phase
165 $ hg phase
166 6: draft
166 6: draft
167 4: secret
167 4: secret
168 $ hg ci -m "merge B' and E"
168 $ hg ci -m "merge B' and E"
169 test-debug-phase: new rev 7: x -> 2
169 test-debug-phase: new rev 7: x -> 2
170 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
170 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> secret
171
171
172 $ hglog
172 $ hglog
173 7 2 merge B' and E
173 7 2 merge B' and E
174 6 1 B'
174 6 1 B'
175 5 2 H
175 5 2 H
176 4 2 E
176 4 2 E
177 3 1 D
177 3 1 D
178 2 1 C
178 2 1 C
179 1 0 B
179 1 0 B
180 0 0 A
180 0 0 A
181
181
182 Test secret changeset are not pushed
182 Test secret changeset are not pushed
183
183
184 $ hg init ../push-dest
184 $ hg init ../push-dest
185 $ cat > ../push-dest/.hg/hgrc << EOF
185 $ cat > ../push-dest/.hg/hgrc << EOF
186 > [phases]
186 > [phases]
187 > publish=False
187 > publish=False
188 > EOF
188 > EOF
189 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
189 $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
190 comparing with ../push-dest
190 comparing with ../push-dest
191 searching for changes
191 searching for changes
192 0 public A
192 0 public A
193 1 public B
193 1 public B
194 2 draft C
194 2 draft C
195 3 draft D
195 3 draft D
196 6 draft B'
196 6 draft B'
197 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
197 $ hg outgoing -r 'branch(default)' ../push-dest --template='{rev} {phase} {desc|firstline}\n'
198 comparing with ../push-dest
198 comparing with ../push-dest
199 searching for changes
199 searching for changes
200 0 public A
200 0 public A
201 1 public B
201 1 public B
202 2 draft C
202 2 draft C
203 3 draft D
203 3 draft D
204 6 draft B'
204 6 draft B'
205
205
206 $ hg push ../push-dest -f # force because we push multiple heads
206 $ hg push ../push-dest -f # force because we push multiple heads
207 pushing to ../push-dest
207 pushing to ../push-dest
208 searching for changes
208 searching for changes
209 adding changesets
209 adding changesets
210 adding manifests
210 adding manifests
211 adding file changes
211 adding file changes
212 added 5 changesets with 5 changes to 5 files (+1 heads)
212 added 5 changesets with 5 changes to 5 files (+1 heads)
213 test-debug-phase: new rev 0: x -> 0
213 test-debug-phase: new rev 0: x -> 0
214 test-debug-phase: new rev 1: x -> 0
214 test-debug-phase: new rev 1: x -> 0
215 test-debug-phase: new rev 2: x -> 1
215 test-debug-phase: new rev 2: x -> 1
216 test-debug-phase: new rev 3: x -> 1
216 test-debug-phase: new rev 3: x -> 1
217 test-debug-phase: new rev 4: x -> 1
217 test-debug-phase: new rev 4: x -> 1
218 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
218 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
219 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
219 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
220 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
220 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> draft
221 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
221 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> draft
222 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
222 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> draft
223 $ hglog
223 $ hglog
224 7 2 merge B' and E
224 7 2 merge B' and E
225 6 1 B'
225 6 1 B'
226 5 2 H
226 5 2 H
227 4 2 E
227 4 2 E
228 3 1 D
228 3 1 D
229 2 1 C
229 2 1 C
230 1 0 B
230 1 0 B
231 0 0 A
231 0 0 A
232 $ cd ../push-dest
232 $ cd ../push-dest
233 $ hglog
233 $ hglog
234 4 1 B'
234 4 1 B'
235 3 1 D
235 3 1 D
236 2 1 C
236 2 1 C
237 1 0 B
237 1 0 B
238 0 0 A
238 0 0 A
239
239
240 (Issue3303)
240 (Issue3303)
241 Check that remote secret changeset are ignore when checking creation of remote heads
241 Check that remote secret changeset are ignore when checking creation of remote heads
242
242
243 We add a secret head into the push destination. This secret head shadows a
243 We add a secret head into the push destination. This secret head shadows a
244 visible shared between the initial repo and the push destination.
244 visible shared between the initial repo and the push destination.
245
245
246 $ hg up -q 4 # B'
246 $ hg up -q 4 # B'
247 $ mkcommit Z --config phases.new-commit=secret
247 $ mkcommit Z --config phases.new-commit=secret
248 test-debug-phase: new rev 5: x -> 2
248 test-debug-phase: new rev 5: x -> 2
249 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
249 test-hook-close-phase: 2713879da13d6eea1ff22b442a5a87cb31a7ce6a: -> secret
250 $ hg phase .
250 $ hg phase .
251 5: secret
251 5: secret
252
252
253 We now try to push a new public changeset that descend from the common public
253 We now try to push a new public changeset that descend from the common public
254 head shadowed by the remote secret head.
254 head shadowed by the remote secret head.
255
255
256 $ cd ../initialrepo
256 $ cd ../initialrepo
257 $ hg up -q 6 #B'
257 $ hg up -q 6 #B'
258 $ mkcommit I
258 $ mkcommit I
259 test-debug-phase: new rev 8: x -> 1
259 test-debug-phase: new rev 8: x -> 1
260 created new head
260 created new head
261 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
261 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
262 $ hg push ../push-dest
262 $ hg push ../push-dest
263 pushing to ../push-dest
263 pushing to ../push-dest
264 searching for changes
264 searching for changes
265 adding changesets
265 adding changesets
266 adding manifests
266 adding manifests
267 adding file changes
267 adding file changes
268 added 1 changesets with 1 changes to 1 files (+1 heads)
268 added 1 changesets with 1 changes to 1 files (+1 heads)
269 test-debug-phase: new rev 6: x -> 1
269 test-debug-phase: new rev 6: x -> 1
270 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
270 test-hook-close-phase: 6d6770faffce199f1fddd1cf87f6f026138cf061: -> draft
271
271
272 :note: The "(+1 heads)" is wrong as we do not had any visible head
272 :note: The "(+1 heads)" is wrong as we do not had any visible head
273
273
274 check that branch cache with "served" filter are properly computed and stored
274 check that branch cache with "served" filter are properly computed and stored
275
275
276 $ ls ../push-dest/.hg/cache/branch2*
276 $ ls ../push-dest/.hg/cache/branch2*
277 ../push-dest/.hg/cache/branch2-base
277 ../push-dest/.hg/cache/branch2-base
278 ../push-dest/.hg/cache/branch2-served
278 ../push-dest/.hg/cache/branch2-served
279 $ cat ../push-dest/.hg/cache/branch2-served
279 $ cat ../push-dest/.hg/cache/branch2-served
280 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
280 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
281 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
281 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
282 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
282 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
283 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
283 $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n' #update visible cache too
284 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
284 6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
285 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
285 5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
286 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
286 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
287 $ ls ../push-dest/.hg/cache/branch2*
287 $ ls ../push-dest/.hg/cache/branch2*
288 ../push-dest/.hg/cache/branch2-base
288 ../push-dest/.hg/cache/branch2-base
289 ../push-dest/.hg/cache/branch2-served
289 ../push-dest/.hg/cache/branch2-served
290 ../push-dest/.hg/cache/branch2-visible
290 ../push-dest/.hg/cache/branch2-visible
291 $ cat ../push-dest/.hg/cache/branch2-served
291 $ cat ../push-dest/.hg/cache/branch2-served
292 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
292 6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
293 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
293 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
294 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
294 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
295 $ cat ../push-dest/.hg/cache/branch2-visible
295 $ cat ../push-dest/.hg/cache/branch2-visible
296 6d6770faffce199f1fddd1cf87f6f026138cf061 6
296 6d6770faffce199f1fddd1cf87f6f026138cf061 6
297 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
297 b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e o default
298 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
298 2713879da13d6eea1ff22b442a5a87cb31a7ce6a o default
299 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
299 6d6770faffce199f1fddd1cf87f6f026138cf061 o default
300
300
301
301
302 Restore condition prior extra insertion.
302 Restore condition prior extra insertion.
303 $ hg -q --config extensions.mq= strip .
303 $ hg -q --config extensions.mq= strip .
304 $ hg up -q 7
304 $ hg up -q 7
305 $ cd ..
305 $ cd ..
306
306
307 Test secret changeset are not pull
307 Test secret changeset are not pull
308
308
309 $ hg init pull-dest
309 $ hg init pull-dest
310 $ cd pull-dest
310 $ cd pull-dest
311 $ hg pull ../initialrepo
311 $ hg pull ../initialrepo
312 pulling from ../initialrepo
312 pulling from ../initialrepo
313 requesting all changes
313 requesting all changes
314 adding changesets
314 adding changesets
315 adding manifests
315 adding manifests
316 adding file changes
316 adding file changes
317 added 5 changesets with 5 changes to 5 files (+1 heads)
317 added 5 changesets with 5 changes to 5 files (+1 heads)
318 new changesets 4a2df7238c3b:cf9fe039dfd6
318 new changesets 4a2df7238c3b:cf9fe039dfd6
319 test-debug-phase: new rev 0: x -> 0
319 test-debug-phase: new rev 0: x -> 0
320 test-debug-phase: new rev 1: x -> 0
320 test-debug-phase: new rev 1: x -> 0
321 test-debug-phase: new rev 2: x -> 0
321 test-debug-phase: new rev 2: x -> 0
322 test-debug-phase: new rev 3: x -> 0
322 test-debug-phase: new rev 3: x -> 0
323 test-debug-phase: new rev 4: x -> 0
323 test-debug-phase: new rev 4: x -> 0
324 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
324 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
325 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
325 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
326 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
326 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
327 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
327 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
328 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
328 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
329 (run 'hg heads' to see heads, 'hg merge' to merge)
329 (run 'hg heads' to see heads, 'hg merge' to merge)
330 $ hglog
330 $ hglog
331 4 0 B'
331 4 0 B'
332 3 0 D
332 3 0 D
333 2 0 C
333 2 0 C
334 1 0 B
334 1 0 B
335 0 0 A
335 0 0 A
336 $ cd ..
336 $ cd ..
337
337
338 But secret can still be bundled explicitly
338 But secret can still be bundled explicitly
339
339
340 $ cd initialrepo
340 $ cd initialrepo
341 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
341 $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
342 4 changesets found
342 4 changesets found
343 $ cd ..
343 $ cd ..
344
344
345 Test secret changeset are not cloned
345 Test secret changeset are not cloned
346 (during local clone)
346 (during local clone)
347
347
348 $ hg clone -qU initialrepo clone-dest
348 $ hg clone -qU initialrepo clone-dest
349 test-debug-phase: new rev 0: x -> 0
349 test-debug-phase: new rev 0: x -> 0
350 test-debug-phase: new rev 1: x -> 0
350 test-debug-phase: new rev 1: x -> 0
351 test-debug-phase: new rev 2: x -> 0
351 test-debug-phase: new rev 2: x -> 0
352 test-debug-phase: new rev 3: x -> 0
352 test-debug-phase: new rev 3: x -> 0
353 test-debug-phase: new rev 4: x -> 0
353 test-debug-phase: new rev 4: x -> 0
354 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
354 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
355 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
355 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
356 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
356 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
357 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
357 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
358 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
358 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
359 $ hglog -R clone-dest
359 $ hglog -R clone-dest
360 4 0 B'
360 4 0 B'
361 3 0 D
361 3 0 D
362 2 0 C
362 2 0 C
363 1 0 B
363 1 0 B
364 0 0 A
364 0 0 A
365
365
366 Test summary
366 Test summary
367
367
368 $ hg summary -R clone-dest --verbose
368 $ hg summary -R clone-dest --verbose
369 parent: -1:000000000000 (no revision checked out)
369 parent: -1:000000000000 (no revision checked out)
370 branch: default
370 branch: default
371 commit: (clean)
371 commit: (clean)
372 update: 5 new changesets (update)
372 update: 5 new changesets (update)
373 $ hg summary -R initialrepo
373 $ hg summary -R initialrepo
374 parent: 7:17a481b3bccb tip
374 parent: 7:17a481b3bccb tip
375 merge B' and E
375 merge B' and E
376 branch: default
376 branch: default
377 commit: (clean) (secret)
377 commit: (clean) (secret)
378 update: 1 new changesets, 2 branch heads (merge)
378 update: 1 new changesets, 2 branch heads (merge)
379 phases: 3 draft, 3 secret
379 phases: 3 draft, 3 secret
380 $ hg summary -R initialrepo --quiet
380 $ hg summary -R initialrepo --quiet
381 parent: 7:17a481b3bccb tip
381 parent: 7:17a481b3bccb tip
382 update: 1 new changesets, 2 branch heads (merge)
382 update: 1 new changesets, 2 branch heads (merge)
383
383
384 Test revset
384 Test revset
385
385
386 $ cd initialrepo
386 $ cd initialrepo
387 $ hglog -r 'public()'
387 $ hglog -r 'public()'
388 0 0 A
388 0 0 A
389 1 0 B
389 1 0 B
390 $ hglog -r 'draft()'
390 $ hglog -r 'draft()'
391 2 1 C
391 2 1 C
392 3 1 D
392 3 1 D
393 6 1 B'
393 6 1 B'
394 $ hglog -r 'secret()'
394 $ hglog -r 'secret()'
395 4 2 E
395 4 2 E
396 5 2 H
396 5 2 H
397 7 2 merge B' and E
397 7 2 merge B' and E
398
398
399 test that phase are displayed in log at debug level
399 test that phase are displayed in log at debug level
400
400
401 $ hg log --debug
401 $ hg log --debug
402 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
402 changeset: 7:17a481b3bccb796c0521ae97903d81c52bfee4af
403 tag: tip
403 tag: tip
404 phase: secret
404 phase: secret
405 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
405 parent: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
406 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
406 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
407 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
407 manifest: 7:5e724ffacba267b2ab726c91fc8b650710deaaa8
408 user: test
408 user: test
409 date: Thu Jan 01 00:00:00 1970 +0000
409 date: Thu Jan 01 00:00:00 1970 +0000
410 files+: C D E
410 files+: C D E
411 extra: branch=default
411 extra: branch=default
412 description:
412 description:
413 merge B' and E
413 merge B' and E
414
414
415
415
416 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
416 changeset: 6:cf9fe039dfd67e829edf6522a45de057b5c86519
417 phase: draft
417 phase: draft
418 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
418 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
419 parent: -1:0000000000000000000000000000000000000000
419 parent: -1:0000000000000000000000000000000000000000
420 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
420 manifest: 6:ab8bfef2392903058bf4ebb9e7746e8d7026b27a
421 user: test
421 user: test
422 date: Thu Jan 01 00:00:00 1970 +0000
422 date: Thu Jan 01 00:00:00 1970 +0000
423 files+: B'
423 files+: B'
424 extra: branch=default
424 extra: branch=default
425 description:
425 description:
426 B'
426 B'
427
427
428
428
429 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
429 changeset: 5:a030c6be5127abc010fcbff1851536552e6951a8
430 phase: secret
430 phase: secret
431 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
431 parent: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
432 parent: -1:0000000000000000000000000000000000000000
432 parent: -1:0000000000000000000000000000000000000000
433 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
433 manifest: 5:5c710aa854874fe3d5fa7192e77bdb314cc08b5a
434 user: test
434 user: test
435 date: Thu Jan 01 00:00:00 1970 +0000
435 date: Thu Jan 01 00:00:00 1970 +0000
436 files+: H
436 files+: H
437 extra: branch=default
437 extra: branch=default
438 description:
438 description:
439 H
439 H
440
440
441
441
442 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
442 changeset: 4:a603bfb5a83e312131cebcd05353c217d4d21dde
443 phase: secret
443 phase: secret
444 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
444 parent: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
445 parent: -1:0000000000000000000000000000000000000000
445 parent: -1:0000000000000000000000000000000000000000
446 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
446 manifest: 4:7173fd1c27119750b959e3a0f47ed78abe75d6dc
447 user: test
447 user: test
448 date: Thu Jan 01 00:00:00 1970 +0000
448 date: Thu Jan 01 00:00:00 1970 +0000
449 files+: E
449 files+: E
450 extra: branch=default
450 extra: branch=default
451 description:
451 description:
452 E
452 E
453
453
454
454
455 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
455 changeset: 3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
456 phase: draft
456 phase: draft
457 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
457 parent: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
458 parent: -1:0000000000000000000000000000000000000000
458 parent: -1:0000000000000000000000000000000000000000
459 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
459 manifest: 3:6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c
460 user: test
460 user: test
461 date: Thu Jan 01 00:00:00 1970 +0000
461 date: Thu Jan 01 00:00:00 1970 +0000
462 files+: D
462 files+: D
463 extra: branch=default
463 extra: branch=default
464 description:
464 description:
465 D
465 D
466
466
467
467
468 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
468 changeset: 2:f838bfaca5c7226600ebcfd84f3c3c13a28d3757
469 phase: draft
469 phase: draft
470 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
470 parent: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
471 parent: -1:0000000000000000000000000000000000000000
471 parent: -1:0000000000000000000000000000000000000000
472 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
472 manifest: 2:66a5a01817fdf5239c273802b5b7618d051c89e4
473 user: test
473 user: test
474 date: Thu Jan 01 00:00:00 1970 +0000
474 date: Thu Jan 01 00:00:00 1970 +0000
475 files+: C
475 files+: C
476 extra: branch=default
476 extra: branch=default
477 description:
477 description:
478 C
478 C
479
479
480
480
481 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
481 changeset: 1:27547f69f25460a52fff66ad004e58da7ad3fb56
482 phase: public
482 phase: public
483 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
483 parent: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
484 parent: -1:0000000000000000000000000000000000000000
484 parent: -1:0000000000000000000000000000000000000000
485 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
485 manifest: 1:cb5cbbc1bfbf24cc34b9e8c16914e9caa2d2a7fd
486 user: test
486 user: test
487 date: Thu Jan 01 00:00:00 1970 +0000
487 date: Thu Jan 01 00:00:00 1970 +0000
488 files+: B
488 files+: B
489 extra: branch=default
489 extra: branch=default
490 description:
490 description:
491 B
491 B
492
492
493
493
494 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
494 changeset: 0:4a2df7238c3b48766b5e22fafbb8a2f506ec8256
495 phase: public
495 phase: public
496 parent: -1:0000000000000000000000000000000000000000
496 parent: -1:0000000000000000000000000000000000000000
497 parent: -1:0000000000000000000000000000000000000000
497 parent: -1:0000000000000000000000000000000000000000
498 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
498 manifest: 0:007d8c9d88841325f5c6b06371b35b4e8a2b1a83
499 user: test
499 user: test
500 date: Thu Jan 01 00:00:00 1970 +0000
500 date: Thu Jan 01 00:00:00 1970 +0000
501 files+: A
501 files+: A
502 extra: branch=default
502 extra: branch=default
503 description:
503 description:
504 A
504 A
505
505
506
506
507
507
508
508
509 (Issue3707)
509 (Issue3707)
510 test invalid phase name
510 test invalid phase name
511
511
512 $ mkcommit I --config phases.new-commit='babar'
512 $ mkcommit I --config phases.new-commit='babar'
513 transaction abort!
513 transaction abort!
514 rollback completed
514 rollback completed
515 abort: phases.new-commit: not a valid phase name ('babar')
515 abort: phases.new-commit: not a valid phase name ('babar')
516 [255]
516 [30]
517 Test phase command
517 Test phase command
518 ===================
518 ===================
519
519
520 initial picture
520 initial picture
521
521
522 $ hg log -G --template "{rev} {phase} {desc}\n"
522 $ hg log -G --template "{rev} {phase} {desc}\n"
523 @ 7 secret merge B' and E
523 @ 7 secret merge B' and E
524 |\
524 |\
525 | o 6 draft B'
525 | o 6 draft B'
526 | |
526 | |
527 +---o 5 secret H
527 +---o 5 secret H
528 | |
528 | |
529 o | 4 secret E
529 o | 4 secret E
530 | |
530 | |
531 o | 3 draft D
531 o | 3 draft D
532 | |
532 | |
533 o | 2 draft C
533 o | 2 draft C
534 |/
534 |/
535 o 1 public B
535 o 1 public B
536 |
536 |
537 o 0 public A
537 o 0 public A
538
538
539
539
540 display changesets phase
540 display changesets phase
541
541
542 (mixing -r and plain rev specification)
542 (mixing -r and plain rev specification)
543
543
544 $ hg phase 1::4 -r 7
544 $ hg phase 1::4 -r 7
545 1: public
545 1: public
546 2: draft
546 2: draft
547 3: draft
547 3: draft
548 4: secret
548 4: secret
549 7: secret
549 7: secret
550
550
551
551
552 move changeset forward
552 move changeset forward
553
553
554 (with -r option)
554 (with -r option)
555
555
556 $ hg phase --public -r 2
556 $ hg phase --public -r 2
557 test-debug-phase: move rev 2: 1 -> 0
557 test-debug-phase: move rev 2: 1 -> 0
558 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
558 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
559 $ hg log -G --template "{rev} {phase} {desc}\n"
559 $ hg log -G --template "{rev} {phase} {desc}\n"
560 @ 7 secret merge B' and E
560 @ 7 secret merge B' and E
561 |\
561 |\
562 | o 6 draft B'
562 | o 6 draft B'
563 | |
563 | |
564 +---o 5 secret H
564 +---o 5 secret H
565 | |
565 | |
566 o | 4 secret E
566 o | 4 secret E
567 | |
567 | |
568 o | 3 draft D
568 o | 3 draft D
569 | |
569 | |
570 o | 2 public C
570 o | 2 public C
571 |/
571 |/
572 o 1 public B
572 o 1 public B
573 |
573 |
574 o 0 public A
574 o 0 public A
575
575
576
576
577 move changeset backward
577 move changeset backward
578
578
579 (without -r option)
579 (without -r option)
580
580
581 $ hg phase --draft --force 2
581 $ hg phase --draft --force 2
582 test-debug-phase: move rev 2: 0 -> 1
582 test-debug-phase: move rev 2: 0 -> 1
583 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
583 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: public -> draft
584 $ hg log -G --template "{rev} {phase} {desc}\n"
584 $ hg log -G --template "{rev} {phase} {desc}\n"
585 @ 7 secret merge B' and E
585 @ 7 secret merge B' and E
586 |\
586 |\
587 | o 6 draft B'
587 | o 6 draft B'
588 | |
588 | |
589 +---o 5 secret H
589 +---o 5 secret H
590 | |
590 | |
591 o | 4 secret E
591 o | 4 secret E
592 | |
592 | |
593 o | 3 draft D
593 o | 3 draft D
594 | |
594 | |
595 o | 2 draft C
595 o | 2 draft C
596 |/
596 |/
597 o 1 public B
597 o 1 public B
598 |
598 |
599 o 0 public A
599 o 0 public A
600
600
601
601
602 move changeset forward and backward
602 move changeset forward and backward
603
603
604 $ hg phase --draft --force 1::4
604 $ hg phase --draft --force 1::4
605 test-debug-phase: move rev 1: 0 -> 1
605 test-debug-phase: move rev 1: 0 -> 1
606 test-debug-phase: move rev 4: 2 -> 1
606 test-debug-phase: move rev 4: 2 -> 1
607 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
607 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: public -> draft
608 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
608 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
609 $ hg log -G --template "{rev} {phase} {desc}\n"
609 $ hg log -G --template "{rev} {phase} {desc}\n"
610 @ 7 secret merge B' and E
610 @ 7 secret merge B' and E
611 |\
611 |\
612 | o 6 draft B'
612 | o 6 draft B'
613 | |
613 | |
614 +---o 5 secret H
614 +---o 5 secret H
615 | |
615 | |
616 o | 4 draft E
616 o | 4 draft E
617 | |
617 | |
618 o | 3 draft D
618 o | 3 draft D
619 | |
619 | |
620 o | 2 draft C
620 o | 2 draft C
621 |/
621 |/
622 o 1 draft B
622 o 1 draft B
623 |
623 |
624 o 0 public A
624 o 0 public A
625
625
626 test partial failure
626 test partial failure
627
627
628 $ hg phase --public 7
628 $ hg phase --public 7
629 test-debug-phase: move rev 1: 1 -> 0
629 test-debug-phase: move rev 1: 1 -> 0
630 test-debug-phase: move rev 2: 1 -> 0
630 test-debug-phase: move rev 2: 1 -> 0
631 test-debug-phase: move rev 3: 1 -> 0
631 test-debug-phase: move rev 3: 1 -> 0
632 test-debug-phase: move rev 4: 1 -> 0
632 test-debug-phase: move rev 4: 1 -> 0
633 test-debug-phase: move rev 6: 1 -> 0
633 test-debug-phase: move rev 6: 1 -> 0
634 test-debug-phase: move rev 7: 2 -> 0
634 test-debug-phase: move rev 7: 2 -> 0
635 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
635 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: draft -> public
636 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
636 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: draft -> public
637 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
637 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: draft -> public
638 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
638 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: draft -> public
639 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
639 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: draft -> public
640 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
640 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> public
641 $ hg phase --draft '5 or 7'
641 $ hg phase --draft '5 or 7'
642 test-debug-phase: move rev 5: 2 -> 1
642 test-debug-phase: move rev 5: 2 -> 1
643 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
643 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: secret -> draft
644 cannot move 1 changesets to a higher phase, use --force
644 cannot move 1 changesets to a higher phase, use --force
645 phase changed for 1 changesets
645 phase changed for 1 changesets
646 [1]
646 [1]
647 $ hg log -G --template "{rev} {phase} {desc}\n"
647 $ hg log -G --template "{rev} {phase} {desc}\n"
648 @ 7 public merge B' and E
648 @ 7 public merge B' and E
649 |\
649 |\
650 | o 6 public B'
650 | o 6 public B'
651 | |
651 | |
652 +---o 5 draft H
652 +---o 5 draft H
653 | |
653 | |
654 o | 4 public E
654 o | 4 public E
655 | |
655 | |
656 o | 3 public D
656 o | 3 public D
657 | |
657 | |
658 o | 2 public C
658 o | 2 public C
659 |/
659 |/
660 o 1 public B
660 o 1 public B
661 |
661 |
662 o 0 public A
662 o 0 public A
663
663
664
664
665 test complete failure
665 test complete failure
666
666
667 $ hg phase --draft 7
667 $ hg phase --draft 7
668 cannot move 1 changesets to a higher phase, use --force
668 cannot move 1 changesets to a higher phase, use --force
669 no phases changed
669 no phases changed
670 [1]
670 [1]
671
671
672 $ cd ..
672 $ cd ..
673
673
674 test hidden changeset are not cloned as public (issue3935)
674 test hidden changeset are not cloned as public (issue3935)
675
675
676 $ cd initialrepo
676 $ cd initialrepo
677
677
678 (enabling evolution)
678 (enabling evolution)
679 $ cat >> $HGRCPATH << EOF
679 $ cat >> $HGRCPATH << EOF
680 > [experimental]
680 > [experimental]
681 > evolution.createmarkers=True
681 > evolution.createmarkers=True
682 > EOF
682 > EOF
683
683
684 (making a changeset hidden; H in that case)
684 (making a changeset hidden; H in that case)
685 $ hg debugobsolete `hg id --debug -r 5`
685 $ hg debugobsolete `hg id --debug -r 5`
686 1 new obsolescence markers
686 1 new obsolescence markers
687 obsoleted 1 changesets
687 obsoleted 1 changesets
688
688
689 $ cd ..
689 $ cd ..
690 $ hg clone initialrepo clonewithobs
690 $ hg clone initialrepo clonewithobs
691 requesting all changes
691 requesting all changes
692 adding changesets
692 adding changesets
693 adding manifests
693 adding manifests
694 adding file changes
694 adding file changes
695 added 7 changesets with 6 changes to 6 files
695 added 7 changesets with 6 changes to 6 files
696 new changesets 4a2df7238c3b:17a481b3bccb
696 new changesets 4a2df7238c3b:17a481b3bccb
697 test-debug-phase: new rev 0: x -> 0
697 test-debug-phase: new rev 0: x -> 0
698 test-debug-phase: new rev 1: x -> 0
698 test-debug-phase: new rev 1: x -> 0
699 test-debug-phase: new rev 2: x -> 0
699 test-debug-phase: new rev 2: x -> 0
700 test-debug-phase: new rev 3: x -> 0
700 test-debug-phase: new rev 3: x -> 0
701 test-debug-phase: new rev 4: x -> 0
701 test-debug-phase: new rev 4: x -> 0
702 test-debug-phase: new rev 5: x -> 0
702 test-debug-phase: new rev 5: x -> 0
703 test-debug-phase: new rev 6: x -> 0
703 test-debug-phase: new rev 6: x -> 0
704 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
704 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> public
705 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
705 test-hook-close-phase: 27547f69f25460a52fff66ad004e58da7ad3fb56: -> public
706 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
706 test-hook-close-phase: f838bfaca5c7226600ebcfd84f3c3c13a28d3757: -> public
707 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
707 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: -> public
708 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
708 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: -> public
709 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
709 test-hook-close-phase: cf9fe039dfd67e829edf6522a45de057b5c86519: -> public
710 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
710 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: -> public
711 updating to branch default
711 updating to branch default
712 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
712 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
713 $ cd clonewithobs
713 $ cd clonewithobs
714 $ hg log -G --template "{rev} {phase} {desc}\n"
714 $ hg log -G --template "{rev} {phase} {desc}\n"
715 @ 6 public merge B' and E
715 @ 6 public merge B' and E
716 |\
716 |\
717 | o 5 public B'
717 | o 5 public B'
718 | |
718 | |
719 o | 4 public E
719 o | 4 public E
720 | |
720 | |
721 o | 3 public D
721 o | 3 public D
722 | |
722 | |
723 o | 2 public C
723 o | 2 public C
724 |/
724 |/
725 o 1 public B
725 o 1 public B
726 |
726 |
727 o 0 public A
727 o 0 public A
728
728
729
729
730 test verify repo containing hidden changesets, which should not abort just
730 test verify repo containing hidden changesets, which should not abort just
731 because repo.cancopy() is False
731 because repo.cancopy() is False
732
732
733 $ cd ../initialrepo
733 $ cd ../initialrepo
734 $ hg verify
734 $ hg verify
735 checking changesets
735 checking changesets
736 checking manifests
736 checking manifests
737 crosschecking files in changesets and manifests
737 crosschecking files in changesets and manifests
738 checking files
738 checking files
739 checked 8 changesets with 7 changes to 7 files
739 checked 8 changesets with 7 changes to 7 files
740
740
741 $ cd ..
741 $ cd ..
742
742
743 check whether HG_PENDING makes pending changes only in related
743 check whether HG_PENDING makes pending changes only in related
744 repositories visible to an external hook.
744 repositories visible to an external hook.
745
745
746 (emulate a transaction running concurrently by copied
746 (emulate a transaction running concurrently by copied
747 .hg/phaseroots.pending in subsequent test)
747 .hg/phaseroots.pending in subsequent test)
748
748
749 $ cat > $TESTTMP/savepending.sh <<EOF
749 $ cat > $TESTTMP/savepending.sh <<EOF
750 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
750 > cp .hg/store/phaseroots.pending .hg/store/phaseroots.pending.saved
751 > exit 1 # to avoid changing phase for subsequent tests
751 > exit 1 # to avoid changing phase for subsequent tests
752 > EOF
752 > EOF
753 $ cd push-dest
753 $ cd push-dest
754 $ hg phase 6
754 $ hg phase 6
755 6: draft
755 6: draft
756 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
756 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" phase -f -s 6
757 transaction abort!
757 transaction abort!
758 rollback completed
758 rollback completed
759 abort: pretxnclose hook exited with status 1
759 abort: pretxnclose hook exited with status 1
760 [255]
760 [255]
761 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
761 $ cp .hg/store/phaseroots.pending.saved .hg/store/phaseroots.pending
762
762
763 (check (in)visibility of phaseroot while transaction running in repo)
763 (check (in)visibility of phaseroot while transaction running in repo)
764
764
765 $ cat > $TESTTMP/checkpending.sh <<EOF
765 $ cat > $TESTTMP/checkpending.sh <<EOF
766 > echo '@initialrepo'
766 > echo '@initialrepo'
767 > hg -R "$TESTTMP/initialrepo" phase 7
767 > hg -R "$TESTTMP/initialrepo" phase 7
768 > echo '@push-dest'
768 > echo '@push-dest'
769 > hg -R "$TESTTMP/push-dest" phase 6
769 > hg -R "$TESTTMP/push-dest" phase 6
770 > exit 1 # to avoid changing phase for subsequent tests
770 > exit 1 # to avoid changing phase for subsequent tests
771 > EOF
771 > EOF
772 $ cd ../initialrepo
772 $ cd ../initialrepo
773 $ hg phase 7
773 $ hg phase 7
774 7: public
774 7: public
775 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
775 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" phase -f -s 7
776 @initialrepo
776 @initialrepo
777 7: secret
777 7: secret
778 @push-dest
778 @push-dest
779 6: draft
779 6: draft
780 transaction abort!
780 transaction abort!
781 rollback completed
781 rollback completed
782 abort: pretxnclose hook exited with status 1
782 abort: pretxnclose hook exited with status 1
783 [255]
783 [255]
784
784
785 Check that pretxnclose-phase hook can control phase movement
785 Check that pretxnclose-phase hook can control phase movement
786
786
787 $ hg phase --force b3325c91a4d9 --secret
787 $ hg phase --force b3325c91a4d9 --secret
788 test-debug-phase: move rev 3: 0 -> 2
788 test-debug-phase: move rev 3: 0 -> 2
789 test-debug-phase: move rev 4: 0 -> 2
789 test-debug-phase: move rev 4: 0 -> 2
790 test-debug-phase: move rev 5: 1 -> 2
790 test-debug-phase: move rev 5: 1 -> 2
791 test-debug-phase: move rev 7: 0 -> 2
791 test-debug-phase: move rev 7: 0 -> 2
792 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
792 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: public -> secret
793 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
793 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: public -> secret
794 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
794 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: draft -> secret
795 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
795 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: public -> secret
796 $ hg log -G -T phases
796 $ hg log -G -T phases
797 @ changeset: 7:17a481b3bccb
797 @ changeset: 7:17a481b3bccb
798 |\ tag: tip
798 |\ tag: tip
799 | | phase: secret
799 | | phase: secret
800 | | parent: 6:cf9fe039dfd6
800 | | parent: 6:cf9fe039dfd6
801 | | parent: 4:a603bfb5a83e
801 | | parent: 4:a603bfb5a83e
802 | | user: test
802 | | user: test
803 | | date: Thu Jan 01 00:00:00 1970 +0000
803 | | date: Thu Jan 01 00:00:00 1970 +0000
804 | | summary: merge B' and E
804 | | summary: merge B' and E
805 | |
805 | |
806 | o changeset: 6:cf9fe039dfd6
806 | o changeset: 6:cf9fe039dfd6
807 | | phase: public
807 | | phase: public
808 | | parent: 1:27547f69f254
808 | | parent: 1:27547f69f254
809 | | user: test
809 | | user: test
810 | | date: Thu Jan 01 00:00:00 1970 +0000
810 | | date: Thu Jan 01 00:00:00 1970 +0000
811 | | summary: B'
811 | | summary: B'
812 | |
812 | |
813 o | changeset: 4:a603bfb5a83e
813 o | changeset: 4:a603bfb5a83e
814 | | phase: secret
814 | | phase: secret
815 | | user: test
815 | | user: test
816 | | date: Thu Jan 01 00:00:00 1970 +0000
816 | | date: Thu Jan 01 00:00:00 1970 +0000
817 | | summary: E
817 | | summary: E
818 | |
818 | |
819 o | changeset: 3:b3325c91a4d9
819 o | changeset: 3:b3325c91a4d9
820 | | phase: secret
820 | | phase: secret
821 | | user: test
821 | | user: test
822 | | date: Thu Jan 01 00:00:00 1970 +0000
822 | | date: Thu Jan 01 00:00:00 1970 +0000
823 | | summary: D
823 | | summary: D
824 | |
824 | |
825 o | changeset: 2:f838bfaca5c7
825 o | changeset: 2:f838bfaca5c7
826 |/ phase: public
826 |/ phase: public
827 | user: test
827 | user: test
828 | date: Thu Jan 01 00:00:00 1970 +0000
828 | date: Thu Jan 01 00:00:00 1970 +0000
829 | summary: C
829 | summary: C
830 |
830 |
831 o changeset: 1:27547f69f254
831 o changeset: 1:27547f69f254
832 | phase: public
832 | phase: public
833 | user: test
833 | user: test
834 | date: Thu Jan 01 00:00:00 1970 +0000
834 | date: Thu Jan 01 00:00:00 1970 +0000
835 | summary: B
835 | summary: B
836 |
836 |
837 o changeset: 0:4a2df7238c3b
837 o changeset: 0:4a2df7238c3b
838 phase: public
838 phase: public
839 user: test
839 user: test
840 date: Thu Jan 01 00:00:00 1970 +0000
840 date: Thu Jan 01 00:00:00 1970 +0000
841 summary: A
841 summary: A
842
842
843
843
844 Install a hook that prevent b3325c91a4d9 to become public
844 Install a hook that prevent b3325c91a4d9 to become public
845
845
846 $ cat >> .hg/hgrc << EOF
846 $ cat >> .hg/hgrc << EOF
847 > [hooks]
847 > [hooks]
848 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
848 > pretxnclose-phase.nopublish_D = sh -c "(echo \$HG_NODE| grep -v b3325c91a4d9>/dev/null) || [ 'public' != \$HG_PHASE ]"
849 > EOF
849 > EOF
850
850
851 Try various actions. only the draft move should succeed
851 Try various actions. only the draft move should succeed
852
852
853 $ hg phase --public b3325c91a4d9
853 $ hg phase --public b3325c91a4d9
854 transaction abort!
854 transaction abort!
855 rollback completed
855 rollback completed
856 abort: pretxnclose-phase.nopublish_D hook exited with status 1
856 abort: pretxnclose-phase.nopublish_D hook exited with status 1
857 [255]
857 [255]
858 $ hg phase --public a603bfb5a83e
858 $ hg phase --public a603bfb5a83e
859 transaction abort!
859 transaction abort!
860 rollback completed
860 rollback completed
861 abort: pretxnclose-phase.nopublish_D hook exited with status 1
861 abort: pretxnclose-phase.nopublish_D hook exited with status 1
862 [255]
862 [255]
863 $ hg phase --draft 17a481b3bccb
863 $ hg phase --draft 17a481b3bccb
864 test-debug-phase: move rev 3: 2 -> 1
864 test-debug-phase: move rev 3: 2 -> 1
865 test-debug-phase: move rev 4: 2 -> 1
865 test-debug-phase: move rev 4: 2 -> 1
866 test-debug-phase: move rev 7: 2 -> 1
866 test-debug-phase: move rev 7: 2 -> 1
867 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
867 test-hook-close-phase: b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e: secret -> draft
868 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
868 test-hook-close-phase: a603bfb5a83e312131cebcd05353c217d4d21dde: secret -> draft
869 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
869 test-hook-close-phase: 17a481b3bccb796c0521ae97903d81c52bfee4af: secret -> draft
870 $ hg phase --public 17a481b3bccb
870 $ hg phase --public 17a481b3bccb
871 transaction abort!
871 transaction abort!
872 rollback completed
872 rollback completed
873 abort: pretxnclose-phase.nopublish_D hook exited with status 1
873 abort: pretxnclose-phase.nopublish_D hook exited with status 1
874 [255]
874 [255]
875
875
876 $ cd ..
876 $ cd ..
877
877
878 Test for the "internal" phase
878 Test for the "internal" phase
879 =============================
879 =============================
880
880
881 Check we deny its usage on older repository
881 Check we deny its usage on older repository
882
882
883 $ hg init no-internal-phase --config format.internal-phase=no
883 $ hg init no-internal-phase --config format.internal-phase=no
884 $ cd no-internal-phase
884 $ cd no-internal-phase
885 $ cat .hg/requires
885 $ cat .hg/requires
886 dotencode
886 dotencode
887 fncache
887 fncache
888 generaldelta
888 generaldelta
889 revlogv1
889 revlogv1
890 sparserevlog
890 sparserevlog
891 store
891 store
892 $ echo X > X
892 $ echo X > X
893 $ hg add X
893 $ hg add X
894 $ hg status
894 $ hg status
895 A X
895 A X
896 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
896 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit" 2>&1 | grep ProgrammingError
897 ** ProgrammingError: this repository does not support the internal phase
897 ** ProgrammingError: this repository does not support the internal phase
898 raise error.ProgrammingError(msg)
898 raise error.ProgrammingError(msg)
899 *ProgrammingError: this repository does not support the internal phase (glob)
899 *ProgrammingError: this repository does not support the internal phase (glob)
900 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
900 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" 2>&1 | grep ProgrammingError
901 ** ProgrammingError: this repository does not support the archived phase
901 ** ProgrammingError: this repository does not support the archived phase
902 raise error.ProgrammingError(msg)
902 raise error.ProgrammingError(msg)
903 *ProgrammingError: this repository does not support the archived phase (glob)
903 *ProgrammingError: this repository does not support the archived phase (glob)
904
904
905 $ cd ..
905 $ cd ..
906
906
907 Check it works fine with repository that supports it.
907 Check it works fine with repository that supports it.
908
908
909 $ hg init internal-phase --config format.internal-phase=yes
909 $ hg init internal-phase --config format.internal-phase=yes
910 $ cd internal-phase
910 $ cd internal-phase
911 $ cat .hg/requires
911 $ cat .hg/requires
912 dotencode
912 dotencode
913 fncache
913 fncache
914 generaldelta
914 generaldelta
915 internal-phase
915 internal-phase
916 revlogv1
916 revlogv1
917 sparserevlog
917 sparserevlog
918 store
918 store
919 $ mkcommit A
919 $ mkcommit A
920 test-debug-phase: new rev 0: x -> 1
920 test-debug-phase: new rev 0: x -> 1
921 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
921 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft
922
922
923 Commit an internal changesets
923 Commit an internal changesets
924
924
925 $ echo B > B
925 $ echo B > B
926 $ hg add B
926 $ hg add B
927 $ hg status
927 $ hg status
928 A B
928 A B
929 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
929 $ hg --config "phases.new-commit=internal" commit -m "my test internal commit"
930 test-debug-phase: new rev 1: x -> 96
930 test-debug-phase: new rev 1: x -> 96
931 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
931 test-hook-close-phase: c01c42dffc7f81223397e99652a0703f83e1c5ea: -> internal
932
932
933 The changeset is a working parent descendant.
933 The changeset is a working parent descendant.
934 Per the usual visibility rules, it is made visible.
934 Per the usual visibility rules, it is made visible.
935
935
936 $ hg log -G -l 3
936 $ hg log -G -l 3
937 @ changeset: 1:c01c42dffc7f
937 @ changeset: 1:c01c42dffc7f
938 | tag: tip
938 | tag: tip
939 | user: test
939 | user: test
940 | date: Thu Jan 01 00:00:00 1970 +0000
940 | date: Thu Jan 01 00:00:00 1970 +0000
941 | summary: my test internal commit
941 | summary: my test internal commit
942 |
942 |
943 o changeset: 0:4a2df7238c3b
943 o changeset: 0:4a2df7238c3b
944 user: test
944 user: test
945 date: Thu Jan 01 00:00:00 1970 +0000
945 date: Thu Jan 01 00:00:00 1970 +0000
946 summary: A
946 summary: A
947
947
948
948
949 Commit is hidden as expected
949 Commit is hidden as expected
950
950
951 $ hg up 0
951 $ hg up 0
952 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
952 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
953 $ hg log -G
953 $ hg log -G
954 @ changeset: 0:4a2df7238c3b
954 @ changeset: 0:4a2df7238c3b
955 tag: tip
955 tag: tip
956 user: test
956 user: test
957 date: Thu Jan 01 00:00:00 1970 +0000
957 date: Thu Jan 01 00:00:00 1970 +0000
958 summary: A
958 summary: A
959
959
960
960
961 Test for archived phase
961 Test for archived phase
962 -----------------------
962 -----------------------
963
963
964 Commit an archived changesets
964 Commit an archived changesets
965
965
966 $ echo B > B
966 $ echo B > B
967 $ hg add B
967 $ hg add B
968 $ hg status
968 $ hg status
969 A B
969 A B
970 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
970 $ hg --config "phases.new-commit=archived" commit -m "my test archived commit"
971 test-debug-phase: new rev 2: x -> 32
971 test-debug-phase: new rev 2: x -> 32
972 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
972 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived
973
973
974 The changeset is a working parent descendant.
974 The changeset is a working parent descendant.
975 Per the usual visibility rules, it is made visible.
975 Per the usual visibility rules, it is made visible.
976
976
977 $ hg log -G -l 3
977 $ hg log -G -l 3
978 @ changeset: 2:8df5997c3361
978 @ changeset: 2:8df5997c3361
979 | tag: tip
979 | tag: tip
980 | parent: 0:4a2df7238c3b
980 | parent: 0:4a2df7238c3b
981 | user: test
981 | user: test
982 | date: Thu Jan 01 00:00:00 1970 +0000
982 | date: Thu Jan 01 00:00:00 1970 +0000
983 | summary: my test archived commit
983 | summary: my test archived commit
984 |
984 |
985 o changeset: 0:4a2df7238c3b
985 o changeset: 0:4a2df7238c3b
986 user: test
986 user: test
987 date: Thu Jan 01 00:00:00 1970 +0000
987 date: Thu Jan 01 00:00:00 1970 +0000
988 summary: A
988 summary: A
989
989
990
990
991 Commit is hidden as expected
991 Commit is hidden as expected
992
992
993 $ hg up 0
993 $ hg up 0
994 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
994 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
995 $ hg log -G
995 $ hg log -G
996 @ changeset: 0:4a2df7238c3b
996 @ changeset: 0:4a2df7238c3b
997 tag: tip
997 tag: tip
998 user: test
998 user: test
999 date: Thu Jan 01 00:00:00 1970 +0000
999 date: Thu Jan 01 00:00:00 1970 +0000
1000 summary: A
1000 summary: A
1001
1001
1002 $ cd ..
1002 $ cd ..
1003
1003
1004 Recommitting an exact match of a public commit shouldn't change it to
1004 Recommitting an exact match of a public commit shouldn't change it to
1005 draft:
1005 draft:
1006
1006
1007 $ cd initialrepo
1007 $ cd initialrepo
1008 $ hg phase -r 2
1008 $ hg phase -r 2
1009 2: public
1009 2: public
1010 $ hg up -C 1
1010 $ hg up -C 1
1011 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1011 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1012 $ mkcommit C
1012 $ mkcommit C
1013 warning: commit already existed in the repository!
1013 warning: commit already existed in the repository!
1014 $ hg phase -r 2
1014 $ hg phase -r 2
1015 2: public
1015 2: public
1016
1016
1017 Same, but for secret:
1017 Same, but for secret:
1018
1018
1019 $ hg up 7
1019 $ hg up 7
1020 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1020 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1021 $ mkcommit F -s
1021 $ mkcommit F -s
1022 test-debug-phase: new rev 8: x -> 2
1022 test-debug-phase: new rev 8: x -> 2
1023 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1023 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1024 $ hg up 7
1024 $ hg up 7
1025 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1025 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1026 $ hg phase
1026 $ hg phase
1027 7: draft
1027 7: draft
1028 $ mkcommit F
1028 $ mkcommit F
1029 test-debug-phase: new rev 8: x -> 2
1029 test-debug-phase: new rev 8: x -> 2
1030 warning: commit already existed in the repository!
1030 warning: commit already existed in the repository!
1031 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1031 test-hook-close-phase: de414268ec5ce2330c590b942fbb5ff0b0ca1a0a: -> secret
1032 $ hg phase -r tip
1032 $ hg phase -r tip
1033 8: secret
1033 8: secret
1034
1034
1035 But what about obsoleted changesets?
1035 But what about obsoleted changesets?
1036
1036
1037 $ hg up 4
1037 $ hg up 4
1038 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1038 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1039 $ mkcommit H
1039 $ mkcommit H
1040 test-debug-phase: new rev 5: x -> 2
1040 test-debug-phase: new rev 5: x -> 2
1041 warning: commit already existed in the repository!
1041 warning: commit already existed in the repository!
1042 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1042 test-hook-close-phase: a030c6be5127abc010fcbff1851536552e6951a8: -> secret
1043 $ hg phase -r 5
1043 $ hg phase -r 5
1044 5: secret
1044 5: secret
1045 $ hg par
1045 $ hg par
1046 changeset: 5:a030c6be5127
1046 changeset: 5:a030c6be5127
1047 user: test
1047 user: test
1048 date: Thu Jan 01 00:00:00 1970 +0000
1048 date: Thu Jan 01 00:00:00 1970 +0000
1049 obsolete: pruned
1049 obsolete: pruned
1050 summary: H
1050 summary: H
1051
1051
1052 $ hg up tip
1052 $ hg up tip
1053 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1053 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1054 $ cd ..
1054 $ cd ..
@@ -1,204 +1,204
1 A new repository uses zlib storage, which doesn't need a requirement
1 A new repository uses zlib storage, which doesn't need a requirement
2
2
3 $ hg init default
3 $ hg init default
4 $ cd default
4 $ cd default
5 $ cat .hg/requires
5 $ cat .hg/requires
6 dotencode
6 dotencode
7 fncache
7 fncache
8 generaldelta
8 generaldelta
9 revlogv1
9 revlogv1
10 sparserevlog
10 sparserevlog
11 store
11 store
12 testonly-simplestore (reposimplestore !)
12 testonly-simplestore (reposimplestore !)
13
13
14 $ touch foo
14 $ touch foo
15 $ hg -q commit -A -m 'initial commit with a lot of repeated repeated repeated text to trigger compression'
15 $ hg -q commit -A -m 'initial commit with a lot of repeated repeated repeated text to trigger compression'
16 $ hg debugrevlog -c | grep 0x78
16 $ hg debugrevlog -c | grep 0x78
17 0x78 (x) : 1 (100.00%)
17 0x78 (x) : 1 (100.00%)
18 0x78 (x) : 110 (100.00%)
18 0x78 (x) : 110 (100.00%)
19
19
20 $ cd ..
20 $ cd ..
21
21
22 Unknown compression engine to format.compression aborts
22 Unknown compression engine to format.compression aborts
23
23
24 $ hg --config format.revlog-compression=unknown init unknown
24 $ hg --config format.revlog-compression=unknown init unknown
25 abort: compression engines "unknown" defined by format.revlog-compression not available
25 abort: compression engines "unknown" defined by format.revlog-compression not available
26 (run "hg debuginstall" to list available compression engines)
26 (run "hg debuginstall" to list available compression engines)
27 [255]
27 [255]
28
28
29 unknown compression engine in a list with known one works fine
29 unknown compression engine in a list with known one works fine
30
30
31 $ hg --config format.revlog-compression=zlib,unknown init zlib-before-unknow
31 $ hg --config format.revlog-compression=zlib,unknown init zlib-before-unknow
32 $ hg --config format.revlog-compression=unknown,zlib init unknown-before-zlib
32 $ hg --config format.revlog-compression=unknown,zlib init unknown-before-zlib
33
33
34 A requirement specifying an unknown compression engine results in bail
34 A requirement specifying an unknown compression engine results in bail
35
35
36 $ hg init unknownrequirement
36 $ hg init unknownrequirement
37 $ cd unknownrequirement
37 $ cd unknownrequirement
38 $ echo exp-compression-unknown >> .hg/requires
38 $ echo exp-compression-unknown >> .hg/requires
39 $ hg log
39 $ hg log
40 abort: repository requires features unknown to this Mercurial: exp-compression-unknown!
40 abort: repository requires features unknown to this Mercurial: exp-compression-unknown!
41 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
41 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
42 [255]
42 [255]
43
43
44 $ cd ..
44 $ cd ..
45
45
46 #if zstd
46 #if zstd
47
47
48 $ hg --config format.revlog-compression=zstd init zstd
48 $ hg --config format.revlog-compression=zstd init zstd
49 $ cd zstd
49 $ cd zstd
50 $ cat .hg/requires
50 $ cat .hg/requires
51 dotencode
51 dotencode
52 fncache
52 fncache
53 generaldelta
53 generaldelta
54 revlog-compression-zstd
54 revlog-compression-zstd
55 revlogv1
55 revlogv1
56 sparserevlog
56 sparserevlog
57 store
57 store
58 testonly-simplestore (reposimplestore !)
58 testonly-simplestore (reposimplestore !)
59
59
60 $ touch foo
60 $ touch foo
61 $ hg -q commit -A -m 'initial commit with a lot of repeated repeated repeated text'
61 $ hg -q commit -A -m 'initial commit with a lot of repeated repeated repeated text'
62
62
63 $ hg debugrevlog -c | grep 0x28
63 $ hg debugrevlog -c | grep 0x28
64 0x28 : 1 (100.00%)
64 0x28 : 1 (100.00%)
65 0x28 : 98 (100.00%)
65 0x28 : 98 (100.00%)
66
66
67 $ cd ..
67 $ cd ..
68
68
69 Specifying a new format.compression on an existing repo won't introduce data
69 Specifying a new format.compression on an existing repo won't introduce data
70 with that engine or a requirement
70 with that engine or a requirement
71
71
72 $ cd default
72 $ cd default
73 $ touch bar
73 $ touch bar
74 $ hg --config format.revlog-compression=zstd -q commit -A -m 'add bar with a lot of repeated repeated repeated text'
74 $ hg --config format.revlog-compression=zstd -q commit -A -m 'add bar with a lot of repeated repeated repeated text'
75
75
76 $ cat .hg/requires
76 $ cat .hg/requires
77 dotencode
77 dotencode
78 fncache
78 fncache
79 generaldelta
79 generaldelta
80 revlogv1
80 revlogv1
81 sparserevlog
81 sparserevlog
82 store
82 store
83 testonly-simplestore (reposimplestore !)
83 testonly-simplestore (reposimplestore !)
84
84
85 $ hg debugrevlog -c | grep 0x78
85 $ hg debugrevlog -c | grep 0x78
86 0x78 (x) : 2 (100.00%)
86 0x78 (x) : 2 (100.00%)
87 0x78 (x) : 199 (100.00%)
87 0x78 (x) : 199 (100.00%)
88
88
89 #endif
89 #endif
90
90
91 checking zlib options
91 checking zlib options
92 =====================
92 =====================
93
93
94 $ hg init zlib-level-default
94 $ hg init zlib-level-default
95 $ hg init zlib-level-1
95 $ hg init zlib-level-1
96 $ cat << EOF >> zlib-level-1/.hg/hgrc
96 $ cat << EOF >> zlib-level-1/.hg/hgrc
97 > [storage]
97 > [storage]
98 > revlog.zlib.level=1
98 > revlog.zlib.level=1
99 > EOF
99 > EOF
100 $ hg init zlib-level-9
100 $ hg init zlib-level-9
101 $ cat << EOF >> zlib-level-9/.hg/hgrc
101 $ cat << EOF >> zlib-level-9/.hg/hgrc
102 > [storage]
102 > [storage]
103 > revlog.zlib.level=9
103 > revlog.zlib.level=9
104 > EOF
104 > EOF
105
105
106
106
107 $ commitone() {
107 $ commitone() {
108 > repo=$1
108 > repo=$1
109 > cp $RUNTESTDIR/bundles/issue4438-r1.hg $repo/a
109 > cp $RUNTESTDIR/bundles/issue4438-r1.hg $repo/a
110 > hg -R $repo add $repo/a
110 > hg -R $repo add $repo/a
111 > hg -R $repo commit -m some-commit
111 > hg -R $repo commit -m some-commit
112 > }
112 > }
113
113
114 $ for repo in zlib-level-default zlib-level-1 zlib-level-9; do
114 $ for repo in zlib-level-default zlib-level-1 zlib-level-9; do
115 > commitone $repo
115 > commitone $repo
116 > done
116 > done
117
117
118 $ $RUNTESTDIR/f -s */.hg/store/data/*
118 $ $RUNTESTDIR/f -s */.hg/store/data/*
119 default/.hg/store/data/foo.i: size=64 (pure !)
119 default/.hg/store/data/foo.i: size=64 (pure !)
120 zlib-level-1/.hg/store/data/a.i: size=4146
120 zlib-level-1/.hg/store/data/a.i: size=4146
121 zlib-level-9/.hg/store/data/a.i: size=4138
121 zlib-level-9/.hg/store/data/a.i: size=4138
122 zlib-level-default/.hg/store/data/a.i: size=4138
122 zlib-level-default/.hg/store/data/a.i: size=4138
123
123
124 Test error cases
124 Test error cases
125
125
126 $ hg init zlib-level-invalid
126 $ hg init zlib-level-invalid
127 $ cat << EOF >> zlib-level-invalid/.hg/hgrc
127 $ cat << EOF >> zlib-level-invalid/.hg/hgrc
128 > [storage]
128 > [storage]
129 > revlog.zlib.level=foobar
129 > revlog.zlib.level=foobar
130 > EOF
130 > EOF
131 $ commitone zlib-level-invalid
131 $ commitone zlib-level-invalid
132 abort: storage.revlog.zlib.level is not a valid integer ('foobar')
132 abort: storage.revlog.zlib.level is not a valid integer ('foobar')
133 abort: storage.revlog.zlib.level is not a valid integer ('foobar')
133 abort: storage.revlog.zlib.level is not a valid integer ('foobar')
134 [255]
134 [30]
135
135
136 $ hg init zlib-level-out-of-range
136 $ hg init zlib-level-out-of-range
137 $ cat << EOF >> zlib-level-out-of-range/.hg/hgrc
137 $ cat << EOF >> zlib-level-out-of-range/.hg/hgrc
138 > [storage]
138 > [storage]
139 > revlog.zlib.level=42
139 > revlog.zlib.level=42
140 > EOF
140 > EOF
141
141
142 $ commitone zlib-level-out-of-range
142 $ commitone zlib-level-out-of-range
143 abort: invalid value for `storage.revlog.zlib.level` config: 42
143 abort: invalid value for `storage.revlog.zlib.level` config: 42
144 abort: invalid value for `storage.revlog.zlib.level` config: 42
144 abort: invalid value for `storage.revlog.zlib.level` config: 42
145 [255]
145 [255]
146
146
147 #if zstd
147 #if zstd
148
148
149 checking zstd options
149 checking zstd options
150 =====================
150 =====================
151
151
152 $ hg init zstd-level-default --config format.revlog-compression=zstd
152 $ hg init zstd-level-default --config format.revlog-compression=zstd
153 $ hg init zstd-level-1 --config format.revlog-compression=zstd
153 $ hg init zstd-level-1 --config format.revlog-compression=zstd
154 $ cat << EOF >> zstd-level-1/.hg/hgrc
154 $ cat << EOF >> zstd-level-1/.hg/hgrc
155 > [storage]
155 > [storage]
156 > revlog.zstd.level=1
156 > revlog.zstd.level=1
157 > EOF
157 > EOF
158 $ hg init zstd-level-22 --config format.revlog-compression=zstd
158 $ hg init zstd-level-22 --config format.revlog-compression=zstd
159 $ cat << EOF >> zstd-level-22/.hg/hgrc
159 $ cat << EOF >> zstd-level-22/.hg/hgrc
160 > [storage]
160 > [storage]
161 > revlog.zstd.level=22
161 > revlog.zstd.level=22
162 > EOF
162 > EOF
163
163
164
164
165 $ commitone() {
165 $ commitone() {
166 > repo=$1
166 > repo=$1
167 > cp $RUNTESTDIR/bundles/issue4438-r1.hg $repo/a
167 > cp $RUNTESTDIR/bundles/issue4438-r1.hg $repo/a
168 > hg -R $repo add $repo/a
168 > hg -R $repo add $repo/a
169 > hg -R $repo commit -m some-commit
169 > hg -R $repo commit -m some-commit
170 > }
170 > }
171
171
172 $ for repo in zstd-level-default zstd-level-1 zstd-level-22; do
172 $ for repo in zstd-level-default zstd-level-1 zstd-level-22; do
173 > commitone $repo
173 > commitone $repo
174 > done
174 > done
175
175
176 $ $RUNTESTDIR/f -s zstd-*/.hg/store/data/*
176 $ $RUNTESTDIR/f -s zstd-*/.hg/store/data/*
177 zstd-level-1/.hg/store/data/a.i: size=4114
177 zstd-level-1/.hg/store/data/a.i: size=4114
178 zstd-level-22/.hg/store/data/a.i: size=4091
178 zstd-level-22/.hg/store/data/a.i: size=4091
179 zstd-level-default/\.hg/store/data/a\.i: size=(4094|4102) (re)
179 zstd-level-default/\.hg/store/data/a\.i: size=(4094|4102) (re)
180
180
181 Test error cases
181 Test error cases
182
182
183 $ hg init zstd-level-invalid --config format.revlog-compression=zstd
183 $ hg init zstd-level-invalid --config format.revlog-compression=zstd
184 $ cat << EOF >> zstd-level-invalid/.hg/hgrc
184 $ cat << EOF >> zstd-level-invalid/.hg/hgrc
185 > [storage]
185 > [storage]
186 > revlog.zstd.level=foobar
186 > revlog.zstd.level=foobar
187 > EOF
187 > EOF
188 $ commitone zstd-level-invalid
188 $ commitone zstd-level-invalid
189 abort: storage.revlog.zstd.level is not a valid integer ('foobar')
189 abort: storage.revlog.zstd.level is not a valid integer ('foobar')
190 abort: storage.revlog.zstd.level is not a valid integer ('foobar')
190 abort: storage.revlog.zstd.level is not a valid integer ('foobar')
191 [255]
191 [30]
192
192
193 $ hg init zstd-level-out-of-range --config format.revlog-compression=zstd
193 $ hg init zstd-level-out-of-range --config format.revlog-compression=zstd
194 $ cat << EOF >> zstd-level-out-of-range/.hg/hgrc
194 $ cat << EOF >> zstd-level-out-of-range/.hg/hgrc
195 > [storage]
195 > [storage]
196 > revlog.zstd.level=42
196 > revlog.zstd.level=42
197 > EOF
197 > EOF
198
198
199 $ commitone zstd-level-out-of-range
199 $ commitone zstd-level-out-of-range
200 abort: invalid value for `storage.revlog.zstd.level` config: 42
200 abort: invalid value for `storage.revlog.zstd.level` config: 42
201 abort: invalid value for `storage.revlog.zstd.level` config: 42
201 abort: invalid value for `storage.revlog.zstd.level` config: 42
202 [255]
202 [255]
203
203
204 #endif
204 #endif
General Comments 0
You need to be logged in to leave comments. Login now