##// END OF EJS Templates
merge with stable
Martin von Zweigbergk -
r32245:3a755652 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,55 +1,55 b''
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3
3
4 <?include guids.wxi ?>
4 <?include guids.wxi ?>
5 <?include defines.wxi ?>
5 <?include defines.wxi ?>
6
6
7 <Fragment>
7 <Fragment>
8 <ComponentGroup Id='helpFolder'>
8 <ComponentGroup Id='helpFolder'>
9 <ComponentRef Id='help.root' />
9 <ComponentRef Id='help.root' />
10 <ComponentRef Id='help.internals' />
10 <ComponentRef Id='help.internals' />
11 </ComponentGroup>
11 </ComponentGroup>
12 </Fragment>
12 </Fragment>
13
13
14 <Fragment>
14 <Fragment>
15 <DirectoryRef Id="INSTALLDIR">
15 <DirectoryRef Id="INSTALLDIR">
16 <Directory Id="helpdir" Name="help" FileSource="$(var.SourceDir)">
16 <Directory Id="helpdir" Name="help" FileSource="$(var.SourceDir)">
17 <Component Id="help.root" Guid="$(var.help.root.guid)" Win64='$(var.IsX64)'>
17 <Component Id="help.root" Guid="$(var.help.root.guid)" Win64='$(var.IsX64)'>
18 <File Name="bundlespec.txt" />
18 <File Name="bundlespec.txt" />
19 <File Name="color.txt" />
19 <File Name="color.txt" />
20 <File Name="config.txt" KeyPath="yes" />
20 <File Name="config.txt" KeyPath="yes" />
21 <File Name="dates.txt" />
21 <File Name="dates.txt" />
22 <File Name="diffs.txt" />
22 <File Name="diffs.txt" />
23 <File Name="environment.txt" />
23 <File Name="environment.txt" />
24 <File Name="extensions.txt" />
24 <File Name="extensions.txt" />
25 <File Name="filesets.txt" />
25 <File Name="filesets.txt" />
26 <File Name="glossary.txt" />
26 <File Name="glossary.txt" />
27 <File Name="hgignore.txt" />
27 <File Name="hgignore.txt" />
28 <File Name="hgweb.txt" />
28 <File Name="hgweb.txt" />
29 <File Name="merge-tools.txt" />
29 <File Name="merge-tools.txt" />
30 <File Name="pager.txt" />
30 <File Name="pager.txt" />
31 <File Name="patterns.txt" />
31 <File Name="patterns.txt" />
32 <File Name="phases.txt" />
32 <File Name="phases.txt" />
33 <File Name="revisions.txt" />
33 <File Name="revisions.txt" />
34 <File Name="scripting.txt" />
34 <File Name="scripting.txt" />
35 <File Name="subrepos.txt" />
35 <File Name="subrepos.txt" />
36 <File Name="templates.txt" />
36 <File Name="templates.txt" />
37 <File Name="urls.txt" />
37 <File Name="urls.txt" />
38 </Component>
38 </Component>
39
39
40 <Directory Id="help.internaldir" Name="internals">
40 <Directory Id="help.internaldir" Name="internals">
41 <Component Id="help.internals" Guid="$(var.help.internals.guid)" Win64='$(var.IsX64)'>
41 <Component Id="help.internals" Guid="$(var.help.internals.guid)" Win64='$(var.IsX64)'>
42 <File Id="internals.bundles.txt" Name="bundles.txt" KeyPath="yes" />
42 <File Id="internals.bundles.txt" Name="bundles.txt" KeyPath="yes" />
43 <File Id="internals.censor.txt" Name="censor.txt" KeyPath="yes" />
43 <File Id="internals.censor.txt" Name="censor.txt" />
44 <File Id="internals.changegroups.txt" Name="changegroups.txt" />
44 <File Id="internals.changegroups.txt" Name="changegroups.txt" />
45 <File Id="internals.requirements.txt" Name="requirements.txt" />
45 <File Id="internals.requirements.txt" Name="requirements.txt" />
46 <File Id="internals.revlogs.txt" Name="revlogs.txt" />
46 <File Id="internals.revlogs.txt" Name="revlogs.txt" />
47 <File Id="internals.wireprotocol.txt" Name="wireprotocol.txt" />
47 <File Id="internals.wireprotocol.txt" Name="wireprotocol.txt" />
48 </Component>
48 </Component>
49 </Directory>
49 </Directory>
50
50
51 </Directory>
51 </Directory>
52 </DirectoryRef>
52 </DirectoryRef>
53 </Fragment>
53 </Fragment>
54
54
55 </Wix>
55 </Wix>
@@ -1,211 +1,211 b''
1 # churn.py - create a graph of revisions count grouped by template
1 # churn.py - create a graph of revisions count grouped by template
2 #
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''command to display statistics about repository history'''
9 '''command to display statistics about repository history'''
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import datetime
13 import datetime
14 import os
14 import os
15 import time
15 import time
16
16
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial import (
18 from mercurial import (
19 cmdutil,
19 cmdutil,
20 commands,
20 commands,
21 encoding,
21 encoding,
22 patch,
22 patch,
23 scmutil,
23 scmutil,
24 util,
24 util,
25 )
25 )
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
29 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
30 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
30 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
31 # be specifying the version(s) of Mercurial they are tested with, or
31 # be specifying the version(s) of Mercurial they are tested with, or
32 # leave the attribute unspecified.
32 # leave the attribute unspecified.
33 testedwith = 'ships-with-hg-core'
33 testedwith = 'ships-with-hg-core'
34
34
35 def maketemplater(ui, repo, tmpl):
35 def maketemplater(ui, repo, tmpl):
36 return cmdutil.changeset_templater(ui, repo, False, None, tmpl, None, False)
36 return cmdutil.changeset_templater(ui, repo, False, None, tmpl, None, False)
37
37
38 def changedlines(ui, repo, ctx1, ctx2, fns):
38 def changedlines(ui, repo, ctx1, ctx2, fns):
39 added, removed = 0, 0
39 added, removed = 0, 0
40 fmatch = scmutil.matchfiles(repo, fns)
40 fmatch = scmutil.matchfiles(repo, fns)
41 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
41 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
42 for l in diff.split('\n'):
42 for l in diff.split('\n'):
43 if l.startswith("+") and not l.startswith("+++ "):
43 if l.startswith("+") and not l.startswith("+++ "):
44 added += 1
44 added += 1
45 elif l.startswith("-") and not l.startswith("--- "):
45 elif l.startswith("-") and not l.startswith("--- "):
46 removed += 1
46 removed += 1
47 return (added, removed)
47 return (added, removed)
48
48
49 def countrate(ui, repo, amap, *pats, **opts):
49 def countrate(ui, repo, amap, *pats, **opts):
50 """Calculate stats"""
50 """Calculate stats"""
51 if opts.get('dateformat'):
51 if opts.get('dateformat'):
52 def getkey(ctx):
52 def getkey(ctx):
53 t, tz = ctx.date()
53 t, tz = ctx.date()
54 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
54 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
55 return date.strftime(opts['dateformat'])
55 return date.strftime(opts['dateformat'])
56 else:
56 else:
57 tmpl = opts.get('oldtemplate') or opts.get('template')
57 tmpl = opts.get('oldtemplate') or opts.get('template')
58 tmpl = maketemplater(ui, repo, tmpl)
58 tmpl = maketemplater(ui, repo, tmpl)
59 def getkey(ctx):
59 def getkey(ctx):
60 ui.pushbuffer()
60 ui.pushbuffer()
61 tmpl.show(ctx)
61 tmpl.show(ctx)
62 return ui.popbuffer()
62 return ui.popbuffer()
63
63
64 state = {'count': 0}
64 state = {'count': 0}
65 rate = {}
65 rate = {}
66 df = False
66 df = False
67 if opts.get('date'):
67 if opts.get('date'):
68 df = util.matchdate(opts['date'])
68 df = util.matchdate(opts['date'])
69
69
70 m = scmutil.match(repo[None], pats, opts)
70 m = scmutil.match(repo[None], pats, opts)
71 def prep(ctx, fns):
71 def prep(ctx, fns):
72 rev = ctx.rev()
72 rev = ctx.rev()
73 if df and not df(ctx.date()[0]): # doesn't match date format
73 if df and not df(ctx.date()[0]): # doesn't match date format
74 return
74 return
75
75
76 key = getkey(ctx).strip()
76 key = getkey(ctx).strip()
77 key = amap.get(key, key) # alias remap
77 key = amap.get(key, key) # alias remap
78 if opts.get('changesets'):
78 if opts.get('changesets'):
79 rate[key] = (rate.get(key, (0,))[0] + 1, 0)
79 rate[key] = (rate.get(key, (0,))[0] + 1, 0)
80 else:
80 else:
81 parents = ctx.parents()
81 parents = ctx.parents()
82 if len(parents) > 1:
82 if len(parents) > 1:
83 ui.note(_('revision %d is a merge, ignoring...\n') % (rev,))
83 ui.note(_('revision %d is a merge, ignoring...\n') % (rev,))
84 return
84 return
85
85
86 ctx1 = parents[0]
86 ctx1 = parents[0]
87 lines = changedlines(ui, repo, ctx1, ctx, fns)
87 lines = changedlines(ui, repo, ctx1, ctx, fns)
88 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
88 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
89
89
90 state['count'] += 1
90 state['count'] += 1
91 ui.progress(_('analyzing'), state['count'], total=len(repo),
91 ui.progress(_('analyzing'), state['count'], total=len(repo),
92 unit=_('revisions'))
92 unit=_('revisions'))
93
93
94 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
94 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
95 continue
95 continue
96
96
97 ui.progress(_('analyzing'), None)
97 ui.progress(_('analyzing'), None)
98
98
99 return rate
99 return rate
100
100
101
101
102 @command('churn',
102 @command('churn',
103 [('r', 'rev', [],
103 [('r', 'rev', [],
104 _('count rate for the specified revision or revset'), _('REV')),
104 _('count rate for the specified revision or revset'), _('REV')),
105 ('d', 'date', '',
105 ('d', 'date', '',
106 _('count rate for revisions matching date spec'), _('DATE')),
106 _('count rate for revisions matching date spec'), _('DATE')),
107 ('t', 'oldtemplate', '',
107 ('t', 'oldtemplate', '',
108 _('template to group changesets (DEPRECATED)'), _('TEMPLATE')),
108 _('template to group changesets (DEPRECATED)'), _('TEMPLATE')),
109 ('T', 'template', '{author|email}',
109 ('T', 'template', '{author|email}',
110 _('template to group changesets'), _('TEMPLATE')),
110 _('template to group changesets'), _('TEMPLATE')),
111 ('f', 'dateformat', '',
111 ('f', 'dateformat', '',
112 _('strftime-compatible format for grouping by date'), _('FORMAT')),
112 _('strftime-compatible format for grouping by date'), _('FORMAT')),
113 ('c', 'changesets', False, _('count rate by number of changesets')),
113 ('c', 'changesets', False, _('count rate by number of changesets')),
114 ('s', 'sort', False, _('sort by key (default: sort by count)')),
114 ('s', 'sort', False, _('sort by key (default: sort by count)')),
115 ('', 'diffstat', False, _('display added/removed lines separately')),
115 ('', 'diffstat', False, _('display added/removed lines separately')),
116 ('', 'aliases', '', _('file with email aliases'), _('FILE')),
116 ('', 'aliases', '', _('file with email aliases'), _('FILE')),
117 ] + commands.walkopts,
117 ] + commands.walkopts,
118 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]"),
118 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]"),
119 inferrepo=True)
119 inferrepo=True)
120 def churn(ui, repo, *pats, **opts):
120 def churn(ui, repo, *pats, **opts):
121 '''histogram of changes to the repository
121 '''histogram of changes to the repository
122
122
123 This command will display a histogram representing the number
123 This command will display a histogram representing the number
124 of changed lines or revisions, grouped according to the given
124 of changed lines or revisions, grouped according to the given
125 template. The default template will group changes by author.
125 template. The default template will group changes by author.
126 The --dateformat option may be used to group the results by
126 The --dateformat option may be used to group the results by
127 date instead.
127 date instead.
128
128
129 Statistics are based on the number of changed lines, or
129 Statistics are based on the number of changed lines, or
130 alternatively the number of matching revisions if the
130 alternatively the number of matching revisions if the
131 --changesets option is specified.
131 --changesets option is specified.
132
132
133 Examples::
133 Examples::
134
134
135 # display count of changed lines for every committer
135 # display count of changed lines for every committer
136 hg churn -t "{author|email}"
136 hg churn -T "{author|email}"
137
137
138 # display daily activity graph
138 # display daily activity graph
139 hg churn -f "%H" -s -c
139 hg churn -f "%H" -s -c
140
140
141 # display activity of developers by month
141 # display activity of developers by month
142 hg churn -f "%Y-%m" -s -c
142 hg churn -f "%Y-%m" -s -c
143
143
144 # display count of lines changed in every year
144 # display count of lines changed in every year
145 hg churn -f "%Y" -s
145 hg churn -f "%Y" -s
146
146
147 It is possible to map alternate email addresses to a main address
147 It is possible to map alternate email addresses to a main address
148 by providing a file using the following format::
148 by providing a file using the following format::
149
149
150 <alias email> = <actual email>
150 <alias email> = <actual email>
151
151
152 Such a file may be specified with the --aliases option, otherwise
152 Such a file may be specified with the --aliases option, otherwise
153 a .hgchurn file will be looked for in the working directory root.
153 a .hgchurn file will be looked for in the working directory root.
154 Aliases will be split from the rightmost "=".
154 Aliases will be split from the rightmost "=".
155 '''
155 '''
156 def pad(s, l):
156 def pad(s, l):
157 return s + " " * (l - encoding.colwidth(s))
157 return s + " " * (l - encoding.colwidth(s))
158
158
159 amap = {}
159 amap = {}
160 aliases = opts.get('aliases')
160 aliases = opts.get('aliases')
161 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
161 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
162 aliases = repo.wjoin('.hgchurn')
162 aliases = repo.wjoin('.hgchurn')
163 if aliases:
163 if aliases:
164 for l in open(aliases, "r"):
164 for l in open(aliases, "r"):
165 try:
165 try:
166 alias, actual = l.rsplit('=' in l and '=' or None, 1)
166 alias, actual = l.rsplit('=' in l and '=' or None, 1)
167 amap[alias.strip()] = actual.strip()
167 amap[alias.strip()] = actual.strip()
168 except ValueError:
168 except ValueError:
169 l = l.strip()
169 l = l.strip()
170 if l:
170 if l:
171 ui.warn(_("skipping malformed alias: %s\n") % l)
171 ui.warn(_("skipping malformed alias: %s\n") % l)
172 continue
172 continue
173
173
174 rate = countrate(ui, repo, amap, *pats, **opts).items()
174 rate = countrate(ui, repo, amap, *pats, **opts).items()
175 if not rate:
175 if not rate:
176 return
176 return
177
177
178 if opts.get('sort'):
178 if opts.get('sort'):
179 rate.sort()
179 rate.sort()
180 else:
180 else:
181 rate.sort(key=lambda x: (-sum(x[1]), x))
181 rate.sort(key=lambda x: (-sum(x[1]), x))
182
182
183 # Be careful not to have a zero maxcount (issue833)
183 # Be careful not to have a zero maxcount (issue833)
184 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
184 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
185 maxname = max(len(k) for k, v in rate)
185 maxname = max(len(k) for k, v in rate)
186
186
187 ttywidth = ui.termwidth()
187 ttywidth = ui.termwidth()
188 ui.debug("assuming %i character terminal\n" % ttywidth)
188 ui.debug("assuming %i character terminal\n" % ttywidth)
189 width = ttywidth - maxname - 2 - 2 - 2
189 width = ttywidth - maxname - 2 - 2 - 2
190
190
191 if opts.get('diffstat'):
191 if opts.get('diffstat'):
192 width -= 15
192 width -= 15
193 def format(name, diffstat):
193 def format(name, diffstat):
194 added, removed = diffstat
194 added, removed = diffstat
195 return "%s %15s %s%s\n" % (pad(name, maxname),
195 return "%s %15s %s%s\n" % (pad(name, maxname),
196 '+%d/-%d' % (added, removed),
196 '+%d/-%d' % (added, removed),
197 ui.label('+' * charnum(added),
197 ui.label('+' * charnum(added),
198 'diffstat.inserted'),
198 'diffstat.inserted'),
199 ui.label('-' * charnum(removed),
199 ui.label('-' * charnum(removed),
200 'diffstat.deleted'))
200 'diffstat.deleted'))
201 else:
201 else:
202 width -= 6
202 width -= 6
203 def format(name, count):
203 def format(name, count):
204 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
204 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
205 '*' * charnum(sum(count)))
205 '*' * charnum(sum(count)))
206
206
207 def charnum(count):
207 def charnum(count):
208 return int(round(count * width / maxcount))
208 return int(round(count * width / maxcount))
209
209
210 for name, count in rate:
210 for name, count in rate:
211 ui.write(format(name, count))
211 ui.write(format(name, count))
@@ -1,204 +1,210 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles extension: uisetup'''
9 '''setup for largefiles extension: uisetup'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 from mercurial.hgweb import (
14 from mercurial.hgweb import (
15 hgweb_mod,
15 hgweb_mod,
16 webcommands,
16 webcommands,
17 )
17 )
18
18
19 from mercurial import (
19 from mercurial import (
20 archival,
20 archival,
21 cmdutil,
21 cmdutil,
22 commands,
22 commands,
23 copies,
23 copies,
24 debugcommands,
24 exchange,
25 exchange,
25 extensions,
26 extensions,
26 filemerge,
27 filemerge,
27 hg,
28 hg,
28 httppeer,
29 httppeer,
29 merge,
30 merge,
30 scmutil,
31 scmutil,
31 sshpeer,
32 sshpeer,
32 subrepo,
33 subrepo,
33 wireproto,
34 wireproto,
34 )
35 )
35
36
36 from . import (
37 from . import (
37 overrides,
38 overrides,
38 proto,
39 proto,
39 )
40 )
40
41
41 def uisetup(ui):
42 def uisetup(ui):
43 # TODO: debugcommands should use a separate command table
44 # Side-effect of accessing is debugcommands module is guaranteed to be
45 # imported and commands.table is populated.
46 debugcommands.command
47
42 # Disable auto-status for some commands which assume that all
48 # Disable auto-status for some commands which assume that all
43 # files in the result are under Mercurial's control
49 # files in the result are under Mercurial's control
44
50
45 entry = extensions.wrapcommand(commands.table, 'add',
51 entry = extensions.wrapcommand(commands.table, 'add',
46 overrides.overrideadd)
52 overrides.overrideadd)
47 addopt = [('', 'large', None, _('add as largefile')),
53 addopt = [('', 'large', None, _('add as largefile')),
48 ('', 'normal', None, _('add as normal file')),
54 ('', 'normal', None, _('add as normal file')),
49 ('', 'lfsize', '', _('add all files above this size '
55 ('', 'lfsize', '', _('add all files above this size '
50 '(in megabytes) as largefiles '
56 '(in megabytes) as largefiles '
51 '(default: 10)'))]
57 '(default: 10)'))]
52 entry[1].extend(addopt)
58 entry[1].extend(addopt)
53
59
54 # The scmutil function is called both by the (trivial) addremove command,
60 # The scmutil function is called both by the (trivial) addremove command,
55 # and in the process of handling commit -A (issue3542)
61 # and in the process of handling commit -A (issue3542)
56 entry = extensions.wrapfunction(scmutil, 'addremove',
62 entry = extensions.wrapfunction(scmutil, 'addremove',
57 overrides.scmutiladdremove)
63 overrides.scmutiladdremove)
58 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
64 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
59 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
65 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
60 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
66 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
61
67
62 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
68 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
63
69
64 # Subrepos call status function
70 # Subrepos call status function
65 entry = extensions.wrapcommand(commands.table, 'status',
71 entry = extensions.wrapcommand(commands.table, 'status',
66 overrides.overridestatus)
72 overrides.overridestatus)
67 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
73 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
68 overrides.overridestatusfn)
74 overrides.overridestatusfn)
69
75
70 entry = extensions.wrapcommand(commands.table, 'log',
76 entry = extensions.wrapcommand(commands.table, 'log',
71 overrides.overridelog)
77 overrides.overridelog)
72 entry = extensions.wrapcommand(commands.table, 'rollback',
78 entry = extensions.wrapcommand(commands.table, 'rollback',
73 overrides.overriderollback)
79 overrides.overriderollback)
74 entry = extensions.wrapcommand(commands.table, 'verify',
80 entry = extensions.wrapcommand(commands.table, 'verify',
75 overrides.overrideverify)
81 overrides.overrideverify)
76
82
77 verifyopt = [('', 'large', None,
83 verifyopt = [('', 'large', None,
78 _('verify that all largefiles in current revision exists')),
84 _('verify that all largefiles in current revision exists')),
79 ('', 'lfa', None,
85 ('', 'lfa', None,
80 _('verify largefiles in all revisions, not just current')),
86 _('verify largefiles in all revisions, not just current')),
81 ('', 'lfc', None,
87 ('', 'lfc', None,
82 _('verify local largefile contents, not just existence'))]
88 _('verify local largefile contents, not just existence'))]
83 entry[1].extend(verifyopt)
89 entry[1].extend(verifyopt)
84
90
85 entry = extensions.wrapcommand(commands.table, 'debugstate',
91 entry = extensions.wrapcommand(commands.table, 'debugstate',
86 overrides.overridedebugstate)
92 overrides.overridedebugstate)
87 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
93 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
88 entry[1].extend(debugstateopt)
94 entry[1].extend(debugstateopt)
89
95
90 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
96 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
91 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
97 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
92 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
98 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
93 entry[1].extend(outgoingopt)
99 entry[1].extend(outgoingopt)
94 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
100 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
95 entry = extensions.wrapcommand(commands.table, 'summary',
101 entry = extensions.wrapcommand(commands.table, 'summary',
96 overrides.overridesummary)
102 overrides.overridesummary)
97 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
103 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
98 entry[1].extend(summaryopt)
104 entry[1].extend(summaryopt)
99 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
105 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
100
106
101 entry = extensions.wrapcommand(commands.table, 'pull',
107 entry = extensions.wrapcommand(commands.table, 'pull',
102 overrides.overridepull)
108 overrides.overridepull)
103 pullopt = [('', 'all-largefiles', None,
109 pullopt = [('', 'all-largefiles', None,
104 _('download all pulled versions of largefiles (DEPRECATED)')),
110 _('download all pulled versions of largefiles (DEPRECATED)')),
105 ('', 'lfrev', [],
111 ('', 'lfrev', [],
106 _('download largefiles for these revisions'), _('REV'))]
112 _('download largefiles for these revisions'), _('REV'))]
107 entry[1].extend(pullopt)
113 entry[1].extend(pullopt)
108
114
109 entry = extensions.wrapcommand(commands.table, 'push',
115 entry = extensions.wrapcommand(commands.table, 'push',
110 overrides.overridepush)
116 overrides.overridepush)
111 pushopt = [('', 'lfrev', [],
117 pushopt = [('', 'lfrev', [],
112 _('upload largefiles for these revisions'), _('REV'))]
118 _('upload largefiles for these revisions'), _('REV'))]
113 entry[1].extend(pushopt)
119 entry[1].extend(pushopt)
114 entry = extensions.wrapfunction(exchange, 'pushoperation',
120 entry = extensions.wrapfunction(exchange, 'pushoperation',
115 overrides.exchangepushoperation)
121 overrides.exchangepushoperation)
116
122
117 entry = extensions.wrapcommand(commands.table, 'clone',
123 entry = extensions.wrapcommand(commands.table, 'clone',
118 overrides.overrideclone)
124 overrides.overrideclone)
119 cloneopt = [('', 'all-largefiles', None,
125 cloneopt = [('', 'all-largefiles', None,
120 _('download all versions of all largefiles'))]
126 _('download all versions of all largefiles'))]
121 entry[1].extend(cloneopt)
127 entry[1].extend(cloneopt)
122 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
128 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
123 entry = extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
129 entry = extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
124
130
125 entry = extensions.wrapcommand(commands.table, 'cat',
131 entry = extensions.wrapcommand(commands.table, 'cat',
126 overrides.overridecat)
132 overrides.overridecat)
127 entry = extensions.wrapfunction(merge, '_checkunknownfile',
133 entry = extensions.wrapfunction(merge, '_checkunknownfile',
128 overrides.overridecheckunknownfile)
134 overrides.overridecheckunknownfile)
129 entry = extensions.wrapfunction(merge, 'calculateupdates',
135 entry = extensions.wrapfunction(merge, 'calculateupdates',
130 overrides.overridecalculateupdates)
136 overrides.overridecalculateupdates)
131 entry = extensions.wrapfunction(merge, 'recordupdates',
137 entry = extensions.wrapfunction(merge, 'recordupdates',
132 overrides.mergerecordupdates)
138 overrides.mergerecordupdates)
133 entry = extensions.wrapfunction(merge, 'update',
139 entry = extensions.wrapfunction(merge, 'update',
134 overrides.mergeupdate)
140 overrides.mergeupdate)
135 entry = extensions.wrapfunction(filemerge, '_filemerge',
141 entry = extensions.wrapfunction(filemerge, '_filemerge',
136 overrides.overridefilemerge)
142 overrides.overridefilemerge)
137 entry = extensions.wrapfunction(cmdutil, 'copy',
143 entry = extensions.wrapfunction(cmdutil, 'copy',
138 overrides.overridecopy)
144 overrides.overridecopy)
139
145
140 # Summary calls dirty on the subrepos
146 # Summary calls dirty on the subrepos
141 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
147 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
142 overrides.overridedirty)
148 overrides.overridedirty)
143
149
144 entry = extensions.wrapfunction(cmdutil, 'revert',
150 entry = extensions.wrapfunction(cmdutil, 'revert',
145 overrides.overriderevert)
151 overrides.overriderevert)
146
152
147 extensions.wrapcommand(commands.table, 'archive',
153 extensions.wrapcommand(commands.table, 'archive',
148 overrides.overridearchivecmd)
154 overrides.overridearchivecmd)
149 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
155 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
150 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
156 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
151 overrides.hgsubrepoarchive)
157 overrides.hgsubrepoarchive)
152 extensions.wrapfunction(webcommands, 'archive',
158 extensions.wrapfunction(webcommands, 'archive',
153 overrides.hgwebarchive)
159 overrides.hgwebarchive)
154 extensions.wrapfunction(cmdutil, 'bailifchanged',
160 extensions.wrapfunction(cmdutil, 'bailifchanged',
155 overrides.overridebailifchanged)
161 overrides.overridebailifchanged)
156
162
157 extensions.wrapfunction(cmdutil, 'postcommitstatus',
163 extensions.wrapfunction(cmdutil, 'postcommitstatus',
158 overrides.postcommitstatus)
164 overrides.postcommitstatus)
159 extensions.wrapfunction(scmutil, 'marktouched',
165 extensions.wrapfunction(scmutil, 'marktouched',
160 overrides.scmutilmarktouched)
166 overrides.scmutilmarktouched)
161
167
162 # create the new wireproto commands ...
168 # create the new wireproto commands ...
163 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
169 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
164 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
170 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
165 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
171 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
166
172
167 # ... and wrap some existing ones
173 # ... and wrap some existing ones
168 wireproto.commands['capabilities'] = (proto.capabilities, '')
174 wireproto.commands['capabilities'] = (proto.capabilities, '')
169 wireproto.commands['heads'] = (proto.heads, '')
175 wireproto.commands['heads'] = (proto.heads, '')
170 wireproto.commands['lheads'] = (wireproto.heads, '')
176 wireproto.commands['lheads'] = (wireproto.heads, '')
171
177
172 # make putlfile behave the same as push and {get,stat}lfile behave
178 # make putlfile behave the same as push and {get,stat}lfile behave
173 # the same as pull w.r.t. permissions checks
179 # the same as pull w.r.t. permissions checks
174 hgweb_mod.perms['putlfile'] = 'push'
180 hgweb_mod.perms['putlfile'] = 'push'
175 hgweb_mod.perms['getlfile'] = 'pull'
181 hgweb_mod.perms['getlfile'] = 'pull'
176 hgweb_mod.perms['statlfile'] = 'pull'
182 hgweb_mod.perms['statlfile'] = 'pull'
177
183
178 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
184 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
179
185
180 # the hello wireproto command uses wireproto.capabilities, so it won't see
186 # the hello wireproto command uses wireproto.capabilities, so it won't see
181 # our largefiles capability unless we replace the actual function as well.
187 # our largefiles capability unless we replace the actual function as well.
182 proto.capabilitiesorig = wireproto.capabilities
188 proto.capabilitiesorig = wireproto.capabilities
183 wireproto.capabilities = proto.capabilities
189 wireproto.capabilities = proto.capabilities
184
190
185 # can't do this in reposetup because it needs to have happened before
191 # can't do this in reposetup because it needs to have happened before
186 # wirerepo.__init__ is called
192 # wirerepo.__init__ is called
187 proto.ssholdcallstream = sshpeer.sshpeer._callstream
193 proto.ssholdcallstream = sshpeer.sshpeer._callstream
188 proto.httpoldcallstream = httppeer.httppeer._callstream
194 proto.httpoldcallstream = httppeer.httppeer._callstream
189 sshpeer.sshpeer._callstream = proto.sshrepocallstream
195 sshpeer.sshpeer._callstream = proto.sshrepocallstream
190 httppeer.httppeer._callstream = proto.httprepocallstream
196 httppeer.httppeer._callstream = proto.httprepocallstream
191
197
192 # override some extensions' stuff as well
198 # override some extensions' stuff as well
193 for name, module in extensions.extensions():
199 for name, module in extensions.extensions():
194 if name == 'purge':
200 if name == 'purge':
195 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
201 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
196 overrides.overridepurge)
202 overrides.overridepurge)
197 if name == 'rebase':
203 if name == 'rebase':
198 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
204 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
199 overrides.overriderebase)
205 overrides.overriderebase)
200 extensions.wrapfunction(module, 'rebase',
206 extensions.wrapfunction(module, 'rebase',
201 overrides.overriderebase)
207 overrides.overriderebase)
202 if name == 'transplant':
208 if name == 'transplant':
203 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
209 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
204 overrides.overridetransplant)
210 overrides.overridetransplant)
@@ -1,195 +1,196 b''
1 # win32mbcs.py -- MBCS filename support for Mercurial
1 # win32mbcs.py -- MBCS filename support for Mercurial
2 #
2 #
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 #
4 #
5 # Version: 0.3
5 # Version: 0.3
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10 #
10 #
11
11
12 '''allow the use of MBCS paths with problematic encodings
12 '''allow the use of MBCS paths with problematic encodings
13
13
14 Some MBCS encodings are not good for some path operations (i.e.
14 Some MBCS encodings are not good for some path operations (i.e.
15 splitting path, case conversion, etc.) with its encoded bytes. We call
15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 This extension can be used to fix the issue with those encodings by
17 This extension can be used to fix the issue with those encodings by
18 wrapping some functions to convert to Unicode string before path
18 wrapping some functions to convert to Unicode string before path
19 operation.
19 operation.
20
20
21 This extension is useful for:
21 This extension is useful for:
22
22
23 - Japanese Windows users using shift_jis encoding.
23 - Japanese Windows users using shift_jis encoding.
24 - Chinese Windows users using big5 encoding.
24 - Chinese Windows users using big5 encoding.
25 - All users who use a repository with one of problematic encodings on
25 - All users who use a repository with one of problematic encodings on
26 case-insensitive file system.
26 case-insensitive file system.
27
27
28 This extension is not needed for:
28 This extension is not needed for:
29
29
30 - Any user who use only ASCII chars in path.
30 - Any user who use only ASCII chars in path.
31 - Any user who do not use any of problematic encodings.
31 - Any user who do not use any of problematic encodings.
32
32
33 Note that there are some limitations on using this extension:
33 Note that there are some limitations on using this extension:
34
34
35 - You should use single encoding in one repository.
35 - You should use single encoding in one repository.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
37 - win32mbcs is not compatible with fixutf8 extension.
37 - win32mbcs is not compatible with fixutf8 extension.
38
38
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
40 You can specify the encoding by config option::
40 You can specify the encoding by config option::
41
41
42 [win32mbcs]
42 [win32mbcs]
43 encoding = sjis
43 encoding = sjis
44
44
45 It is useful for the users who want to commit with UTF-8 log message.
45 It is useful for the users who want to commit with UTF-8 log message.
46 '''
46 '''
47 from __future__ import absolute_import
47 from __future__ import absolute_import
48
48
49 import os
49 import os
50 import sys
50 import sys
51
51
52 from mercurial.i18n import _
52 from mercurial.i18n import _
53 from mercurial import (
53 from mercurial import (
54 encoding,
54 encoding,
55 error,
55 error,
56 pycompat,
56 pycompat,
57 )
57 )
58
58
59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
61 # be specifying the version(s) of Mercurial they are tested with, or
61 # be specifying the version(s) of Mercurial they are tested with, or
62 # leave the attribute unspecified.
62 # leave the attribute unspecified.
63 testedwith = 'ships-with-hg-core'
63 testedwith = 'ships-with-hg-core'
64
64
65 _encoding = None # see extsetup
65 _encoding = None # see extsetup
66
66
67 def decode(arg):
67 def decode(arg):
68 if isinstance(arg, str):
68 if isinstance(arg, str):
69 uarg = arg.decode(_encoding)
69 uarg = arg.decode(_encoding)
70 if arg == uarg.encode(_encoding):
70 if arg == uarg.encode(_encoding):
71 return uarg
71 return uarg
72 raise UnicodeError("Not local encoding")
72 raise UnicodeError("Not local encoding")
73 elif isinstance(arg, tuple):
73 elif isinstance(arg, tuple):
74 return tuple(map(decode, arg))
74 return tuple(map(decode, arg))
75 elif isinstance(arg, list):
75 elif isinstance(arg, list):
76 return map(decode, arg)
76 return map(decode, arg)
77 elif isinstance(arg, dict):
77 elif isinstance(arg, dict):
78 for k, v in arg.items():
78 for k, v in arg.items():
79 arg[k] = decode(v)
79 arg[k] = decode(v)
80 return arg
80 return arg
81
81
82 def encode(arg):
82 def encode(arg):
83 if isinstance(arg, unicode):
83 if isinstance(arg, unicode):
84 return arg.encode(_encoding)
84 return arg.encode(_encoding)
85 elif isinstance(arg, tuple):
85 elif isinstance(arg, tuple):
86 return tuple(map(encode, arg))
86 return tuple(map(encode, arg))
87 elif isinstance(arg, list):
87 elif isinstance(arg, list):
88 return map(encode, arg)
88 return map(encode, arg)
89 elif isinstance(arg, dict):
89 elif isinstance(arg, dict):
90 for k, v in arg.items():
90 for k, v in arg.items():
91 arg[k] = encode(v)
91 arg[k] = encode(v)
92 return arg
92 return arg
93
93
94 def appendsep(s):
94 def appendsep(s):
95 # ensure the path ends with os.sep, appending it if necessary.
95 # ensure the path ends with os.sep, appending it if necessary.
96 try:
96 try:
97 us = decode(s)
97 us = decode(s)
98 except UnicodeError:
98 except UnicodeError:
99 us = s
99 us = s
100 if us and us[-1] not in ':/\\':
100 if us and us[-1] not in ':/\\':
101 s += pycompat.ossep
101 s += pycompat.ossep
102 return s
102 return s
103
103
104
104
105 def basewrapper(func, argtype, enc, dec, args, kwds):
105 def basewrapper(func, argtype, enc, dec, args, kwds):
106 # check check already converted, then call original
106 # check check already converted, then call original
107 for arg in args:
107 for arg in args:
108 if isinstance(arg, argtype):
108 if isinstance(arg, argtype):
109 return func(*args, **kwds)
109 return func(*args, **kwds)
110
110
111 try:
111 try:
112 # convert string arguments, call func, then convert back the
112 # convert string arguments, call func, then convert back the
113 # return value.
113 # return value.
114 return enc(func(*dec(args), **dec(kwds)))
114 return enc(func(*dec(args), **dec(kwds)))
115 except UnicodeError:
115 except UnicodeError:
116 raise error.Abort(_("[win32mbcs] filename conversion failed with"
116 raise error.Abort(_("[win32mbcs] filename conversion failed with"
117 " %s encoding\n") % (_encoding))
117 " %s encoding\n") % (_encoding))
118
118
119 def wrapper(func, args, kwds):
119 def wrapper(func, args, kwds):
120 return basewrapper(func, unicode, encode, decode, args, kwds)
120 return basewrapper(func, unicode, encode, decode, args, kwds)
121
121
122
122
123 def reversewrapper(func, args, kwds):
123 def reversewrapper(func, args, kwds):
124 return basewrapper(func, str, decode, encode, args, kwds)
124 return basewrapper(func, str, decode, encode, args, kwds)
125
125
126 def wrapperforlistdir(func, args, kwds):
126 def wrapperforlistdir(func, args, kwds):
127 # Ensure 'path' argument ends with os.sep to avoids
127 # Ensure 'path' argument ends with os.sep to avoids
128 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
128 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
129 if args:
129 if args:
130 args = list(args)
130 args = list(args)
131 args[0] = appendsep(args[0])
131 args[0] = appendsep(args[0])
132 if 'path' in kwds:
132 if 'path' in kwds:
133 kwds['path'] = appendsep(kwds['path'])
133 kwds['path'] = appendsep(kwds['path'])
134 return func(*args, **kwds)
134 return func(*args, **kwds)
135
135
136 def wrapname(name, wrapper):
136 def wrapname(name, wrapper):
137 module, name = name.rsplit('.', 1)
137 module, name = name.rsplit('.', 1)
138 module = sys.modules[module]
138 module = sys.modules[module]
139 func = getattr(module, name)
139 func = getattr(module, name)
140 def f(*args, **kwds):
140 def f(*args, **kwds):
141 return wrapper(func, args, kwds)
141 return wrapper(func, args, kwds)
142 f.__name__ = func.__name__
142 f.__name__ = func.__name__
143 setattr(module, name, f)
143 setattr(module, name, f)
144
144
145 # List of functions to be wrapped.
145 # List of functions to be wrapped.
146 # NOTE: os.path.dirname() and os.path.basename() are safe because
146 # NOTE: os.path.dirname() and os.path.basename() are safe because
147 # they use result of os.path.split()
147 # they use result of os.path.split()
148 funcs = '''os.path.join os.path.split os.path.splitext
148 funcs = '''os.path.join os.path.split os.path.splitext
149 os.path.normpath os.makedirs mercurial.util.endswithsep
149 os.path.normpath os.makedirs mercurial.util.endswithsep
150 mercurial.util.splitpath mercurial.util.fscasesensitive
150 mercurial.util.splitpath mercurial.util.fscasesensitive
151 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
151 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
152 mercurial.util.checkwinfilename mercurial.util.checkosfilename
152 mercurial.util.checkwinfilename mercurial.util.checkosfilename
153 mercurial.util.split'''
153 mercurial.util.split'''
154
154
155 # These functions are required to be called with local encoded string
155 # These functions are required to be called with local encoded string
156 # because they expects argument is local encoded string and cause
156 # because they expects argument is local encoded string and cause
157 # problem with unicode string.
157 # problem with unicode string.
158 rfuncs = '''mercurial.encoding.upper mercurial.encoding.lower'''
158 rfuncs = '''mercurial.encoding.upper mercurial.encoding.lower
159 mercurial.pycompat.bytestr'''
159
160
160 # List of Windows specific functions to be wrapped.
161 # List of Windows specific functions to be wrapped.
161 winfuncs = '''os.path.splitunc'''
162 winfuncs = '''os.path.splitunc'''
162
163
163 # codec and alias names of sjis and big5 to be faked.
164 # codec and alias names of sjis and big5 to be faked.
164 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
165 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
165 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
166 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
166 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
167 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
167 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
168 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
168
169
169 def extsetup(ui):
170 def extsetup(ui):
170 # TODO: decide use of config section for this extension
171 # TODO: decide use of config section for this extension
171 if ((not os.path.supports_unicode_filenames) and
172 if ((not os.path.supports_unicode_filenames) and
172 (pycompat.sysplatform != 'cygwin')):
173 (pycompat.sysplatform != 'cygwin')):
173 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
174 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
174 return
175 return
175 # determine encoding for filename
176 # determine encoding for filename
176 global _encoding
177 global _encoding
177 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
178 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
178 # fake is only for relevant environment.
179 # fake is only for relevant environment.
179 if _encoding.lower() in problematic_encodings.split():
180 if _encoding.lower() in problematic_encodings.split():
180 for f in funcs.split():
181 for f in funcs.split():
181 wrapname(f, wrapper)
182 wrapname(f, wrapper)
182 if pycompat.osname == 'nt':
183 if pycompat.osname == 'nt':
183 for f in winfuncs.split():
184 for f in winfuncs.split():
184 wrapname(f, wrapper)
185 wrapname(f, wrapper)
185 wrapname("mercurial.util.listdir", wrapperforlistdir)
186 wrapname("mercurial.util.listdir", wrapperforlistdir)
186 wrapname("mercurial.windows.listdir", wrapperforlistdir)
187 wrapname("mercurial.windows.listdir", wrapperforlistdir)
187 # wrap functions to be called with local byte string arguments
188 # wrap functions to be called with local byte string arguments
188 for f in rfuncs.split():
189 for f in rfuncs.split():
189 wrapname(f, reversewrapper)
190 wrapname(f, reversewrapper)
190 # Check sys.args manually instead of using ui.debug() because
191 # Check sys.args manually instead of using ui.debug() because
191 # command line options is not yet applied when
192 # command line options is not yet applied when
192 # extensions.loadall() is called.
193 # extensions.loadall() is called.
193 if '--debug' in sys.argv:
194 if '--debug' in sys.argv:
194 ui.write(("[win32mbcs] activated with encoding: %s\n")
195 ui.write(("[win32mbcs] activated with encoding: %s\n")
195 % _encoding)
196 % _encoding)
@@ -1,5518 +1,5520 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from . import (
22 from . import (
23 archival,
23 archival,
24 bookmarks,
24 bookmarks,
25 bundle2,
25 bundle2,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 destutil,
29 destutil,
30 dirstateguard,
30 dirstateguard,
31 discovery,
31 discovery,
32 encoding,
32 encoding,
33 error,
33 error,
34 exchange,
34 exchange,
35 extensions,
35 extensions,
36 graphmod,
36 graphmod,
37 hbisect,
37 hbisect,
38 help,
38 help,
39 hg,
39 hg,
40 lock as lockmod,
40 lock as lockmod,
41 merge as mergemod,
41 merge as mergemod,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 phases,
44 phases,
45 pycompat,
45 pycompat,
46 rcutil,
46 rcutil,
47 revsetlang,
47 revsetlang,
48 scmutil,
48 scmutil,
49 server,
49 server,
50 sshserver,
50 sshserver,
51 streamclone,
51 streamclone,
52 tags as tagsmod,
52 tags as tagsmod,
53 templatekw,
53 templatekw,
54 ui as uimod,
54 ui as uimod,
55 util,
55 util,
56 )
56 )
57
57
58 release = lockmod.release
58 release = lockmod.release
59
59
60 table = {}
60 table = {}
61
61
62 command = cmdutil.command(table)
62 command = cmdutil.command(table)
63
63
64 # label constants
64 # label constants
65 # until 3.5, bookmarks.current was the advertised name, not
65 # until 3.5, bookmarks.current was the advertised name, not
66 # bookmarks.active, so we must use both to avoid breaking old
66 # bookmarks.active, so we must use both to avoid breaking old
67 # custom styles
67 # custom styles
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
68 activebookmarklabel = 'bookmarks.active bookmarks.current'
69
69
70 # common command options
70 # common command options
71
71
72 globalopts = [
72 globalopts = [
73 ('R', 'repository', '',
73 ('R', 'repository', '',
74 _('repository root directory or name of overlay bundle file'),
74 _('repository root directory or name of overlay bundle file'),
75 _('REPO')),
75 _('REPO')),
76 ('', 'cwd', '',
76 ('', 'cwd', '',
77 _('change working directory'), _('DIR')),
77 _('change working directory'), _('DIR')),
78 ('y', 'noninteractive', None,
78 ('y', 'noninteractive', None,
79 _('do not prompt, automatically pick the first choice for all prompts')),
79 _('do not prompt, automatically pick the first choice for all prompts')),
80 ('q', 'quiet', None, _('suppress output')),
80 ('q', 'quiet', None, _('suppress output')),
81 ('v', 'verbose', None, _('enable additional output')),
81 ('v', 'verbose', None, _('enable additional output')),
82 ('', 'color', '',
82 ('', 'color', '',
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
84 # and should not be translated
84 # and should not be translated
85 _("when to colorize (boolean, always, auto, never, or debug)"),
85 _("when to colorize (boolean, always, auto, never, or debug)"),
86 _('TYPE')),
86 _('TYPE')),
87 ('', 'config', [],
87 ('', 'config', [],
88 _('set/override config option (use \'section.name=value\')'),
88 _('set/override config option (use \'section.name=value\')'),
89 _('CONFIG')),
89 _('CONFIG')),
90 ('', 'debug', None, _('enable debugging output')),
90 ('', 'debug', None, _('enable debugging output')),
91 ('', 'debugger', None, _('start debugger')),
91 ('', 'debugger', None, _('start debugger')),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
93 _('ENCODE')),
93 _('ENCODE')),
94 ('', 'encodingmode', encoding.encodingmode,
94 ('', 'encodingmode', encoding.encodingmode,
95 _('set the charset encoding mode'), _('MODE')),
95 _('set the charset encoding mode'), _('MODE')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
96 ('', 'traceback', None, _('always print a traceback on exception')),
97 ('', 'time', None, _('time how long the command takes')),
97 ('', 'time', None, _('time how long the command takes')),
98 ('', 'profile', None, _('print command execution profile')),
98 ('', 'profile', None, _('print command execution profile')),
99 ('', 'version', None, _('output version information and exit')),
99 ('', 'version', None, _('output version information and exit')),
100 ('h', 'help', None, _('display help and exit')),
100 ('h', 'help', None, _('display help and exit')),
101 ('', 'hidden', False, _('consider hidden changesets')),
101 ('', 'hidden', False, _('consider hidden changesets')),
102 ('', 'pager', 'auto',
102 ('', 'pager', 'auto',
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
104 ]
104 ]
105
105
106 dryrunopts = [('n', 'dry-run', None,
106 dryrunopts = [('n', 'dry-run', None,
107 _('do not perform actions, just print output'))]
107 _('do not perform actions, just print output'))]
108
108
109 remoteopts = [
109 remoteopts = [
110 ('e', 'ssh', '',
110 ('e', 'ssh', '',
111 _('specify ssh command to use'), _('CMD')),
111 _('specify ssh command to use'), _('CMD')),
112 ('', 'remotecmd', '',
112 ('', 'remotecmd', '',
113 _('specify hg command to run on the remote side'), _('CMD')),
113 _('specify hg command to run on the remote side'), _('CMD')),
114 ('', 'insecure', None,
114 ('', 'insecure', None,
115 _('do not verify server certificate (ignoring web.cacerts config)')),
115 _('do not verify server certificate (ignoring web.cacerts config)')),
116 ]
116 ]
117
117
118 walkopts = [
118 walkopts = [
119 ('I', 'include', [],
119 ('I', 'include', [],
120 _('include names matching the given patterns'), _('PATTERN')),
120 _('include names matching the given patterns'), _('PATTERN')),
121 ('X', 'exclude', [],
121 ('X', 'exclude', [],
122 _('exclude names matching the given patterns'), _('PATTERN')),
122 _('exclude names matching the given patterns'), _('PATTERN')),
123 ]
123 ]
124
124
125 commitopts = [
125 commitopts = [
126 ('m', 'message', '',
126 ('m', 'message', '',
127 _('use text as commit message'), _('TEXT')),
127 _('use text as commit message'), _('TEXT')),
128 ('l', 'logfile', '',
128 ('l', 'logfile', '',
129 _('read commit message from file'), _('FILE')),
129 _('read commit message from file'), _('FILE')),
130 ]
130 ]
131
131
132 commitopts2 = [
132 commitopts2 = [
133 ('d', 'date', '',
133 ('d', 'date', '',
134 _('record the specified date as commit date'), _('DATE')),
134 _('record the specified date as commit date'), _('DATE')),
135 ('u', 'user', '',
135 ('u', 'user', '',
136 _('record the specified user as committer'), _('USER')),
136 _('record the specified user as committer'), _('USER')),
137 ]
137 ]
138
138
139 # hidden for now
139 # hidden for now
140 formatteropts = [
140 formatteropts = [
141 ('T', 'template', '',
141 ('T', 'template', '',
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
143 ]
143 ]
144
144
145 templateopts = [
145 templateopts = [
146 ('', 'style', '',
146 ('', 'style', '',
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
147 _('display using template map file (DEPRECATED)'), _('STYLE')),
148 ('T', 'template', '',
148 ('T', 'template', '',
149 _('display with template'), _('TEMPLATE')),
149 _('display with template'), _('TEMPLATE')),
150 ]
150 ]
151
151
152 logopts = [
152 logopts = [
153 ('p', 'patch', None, _('show patch')),
153 ('p', 'patch', None, _('show patch')),
154 ('g', 'git', None, _('use git extended diff format')),
154 ('g', 'git', None, _('use git extended diff format')),
155 ('l', 'limit', '',
155 ('l', 'limit', '',
156 _('limit number of changes displayed'), _('NUM')),
156 _('limit number of changes displayed'), _('NUM')),
157 ('M', 'no-merges', None, _('do not show merges')),
157 ('M', 'no-merges', None, _('do not show merges')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
158 ('', 'stat', None, _('output diffstat-style summary of changes')),
159 ('G', 'graph', None, _("show the revision DAG")),
159 ('G', 'graph', None, _("show the revision DAG")),
160 ] + templateopts
160 ] + templateopts
161
161
162 diffopts = [
162 diffopts = [
163 ('a', 'text', None, _('treat all files as text')),
163 ('a', 'text', None, _('treat all files as text')),
164 ('g', 'git', None, _('use git extended diff format')),
164 ('g', 'git', None, _('use git extended diff format')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
166 ('', 'nodates', None, _('omit dates from diff headers'))
166 ('', 'nodates', None, _('omit dates from diff headers'))
167 ]
167 ]
168
168
169 diffwsopts = [
169 diffwsopts = [
170 ('w', 'ignore-all-space', None,
170 ('w', 'ignore-all-space', None,
171 _('ignore white space when comparing lines')),
171 _('ignore white space when comparing lines')),
172 ('b', 'ignore-space-change', None,
172 ('b', 'ignore-space-change', None,
173 _('ignore changes in the amount of white space')),
173 _('ignore changes in the amount of white space')),
174 ('B', 'ignore-blank-lines', None,
174 ('B', 'ignore-blank-lines', None,
175 _('ignore changes whose lines are all blank')),
175 _('ignore changes whose lines are all blank')),
176 ]
176 ]
177
177
178 diffopts2 = [
178 diffopts2 = [
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
180 ('p', 'show-function', None, _('show which function each change is in')),
180 ('p', 'show-function', None, _('show which function each change is in')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
182 ] + diffwsopts + [
182 ] + diffwsopts + [
183 ('U', 'unified', '',
183 ('U', 'unified', '',
184 _('number of lines of context to show'), _('NUM')),
184 _('number of lines of context to show'), _('NUM')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
185 ('', 'stat', None, _('output diffstat-style summary of changes')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
187 ]
187 ]
188
188
189 mergetoolopts = [
189 mergetoolopts = [
190 ('t', 'tool', '', _('specify merge tool')),
190 ('t', 'tool', '', _('specify merge tool')),
191 ]
191 ]
192
192
193 similarityopts = [
193 similarityopts = [
194 ('s', 'similarity', '',
194 ('s', 'similarity', '',
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
196 ]
196 ]
197
197
198 subrepoopts = [
198 subrepoopts = [
199 ('S', 'subrepos', None,
199 ('S', 'subrepos', None,
200 _('recurse into subrepositories'))
200 _('recurse into subrepositories'))
201 ]
201 ]
202
202
203 debugrevlogopts = [
203 debugrevlogopts = [
204 ('c', 'changelog', False, _('open changelog')),
204 ('c', 'changelog', False, _('open changelog')),
205 ('m', 'manifest', False, _('open manifest')),
205 ('m', 'manifest', False, _('open manifest')),
206 ('', 'dir', '', _('open directory manifest')),
206 ('', 'dir', '', _('open directory manifest')),
207 ]
207 ]
208
208
209 # Commands start here, listed alphabetically
209 # Commands start here, listed alphabetically
210
210
211 @command('^add',
211 @command('^add',
212 walkopts + subrepoopts + dryrunopts,
212 walkopts + subrepoopts + dryrunopts,
213 _('[OPTION]... [FILE]...'),
213 _('[OPTION]... [FILE]...'),
214 inferrepo=True)
214 inferrepo=True)
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
260 return rejected and 1 or 0
260 return rejected and 1 or 0
261
261
262 @command('addremove',
262 @command('addremove',
263 similarityopts + subrepoopts + walkopts + dryrunopts,
263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 _('[OPTION]... [FILE]...'),
264 _('[OPTION]... [FILE]...'),
265 inferrepo=True)
265 inferrepo=True)
266 def addremove(ui, repo, *pats, **opts):
266 def addremove(ui, repo, *pats, **opts):
267 """add all new files, delete all missing files
267 """add all new files, delete all missing files
268
268
269 Add all new files and remove all missing files from the
269 Add all new files and remove all missing files from the
270 repository.
270 repository.
271
271
272 Unless names are given, new files are ignored if they match any of
272 Unless names are given, new files are ignored if they match any of
273 the patterns in ``.hgignore``. As with add, these changes take
273 the patterns in ``.hgignore``. As with add, these changes take
274 effect at the next commit.
274 effect at the next commit.
275
275
276 Use the -s/--similarity option to detect renamed files. This
276 Use the -s/--similarity option to detect renamed files. This
277 option takes a percentage between 0 (disabled) and 100 (files must
277 option takes a percentage between 0 (disabled) and 100 (files must
278 be identical) as its parameter. With a parameter greater than 0,
278 be identical) as its parameter. With a parameter greater than 0,
279 this compares every removed file with every added file and records
279 this compares every removed file with every added file and records
280 those similar enough as renames. Detecting renamed files this way
280 those similar enough as renames. Detecting renamed files this way
281 can be expensive. After using this option, :hg:`status -C` can be
281 can be expensive. After using this option, :hg:`status -C` can be
282 used to check which files were identified as moved or renamed. If
282 used to check which files were identified as moved or renamed. If
283 not specified, -s/--similarity defaults to 100 and only renames of
283 not specified, -s/--similarity defaults to 100 and only renames of
284 identical files are detected.
284 identical files are detected.
285
285
286 .. container:: verbose
286 .. container:: verbose
287
287
288 Examples:
288 Examples:
289
289
290 - A number of files (bar.c and foo.c) are new,
290 - A number of files (bar.c and foo.c) are new,
291 while foobar.c has been removed (without using :hg:`remove`)
291 while foobar.c has been removed (without using :hg:`remove`)
292 from the repository::
292 from the repository::
293
293
294 $ ls
294 $ ls
295 bar.c foo.c
295 bar.c foo.c
296 $ hg status
296 $ hg status
297 ! foobar.c
297 ! foobar.c
298 ? bar.c
298 ? bar.c
299 ? foo.c
299 ? foo.c
300 $ hg addremove
300 $ hg addremove
301 adding bar.c
301 adding bar.c
302 adding foo.c
302 adding foo.c
303 removing foobar.c
303 removing foobar.c
304 $ hg status
304 $ hg status
305 A bar.c
305 A bar.c
306 A foo.c
306 A foo.c
307 R foobar.c
307 R foobar.c
308
308
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 Afterwards, it was edited slightly::
310 Afterwards, it was edited slightly::
311
311
312 $ ls
312 $ ls
313 foo.c
313 foo.c
314 $ hg status
314 $ hg status
315 ! foobar.c
315 ! foobar.c
316 ? foo.c
316 ? foo.c
317 $ hg addremove --similarity 90
317 $ hg addremove --similarity 90
318 removing foobar.c
318 removing foobar.c
319 adding foo.c
319 adding foo.c
320 recording removal of foobar.c as rename to foo.c (94% similar)
320 recording removal of foobar.c as rename to foo.c (94% similar)
321 $ hg status -C
321 $ hg status -C
322 A foo.c
322 A foo.c
323 foobar.c
323 foobar.c
324 R foobar.c
324 R foobar.c
325
325
326 Returns 0 if all files are successfully added.
326 Returns 0 if all files are successfully added.
327 """
327 """
328 opts = pycompat.byteskwargs(opts)
328 opts = pycompat.byteskwargs(opts)
329 try:
329 try:
330 sim = float(opts.get('similarity') or 100)
330 sim = float(opts.get('similarity') or 100)
331 except ValueError:
331 except ValueError:
332 raise error.Abort(_('similarity must be a number'))
332 raise error.Abort(_('similarity must be a number'))
333 if sim < 0 or sim > 100:
333 if sim < 0 or sim > 100:
334 raise error.Abort(_('similarity must be between 0 and 100'))
334 raise error.Abort(_('similarity must be between 0 and 100'))
335 matcher = scmutil.match(repo[None], pats, opts)
335 matcher = scmutil.match(repo[None], pats, opts)
336 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
336 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
337
337
338 @command('^annotate|blame',
338 @command('^annotate|blame',
339 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
339 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
340 ('', 'follow', None,
340 ('', 'follow', None,
341 _('follow copies/renames and list the filename (DEPRECATED)')),
341 _('follow copies/renames and list the filename (DEPRECATED)')),
342 ('', 'no-follow', None, _("don't follow copies and renames")),
342 ('', 'no-follow', None, _("don't follow copies and renames")),
343 ('a', 'text', None, _('treat all files as text')),
343 ('a', 'text', None, _('treat all files as text')),
344 ('u', 'user', None, _('list the author (long with -v)')),
344 ('u', 'user', None, _('list the author (long with -v)')),
345 ('f', 'file', None, _('list the filename')),
345 ('f', 'file', None, _('list the filename')),
346 ('d', 'date', None, _('list the date (short with -q)')),
346 ('d', 'date', None, _('list the date (short with -q)')),
347 ('n', 'number', None, _('list the revision number (default)')),
347 ('n', 'number', None, _('list the revision number (default)')),
348 ('c', 'changeset', None, _('list the changeset')),
348 ('c', 'changeset', None, _('list the changeset')),
349 ('l', 'line-number', None, _('show line number at the first appearance'))
349 ('l', 'line-number', None, _('show line number at the first appearance'))
350 ] + diffwsopts + walkopts + formatteropts,
350 ] + diffwsopts + walkopts + formatteropts,
351 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
351 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
352 inferrepo=True)
352 inferrepo=True)
353 def annotate(ui, repo, *pats, **opts):
353 def annotate(ui, repo, *pats, **opts):
354 """show changeset information by line for each file
354 """show changeset information by line for each file
355
355
356 List changes in files, showing the revision id responsible for
356 List changes in files, showing the revision id responsible for
357 each line.
357 each line.
358
358
359 This command is useful for discovering when a change was made and
359 This command is useful for discovering when a change was made and
360 by whom.
360 by whom.
361
361
362 If you include --file, --user, or --date, the revision number is
362 If you include --file, --user, or --date, the revision number is
363 suppressed unless you also include --number.
363 suppressed unless you also include --number.
364
364
365 Without the -a/--text option, annotate will avoid processing files
365 Without the -a/--text option, annotate will avoid processing files
366 it detects as binary. With -a, annotate will annotate the file
366 it detects as binary. With -a, annotate will annotate the file
367 anyway, although the results will probably be neither useful
367 anyway, although the results will probably be neither useful
368 nor desirable.
368 nor desirable.
369
369
370 Returns 0 on success.
370 Returns 0 on success.
371 """
371 """
372 opts = pycompat.byteskwargs(opts)
372 opts = pycompat.byteskwargs(opts)
373 if not pats:
373 if not pats:
374 raise error.Abort(_('at least one filename or pattern is required'))
374 raise error.Abort(_('at least one filename or pattern is required'))
375
375
376 if opts.get('follow'):
376 if opts.get('follow'):
377 # --follow is deprecated and now just an alias for -f/--file
377 # --follow is deprecated and now just an alias for -f/--file
378 # to mimic the behavior of Mercurial before version 1.5
378 # to mimic the behavior of Mercurial before version 1.5
379 opts['file'] = True
379 opts['file'] = True
380
380
381 ctx = scmutil.revsingle(repo, opts.get('rev'))
381 ctx = scmutil.revsingle(repo, opts.get('rev'))
382
382
383 fm = ui.formatter('annotate', opts)
383 fm = ui.formatter('annotate', opts)
384 if ui.quiet:
384 if ui.quiet:
385 datefunc = util.shortdate
385 datefunc = util.shortdate
386 else:
386 else:
387 datefunc = util.datestr
387 datefunc = util.datestr
388 if ctx.rev() is None:
388 if ctx.rev() is None:
389 def hexfn(node):
389 def hexfn(node):
390 if node is None:
390 if node is None:
391 return None
391 return None
392 else:
392 else:
393 return fm.hexfunc(node)
393 return fm.hexfunc(node)
394 if opts.get('changeset'):
394 if opts.get('changeset'):
395 # omit "+" suffix which is appended to node hex
395 # omit "+" suffix which is appended to node hex
396 def formatrev(rev):
396 def formatrev(rev):
397 if rev is None:
397 if rev is None:
398 return '%d' % ctx.p1().rev()
398 return '%d' % ctx.p1().rev()
399 else:
399 else:
400 return '%d' % rev
400 return '%d' % rev
401 else:
401 else:
402 def formatrev(rev):
402 def formatrev(rev):
403 if rev is None:
403 if rev is None:
404 return '%d+' % ctx.p1().rev()
404 return '%d+' % ctx.p1().rev()
405 else:
405 else:
406 return '%d ' % rev
406 return '%d ' % rev
407 def formathex(hex):
407 def formathex(hex):
408 if hex is None:
408 if hex is None:
409 return '%s+' % fm.hexfunc(ctx.p1().node())
409 return '%s+' % fm.hexfunc(ctx.p1().node())
410 else:
410 else:
411 return '%s ' % hex
411 return '%s ' % hex
412 else:
412 else:
413 hexfn = fm.hexfunc
413 hexfn = fm.hexfunc
414 formatrev = formathex = str
414 formatrev = formathex = str
415
415
416 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
416 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
417 ('number', ' ', lambda x: x[0].rev(), formatrev),
417 ('number', ' ', lambda x: x[0].rev(), formatrev),
418 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
418 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
419 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
419 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
420 ('file', ' ', lambda x: x[0].path(), str),
420 ('file', ' ', lambda x: x[0].path(), str),
421 ('line_number', ':', lambda x: x[1], str),
421 ('line_number', ':', lambda x: x[1], str),
422 ]
422 ]
423 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
423 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
424
424
425 if (not opts.get('user') and not opts.get('changeset')
425 if (not opts.get('user') and not opts.get('changeset')
426 and not opts.get('date') and not opts.get('file')):
426 and not opts.get('date') and not opts.get('file')):
427 opts['number'] = True
427 opts['number'] = True
428
428
429 linenumber = opts.get('line_number') is not None
429 linenumber = opts.get('line_number') is not None
430 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
430 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
431 raise error.Abort(_('at least one of -n/-c is required for -l'))
431 raise error.Abort(_('at least one of -n/-c is required for -l'))
432
432
433 ui.pager('annotate')
433 ui.pager('annotate')
434
434
435 if fm.isplain():
435 if fm.isplain():
436 def makefunc(get, fmt):
436 def makefunc(get, fmt):
437 return lambda x: fmt(get(x))
437 return lambda x: fmt(get(x))
438 else:
438 else:
439 def makefunc(get, fmt):
439 def makefunc(get, fmt):
440 return get
440 return get
441 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
441 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
442 if opts.get(op)]
442 if opts.get(op)]
443 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
443 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
444 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
444 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
445 if opts.get(op))
445 if opts.get(op))
446
446
447 def bad(x, y):
447 def bad(x, y):
448 raise error.Abort("%s: %s" % (x, y))
448 raise error.Abort("%s: %s" % (x, y))
449
449
450 m = scmutil.match(ctx, pats, opts, badfn=bad)
450 m = scmutil.match(ctx, pats, opts, badfn=bad)
451
451
452 follow = not opts.get('no_follow')
452 follow = not opts.get('no_follow')
453 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
453 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
454 whitespace=True)
454 whitespace=True)
455 for abs in ctx.walk(m):
455 for abs in ctx.walk(m):
456 fctx = ctx[abs]
456 fctx = ctx[abs]
457 if not opts.get('text') and fctx.isbinary():
457 if not opts.get('text') and fctx.isbinary():
458 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
458 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
459 continue
459 continue
460
460
461 lines = fctx.annotate(follow=follow, linenumber=linenumber,
461 lines = fctx.annotate(follow=follow, linenumber=linenumber,
462 diffopts=diffopts)
462 diffopts=diffopts)
463 if not lines:
463 if not lines:
464 continue
464 continue
465 formats = []
465 formats = []
466 pieces = []
466 pieces = []
467
467
468 for f, sep in funcmap:
468 for f, sep in funcmap:
469 l = [f(n) for n, dummy in lines]
469 l = [f(n) for n, dummy in lines]
470 if fm.isplain():
470 if fm.isplain():
471 sizes = [encoding.colwidth(x) for x in l]
471 sizes = [encoding.colwidth(x) for x in l]
472 ml = max(sizes)
472 ml = max(sizes)
473 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
473 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
474 else:
474 else:
475 formats.append(['%s' for x in l])
475 formats.append(['%s' for x in l])
476 pieces.append(l)
476 pieces.append(l)
477
477
478 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
478 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
479 fm.startitem()
479 fm.startitem()
480 fm.write(fields, "".join(f), *p)
480 fm.write(fields, "".join(f), *p)
481 fm.write('line', ": %s", l[1])
481 fm.write('line', ": %s", l[1])
482
482
483 if not lines[-1][1].endswith('\n'):
483 if not lines[-1][1].endswith('\n'):
484 fm.plain('\n')
484 fm.plain('\n')
485
485
486 fm.end()
486 fm.end()
487
487
488 @command('archive',
488 @command('archive',
489 [('', 'no-decode', None, _('do not pass files through decoders')),
489 [('', 'no-decode', None, _('do not pass files through decoders')),
490 ('p', 'prefix', '', _('directory prefix for files in archive'),
490 ('p', 'prefix', '', _('directory prefix for files in archive'),
491 _('PREFIX')),
491 _('PREFIX')),
492 ('r', 'rev', '', _('revision to distribute'), _('REV')),
492 ('r', 'rev', '', _('revision to distribute'), _('REV')),
493 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
493 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
494 ] + subrepoopts + walkopts,
494 ] + subrepoopts + walkopts,
495 _('[OPTION]... DEST'))
495 _('[OPTION]... DEST'))
496 def archive(ui, repo, dest, **opts):
496 def archive(ui, repo, dest, **opts):
497 '''create an unversioned archive of a repository revision
497 '''create an unversioned archive of a repository revision
498
498
499 By default, the revision used is the parent of the working
499 By default, the revision used is the parent of the working
500 directory; use -r/--rev to specify a different revision.
500 directory; use -r/--rev to specify a different revision.
501
501
502 The archive type is automatically detected based on file
502 The archive type is automatically detected based on file
503 extension (to override, use -t/--type).
503 extension (to override, use -t/--type).
504
504
505 .. container:: verbose
505 .. container:: verbose
506
506
507 Examples:
507 Examples:
508
508
509 - create a zip file containing the 1.0 release::
509 - create a zip file containing the 1.0 release::
510
510
511 hg archive -r 1.0 project-1.0.zip
511 hg archive -r 1.0 project-1.0.zip
512
512
513 - create a tarball excluding .hg files::
513 - create a tarball excluding .hg files::
514
514
515 hg archive project.tar.gz -X ".hg*"
515 hg archive project.tar.gz -X ".hg*"
516
516
517 Valid types are:
517 Valid types are:
518
518
519 :``files``: a directory full of files (default)
519 :``files``: a directory full of files (default)
520 :``tar``: tar archive, uncompressed
520 :``tar``: tar archive, uncompressed
521 :``tbz2``: tar archive, compressed using bzip2
521 :``tbz2``: tar archive, compressed using bzip2
522 :``tgz``: tar archive, compressed using gzip
522 :``tgz``: tar archive, compressed using gzip
523 :``uzip``: zip archive, uncompressed
523 :``uzip``: zip archive, uncompressed
524 :``zip``: zip archive, compressed using deflate
524 :``zip``: zip archive, compressed using deflate
525
525
526 The exact name of the destination archive or directory is given
526 The exact name of the destination archive or directory is given
527 using a format string; see :hg:`help export` for details.
527 using a format string; see :hg:`help export` for details.
528
528
529 Each member added to an archive file has a directory prefix
529 Each member added to an archive file has a directory prefix
530 prepended. Use -p/--prefix to specify a format string for the
530 prepended. Use -p/--prefix to specify a format string for the
531 prefix. The default is the basename of the archive, with suffixes
531 prefix. The default is the basename of the archive, with suffixes
532 removed.
532 removed.
533
533
534 Returns 0 on success.
534 Returns 0 on success.
535 '''
535 '''
536
536
537 opts = pycompat.byteskwargs(opts)
537 opts = pycompat.byteskwargs(opts)
538 ctx = scmutil.revsingle(repo, opts.get('rev'))
538 ctx = scmutil.revsingle(repo, opts.get('rev'))
539 if not ctx:
539 if not ctx:
540 raise error.Abort(_('no working directory: please specify a revision'))
540 raise error.Abort(_('no working directory: please specify a revision'))
541 node = ctx.node()
541 node = ctx.node()
542 dest = cmdutil.makefilename(repo, dest, node)
542 dest = cmdutil.makefilename(repo, dest, node)
543 if os.path.realpath(dest) == repo.root:
543 if os.path.realpath(dest) == repo.root:
544 raise error.Abort(_('repository root cannot be destination'))
544 raise error.Abort(_('repository root cannot be destination'))
545
545
546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
547 prefix = opts.get('prefix')
547 prefix = opts.get('prefix')
548
548
549 if dest == '-':
549 if dest == '-':
550 if kind == 'files':
550 if kind == 'files':
551 raise error.Abort(_('cannot archive plain files to stdout'))
551 raise error.Abort(_('cannot archive plain files to stdout'))
552 dest = cmdutil.makefileobj(repo, dest)
552 dest = cmdutil.makefileobj(repo, dest)
553 if not prefix:
553 if not prefix:
554 prefix = os.path.basename(repo.root) + '-%h'
554 prefix = os.path.basename(repo.root) + '-%h'
555
555
556 prefix = cmdutil.makefilename(repo, prefix, node)
556 prefix = cmdutil.makefilename(repo, prefix, node)
557 matchfn = scmutil.match(ctx, [], opts)
557 matchfn = scmutil.match(ctx, [], opts)
558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
559 matchfn, prefix, subrepos=opts.get('subrepos'))
559 matchfn, prefix, subrepos=opts.get('subrepos'))
560
560
561 @command('backout',
561 @command('backout',
562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
563 ('', 'commit', None,
563 ('', 'commit', None,
564 _('commit if no conflicts were encountered (DEPRECATED)')),
564 _('commit if no conflicts were encountered (DEPRECATED)')),
565 ('', 'no-commit', None, _('do not commit')),
565 ('', 'no-commit', None, _('do not commit')),
566 ('', 'parent', '',
566 ('', 'parent', '',
567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
568 ('r', 'rev', '', _('revision to backout'), _('REV')),
568 ('r', 'rev', '', _('revision to backout'), _('REV')),
569 ('e', 'edit', False, _('invoke editor on commit messages')),
569 ('e', 'edit', False, _('invoke editor on commit messages')),
570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
571 _('[OPTION]... [-r] REV'))
571 _('[OPTION]... [-r] REV'))
572 def backout(ui, repo, node=None, rev=None, **opts):
572 def backout(ui, repo, node=None, rev=None, **opts):
573 '''reverse effect of earlier changeset
573 '''reverse effect of earlier changeset
574
574
575 Prepare a new changeset with the effect of REV undone in the
575 Prepare a new changeset with the effect of REV undone in the
576 current working directory. If no conflicts were encountered,
576 current working directory. If no conflicts were encountered,
577 it will be committed immediately.
577 it will be committed immediately.
578
578
579 If REV is the parent of the working directory, then this new changeset
579 If REV is the parent of the working directory, then this new changeset
580 is committed automatically (unless --no-commit is specified).
580 is committed automatically (unless --no-commit is specified).
581
581
582 .. note::
582 .. note::
583
583
584 :hg:`backout` cannot be used to fix either an unwanted or
584 :hg:`backout` cannot be used to fix either an unwanted or
585 incorrect merge.
585 incorrect merge.
586
586
587 .. container:: verbose
587 .. container:: verbose
588
588
589 Examples:
589 Examples:
590
590
591 - Reverse the effect of the parent of the working directory.
591 - Reverse the effect of the parent of the working directory.
592 This backout will be committed immediately::
592 This backout will be committed immediately::
593
593
594 hg backout -r .
594 hg backout -r .
595
595
596 - Reverse the effect of previous bad revision 23::
596 - Reverse the effect of previous bad revision 23::
597
597
598 hg backout -r 23
598 hg backout -r 23
599
599
600 - Reverse the effect of previous bad revision 23 and
600 - Reverse the effect of previous bad revision 23 and
601 leave changes uncommitted::
601 leave changes uncommitted::
602
602
603 hg backout -r 23 --no-commit
603 hg backout -r 23 --no-commit
604 hg commit -m "Backout revision 23"
604 hg commit -m "Backout revision 23"
605
605
606 By default, the pending changeset will have one parent,
606 By default, the pending changeset will have one parent,
607 maintaining a linear history. With --merge, the pending
607 maintaining a linear history. With --merge, the pending
608 changeset will instead have two parents: the old parent of the
608 changeset will instead have two parents: the old parent of the
609 working directory and a new child of REV that simply undoes REV.
609 working directory and a new child of REV that simply undoes REV.
610
610
611 Before version 1.7, the behavior without --merge was equivalent
611 Before version 1.7, the behavior without --merge was equivalent
612 to specifying --merge followed by :hg:`update --clean .` to
612 to specifying --merge followed by :hg:`update --clean .` to
613 cancel the merge and leave the child of REV as a head to be
613 cancel the merge and leave the child of REV as a head to be
614 merged separately.
614 merged separately.
615
615
616 See :hg:`help dates` for a list of formats valid for -d/--date.
616 See :hg:`help dates` for a list of formats valid for -d/--date.
617
617
618 See :hg:`help revert` for a way to restore files to the state
618 See :hg:`help revert` for a way to restore files to the state
619 of another revision.
619 of another revision.
620
620
621 Returns 0 on success, 1 if nothing to backout or there are unresolved
621 Returns 0 on success, 1 if nothing to backout or there are unresolved
622 files.
622 files.
623 '''
623 '''
624 wlock = lock = None
624 wlock = lock = None
625 try:
625 try:
626 wlock = repo.wlock()
626 wlock = repo.wlock()
627 lock = repo.lock()
627 lock = repo.lock()
628 return _dobackout(ui, repo, node, rev, **opts)
628 return _dobackout(ui, repo, node, rev, **opts)
629 finally:
629 finally:
630 release(lock, wlock)
630 release(lock, wlock)
631
631
632 def _dobackout(ui, repo, node=None, rev=None, **opts):
632 def _dobackout(ui, repo, node=None, rev=None, **opts):
633 opts = pycompat.byteskwargs(opts)
633 opts = pycompat.byteskwargs(opts)
634 if opts.get('commit') and opts.get('no_commit'):
634 if opts.get('commit') and opts.get('no_commit'):
635 raise error.Abort(_("cannot use --commit with --no-commit"))
635 raise error.Abort(_("cannot use --commit with --no-commit"))
636 if opts.get('merge') and opts.get('no_commit'):
636 if opts.get('merge') and opts.get('no_commit'):
637 raise error.Abort(_("cannot use --merge with --no-commit"))
637 raise error.Abort(_("cannot use --merge with --no-commit"))
638
638
639 if rev and node:
639 if rev and node:
640 raise error.Abort(_("please specify just one revision"))
640 raise error.Abort(_("please specify just one revision"))
641
641
642 if not rev:
642 if not rev:
643 rev = node
643 rev = node
644
644
645 if not rev:
645 if not rev:
646 raise error.Abort(_("please specify a revision to backout"))
646 raise error.Abort(_("please specify a revision to backout"))
647
647
648 date = opts.get('date')
648 date = opts.get('date')
649 if date:
649 if date:
650 opts['date'] = util.parsedate(date)
650 opts['date'] = util.parsedate(date)
651
651
652 cmdutil.checkunfinished(repo)
652 cmdutil.checkunfinished(repo)
653 cmdutil.bailifchanged(repo)
653 cmdutil.bailifchanged(repo)
654 node = scmutil.revsingle(repo, rev).node()
654 node = scmutil.revsingle(repo, rev).node()
655
655
656 op1, op2 = repo.dirstate.parents()
656 op1, op2 = repo.dirstate.parents()
657 if not repo.changelog.isancestor(node, op1):
657 if not repo.changelog.isancestor(node, op1):
658 raise error.Abort(_('cannot backout change that is not an ancestor'))
658 raise error.Abort(_('cannot backout change that is not an ancestor'))
659
659
660 p1, p2 = repo.changelog.parents(node)
660 p1, p2 = repo.changelog.parents(node)
661 if p1 == nullid:
661 if p1 == nullid:
662 raise error.Abort(_('cannot backout a change with no parents'))
662 raise error.Abort(_('cannot backout a change with no parents'))
663 if p2 != nullid:
663 if p2 != nullid:
664 if not opts.get('parent'):
664 if not opts.get('parent'):
665 raise error.Abort(_('cannot backout a merge changeset'))
665 raise error.Abort(_('cannot backout a merge changeset'))
666 p = repo.lookup(opts['parent'])
666 p = repo.lookup(opts['parent'])
667 if p not in (p1, p2):
667 if p not in (p1, p2):
668 raise error.Abort(_('%s is not a parent of %s') %
668 raise error.Abort(_('%s is not a parent of %s') %
669 (short(p), short(node)))
669 (short(p), short(node)))
670 parent = p
670 parent = p
671 else:
671 else:
672 if opts.get('parent'):
672 if opts.get('parent'):
673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
674 parent = p1
674 parent = p1
675
675
676 # the backout should appear on the same branch
676 # the backout should appear on the same branch
677 branch = repo.dirstate.branch()
677 branch = repo.dirstate.branch()
678 bheads = repo.branchheads(branch)
678 bheads = repo.branchheads(branch)
679 rctx = scmutil.revsingle(repo, hex(parent))
679 rctx = scmutil.revsingle(repo, hex(parent))
680 if not opts.get('merge') and op1 != node:
680 if not opts.get('merge') and op1 != node:
681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
682 try:
682 try:
683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
684 'backout')
684 'backout')
685 stats = mergemod.update(repo, parent, True, True, node, False)
685 stats = mergemod.update(repo, parent, True, True, node, False)
686 repo.setparents(op1, op2)
686 repo.setparents(op1, op2)
687 dsguard.close()
687 dsguard.close()
688 hg._showstats(repo, stats)
688 hg._showstats(repo, stats)
689 if stats[3]:
689 if stats[3]:
690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
691 "file merges\n"))
691 "file merges\n"))
692 return 1
692 return 1
693 finally:
693 finally:
694 ui.setconfig('ui', 'forcemerge', '', '')
694 ui.setconfig('ui', 'forcemerge', '', '')
695 lockmod.release(dsguard)
695 lockmod.release(dsguard)
696 else:
696 else:
697 hg.clean(repo, node, show_stats=False)
697 hg.clean(repo, node, show_stats=False)
698 repo.dirstate.setbranch(branch)
698 repo.dirstate.setbranch(branch)
699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
700
700
701 if opts.get('no_commit'):
701 if opts.get('no_commit'):
702 msg = _("changeset %s backed out, "
702 msg = _("changeset %s backed out, "
703 "don't forget to commit.\n")
703 "don't forget to commit.\n")
704 ui.status(msg % short(node))
704 ui.status(msg % short(node))
705 return 0
705 return 0
706
706
707 def commitfunc(ui, repo, message, match, opts):
707 def commitfunc(ui, repo, message, match, opts):
708 editform = 'backout'
708 editform = 'backout'
709 e = cmdutil.getcommiteditor(editform=editform,
709 e = cmdutil.getcommiteditor(editform=editform,
710 **pycompat.strkwargs(opts))
710 **pycompat.strkwargs(opts))
711 if not message:
711 if not message:
712 # we don't translate commit messages
712 # we don't translate commit messages
713 message = "Backed out changeset %s" % short(node)
713 message = "Backed out changeset %s" % short(node)
714 e = cmdutil.getcommiteditor(edit=True, editform=editform)
714 e = cmdutil.getcommiteditor(edit=True, editform=editform)
715 return repo.commit(message, opts.get('user'), opts.get('date'),
715 return repo.commit(message, opts.get('user'), opts.get('date'),
716 match, editor=e)
716 match, editor=e)
717 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
717 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
718 if not newnode:
718 if not newnode:
719 ui.status(_("nothing changed\n"))
719 ui.status(_("nothing changed\n"))
720 return 1
720 return 1
721 cmdutil.commitstatus(repo, newnode, branch, bheads)
721 cmdutil.commitstatus(repo, newnode, branch, bheads)
722
722
723 def nice(node):
723 def nice(node):
724 return '%d:%s' % (repo.changelog.rev(node), short(node))
724 return '%d:%s' % (repo.changelog.rev(node), short(node))
725 ui.status(_('changeset %s backs out changeset %s\n') %
725 ui.status(_('changeset %s backs out changeset %s\n') %
726 (nice(repo.changelog.tip()), nice(node)))
726 (nice(repo.changelog.tip()), nice(node)))
727 if opts.get('merge') and op1 != node:
727 if opts.get('merge') and op1 != node:
728 hg.clean(repo, op1, show_stats=False)
728 hg.clean(repo, op1, show_stats=False)
729 ui.status(_('merging with changeset %s\n')
729 ui.status(_('merging with changeset %s\n')
730 % nice(repo.changelog.tip()))
730 % nice(repo.changelog.tip()))
731 try:
731 try:
732 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
732 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
733 'backout')
733 'backout')
734 return hg.merge(repo, hex(repo.changelog.tip()))
734 return hg.merge(repo, hex(repo.changelog.tip()))
735 finally:
735 finally:
736 ui.setconfig('ui', 'forcemerge', '', '')
736 ui.setconfig('ui', 'forcemerge', '', '')
737 return 0
737 return 0
738
738
739 @command('bisect',
739 @command('bisect',
740 [('r', 'reset', False, _('reset bisect state')),
740 [('r', 'reset', False, _('reset bisect state')),
741 ('g', 'good', False, _('mark changeset good')),
741 ('g', 'good', False, _('mark changeset good')),
742 ('b', 'bad', False, _('mark changeset bad')),
742 ('b', 'bad', False, _('mark changeset bad')),
743 ('s', 'skip', False, _('skip testing changeset')),
743 ('s', 'skip', False, _('skip testing changeset')),
744 ('e', 'extend', False, _('extend the bisect range')),
744 ('e', 'extend', False, _('extend the bisect range')),
745 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
745 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
746 ('U', 'noupdate', False, _('do not update to target'))],
746 ('U', 'noupdate', False, _('do not update to target'))],
747 _("[-gbsr] [-U] [-c CMD] [REV]"))
747 _("[-gbsr] [-U] [-c CMD] [REV]"))
748 def bisect(ui, repo, rev=None, extra=None, command=None,
748 def bisect(ui, repo, rev=None, extra=None, command=None,
749 reset=None, good=None, bad=None, skip=None, extend=None,
749 reset=None, good=None, bad=None, skip=None, extend=None,
750 noupdate=None):
750 noupdate=None):
751 """subdivision search of changesets
751 """subdivision search of changesets
752
752
753 This command helps to find changesets which introduce problems. To
753 This command helps to find changesets which introduce problems. To
754 use, mark the earliest changeset you know exhibits the problem as
754 use, mark the earliest changeset you know exhibits the problem as
755 bad, then mark the latest changeset which is free from the problem
755 bad, then mark the latest changeset which is free from the problem
756 as good. Bisect will update your working directory to a revision
756 as good. Bisect will update your working directory to a revision
757 for testing (unless the -U/--noupdate option is specified). Once
757 for testing (unless the -U/--noupdate option is specified). Once
758 you have performed tests, mark the working directory as good or
758 you have performed tests, mark the working directory as good or
759 bad, and bisect will either update to another candidate changeset
759 bad, and bisect will either update to another candidate changeset
760 or announce that it has found the bad revision.
760 or announce that it has found the bad revision.
761
761
762 As a shortcut, you can also use the revision argument to mark a
762 As a shortcut, you can also use the revision argument to mark a
763 revision as good or bad without checking it out first.
763 revision as good or bad without checking it out first.
764
764
765 If you supply a command, it will be used for automatic bisection.
765 If you supply a command, it will be used for automatic bisection.
766 The environment variable HG_NODE will contain the ID of the
766 The environment variable HG_NODE will contain the ID of the
767 changeset being tested. The exit status of the command will be
767 changeset being tested. The exit status of the command will be
768 used to mark revisions as good or bad: status 0 means good, 125
768 used to mark revisions as good or bad: status 0 means good, 125
769 means to skip the revision, 127 (command not found) will abort the
769 means to skip the revision, 127 (command not found) will abort the
770 bisection, and any other non-zero exit status means the revision
770 bisection, and any other non-zero exit status means the revision
771 is bad.
771 is bad.
772
772
773 .. container:: verbose
773 .. container:: verbose
774
774
775 Some examples:
775 Some examples:
776
776
777 - start a bisection with known bad revision 34, and good revision 12::
777 - start a bisection with known bad revision 34, and good revision 12::
778
778
779 hg bisect --bad 34
779 hg bisect --bad 34
780 hg bisect --good 12
780 hg bisect --good 12
781
781
782 - advance the current bisection by marking current revision as good or
782 - advance the current bisection by marking current revision as good or
783 bad::
783 bad::
784
784
785 hg bisect --good
785 hg bisect --good
786 hg bisect --bad
786 hg bisect --bad
787
787
788 - mark the current revision, or a known revision, to be skipped (e.g. if
788 - mark the current revision, or a known revision, to be skipped (e.g. if
789 that revision is not usable because of another issue)::
789 that revision is not usable because of another issue)::
790
790
791 hg bisect --skip
791 hg bisect --skip
792 hg bisect --skip 23
792 hg bisect --skip 23
793
793
794 - skip all revisions that do not touch directories ``foo`` or ``bar``::
794 - skip all revisions that do not touch directories ``foo`` or ``bar``::
795
795
796 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
796 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
797
797
798 - forget the current bisection::
798 - forget the current bisection::
799
799
800 hg bisect --reset
800 hg bisect --reset
801
801
802 - use 'make && make tests' to automatically find the first broken
802 - use 'make && make tests' to automatically find the first broken
803 revision::
803 revision::
804
804
805 hg bisect --reset
805 hg bisect --reset
806 hg bisect --bad 34
806 hg bisect --bad 34
807 hg bisect --good 12
807 hg bisect --good 12
808 hg bisect --command "make && make tests"
808 hg bisect --command "make && make tests"
809
809
810 - see all changesets whose states are already known in the current
810 - see all changesets whose states are already known in the current
811 bisection::
811 bisection::
812
812
813 hg log -r "bisect(pruned)"
813 hg log -r "bisect(pruned)"
814
814
815 - see the changeset currently being bisected (especially useful
815 - see the changeset currently being bisected (especially useful
816 if running with -U/--noupdate)::
816 if running with -U/--noupdate)::
817
817
818 hg log -r "bisect(current)"
818 hg log -r "bisect(current)"
819
819
820 - see all changesets that took part in the current bisection::
820 - see all changesets that took part in the current bisection::
821
821
822 hg log -r "bisect(range)"
822 hg log -r "bisect(range)"
823
823
824 - you can even get a nice graph::
824 - you can even get a nice graph::
825
825
826 hg log --graph -r "bisect(range)"
826 hg log --graph -r "bisect(range)"
827
827
828 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
828 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
829
829
830 Returns 0 on success.
830 Returns 0 on success.
831 """
831 """
832 # backward compatibility
832 # backward compatibility
833 if rev in "good bad reset init".split():
833 if rev in "good bad reset init".split():
834 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
834 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
835 cmd, rev, extra = rev, extra, None
835 cmd, rev, extra = rev, extra, None
836 if cmd == "good":
836 if cmd == "good":
837 good = True
837 good = True
838 elif cmd == "bad":
838 elif cmd == "bad":
839 bad = True
839 bad = True
840 else:
840 else:
841 reset = True
841 reset = True
842 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
842 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
843 raise error.Abort(_('incompatible arguments'))
843 raise error.Abort(_('incompatible arguments'))
844
844
845 if reset:
845 if reset:
846 hbisect.resetstate(repo)
846 hbisect.resetstate(repo)
847 return
847 return
848
848
849 state = hbisect.load_state(repo)
849 state = hbisect.load_state(repo)
850
850
851 # update state
851 # update state
852 if good or bad or skip:
852 if good or bad or skip:
853 if rev:
853 if rev:
854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
855 else:
855 else:
856 nodes = [repo.lookup('.')]
856 nodes = [repo.lookup('.')]
857 if good:
857 if good:
858 state['good'] += nodes
858 state['good'] += nodes
859 elif bad:
859 elif bad:
860 state['bad'] += nodes
860 state['bad'] += nodes
861 elif skip:
861 elif skip:
862 state['skip'] += nodes
862 state['skip'] += nodes
863 hbisect.save_state(repo, state)
863 hbisect.save_state(repo, state)
864 if not (state['good'] and state['bad']):
864 if not (state['good'] and state['bad']):
865 return
865 return
866
866
867 def mayupdate(repo, node, show_stats=True):
867 def mayupdate(repo, node, show_stats=True):
868 """common used update sequence"""
868 """common used update sequence"""
869 if noupdate:
869 if noupdate:
870 return
870 return
871 cmdutil.checkunfinished(repo)
871 cmdutil.checkunfinished(repo)
872 cmdutil.bailifchanged(repo)
872 cmdutil.bailifchanged(repo)
873 return hg.clean(repo, node, show_stats=show_stats)
873 return hg.clean(repo, node, show_stats=show_stats)
874
874
875 displayer = cmdutil.show_changeset(ui, repo, {})
875 displayer = cmdutil.show_changeset(ui, repo, {})
876
876
877 if command:
877 if command:
878 changesets = 1
878 changesets = 1
879 if noupdate:
879 if noupdate:
880 try:
880 try:
881 node = state['current'][0]
881 node = state['current'][0]
882 except LookupError:
882 except LookupError:
883 raise error.Abort(_('current bisect revision is unknown - '
883 raise error.Abort(_('current bisect revision is unknown - '
884 'start a new bisect to fix'))
884 'start a new bisect to fix'))
885 else:
885 else:
886 node, p2 = repo.dirstate.parents()
886 node, p2 = repo.dirstate.parents()
887 if p2 != nullid:
887 if p2 != nullid:
888 raise error.Abort(_('current bisect revision is a merge'))
888 raise error.Abort(_('current bisect revision is a merge'))
889 if rev:
889 if rev:
890 node = repo[scmutil.revsingle(repo, rev, node)].node()
890 node = repo[scmutil.revsingle(repo, rev, node)].node()
891 try:
891 try:
892 while changesets:
892 while changesets:
893 # update state
893 # update state
894 state['current'] = [node]
894 state['current'] = [node]
895 hbisect.save_state(repo, state)
895 hbisect.save_state(repo, state)
896 status = ui.system(command, environ={'HG_NODE': hex(node)},
896 status = ui.system(command, environ={'HG_NODE': hex(node)},
897 blockedtag='bisect_check')
897 blockedtag='bisect_check')
898 if status == 125:
898 if status == 125:
899 transition = "skip"
899 transition = "skip"
900 elif status == 0:
900 elif status == 0:
901 transition = "good"
901 transition = "good"
902 # status < 0 means process was killed
902 # status < 0 means process was killed
903 elif status == 127:
903 elif status == 127:
904 raise error.Abort(_("failed to execute %s") % command)
904 raise error.Abort(_("failed to execute %s") % command)
905 elif status < 0:
905 elif status < 0:
906 raise error.Abort(_("%s killed") % command)
906 raise error.Abort(_("%s killed") % command)
907 else:
907 else:
908 transition = "bad"
908 transition = "bad"
909 state[transition].append(node)
909 state[transition].append(node)
910 ctx = repo[node]
910 ctx = repo[node]
911 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
911 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
912 hbisect.checkstate(state)
912 hbisect.checkstate(state)
913 # bisect
913 # bisect
914 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
914 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
915 # update to next check
915 # update to next check
916 node = nodes[0]
916 node = nodes[0]
917 mayupdate(repo, node, show_stats=False)
917 mayupdate(repo, node, show_stats=False)
918 finally:
918 finally:
919 state['current'] = [node]
919 state['current'] = [node]
920 hbisect.save_state(repo, state)
920 hbisect.save_state(repo, state)
921 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
921 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
922 return
922 return
923
923
924 hbisect.checkstate(state)
924 hbisect.checkstate(state)
925
925
926 # actually bisect
926 # actually bisect
927 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
927 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
928 if extend:
928 if extend:
929 if not changesets:
929 if not changesets:
930 extendnode = hbisect.extendrange(repo, state, nodes, good)
930 extendnode = hbisect.extendrange(repo, state, nodes, good)
931 if extendnode is not None:
931 if extendnode is not None:
932 ui.write(_("Extending search to changeset %d:%s\n")
932 ui.write(_("Extending search to changeset %d:%s\n")
933 % (extendnode.rev(), extendnode))
933 % (extendnode.rev(), extendnode))
934 state['current'] = [extendnode.node()]
934 state['current'] = [extendnode.node()]
935 hbisect.save_state(repo, state)
935 hbisect.save_state(repo, state)
936 return mayupdate(repo, extendnode.node())
936 return mayupdate(repo, extendnode.node())
937 raise error.Abort(_("nothing to extend"))
937 raise error.Abort(_("nothing to extend"))
938
938
939 if changesets == 0:
939 if changesets == 0:
940 hbisect.printresult(ui, repo, state, displayer, nodes, good)
940 hbisect.printresult(ui, repo, state, displayer, nodes, good)
941 else:
941 else:
942 assert len(nodes) == 1 # only a single node can be tested next
942 assert len(nodes) == 1 # only a single node can be tested next
943 node = nodes[0]
943 node = nodes[0]
944 # compute the approximate number of remaining tests
944 # compute the approximate number of remaining tests
945 tests, size = 0, 2
945 tests, size = 0, 2
946 while size <= changesets:
946 while size <= changesets:
947 tests, size = tests + 1, size * 2
947 tests, size = tests + 1, size * 2
948 rev = repo.changelog.rev(node)
948 rev = repo.changelog.rev(node)
949 ui.write(_("Testing changeset %d:%s "
949 ui.write(_("Testing changeset %d:%s "
950 "(%d changesets remaining, ~%d tests)\n")
950 "(%d changesets remaining, ~%d tests)\n")
951 % (rev, short(node), changesets, tests))
951 % (rev, short(node), changesets, tests))
952 state['current'] = [node]
952 state['current'] = [node]
953 hbisect.save_state(repo, state)
953 hbisect.save_state(repo, state)
954 return mayupdate(repo, node)
954 return mayupdate(repo, node)
955
955
956 @command('bookmarks|bookmark',
956 @command('bookmarks|bookmark',
957 [('f', 'force', False, _('force')),
957 [('f', 'force', False, _('force')),
958 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
958 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
959 ('d', 'delete', False, _('delete a given bookmark')),
959 ('d', 'delete', False, _('delete a given bookmark')),
960 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
960 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
961 ('i', 'inactive', False, _('mark a bookmark inactive')),
961 ('i', 'inactive', False, _('mark a bookmark inactive')),
962 ] + formatteropts,
962 ] + formatteropts,
963 _('hg bookmarks [OPTIONS]... [NAME]...'))
963 _('hg bookmarks [OPTIONS]... [NAME]...'))
964 def bookmark(ui, repo, *names, **opts):
964 def bookmark(ui, repo, *names, **opts):
965 '''create a new bookmark or list existing bookmarks
965 '''create a new bookmark or list existing bookmarks
966
966
967 Bookmarks are labels on changesets to help track lines of development.
967 Bookmarks are labels on changesets to help track lines of development.
968 Bookmarks are unversioned and can be moved, renamed and deleted.
968 Bookmarks are unversioned and can be moved, renamed and deleted.
969 Deleting or moving a bookmark has no effect on the associated changesets.
969 Deleting or moving a bookmark has no effect on the associated changesets.
970
970
971 Creating or updating to a bookmark causes it to be marked as 'active'.
971 Creating or updating to a bookmark causes it to be marked as 'active'.
972 The active bookmark is indicated with a '*'.
972 The active bookmark is indicated with a '*'.
973 When a commit is made, the active bookmark will advance to the new commit.
973 When a commit is made, the active bookmark will advance to the new commit.
974 A plain :hg:`update` will also advance an active bookmark, if possible.
974 A plain :hg:`update` will also advance an active bookmark, if possible.
975 Updating away from a bookmark will cause it to be deactivated.
975 Updating away from a bookmark will cause it to be deactivated.
976
976
977 Bookmarks can be pushed and pulled between repositories (see
977 Bookmarks can be pushed and pulled between repositories (see
978 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
978 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
979 diverged, a new 'divergent bookmark' of the form 'name@path' will
979 diverged, a new 'divergent bookmark' of the form 'name@path' will
980 be created. Using :hg:`merge` will resolve the divergence.
980 be created. Using :hg:`merge` will resolve the divergence.
981
981
982 A bookmark named '@' has the special property that :hg:`clone` will
982 A bookmark named '@' has the special property that :hg:`clone` will
983 check it out by default if it exists.
983 check it out by default if it exists.
984
984
985 .. container:: verbose
985 .. container:: verbose
986
986
987 Examples:
987 Examples:
988
988
989 - create an active bookmark for a new line of development::
989 - create an active bookmark for a new line of development::
990
990
991 hg book new-feature
991 hg book new-feature
992
992
993 - create an inactive bookmark as a place marker::
993 - create an inactive bookmark as a place marker::
994
994
995 hg book -i reviewed
995 hg book -i reviewed
996
996
997 - create an inactive bookmark on another changeset::
997 - create an inactive bookmark on another changeset::
998
998
999 hg book -r .^ tested
999 hg book -r .^ tested
1000
1000
1001 - rename bookmark turkey to dinner::
1001 - rename bookmark turkey to dinner::
1002
1002
1003 hg book -m turkey dinner
1003 hg book -m turkey dinner
1004
1004
1005 - move the '@' bookmark from another branch::
1005 - move the '@' bookmark from another branch::
1006
1006
1007 hg book -f @
1007 hg book -f @
1008 '''
1008 '''
1009 opts = pycompat.byteskwargs(opts)
1009 opts = pycompat.byteskwargs(opts)
1010 force = opts.get('force')
1010 force = opts.get('force')
1011 rev = opts.get('rev')
1011 rev = opts.get('rev')
1012 delete = opts.get('delete')
1012 delete = opts.get('delete')
1013 rename = opts.get('rename')
1013 rename = opts.get('rename')
1014 inactive = opts.get('inactive')
1014 inactive = opts.get('inactive')
1015
1015
1016 def checkformat(mark):
1016 def checkformat(mark):
1017 mark = mark.strip()
1017 mark = mark.strip()
1018 if not mark:
1018 if not mark:
1019 raise error.Abort(_("bookmark names cannot consist entirely of "
1019 raise error.Abort(_("bookmark names cannot consist entirely of "
1020 "whitespace"))
1020 "whitespace"))
1021 scmutil.checknewlabel(repo, mark, 'bookmark')
1021 scmutil.checknewlabel(repo, mark, 'bookmark')
1022 return mark
1022 return mark
1023
1023
1024 def checkconflict(repo, mark, cur, force=False, target=None):
1024 def checkconflict(repo, mark, cur, force=False, target=None):
1025 if mark in marks and not force:
1025 if mark in marks and not force:
1026 if target:
1026 if target:
1027 if marks[mark] == target and target == cur:
1027 if marks[mark] == target and target == cur:
1028 # re-activating a bookmark
1028 # re-activating a bookmark
1029 return
1029 return
1030 anc = repo.changelog.ancestors([repo[target].rev()])
1030 anc = repo.changelog.ancestors([repo[target].rev()])
1031 bmctx = repo[marks[mark]]
1031 bmctx = repo[marks[mark]]
1032 divs = [repo[b].node() for b in marks
1032 divs = [repo[b].node() for b in marks
1033 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1033 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1034
1034
1035 # allow resolving a single divergent bookmark even if moving
1035 # allow resolving a single divergent bookmark even if moving
1036 # the bookmark across branches when a revision is specified
1036 # the bookmark across branches when a revision is specified
1037 # that contains a divergent bookmark
1037 # that contains a divergent bookmark
1038 if bmctx.rev() not in anc and target in divs:
1038 if bmctx.rev() not in anc and target in divs:
1039 bookmarks.deletedivergent(repo, [target], mark)
1039 bookmarks.deletedivergent(repo, [target], mark)
1040 return
1040 return
1041
1041
1042 deletefrom = [b for b in divs
1042 deletefrom = [b for b in divs
1043 if repo[b].rev() in anc or b == target]
1043 if repo[b].rev() in anc or b == target]
1044 bookmarks.deletedivergent(repo, deletefrom, mark)
1044 bookmarks.deletedivergent(repo, deletefrom, mark)
1045 if bookmarks.validdest(repo, bmctx, repo[target]):
1045 if bookmarks.validdest(repo, bmctx, repo[target]):
1046 ui.status(_("moving bookmark '%s' forward from %s\n") %
1046 ui.status(_("moving bookmark '%s' forward from %s\n") %
1047 (mark, short(bmctx.node())))
1047 (mark, short(bmctx.node())))
1048 return
1048 return
1049 raise error.Abort(_("bookmark '%s' already exists "
1049 raise error.Abort(_("bookmark '%s' already exists "
1050 "(use -f to force)") % mark)
1050 "(use -f to force)") % mark)
1051 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1051 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1052 and not force):
1052 and not force):
1053 raise error.Abort(
1053 raise error.Abort(
1054 _("a bookmark cannot have the name of an existing branch"))
1054 _("a bookmark cannot have the name of an existing branch"))
1055
1055
1056 if delete and rename:
1056 if delete and rename:
1057 raise error.Abort(_("--delete and --rename are incompatible"))
1057 raise error.Abort(_("--delete and --rename are incompatible"))
1058 if delete and rev:
1058 if delete and rev:
1059 raise error.Abort(_("--rev is incompatible with --delete"))
1059 raise error.Abort(_("--rev is incompatible with --delete"))
1060 if rename and rev:
1060 if rename and rev:
1061 raise error.Abort(_("--rev is incompatible with --rename"))
1061 raise error.Abort(_("--rev is incompatible with --rename"))
1062 if not names and (delete or rev):
1062 if not names and (delete or rev):
1063 raise error.Abort(_("bookmark name required"))
1063 raise error.Abort(_("bookmark name required"))
1064
1064
1065 if delete or rename or names or inactive:
1065 if delete or rename or names or inactive:
1066 wlock = lock = tr = None
1066 wlock = lock = tr = None
1067 try:
1067 try:
1068 wlock = repo.wlock()
1068 wlock = repo.wlock()
1069 lock = repo.lock()
1069 lock = repo.lock()
1070 cur = repo.changectx('.').node()
1070 cur = repo.changectx('.').node()
1071 marks = repo._bookmarks
1071 marks = repo._bookmarks
1072 if delete:
1072 if delete:
1073 tr = repo.transaction('bookmark')
1073 tr = repo.transaction('bookmark')
1074 for mark in names:
1074 for mark in names:
1075 if mark not in marks:
1075 if mark not in marks:
1076 raise error.Abort(_("bookmark '%s' does not exist") %
1076 raise error.Abort(_("bookmark '%s' does not exist") %
1077 mark)
1077 mark)
1078 if mark == repo._activebookmark:
1078 if mark == repo._activebookmark:
1079 bookmarks.deactivate(repo)
1079 bookmarks.deactivate(repo)
1080 del marks[mark]
1080 del marks[mark]
1081
1081
1082 elif rename:
1082 elif rename:
1083 tr = repo.transaction('bookmark')
1083 tr = repo.transaction('bookmark')
1084 if not names:
1084 if not names:
1085 raise error.Abort(_("new bookmark name required"))
1085 raise error.Abort(_("new bookmark name required"))
1086 elif len(names) > 1:
1086 elif len(names) > 1:
1087 raise error.Abort(_("only one new bookmark name allowed"))
1087 raise error.Abort(_("only one new bookmark name allowed"))
1088 mark = checkformat(names[0])
1088 mark = checkformat(names[0])
1089 if rename not in marks:
1089 if rename not in marks:
1090 raise error.Abort(_("bookmark '%s' does not exist")
1090 raise error.Abort(_("bookmark '%s' does not exist")
1091 % rename)
1091 % rename)
1092 checkconflict(repo, mark, cur, force)
1092 checkconflict(repo, mark, cur, force)
1093 marks[mark] = marks[rename]
1093 marks[mark] = marks[rename]
1094 if repo._activebookmark == rename and not inactive:
1094 if repo._activebookmark == rename and not inactive:
1095 bookmarks.activate(repo, mark)
1095 bookmarks.activate(repo, mark)
1096 del marks[rename]
1096 del marks[rename]
1097 elif names:
1097 elif names:
1098 tr = repo.transaction('bookmark')
1098 tr = repo.transaction('bookmark')
1099 newact = None
1099 newact = None
1100 for mark in names:
1100 for mark in names:
1101 mark = checkformat(mark)
1101 mark = checkformat(mark)
1102 if newact is None:
1102 if newact is None:
1103 newact = mark
1103 newact = mark
1104 if inactive and mark == repo._activebookmark:
1104 if inactive and mark == repo._activebookmark:
1105 bookmarks.deactivate(repo)
1105 bookmarks.deactivate(repo)
1106 return
1106 return
1107 tgt = cur
1107 tgt = cur
1108 if rev:
1108 if rev:
1109 tgt = scmutil.revsingle(repo, rev).node()
1109 tgt = scmutil.revsingle(repo, rev).node()
1110 checkconflict(repo, mark, cur, force, tgt)
1110 checkconflict(repo, mark, cur, force, tgt)
1111 marks[mark] = tgt
1111 marks[mark] = tgt
1112 if not inactive and cur == marks[newact] and not rev:
1112 if not inactive and cur == marks[newact] and not rev:
1113 bookmarks.activate(repo, newact)
1113 bookmarks.activate(repo, newact)
1114 elif cur != tgt and newact == repo._activebookmark:
1114 elif cur != tgt and newact == repo._activebookmark:
1115 bookmarks.deactivate(repo)
1115 bookmarks.deactivate(repo)
1116 elif inactive:
1116 elif inactive:
1117 if len(marks) == 0:
1117 if len(marks) == 0:
1118 ui.status(_("no bookmarks set\n"))
1118 ui.status(_("no bookmarks set\n"))
1119 elif not repo._activebookmark:
1119 elif not repo._activebookmark:
1120 ui.status(_("no active bookmark\n"))
1120 ui.status(_("no active bookmark\n"))
1121 else:
1121 else:
1122 bookmarks.deactivate(repo)
1122 bookmarks.deactivate(repo)
1123 if tr is not None:
1123 if tr is not None:
1124 marks.recordchange(tr)
1124 marks.recordchange(tr)
1125 tr.close()
1125 tr.close()
1126 finally:
1126 finally:
1127 lockmod.release(tr, lock, wlock)
1127 lockmod.release(tr, lock, wlock)
1128 else: # show bookmarks
1128 else: # show bookmarks
1129 fm = ui.formatter('bookmarks', opts)
1129 fm = ui.formatter('bookmarks', opts)
1130 hexfn = fm.hexfunc
1130 hexfn = fm.hexfunc
1131 marks = repo._bookmarks
1131 marks = repo._bookmarks
1132 if len(marks) == 0 and fm.isplain():
1132 if len(marks) == 0 and fm.isplain():
1133 ui.status(_("no bookmarks set\n"))
1133 ui.status(_("no bookmarks set\n"))
1134 for bmark, n in sorted(marks.iteritems()):
1134 for bmark, n in sorted(marks.iteritems()):
1135 active = repo._activebookmark
1135 active = repo._activebookmark
1136 if bmark == active:
1136 if bmark == active:
1137 prefix, label = '*', activebookmarklabel
1137 prefix, label = '*', activebookmarklabel
1138 else:
1138 else:
1139 prefix, label = ' ', ''
1139 prefix, label = ' ', ''
1140
1140
1141 fm.startitem()
1141 fm.startitem()
1142 if not ui.quiet:
1142 if not ui.quiet:
1143 fm.plain(' %s ' % prefix, label=label)
1143 fm.plain(' %s ' % prefix, label=label)
1144 fm.write('bookmark', '%s', bmark, label=label)
1144 fm.write('bookmark', '%s', bmark, label=label)
1145 pad = " " * (25 - encoding.colwidth(bmark))
1145 pad = " " * (25 - encoding.colwidth(bmark))
1146 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1146 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1147 repo.changelog.rev(n), hexfn(n), label=label)
1147 repo.changelog.rev(n), hexfn(n), label=label)
1148 fm.data(active=(bmark == active))
1148 fm.data(active=(bmark == active))
1149 fm.plain('\n')
1149 fm.plain('\n')
1150 fm.end()
1150 fm.end()
1151
1151
1152 @command('branch',
1152 @command('branch',
1153 [('f', 'force', None,
1153 [('f', 'force', None,
1154 _('set branch name even if it shadows an existing branch')),
1154 _('set branch name even if it shadows an existing branch')),
1155 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1155 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1156 _('[-fC] [NAME]'))
1156 _('[-fC] [NAME]'))
1157 def branch(ui, repo, label=None, **opts):
1157 def branch(ui, repo, label=None, **opts):
1158 """set or show the current branch name
1158 """set or show the current branch name
1159
1159
1160 .. note::
1160 .. note::
1161
1161
1162 Branch names are permanent and global. Use :hg:`bookmark` to create a
1162 Branch names are permanent and global. Use :hg:`bookmark` to create a
1163 light-weight bookmark instead. See :hg:`help glossary` for more
1163 light-weight bookmark instead. See :hg:`help glossary` for more
1164 information about named branches and bookmarks.
1164 information about named branches and bookmarks.
1165
1165
1166 With no argument, show the current branch name. With one argument,
1166 With no argument, show the current branch name. With one argument,
1167 set the working directory branch name (the branch will not exist
1167 set the working directory branch name (the branch will not exist
1168 in the repository until the next commit). Standard practice
1168 in the repository until the next commit). Standard practice
1169 recommends that primary development take place on the 'default'
1169 recommends that primary development take place on the 'default'
1170 branch.
1170 branch.
1171
1171
1172 Unless -f/--force is specified, branch will not let you set a
1172 Unless -f/--force is specified, branch will not let you set a
1173 branch name that already exists.
1173 branch name that already exists.
1174
1174
1175 Use -C/--clean to reset the working directory branch to that of
1175 Use -C/--clean to reset the working directory branch to that of
1176 the parent of the working directory, negating a previous branch
1176 the parent of the working directory, negating a previous branch
1177 change.
1177 change.
1178
1178
1179 Use the command :hg:`update` to switch to an existing branch. Use
1179 Use the command :hg:`update` to switch to an existing branch. Use
1180 :hg:`commit --close-branch` to mark this branch head as closed.
1180 :hg:`commit --close-branch` to mark this branch head as closed.
1181 When all heads of a branch are closed, the branch will be
1181 When all heads of a branch are closed, the branch will be
1182 considered closed.
1182 considered closed.
1183
1183
1184 Returns 0 on success.
1184 Returns 0 on success.
1185 """
1185 """
1186 opts = pycompat.byteskwargs(opts)
1186 opts = pycompat.byteskwargs(opts)
1187 if label:
1187 if label:
1188 label = label.strip()
1188 label = label.strip()
1189
1189
1190 if not opts.get('clean') and not label:
1190 if not opts.get('clean') and not label:
1191 ui.write("%s\n" % repo.dirstate.branch())
1191 ui.write("%s\n" % repo.dirstate.branch())
1192 return
1192 return
1193
1193
1194 with repo.wlock():
1194 with repo.wlock():
1195 if opts.get('clean'):
1195 if opts.get('clean'):
1196 label = repo[None].p1().branch()
1196 label = repo[None].p1().branch()
1197 repo.dirstate.setbranch(label)
1197 repo.dirstate.setbranch(label)
1198 ui.status(_('reset working directory to branch %s\n') % label)
1198 ui.status(_('reset working directory to branch %s\n') % label)
1199 elif label:
1199 elif label:
1200 if not opts.get('force') and label in repo.branchmap():
1200 if not opts.get('force') and label in repo.branchmap():
1201 if label not in [p.branch() for p in repo[None].parents()]:
1201 if label not in [p.branch() for p in repo[None].parents()]:
1202 raise error.Abort(_('a branch of the same name already'
1202 raise error.Abort(_('a branch of the same name already'
1203 ' exists'),
1203 ' exists'),
1204 # i18n: "it" refers to an existing branch
1204 # i18n: "it" refers to an existing branch
1205 hint=_("use 'hg update' to switch to it"))
1205 hint=_("use 'hg update' to switch to it"))
1206 scmutil.checknewlabel(repo, label, 'branch')
1206 scmutil.checknewlabel(repo, label, 'branch')
1207 repo.dirstate.setbranch(label)
1207 repo.dirstate.setbranch(label)
1208 ui.status(_('marked working directory as branch %s\n') % label)
1208 ui.status(_('marked working directory as branch %s\n') % label)
1209
1209
1210 # find any open named branches aside from default
1210 # find any open named branches aside from default
1211 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1211 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1212 if n != "default" and not c]
1212 if n != "default" and not c]
1213 if not others:
1213 if not others:
1214 ui.status(_('(branches are permanent and global, '
1214 ui.status(_('(branches are permanent and global, '
1215 'did you want a bookmark?)\n'))
1215 'did you want a bookmark?)\n'))
1216
1216
1217 @command('branches',
1217 @command('branches',
1218 [('a', 'active', False,
1218 [('a', 'active', False,
1219 _('show only branches that have unmerged heads (DEPRECATED)')),
1219 _('show only branches that have unmerged heads (DEPRECATED)')),
1220 ('c', 'closed', False, _('show normal and closed branches')),
1220 ('c', 'closed', False, _('show normal and closed branches')),
1221 ] + formatteropts,
1221 ] + formatteropts,
1222 _('[-c]'))
1222 _('[-c]'))
1223 def branches(ui, repo, active=False, closed=False, **opts):
1223 def branches(ui, repo, active=False, closed=False, **opts):
1224 """list repository named branches
1224 """list repository named branches
1225
1225
1226 List the repository's named branches, indicating which ones are
1226 List the repository's named branches, indicating which ones are
1227 inactive. If -c/--closed is specified, also list branches which have
1227 inactive. If -c/--closed is specified, also list branches which have
1228 been marked closed (see :hg:`commit --close-branch`).
1228 been marked closed (see :hg:`commit --close-branch`).
1229
1229
1230 Use the command :hg:`update` to switch to an existing branch.
1230 Use the command :hg:`update` to switch to an existing branch.
1231
1231
1232 Returns 0.
1232 Returns 0.
1233 """
1233 """
1234
1234
1235 opts = pycompat.byteskwargs(opts)
1235 opts = pycompat.byteskwargs(opts)
1236 ui.pager('branches')
1236 ui.pager('branches')
1237 fm = ui.formatter('branches', opts)
1237 fm = ui.formatter('branches', opts)
1238 hexfunc = fm.hexfunc
1238 hexfunc = fm.hexfunc
1239
1239
1240 allheads = set(repo.heads())
1240 allheads = set(repo.heads())
1241 branches = []
1241 branches = []
1242 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1242 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1243 isactive = not isclosed and bool(set(heads) & allheads)
1243 isactive = not isclosed and bool(set(heads) & allheads)
1244 branches.append((tag, repo[tip], isactive, not isclosed))
1244 branches.append((tag, repo[tip], isactive, not isclosed))
1245 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1245 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1246 reverse=True)
1246 reverse=True)
1247
1247
1248 for tag, ctx, isactive, isopen in branches:
1248 for tag, ctx, isactive, isopen in branches:
1249 if active and not isactive:
1249 if active and not isactive:
1250 continue
1250 continue
1251 if isactive:
1251 if isactive:
1252 label = 'branches.active'
1252 label = 'branches.active'
1253 notice = ''
1253 notice = ''
1254 elif not isopen:
1254 elif not isopen:
1255 if not closed:
1255 if not closed:
1256 continue
1256 continue
1257 label = 'branches.closed'
1257 label = 'branches.closed'
1258 notice = _(' (closed)')
1258 notice = _(' (closed)')
1259 else:
1259 else:
1260 label = 'branches.inactive'
1260 label = 'branches.inactive'
1261 notice = _(' (inactive)')
1261 notice = _(' (inactive)')
1262 current = (tag == repo.dirstate.branch())
1262 current = (tag == repo.dirstate.branch())
1263 if current:
1263 if current:
1264 label = 'branches.current'
1264 label = 'branches.current'
1265
1265
1266 fm.startitem()
1266 fm.startitem()
1267 fm.write('branch', '%s', tag, label=label)
1267 fm.write('branch', '%s', tag, label=label)
1268 rev = ctx.rev()
1268 rev = ctx.rev()
1269 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1269 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1270 fmt = ' ' * padsize + ' %d:%s'
1270 fmt = ' ' * padsize + ' %d:%s'
1271 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1271 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1272 label='log.changeset changeset.%s' % ctx.phasestr())
1272 label='log.changeset changeset.%s' % ctx.phasestr())
1273 fm.context(ctx=ctx)
1273 fm.context(ctx=ctx)
1274 fm.data(active=isactive, closed=not isopen, current=current)
1274 fm.data(active=isactive, closed=not isopen, current=current)
1275 if not ui.quiet:
1275 if not ui.quiet:
1276 fm.plain(notice)
1276 fm.plain(notice)
1277 fm.plain('\n')
1277 fm.plain('\n')
1278 fm.end()
1278 fm.end()
1279
1279
1280 @command('bundle',
1280 @command('bundle',
1281 [('f', 'force', None, _('run even when the destination is unrelated')),
1281 [('f', 'force', None, _('run even when the destination is unrelated')),
1282 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1282 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1283 _('REV')),
1283 _('REV')),
1284 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1284 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1285 _('BRANCH')),
1285 _('BRANCH')),
1286 ('', 'base', [],
1286 ('', 'base', [],
1287 _('a base changeset assumed to be available at the destination'),
1287 _('a base changeset assumed to be available at the destination'),
1288 _('REV')),
1288 _('REV')),
1289 ('a', 'all', None, _('bundle all changesets in the repository')),
1289 ('a', 'all', None, _('bundle all changesets in the repository')),
1290 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1290 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1291 ] + remoteopts,
1291 ] + remoteopts,
1292 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1292 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1293 def bundle(ui, repo, fname, dest=None, **opts):
1293 def bundle(ui, repo, fname, dest=None, **opts):
1294 """create a bundle file
1294 """create a bundle file
1295
1295
1296 Generate a bundle file containing data to be added to a repository.
1296 Generate a bundle file containing data to be added to a repository.
1297
1297
1298 To create a bundle containing all changesets, use -a/--all
1298 To create a bundle containing all changesets, use -a/--all
1299 (or --base null). Otherwise, hg assumes the destination will have
1299 (or --base null). Otherwise, hg assumes the destination will have
1300 all the nodes you specify with --base parameters. Otherwise, hg
1300 all the nodes you specify with --base parameters. Otherwise, hg
1301 will assume the repository has all the nodes in destination, or
1301 will assume the repository has all the nodes in destination, or
1302 default-push/default if no destination is specified.
1302 default-push/default if no destination is specified.
1303
1303
1304 You can change bundle format with the -t/--type option. See
1304 You can change bundle format with the -t/--type option. See
1305 :hg:`help bundlespec` for documentation on this format. By default,
1305 :hg:`help bundlespec` for documentation on this format. By default,
1306 the most appropriate format is used and compression defaults to
1306 the most appropriate format is used and compression defaults to
1307 bzip2.
1307 bzip2.
1308
1308
1309 The bundle file can then be transferred using conventional means
1309 The bundle file can then be transferred using conventional means
1310 and applied to another repository with the unbundle or pull
1310 and applied to another repository with the unbundle or pull
1311 command. This is useful when direct push and pull are not
1311 command. This is useful when direct push and pull are not
1312 available or when exporting an entire repository is undesirable.
1312 available or when exporting an entire repository is undesirable.
1313
1313
1314 Applying bundles preserves all changeset contents including
1314 Applying bundles preserves all changeset contents including
1315 permissions, copy/rename information, and revision history.
1315 permissions, copy/rename information, and revision history.
1316
1316
1317 Returns 0 on success, 1 if no changes found.
1317 Returns 0 on success, 1 if no changes found.
1318 """
1318 """
1319 opts = pycompat.byteskwargs(opts)
1319 opts = pycompat.byteskwargs(opts)
1320 revs = None
1320 revs = None
1321 if 'rev' in opts:
1321 if 'rev' in opts:
1322 revstrings = opts['rev']
1322 revstrings = opts['rev']
1323 revs = scmutil.revrange(repo, revstrings)
1323 revs = scmutil.revrange(repo, revstrings)
1324 if revstrings and not revs:
1324 if revstrings and not revs:
1325 raise error.Abort(_('no commits to bundle'))
1325 raise error.Abort(_('no commits to bundle'))
1326
1326
1327 bundletype = opts.get('type', 'bzip2').lower()
1327 bundletype = opts.get('type', 'bzip2').lower()
1328 try:
1328 try:
1329 bcompression, cgversion, params = exchange.parsebundlespec(
1329 bcompression, cgversion, params = exchange.parsebundlespec(
1330 repo, bundletype, strict=False)
1330 repo, bundletype, strict=False)
1331 except error.UnsupportedBundleSpecification as e:
1331 except error.UnsupportedBundleSpecification as e:
1332 raise error.Abort(str(e),
1332 raise error.Abort(str(e),
1333 hint=_("see 'hg help bundlespec' for supported "
1333 hint=_("see 'hg help bundlespec' for supported "
1334 "values for --type"))
1334 "values for --type"))
1335
1335
1336 # Packed bundles are a pseudo bundle format for now.
1336 # Packed bundles are a pseudo bundle format for now.
1337 if cgversion == 's1':
1337 if cgversion == 's1':
1338 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1338 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1339 hint=_("use 'hg debugcreatestreamclonebundle'"))
1339 hint=_("use 'hg debugcreatestreamclonebundle'"))
1340
1340
1341 if opts.get('all'):
1341 if opts.get('all'):
1342 if dest:
1342 if dest:
1343 raise error.Abort(_("--all is incompatible with specifying "
1343 raise error.Abort(_("--all is incompatible with specifying "
1344 "a destination"))
1344 "a destination"))
1345 if opts.get('base'):
1345 if opts.get('base'):
1346 ui.warn(_("ignoring --base because --all was specified\n"))
1346 ui.warn(_("ignoring --base because --all was specified\n"))
1347 base = ['null']
1347 base = ['null']
1348 else:
1348 else:
1349 base = scmutil.revrange(repo, opts.get('base'))
1349 base = scmutil.revrange(repo, opts.get('base'))
1350 if cgversion not in changegroup.supportedoutgoingversions(repo):
1350 if cgversion not in changegroup.supportedoutgoingversions(repo):
1351 raise error.Abort(_("repository does not support bundle version %s") %
1351 raise error.Abort(_("repository does not support bundle version %s") %
1352 cgversion)
1352 cgversion)
1353
1353
1354 if base:
1354 if base:
1355 if dest:
1355 if dest:
1356 raise error.Abort(_("--base is incompatible with specifying "
1356 raise error.Abort(_("--base is incompatible with specifying "
1357 "a destination"))
1357 "a destination"))
1358 common = [repo.lookup(rev) for rev in base]
1358 common = [repo.lookup(rev) for rev in base]
1359 heads = revs and map(repo.lookup, revs) or None
1359 heads = revs and map(repo.lookup, revs) or None
1360 outgoing = discovery.outgoing(repo, common, heads)
1360 outgoing = discovery.outgoing(repo, common, heads)
1361 else:
1361 else:
1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1364 other = hg.peer(repo, opts, dest)
1364 other = hg.peer(repo, opts, dest)
1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1366 heads = revs and map(repo.lookup, revs) or revs
1366 heads = revs and map(repo.lookup, revs) or revs
1367 outgoing = discovery.findcommonoutgoing(repo, other,
1367 outgoing = discovery.findcommonoutgoing(repo, other,
1368 onlyheads=heads,
1368 onlyheads=heads,
1369 force=opts.get('force'),
1369 force=opts.get('force'),
1370 portable=True)
1370 portable=True)
1371
1371
1372 if not outgoing.missing:
1372 if not outgoing.missing:
1373 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1373 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1374 return 1
1374 return 1
1375
1375
1376 if cgversion == '01': #bundle1
1376 if cgversion == '01': #bundle1
1377 if bcompression is None:
1377 if bcompression is None:
1378 bcompression = 'UN'
1378 bcompression = 'UN'
1379 bversion = 'HG10' + bcompression
1379 bversion = 'HG10' + bcompression
1380 bcompression = None
1380 bcompression = None
1381 elif cgversion in ('02', '03'):
1381 elif cgversion in ('02', '03'):
1382 bversion = 'HG20'
1382 bversion = 'HG20'
1383 else:
1383 else:
1384 raise error.ProgrammingError(
1384 raise error.ProgrammingError(
1385 'bundle: unexpected changegroup version %s' % cgversion)
1385 'bundle: unexpected changegroup version %s' % cgversion)
1386
1386
1387 # TODO compression options should be derived from bundlespec parsing.
1387 # TODO compression options should be derived from bundlespec parsing.
1388 # This is a temporary hack to allow adjusting bundle compression
1388 # This is a temporary hack to allow adjusting bundle compression
1389 # level without a) formalizing the bundlespec changes to declare it
1389 # level without a) formalizing the bundlespec changes to declare it
1390 # b) introducing a command flag.
1390 # b) introducing a command flag.
1391 compopts = {}
1391 compopts = {}
1392 complevel = ui.configint('experimental', 'bundlecomplevel')
1392 complevel = ui.configint('experimental', 'bundlecomplevel')
1393 if complevel is not None:
1393 if complevel is not None:
1394 compopts['level'] = complevel
1394 compopts['level'] = complevel
1395
1395
1396
1396
1397 contentopts = {'cg.version': cgversion}
1397 contentopts = {'cg.version': cgversion}
1398 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1398 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1399 contentopts, compression=bcompression,
1399 contentopts, compression=bcompression,
1400 compopts=compopts)
1400 compopts=compopts)
1401
1401
1402 @command('cat',
1402 @command('cat',
1403 [('o', 'output', '',
1403 [('o', 'output', '',
1404 _('print output to file with formatted name'), _('FORMAT')),
1404 _('print output to file with formatted name'), _('FORMAT')),
1405 ('r', 'rev', '', _('print the given revision'), _('REV')),
1405 ('r', 'rev', '', _('print the given revision'), _('REV')),
1406 ('', 'decode', None, _('apply any matching decode filter')),
1406 ('', 'decode', None, _('apply any matching decode filter')),
1407 ] + walkopts,
1407 ] + walkopts,
1408 _('[OPTION]... FILE...'),
1408 _('[OPTION]... FILE...'),
1409 inferrepo=True)
1409 inferrepo=True)
1410 def cat(ui, repo, file1, *pats, **opts):
1410 def cat(ui, repo, file1, *pats, **opts):
1411 """output the current or given revision of files
1411 """output the current or given revision of files
1412
1412
1413 Print the specified files as they were at the given revision. If
1413 Print the specified files as they were at the given revision. If
1414 no revision is given, the parent of the working directory is used.
1414 no revision is given, the parent of the working directory is used.
1415
1415
1416 Output may be to a file, in which case the name of the file is
1416 Output may be to a file, in which case the name of the file is
1417 given using a format string. The formatting rules as follows:
1417 given using a format string. The formatting rules as follows:
1418
1418
1419 :``%%``: literal "%" character
1419 :``%%``: literal "%" character
1420 :``%s``: basename of file being printed
1420 :``%s``: basename of file being printed
1421 :``%d``: dirname of file being printed, or '.' if in repository root
1421 :``%d``: dirname of file being printed, or '.' if in repository root
1422 :``%p``: root-relative path name of file being printed
1422 :``%p``: root-relative path name of file being printed
1423 :``%H``: changeset hash (40 hexadecimal digits)
1423 :``%H``: changeset hash (40 hexadecimal digits)
1424 :``%R``: changeset revision number
1424 :``%R``: changeset revision number
1425 :``%h``: short-form changeset hash (12 hexadecimal digits)
1425 :``%h``: short-form changeset hash (12 hexadecimal digits)
1426 :``%r``: zero-padded changeset revision number
1426 :``%r``: zero-padded changeset revision number
1427 :``%b``: basename of the exporting repository
1427 :``%b``: basename of the exporting repository
1428
1428
1429 Returns 0 on success.
1429 Returns 0 on success.
1430 """
1430 """
1431 ctx = scmutil.revsingle(repo, opts.get('rev'))
1431 ctx = scmutil.revsingle(repo, opts.get('rev'))
1432 m = scmutil.match(ctx, (file1,) + pats, opts)
1432 m = scmutil.match(ctx, (file1,) + pats, opts)
1433
1433
1434 ui.pager('cat')
1434 ui.pager('cat')
1435 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1435 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1436
1436
1437 @command('^clone',
1437 @command('^clone',
1438 [('U', 'noupdate', None, _('the clone will include an empty working '
1438 [('U', 'noupdate', None, _('the clone will include an empty working '
1439 'directory (only a repository)')),
1439 'directory (only a repository)')),
1440 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1440 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1441 _('REV')),
1441 _('REV')),
1442 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1442 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1443 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1443 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1444 ('', 'pull', None, _('use pull protocol to copy metadata')),
1444 ('', 'pull', None, _('use pull protocol to copy metadata')),
1445 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1445 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1446 ] + remoteopts,
1446 ] + remoteopts,
1447 _('[OPTION]... SOURCE [DEST]'),
1447 _('[OPTION]... SOURCE [DEST]'),
1448 norepo=True)
1448 norepo=True)
1449 def clone(ui, source, dest=None, **opts):
1449 def clone(ui, source, dest=None, **opts):
1450 """make a copy of an existing repository
1450 """make a copy of an existing repository
1451
1451
1452 Create a copy of an existing repository in a new directory.
1452 Create a copy of an existing repository in a new directory.
1453
1453
1454 If no destination directory name is specified, it defaults to the
1454 If no destination directory name is specified, it defaults to the
1455 basename of the source.
1455 basename of the source.
1456
1456
1457 The location of the source is added to the new repository's
1457 The location of the source is added to the new repository's
1458 ``.hg/hgrc`` file, as the default to be used for future pulls.
1458 ``.hg/hgrc`` file, as the default to be used for future pulls.
1459
1459
1460 Only local paths and ``ssh://`` URLs are supported as
1460 Only local paths and ``ssh://`` URLs are supported as
1461 destinations. For ``ssh://`` destinations, no working directory or
1461 destinations. For ``ssh://`` destinations, no working directory or
1462 ``.hg/hgrc`` will be created on the remote side.
1462 ``.hg/hgrc`` will be created on the remote side.
1463
1463
1464 If the source repository has a bookmark called '@' set, that
1464 If the source repository has a bookmark called '@' set, that
1465 revision will be checked out in the new repository by default.
1465 revision will be checked out in the new repository by default.
1466
1466
1467 To check out a particular version, use -u/--update, or
1467 To check out a particular version, use -u/--update, or
1468 -U/--noupdate to create a clone with no working directory.
1468 -U/--noupdate to create a clone with no working directory.
1469
1469
1470 To pull only a subset of changesets, specify one or more revisions
1470 To pull only a subset of changesets, specify one or more revisions
1471 identifiers with -r/--rev or branches with -b/--branch. The
1471 identifiers with -r/--rev or branches with -b/--branch. The
1472 resulting clone will contain only the specified changesets and
1472 resulting clone will contain only the specified changesets and
1473 their ancestors. These options (or 'clone src#rev dest') imply
1473 their ancestors. These options (or 'clone src#rev dest') imply
1474 --pull, even for local source repositories.
1474 --pull, even for local source repositories.
1475
1475
1476 .. note::
1476 .. note::
1477
1477
1478 Specifying a tag will include the tagged changeset but not the
1478 Specifying a tag will include the tagged changeset but not the
1479 changeset containing the tag.
1479 changeset containing the tag.
1480
1480
1481 .. container:: verbose
1481 .. container:: verbose
1482
1482
1483 For efficiency, hardlinks are used for cloning whenever the
1483 For efficiency, hardlinks are used for cloning whenever the
1484 source and destination are on the same filesystem (note this
1484 source and destination are on the same filesystem (note this
1485 applies only to the repository data, not to the working
1485 applies only to the repository data, not to the working
1486 directory). Some filesystems, such as AFS, implement hardlinking
1486 directory). Some filesystems, such as AFS, implement hardlinking
1487 incorrectly, but do not report errors. In these cases, use the
1487 incorrectly, but do not report errors. In these cases, use the
1488 --pull option to avoid hardlinking.
1488 --pull option to avoid hardlinking.
1489
1489
1490 In some cases, you can clone repositories and the working
1490 In some cases, you can clone repositories and the working
1491 directory using full hardlinks with ::
1491 directory using full hardlinks with ::
1492
1492
1493 $ cp -al REPO REPOCLONE
1493 $ cp -al REPO REPOCLONE
1494
1494
1495 This is the fastest way to clone, but it is not always safe. The
1495 This is the fastest way to clone, but it is not always safe. The
1496 operation is not atomic (making sure REPO is not modified during
1496 operation is not atomic (making sure REPO is not modified during
1497 the operation is up to you) and you have to make sure your
1497 the operation is up to you) and you have to make sure your
1498 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1498 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1499 so). Also, this is not compatible with certain extensions that
1499 so). Also, this is not compatible with certain extensions that
1500 place their metadata under the .hg directory, such as mq.
1500 place their metadata under the .hg directory, such as mq.
1501
1501
1502 Mercurial will update the working directory to the first applicable
1502 Mercurial will update the working directory to the first applicable
1503 revision from this list:
1503 revision from this list:
1504
1504
1505 a) null if -U or the source repository has no changesets
1505 a) null if -U or the source repository has no changesets
1506 b) if -u . and the source repository is local, the first parent of
1506 b) if -u . and the source repository is local, the first parent of
1507 the source repository's working directory
1507 the source repository's working directory
1508 c) the changeset specified with -u (if a branch name, this means the
1508 c) the changeset specified with -u (if a branch name, this means the
1509 latest head of that branch)
1509 latest head of that branch)
1510 d) the changeset specified with -r
1510 d) the changeset specified with -r
1511 e) the tipmost head specified with -b
1511 e) the tipmost head specified with -b
1512 f) the tipmost head specified with the url#branch source syntax
1512 f) the tipmost head specified with the url#branch source syntax
1513 g) the revision marked with the '@' bookmark, if present
1513 g) the revision marked with the '@' bookmark, if present
1514 h) the tipmost head of the default branch
1514 h) the tipmost head of the default branch
1515 i) tip
1515 i) tip
1516
1516
1517 When cloning from servers that support it, Mercurial may fetch
1517 When cloning from servers that support it, Mercurial may fetch
1518 pre-generated data from a server-advertised URL. When this is done,
1518 pre-generated data from a server-advertised URL. When this is done,
1519 hooks operating on incoming changesets and changegroups may fire twice,
1519 hooks operating on incoming changesets and changegroups may fire twice,
1520 once for the bundle fetched from the URL and another for any additional
1520 once for the bundle fetched from the URL and another for any additional
1521 data not fetched from this URL. In addition, if an error occurs, the
1521 data not fetched from this URL. In addition, if an error occurs, the
1522 repository may be rolled back to a partial clone. This behavior may
1522 repository may be rolled back to a partial clone. This behavior may
1523 change in future releases. See :hg:`help -e clonebundles` for more.
1523 change in future releases. See :hg:`help -e clonebundles` for more.
1524
1524
1525 Examples:
1525 Examples:
1526
1526
1527 - clone a remote repository to a new directory named hg/::
1527 - clone a remote repository to a new directory named hg/::
1528
1528
1529 hg clone https://www.mercurial-scm.org/repo/hg/
1529 hg clone https://www.mercurial-scm.org/repo/hg/
1530
1530
1531 - create a lightweight local clone::
1531 - create a lightweight local clone::
1532
1532
1533 hg clone project/ project-feature/
1533 hg clone project/ project-feature/
1534
1534
1535 - clone from an absolute path on an ssh server (note double-slash)::
1535 - clone from an absolute path on an ssh server (note double-slash)::
1536
1536
1537 hg clone ssh://user@server//home/projects/alpha/
1537 hg clone ssh://user@server//home/projects/alpha/
1538
1538
1539 - do a high-speed clone over a LAN while checking out a
1539 - do a high-speed clone over a LAN while checking out a
1540 specified version::
1540 specified version::
1541
1541
1542 hg clone --uncompressed http://server/repo -u 1.5
1542 hg clone --uncompressed http://server/repo -u 1.5
1543
1543
1544 - create a repository without changesets after a particular revision::
1544 - create a repository without changesets after a particular revision::
1545
1545
1546 hg clone -r 04e544 experimental/ good/
1546 hg clone -r 04e544 experimental/ good/
1547
1547
1548 - clone (and track) a particular named branch::
1548 - clone (and track) a particular named branch::
1549
1549
1550 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1550 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1551
1551
1552 See :hg:`help urls` for details on specifying URLs.
1552 See :hg:`help urls` for details on specifying URLs.
1553
1553
1554 Returns 0 on success.
1554 Returns 0 on success.
1555 """
1555 """
1556 opts = pycompat.byteskwargs(opts)
1556 opts = pycompat.byteskwargs(opts)
1557 if opts.get('noupdate') and opts.get('updaterev'):
1557 if opts.get('noupdate') and opts.get('updaterev'):
1558 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1558 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1559
1559
1560 r = hg.clone(ui, opts, source, dest,
1560 r = hg.clone(ui, opts, source, dest,
1561 pull=opts.get('pull'),
1561 pull=opts.get('pull'),
1562 stream=opts.get('uncompressed'),
1562 stream=opts.get('uncompressed'),
1563 rev=opts.get('rev'),
1563 rev=opts.get('rev'),
1564 update=opts.get('updaterev') or not opts.get('noupdate'),
1564 update=opts.get('updaterev') or not opts.get('noupdate'),
1565 branch=opts.get('branch'),
1565 branch=opts.get('branch'),
1566 shareopts=opts.get('shareopts'))
1566 shareopts=opts.get('shareopts'))
1567
1567
1568 return r is None
1568 return r is None
1569
1569
1570 @command('^commit|ci',
1570 @command('^commit|ci',
1571 [('A', 'addremove', None,
1571 [('A', 'addremove', None,
1572 _('mark new/missing files as added/removed before committing')),
1572 _('mark new/missing files as added/removed before committing')),
1573 ('', 'close-branch', None,
1573 ('', 'close-branch', None,
1574 _('mark a branch head as closed')),
1574 _('mark a branch head as closed')),
1575 ('', 'amend', None, _('amend the parent of the working directory')),
1575 ('', 'amend', None, _('amend the parent of the working directory')),
1576 ('s', 'secret', None, _('use the secret phase for committing')),
1576 ('s', 'secret', None, _('use the secret phase for committing')),
1577 ('e', 'edit', None, _('invoke editor on commit messages')),
1577 ('e', 'edit', None, _('invoke editor on commit messages')),
1578 ('i', 'interactive', None, _('use interactive mode')),
1578 ('i', 'interactive', None, _('use interactive mode')),
1579 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1579 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1580 _('[OPTION]... [FILE]...'),
1580 _('[OPTION]... [FILE]...'),
1581 inferrepo=True)
1581 inferrepo=True)
1582 def commit(ui, repo, *pats, **opts):
1582 def commit(ui, repo, *pats, **opts):
1583 """commit the specified files or all outstanding changes
1583 """commit the specified files or all outstanding changes
1584
1584
1585 Commit changes to the given files into the repository. Unlike a
1585 Commit changes to the given files into the repository. Unlike a
1586 centralized SCM, this operation is a local operation. See
1586 centralized SCM, this operation is a local operation. See
1587 :hg:`push` for a way to actively distribute your changes.
1587 :hg:`push` for a way to actively distribute your changes.
1588
1588
1589 If a list of files is omitted, all changes reported by :hg:`status`
1589 If a list of files is omitted, all changes reported by :hg:`status`
1590 will be committed.
1590 will be committed.
1591
1591
1592 If you are committing the result of a merge, do not provide any
1592 If you are committing the result of a merge, do not provide any
1593 filenames or -I/-X filters.
1593 filenames or -I/-X filters.
1594
1594
1595 If no commit message is specified, Mercurial starts your
1595 If no commit message is specified, Mercurial starts your
1596 configured editor where you can enter a message. In case your
1596 configured editor where you can enter a message. In case your
1597 commit fails, you will find a backup of your message in
1597 commit fails, you will find a backup of your message in
1598 ``.hg/last-message.txt``.
1598 ``.hg/last-message.txt``.
1599
1599
1600 The --close-branch flag can be used to mark the current branch
1600 The --close-branch flag can be used to mark the current branch
1601 head closed. When all heads of a branch are closed, the branch
1601 head closed. When all heads of a branch are closed, the branch
1602 will be considered closed and no longer listed.
1602 will be considered closed and no longer listed.
1603
1603
1604 The --amend flag can be used to amend the parent of the
1604 The --amend flag can be used to amend the parent of the
1605 working directory with a new commit that contains the changes
1605 working directory with a new commit that contains the changes
1606 in the parent in addition to those currently reported by :hg:`status`,
1606 in the parent in addition to those currently reported by :hg:`status`,
1607 if there are any. The old commit is stored in a backup bundle in
1607 if there are any. The old commit is stored in a backup bundle in
1608 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1608 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1609 on how to restore it).
1609 on how to restore it).
1610
1610
1611 Message, user and date are taken from the amended commit unless
1611 Message, user and date are taken from the amended commit unless
1612 specified. When a message isn't specified on the command line,
1612 specified. When a message isn't specified on the command line,
1613 the editor will open with the message of the amended commit.
1613 the editor will open with the message of the amended commit.
1614
1614
1615 It is not possible to amend public changesets (see :hg:`help phases`)
1615 It is not possible to amend public changesets (see :hg:`help phases`)
1616 or changesets that have children.
1616 or changesets that have children.
1617
1617
1618 See :hg:`help dates` for a list of formats valid for -d/--date.
1618 See :hg:`help dates` for a list of formats valid for -d/--date.
1619
1619
1620 Returns 0 on success, 1 if nothing changed.
1620 Returns 0 on success, 1 if nothing changed.
1621
1621
1622 .. container:: verbose
1622 .. container:: verbose
1623
1623
1624 Examples:
1624 Examples:
1625
1625
1626 - commit all files ending in .py::
1626 - commit all files ending in .py::
1627
1627
1628 hg commit --include "set:**.py"
1628 hg commit --include "set:**.py"
1629
1629
1630 - commit all non-binary files::
1630 - commit all non-binary files::
1631
1631
1632 hg commit --exclude "set:binary()"
1632 hg commit --exclude "set:binary()"
1633
1633
1634 - amend the current commit and set the date to now::
1634 - amend the current commit and set the date to now::
1635
1635
1636 hg commit --amend --date now
1636 hg commit --amend --date now
1637 """
1637 """
1638 wlock = lock = None
1638 wlock = lock = None
1639 try:
1639 try:
1640 wlock = repo.wlock()
1640 wlock = repo.wlock()
1641 lock = repo.lock()
1641 lock = repo.lock()
1642 return _docommit(ui, repo, *pats, **opts)
1642 return _docommit(ui, repo, *pats, **opts)
1643 finally:
1643 finally:
1644 release(lock, wlock)
1644 release(lock, wlock)
1645
1645
1646 def _docommit(ui, repo, *pats, **opts):
1646 def _docommit(ui, repo, *pats, **opts):
1647 if opts.get(r'interactive'):
1647 if opts.get(r'interactive'):
1648 opts.pop(r'interactive')
1648 opts.pop(r'interactive')
1649 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1649 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1650 cmdutil.recordfilter, *pats,
1650 cmdutil.recordfilter, *pats,
1651 **opts)
1651 **opts)
1652 # ret can be 0 (no changes to record) or the value returned by
1652 # ret can be 0 (no changes to record) or the value returned by
1653 # commit(), 1 if nothing changed or None on success.
1653 # commit(), 1 if nothing changed or None on success.
1654 return 1 if ret == 0 else ret
1654 return 1 if ret == 0 else ret
1655
1655
1656 opts = pycompat.byteskwargs(opts)
1656 opts = pycompat.byteskwargs(opts)
1657 if opts.get('subrepos'):
1657 if opts.get('subrepos'):
1658 if opts.get('amend'):
1658 if opts.get('amend'):
1659 raise error.Abort(_('cannot amend with --subrepos'))
1659 raise error.Abort(_('cannot amend with --subrepos'))
1660 # Let --subrepos on the command line override config setting.
1660 # Let --subrepos on the command line override config setting.
1661 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1661 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1662
1662
1663 cmdutil.checkunfinished(repo, commit=True)
1663 cmdutil.checkunfinished(repo, commit=True)
1664
1664
1665 branch = repo[None].branch()
1665 branch = repo[None].branch()
1666 bheads = repo.branchheads(branch)
1666 bheads = repo.branchheads(branch)
1667
1667
1668 extra = {}
1668 extra = {}
1669 if opts.get('close_branch'):
1669 if opts.get('close_branch'):
1670 extra['close'] = 1
1670 extra['close'] = 1
1671
1671
1672 if not bheads:
1672 if not bheads:
1673 raise error.Abort(_('can only close branch heads'))
1673 raise error.Abort(_('can only close branch heads'))
1674 elif opts.get('amend'):
1674 elif opts.get('amend'):
1675 if repo[None].parents()[0].p1().branch() != branch and \
1675 if repo[None].parents()[0].p1().branch() != branch and \
1676 repo[None].parents()[0].p2().branch() != branch:
1676 repo[None].parents()[0].p2().branch() != branch:
1677 raise error.Abort(_('can only close branch heads'))
1677 raise error.Abort(_('can only close branch heads'))
1678
1678
1679 if opts.get('amend'):
1679 if opts.get('amend'):
1680 if ui.configbool('ui', 'commitsubrepos'):
1680 if ui.configbool('ui', 'commitsubrepos'):
1681 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1681 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1682
1682
1683 old = repo['.']
1683 old = repo['.']
1684 if not old.mutable():
1684 if not old.mutable():
1685 raise error.Abort(_('cannot amend public changesets'))
1685 raise error.Abort(_('cannot amend public changesets'))
1686 if len(repo[None].parents()) > 1:
1686 if len(repo[None].parents()) > 1:
1687 raise error.Abort(_('cannot amend while merging'))
1687 raise error.Abort(_('cannot amend while merging'))
1688 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1688 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1689 if not allowunstable and old.children():
1689 if not allowunstable and old.children():
1690 raise error.Abort(_('cannot amend changeset with children'))
1690 raise error.Abort(_('cannot amend changeset with children'))
1691
1691
1692 # Currently histedit gets confused if an amend happens while histedit
1692 # Currently histedit gets confused if an amend happens while histedit
1693 # is in progress. Since we have a checkunfinished command, we are
1693 # is in progress. Since we have a checkunfinished command, we are
1694 # temporarily honoring it.
1694 # temporarily honoring it.
1695 #
1695 #
1696 # Note: eventually this guard will be removed. Please do not expect
1696 # Note: eventually this guard will be removed. Please do not expect
1697 # this behavior to remain.
1697 # this behavior to remain.
1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1699 cmdutil.checkunfinished(repo)
1699 cmdutil.checkunfinished(repo)
1700
1700
1701 # commitfunc is used only for temporary amend commit by cmdutil.amend
1701 # commitfunc is used only for temporary amend commit by cmdutil.amend
1702 def commitfunc(ui, repo, message, match, opts):
1702 def commitfunc(ui, repo, message, match, opts):
1703 return repo.commit(message,
1703 return repo.commit(message,
1704 opts.get('user') or old.user(),
1704 opts.get('user') or old.user(),
1705 opts.get('date') or old.date(),
1705 opts.get('date') or old.date(),
1706 match,
1706 match,
1707 extra=extra)
1707 extra=extra)
1708
1708
1709 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1709 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1710 if node == old.node():
1710 if node == old.node():
1711 ui.status(_("nothing changed\n"))
1711 ui.status(_("nothing changed\n"))
1712 return 1
1712 return 1
1713 else:
1713 else:
1714 def commitfunc(ui, repo, message, match, opts):
1714 def commitfunc(ui, repo, message, match, opts):
1715 overrides = {}
1715 overrides = {}
1716 if opts.get('secret'):
1716 if opts.get('secret'):
1717 overrides[('phases', 'new-commit')] = 'secret'
1717 overrides[('phases', 'new-commit')] = 'secret'
1718
1718
1719 baseui = repo.baseui
1719 baseui = repo.baseui
1720 with baseui.configoverride(overrides, 'commit'):
1720 with baseui.configoverride(overrides, 'commit'):
1721 with ui.configoverride(overrides, 'commit'):
1721 with ui.configoverride(overrides, 'commit'):
1722 editform = cmdutil.mergeeditform(repo[None],
1722 editform = cmdutil.mergeeditform(repo[None],
1723 'commit.normal')
1723 'commit.normal')
1724 editor = cmdutil.getcommiteditor(
1724 editor = cmdutil.getcommiteditor(
1725 editform=editform, **pycompat.strkwargs(opts))
1725 editform=editform, **pycompat.strkwargs(opts))
1726 return repo.commit(message,
1726 return repo.commit(message,
1727 opts.get('user'),
1727 opts.get('user'),
1728 opts.get('date'),
1728 opts.get('date'),
1729 match,
1729 match,
1730 editor=editor,
1730 editor=editor,
1731 extra=extra)
1731 extra=extra)
1732
1732
1733 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1733 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1734
1734
1735 if not node:
1735 if not node:
1736 stat = cmdutil.postcommitstatus(repo, pats, opts)
1736 stat = cmdutil.postcommitstatus(repo, pats, opts)
1737 if stat[3]:
1737 if stat[3]:
1738 ui.status(_("nothing changed (%d missing files, see "
1738 ui.status(_("nothing changed (%d missing files, see "
1739 "'hg status')\n") % len(stat[3]))
1739 "'hg status')\n") % len(stat[3]))
1740 else:
1740 else:
1741 ui.status(_("nothing changed\n"))
1741 ui.status(_("nothing changed\n"))
1742 return 1
1742 return 1
1743
1743
1744 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1744 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1745
1745
1746 @command('config|showconfig|debugconfig',
1746 @command('config|showconfig|debugconfig',
1747 [('u', 'untrusted', None, _('show untrusted configuration options')),
1747 [('u', 'untrusted', None, _('show untrusted configuration options')),
1748 ('e', 'edit', None, _('edit user config')),
1748 ('e', 'edit', None, _('edit user config')),
1749 ('l', 'local', None, _('edit repository config')),
1749 ('l', 'local', None, _('edit repository config')),
1750 ('g', 'global', None, _('edit global config'))] + formatteropts,
1750 ('g', 'global', None, _('edit global config'))] + formatteropts,
1751 _('[-u] [NAME]...'),
1751 _('[-u] [NAME]...'),
1752 optionalrepo=True)
1752 optionalrepo=True)
1753 def config(ui, repo, *values, **opts):
1753 def config(ui, repo, *values, **opts):
1754 """show combined config settings from all hgrc files
1754 """show combined config settings from all hgrc files
1755
1755
1756 With no arguments, print names and values of all config items.
1756 With no arguments, print names and values of all config items.
1757
1757
1758 With one argument of the form section.name, print just the value
1758 With one argument of the form section.name, print just the value
1759 of that config item.
1759 of that config item.
1760
1760
1761 With multiple arguments, print names and values of all config
1761 With multiple arguments, print names and values of all config
1762 items with matching section names.
1762 items with matching section names.
1763
1763
1764 With --edit, start an editor on the user-level config file. With
1764 With --edit, start an editor on the user-level config file. With
1765 --global, edit the system-wide config file. With --local, edit the
1765 --global, edit the system-wide config file. With --local, edit the
1766 repository-level config file.
1766 repository-level config file.
1767
1767
1768 With --debug, the source (filename and line number) is printed
1768 With --debug, the source (filename and line number) is printed
1769 for each config item.
1769 for each config item.
1770
1770
1771 See :hg:`help config` for more information about config files.
1771 See :hg:`help config` for more information about config files.
1772
1772
1773 Returns 0 on success, 1 if NAME does not exist.
1773 Returns 0 on success, 1 if NAME does not exist.
1774
1774
1775 """
1775 """
1776
1776
1777 opts = pycompat.byteskwargs(opts)
1777 opts = pycompat.byteskwargs(opts)
1778 if opts.get('edit') or opts.get('local') or opts.get('global'):
1778 if opts.get('edit') or opts.get('local') or opts.get('global'):
1779 if opts.get('local') and opts.get('global'):
1779 if opts.get('local') and opts.get('global'):
1780 raise error.Abort(_("can't use --local and --global together"))
1780 raise error.Abort(_("can't use --local and --global together"))
1781
1781
1782 if opts.get('local'):
1782 if opts.get('local'):
1783 if not repo:
1783 if not repo:
1784 raise error.Abort(_("can't use --local outside a repository"))
1784 raise error.Abort(_("can't use --local outside a repository"))
1785 paths = [repo.vfs.join('hgrc')]
1785 paths = [repo.vfs.join('hgrc')]
1786 elif opts.get('global'):
1786 elif opts.get('global'):
1787 paths = rcutil.systemrcpath()
1787 paths = rcutil.systemrcpath()
1788 else:
1788 else:
1789 paths = rcutil.userrcpath()
1789 paths = rcutil.userrcpath()
1790
1790
1791 for f in paths:
1791 for f in paths:
1792 if os.path.exists(f):
1792 if os.path.exists(f):
1793 break
1793 break
1794 else:
1794 else:
1795 if opts.get('global'):
1795 if opts.get('global'):
1796 samplehgrc = uimod.samplehgrcs['global']
1796 samplehgrc = uimod.samplehgrcs['global']
1797 elif opts.get('local'):
1797 elif opts.get('local'):
1798 samplehgrc = uimod.samplehgrcs['local']
1798 samplehgrc = uimod.samplehgrcs['local']
1799 else:
1799 else:
1800 samplehgrc = uimod.samplehgrcs['user']
1800 samplehgrc = uimod.samplehgrcs['user']
1801
1801
1802 f = paths[0]
1802 f = paths[0]
1803 fp = open(f, "w")
1803 fp = open(f, "w")
1804 fp.write(samplehgrc)
1804 fp.write(samplehgrc)
1805 fp.close()
1805 fp.close()
1806
1806
1807 editor = ui.geteditor()
1807 editor = ui.geteditor()
1808 ui.system("%s \"%s\"" % (editor, f),
1808 ui.system("%s \"%s\"" % (editor, f),
1809 onerr=error.Abort, errprefix=_("edit failed"),
1809 onerr=error.Abort, errprefix=_("edit failed"),
1810 blockedtag='config_edit')
1810 blockedtag='config_edit')
1811 return
1811 return
1812 ui.pager('config')
1812 ui.pager('config')
1813 fm = ui.formatter('config', opts)
1813 fm = ui.formatter('config', opts)
1814 for t, f in rcutil.rccomponents():
1814 for t, f in rcutil.rccomponents():
1815 if t == 'path':
1815 if t == 'path':
1816 ui.debug('read config from: %s\n' % f)
1816 ui.debug('read config from: %s\n' % f)
1817 elif t == 'items':
1817 elif t == 'items':
1818 for section, name, value, source in f:
1818 for section, name, value, source in f:
1819 ui.debug('set config by: %s\n' % source)
1819 ui.debug('set config by: %s\n' % source)
1820 else:
1820 else:
1821 raise error.ProgrammingError('unknown rctype: %s' % t)
1821 raise error.ProgrammingError('unknown rctype: %s' % t)
1822 untrusted = bool(opts.get('untrusted'))
1822 untrusted = bool(opts.get('untrusted'))
1823 if values:
1823 if values:
1824 sections = [v for v in values if '.' not in v]
1824 sections = [v for v in values if '.' not in v]
1825 items = [v for v in values if '.' in v]
1825 items = [v for v in values if '.' in v]
1826 if len(items) > 1 or items and sections:
1826 if len(items) > 1 or items and sections:
1827 raise error.Abort(_('only one config item permitted'))
1827 raise error.Abort(_('only one config item permitted'))
1828 matched = False
1828 matched = False
1829 for section, name, value in ui.walkconfig(untrusted=untrusted):
1829 for section, name, value in ui.walkconfig(untrusted=untrusted):
1830 source = ui.configsource(section, name, untrusted)
1830 source = ui.configsource(section, name, untrusted)
1831 value = pycompat.bytestr(value)
1831 value = pycompat.bytestr(value)
1832 if fm.isplain():
1832 if fm.isplain():
1833 source = source or 'none'
1833 source = source or 'none'
1834 value = value.replace('\n', '\\n')
1834 value = value.replace('\n', '\\n')
1835 entryname = section + '.' + name
1835 entryname = section + '.' + name
1836 if values:
1836 if values:
1837 for v in values:
1837 for v in values:
1838 if v == section:
1838 if v == section:
1839 fm.startitem()
1839 fm.startitem()
1840 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1840 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1841 fm.write('name value', '%s=%s\n', entryname, value)
1841 fm.write('name value', '%s=%s\n', entryname, value)
1842 matched = True
1842 matched = True
1843 elif v == entryname:
1843 elif v == entryname:
1844 fm.startitem()
1844 fm.startitem()
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1846 fm.write('value', '%s\n', value)
1846 fm.write('value', '%s\n', value)
1847 fm.data(name=entryname)
1847 fm.data(name=entryname)
1848 matched = True
1848 matched = True
1849 else:
1849 else:
1850 fm.startitem()
1850 fm.startitem()
1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1852 fm.write('name value', '%s=%s\n', entryname, value)
1852 fm.write('name value', '%s=%s\n', entryname, value)
1853 matched = True
1853 matched = True
1854 fm.end()
1854 fm.end()
1855 if matched:
1855 if matched:
1856 return 0
1856 return 0
1857 return 1
1857 return 1
1858
1858
1859 @command('copy|cp',
1859 @command('copy|cp',
1860 [('A', 'after', None, _('record a copy that has already occurred')),
1860 [('A', 'after', None, _('record a copy that has already occurred')),
1861 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1861 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1862 ] + walkopts + dryrunopts,
1862 ] + walkopts + dryrunopts,
1863 _('[OPTION]... [SOURCE]... DEST'))
1863 _('[OPTION]... [SOURCE]... DEST'))
1864 def copy(ui, repo, *pats, **opts):
1864 def copy(ui, repo, *pats, **opts):
1865 """mark files as copied for the next commit
1865 """mark files as copied for the next commit
1866
1866
1867 Mark dest as having copies of source files. If dest is a
1867 Mark dest as having copies of source files. If dest is a
1868 directory, copies are put in that directory. If dest is a file,
1868 directory, copies are put in that directory. If dest is a file,
1869 the source must be a single file.
1869 the source must be a single file.
1870
1870
1871 By default, this command copies the contents of files as they
1871 By default, this command copies the contents of files as they
1872 exist in the working directory. If invoked with -A/--after, the
1872 exist in the working directory. If invoked with -A/--after, the
1873 operation is recorded, but no copying is performed.
1873 operation is recorded, but no copying is performed.
1874
1874
1875 This command takes effect with the next commit. To undo a copy
1875 This command takes effect with the next commit. To undo a copy
1876 before that, see :hg:`revert`.
1876 before that, see :hg:`revert`.
1877
1877
1878 Returns 0 on success, 1 if errors are encountered.
1878 Returns 0 on success, 1 if errors are encountered.
1879 """
1879 """
1880 opts = pycompat.byteskwargs(opts)
1880 opts = pycompat.byteskwargs(opts)
1881 with repo.wlock(False):
1881 with repo.wlock(False):
1882 return cmdutil.copy(ui, repo, pats, opts)
1882 return cmdutil.copy(ui, repo, pats, opts)
1883
1883
1884 @command('^diff',
1884 @command('^diff',
1885 [('r', 'rev', [], _('revision'), _('REV')),
1885 [('r', 'rev', [], _('revision'), _('REV')),
1886 ('c', 'change', '', _('change made by revision'), _('REV'))
1886 ('c', 'change', '', _('change made by revision'), _('REV'))
1887 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1887 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1888 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1888 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1889 inferrepo=True)
1889 inferrepo=True)
1890 def diff(ui, repo, *pats, **opts):
1890 def diff(ui, repo, *pats, **opts):
1891 """diff repository (or selected files)
1891 """diff repository (or selected files)
1892
1892
1893 Show differences between revisions for the specified files.
1893 Show differences between revisions for the specified files.
1894
1894
1895 Differences between files are shown using the unified diff format.
1895 Differences between files are shown using the unified diff format.
1896
1896
1897 .. note::
1897 .. note::
1898
1898
1899 :hg:`diff` may generate unexpected results for merges, as it will
1899 :hg:`diff` may generate unexpected results for merges, as it will
1900 default to comparing against the working directory's first
1900 default to comparing against the working directory's first
1901 parent changeset if no revisions are specified.
1901 parent changeset if no revisions are specified.
1902
1902
1903 When two revision arguments are given, then changes are shown
1903 When two revision arguments are given, then changes are shown
1904 between those revisions. If only one revision is specified then
1904 between those revisions. If only one revision is specified then
1905 that revision is compared to the working directory, and, when no
1905 that revision is compared to the working directory, and, when no
1906 revisions are specified, the working directory files are compared
1906 revisions are specified, the working directory files are compared
1907 to its first parent.
1907 to its first parent.
1908
1908
1909 Alternatively you can specify -c/--change with a revision to see
1909 Alternatively you can specify -c/--change with a revision to see
1910 the changes in that changeset relative to its first parent.
1910 the changes in that changeset relative to its first parent.
1911
1911
1912 Without the -a/--text option, diff will avoid generating diffs of
1912 Without the -a/--text option, diff will avoid generating diffs of
1913 files it detects as binary. With -a, diff will generate a diff
1913 files it detects as binary. With -a, diff will generate a diff
1914 anyway, probably with undesirable results.
1914 anyway, probably with undesirable results.
1915
1915
1916 Use the -g/--git option to generate diffs in the git extended diff
1916 Use the -g/--git option to generate diffs in the git extended diff
1917 format. For more information, read :hg:`help diffs`.
1917 format. For more information, read :hg:`help diffs`.
1918
1918
1919 .. container:: verbose
1919 .. container:: verbose
1920
1920
1921 Examples:
1921 Examples:
1922
1922
1923 - compare a file in the current working directory to its parent::
1923 - compare a file in the current working directory to its parent::
1924
1924
1925 hg diff foo.c
1925 hg diff foo.c
1926
1926
1927 - compare two historical versions of a directory, with rename info::
1927 - compare two historical versions of a directory, with rename info::
1928
1928
1929 hg diff --git -r 1.0:1.2 lib/
1929 hg diff --git -r 1.0:1.2 lib/
1930
1930
1931 - get change stats relative to the last change on some date::
1931 - get change stats relative to the last change on some date::
1932
1932
1933 hg diff --stat -r "date('may 2')"
1933 hg diff --stat -r "date('may 2')"
1934
1934
1935 - diff all newly-added files that contain a keyword::
1935 - diff all newly-added files that contain a keyword::
1936
1936
1937 hg diff "set:added() and grep(GNU)"
1937 hg diff "set:added() and grep(GNU)"
1938
1938
1939 - compare a revision and its parents::
1939 - compare a revision and its parents::
1940
1940
1941 hg diff -c 9353 # compare against first parent
1941 hg diff -c 9353 # compare against first parent
1942 hg diff -r 9353^:9353 # same using revset syntax
1942 hg diff -r 9353^:9353 # same using revset syntax
1943 hg diff -r 9353^2:9353 # compare against the second parent
1943 hg diff -r 9353^2:9353 # compare against the second parent
1944
1944
1945 Returns 0 on success.
1945 Returns 0 on success.
1946 """
1946 """
1947
1947
1948 opts = pycompat.byteskwargs(opts)
1948 opts = pycompat.byteskwargs(opts)
1949 revs = opts.get('rev')
1949 revs = opts.get('rev')
1950 change = opts.get('change')
1950 change = opts.get('change')
1951 stat = opts.get('stat')
1951 stat = opts.get('stat')
1952 reverse = opts.get('reverse')
1952 reverse = opts.get('reverse')
1953
1953
1954 if revs and change:
1954 if revs and change:
1955 msg = _('cannot specify --rev and --change at the same time')
1955 msg = _('cannot specify --rev and --change at the same time')
1956 raise error.Abort(msg)
1956 raise error.Abort(msg)
1957 elif change:
1957 elif change:
1958 node2 = scmutil.revsingle(repo, change, None).node()
1958 node2 = scmutil.revsingle(repo, change, None).node()
1959 node1 = repo[node2].p1().node()
1959 node1 = repo[node2].p1().node()
1960 else:
1960 else:
1961 node1, node2 = scmutil.revpair(repo, revs)
1961 node1, node2 = scmutil.revpair(repo, revs)
1962
1962
1963 if reverse:
1963 if reverse:
1964 node1, node2 = node2, node1
1964 node1, node2 = node2, node1
1965
1965
1966 diffopts = patch.diffallopts(ui, opts)
1966 diffopts = patch.diffallopts(ui, opts)
1967 m = scmutil.match(repo[node2], pats, opts)
1967 m = scmutil.match(repo[node2], pats, opts)
1968 ui.pager('diff')
1968 ui.pager('diff')
1969 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1969 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1970 listsubrepos=opts.get('subrepos'),
1970 listsubrepos=opts.get('subrepos'),
1971 root=opts.get('root'))
1971 root=opts.get('root'))
1972
1972
1973 @command('^export',
1973 @command('^export',
1974 [('o', 'output', '',
1974 [('o', 'output', '',
1975 _('print output to file with formatted name'), _('FORMAT')),
1975 _('print output to file with formatted name'), _('FORMAT')),
1976 ('', 'switch-parent', None, _('diff against the second parent')),
1976 ('', 'switch-parent', None, _('diff against the second parent')),
1977 ('r', 'rev', [], _('revisions to export'), _('REV')),
1977 ('r', 'rev', [], _('revisions to export'), _('REV')),
1978 ] + diffopts,
1978 ] + diffopts,
1979 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1979 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1980 def export(ui, repo, *changesets, **opts):
1980 def export(ui, repo, *changesets, **opts):
1981 """dump the header and diffs for one or more changesets
1981 """dump the header and diffs for one or more changesets
1982
1982
1983 Print the changeset header and diffs for one or more revisions.
1983 Print the changeset header and diffs for one or more revisions.
1984 If no revision is given, the parent of the working directory is used.
1984 If no revision is given, the parent of the working directory is used.
1985
1985
1986 The information shown in the changeset header is: author, date,
1986 The information shown in the changeset header is: author, date,
1987 branch name (if non-default), changeset hash, parent(s) and commit
1987 branch name (if non-default), changeset hash, parent(s) and commit
1988 comment.
1988 comment.
1989
1989
1990 .. note::
1990 .. note::
1991
1991
1992 :hg:`export` may generate unexpected diff output for merge
1992 :hg:`export` may generate unexpected diff output for merge
1993 changesets, as it will compare the merge changeset against its
1993 changesets, as it will compare the merge changeset against its
1994 first parent only.
1994 first parent only.
1995
1995
1996 Output may be to a file, in which case the name of the file is
1996 Output may be to a file, in which case the name of the file is
1997 given using a format string. The formatting rules are as follows:
1997 given using a format string. The formatting rules are as follows:
1998
1998
1999 :``%%``: literal "%" character
1999 :``%%``: literal "%" character
2000 :``%H``: changeset hash (40 hexadecimal digits)
2000 :``%H``: changeset hash (40 hexadecimal digits)
2001 :``%N``: number of patches being generated
2001 :``%N``: number of patches being generated
2002 :``%R``: changeset revision number
2002 :``%R``: changeset revision number
2003 :``%b``: basename of the exporting repository
2003 :``%b``: basename of the exporting repository
2004 :``%h``: short-form changeset hash (12 hexadecimal digits)
2004 :``%h``: short-form changeset hash (12 hexadecimal digits)
2005 :``%m``: first line of the commit message (only alphanumeric characters)
2005 :``%m``: first line of the commit message (only alphanumeric characters)
2006 :``%n``: zero-padded sequence number, starting at 1
2006 :``%n``: zero-padded sequence number, starting at 1
2007 :``%r``: zero-padded changeset revision number
2007 :``%r``: zero-padded changeset revision number
2008
2008
2009 Without the -a/--text option, export will avoid generating diffs
2009 Without the -a/--text option, export will avoid generating diffs
2010 of files it detects as binary. With -a, export will generate a
2010 of files it detects as binary. With -a, export will generate a
2011 diff anyway, probably with undesirable results.
2011 diff anyway, probably with undesirable results.
2012
2012
2013 Use the -g/--git option to generate diffs in the git extended diff
2013 Use the -g/--git option to generate diffs in the git extended diff
2014 format. See :hg:`help diffs` for more information.
2014 format. See :hg:`help diffs` for more information.
2015
2015
2016 With the --switch-parent option, the diff will be against the
2016 With the --switch-parent option, the diff will be against the
2017 second parent. It can be useful to review a merge.
2017 second parent. It can be useful to review a merge.
2018
2018
2019 .. container:: verbose
2019 .. container:: verbose
2020
2020
2021 Examples:
2021 Examples:
2022
2022
2023 - use export and import to transplant a bugfix to the current
2023 - use export and import to transplant a bugfix to the current
2024 branch::
2024 branch::
2025
2025
2026 hg export -r 9353 | hg import -
2026 hg export -r 9353 | hg import -
2027
2027
2028 - export all the changesets between two revisions to a file with
2028 - export all the changesets between two revisions to a file with
2029 rename information::
2029 rename information::
2030
2030
2031 hg export --git -r 123:150 > changes.txt
2031 hg export --git -r 123:150 > changes.txt
2032
2032
2033 - split outgoing changes into a series of patches with
2033 - split outgoing changes into a series of patches with
2034 descriptive names::
2034 descriptive names::
2035
2035
2036 hg export -r "outgoing()" -o "%n-%m.patch"
2036 hg export -r "outgoing()" -o "%n-%m.patch"
2037
2037
2038 Returns 0 on success.
2038 Returns 0 on success.
2039 """
2039 """
2040 opts = pycompat.byteskwargs(opts)
2040 opts = pycompat.byteskwargs(opts)
2041 changesets += tuple(opts.get('rev', []))
2041 changesets += tuple(opts.get('rev', []))
2042 if not changesets:
2042 if not changesets:
2043 changesets = ['.']
2043 changesets = ['.']
2044 revs = scmutil.revrange(repo, changesets)
2044 revs = scmutil.revrange(repo, changesets)
2045 if not revs:
2045 if not revs:
2046 raise error.Abort(_("export requires at least one changeset"))
2046 raise error.Abort(_("export requires at least one changeset"))
2047 if len(revs) > 1:
2047 if len(revs) > 1:
2048 ui.note(_('exporting patches:\n'))
2048 ui.note(_('exporting patches:\n'))
2049 else:
2049 else:
2050 ui.note(_('exporting patch:\n'))
2050 ui.note(_('exporting patch:\n'))
2051 ui.pager('export')
2051 ui.pager('export')
2052 cmdutil.export(repo, revs, template=opts.get('output'),
2052 cmdutil.export(repo, revs, template=opts.get('output'),
2053 switch_parent=opts.get('switch_parent'),
2053 switch_parent=opts.get('switch_parent'),
2054 opts=patch.diffallopts(ui, opts))
2054 opts=patch.diffallopts(ui, opts))
2055
2055
2056 @command('files',
2056 @command('files',
2057 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2057 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2058 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2058 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2059 ] + walkopts + formatteropts + subrepoopts,
2059 ] + walkopts + formatteropts + subrepoopts,
2060 _('[OPTION]... [FILE]...'))
2060 _('[OPTION]... [FILE]...'))
2061 def files(ui, repo, *pats, **opts):
2061 def files(ui, repo, *pats, **opts):
2062 """list tracked files
2062 """list tracked files
2063
2063
2064 Print files under Mercurial control in the working directory or
2064 Print files under Mercurial control in the working directory or
2065 specified revision for given files (excluding removed files).
2065 specified revision for given files (excluding removed files).
2066 Files can be specified as filenames or filesets.
2066 Files can be specified as filenames or filesets.
2067
2067
2068 If no files are given to match, this command prints the names
2068 If no files are given to match, this command prints the names
2069 of all files under Mercurial control.
2069 of all files under Mercurial control.
2070
2070
2071 .. container:: verbose
2071 .. container:: verbose
2072
2072
2073 Examples:
2073 Examples:
2074
2074
2075 - list all files under the current directory::
2075 - list all files under the current directory::
2076
2076
2077 hg files .
2077 hg files .
2078
2078
2079 - shows sizes and flags for current revision::
2079 - shows sizes and flags for current revision::
2080
2080
2081 hg files -vr .
2081 hg files -vr .
2082
2082
2083 - list all files named README::
2083 - list all files named README::
2084
2084
2085 hg files -I "**/README"
2085 hg files -I "**/README"
2086
2086
2087 - list all binary files::
2087 - list all binary files::
2088
2088
2089 hg files "set:binary()"
2089 hg files "set:binary()"
2090
2090
2091 - find files containing a regular expression::
2091 - find files containing a regular expression::
2092
2092
2093 hg files "set:grep('bob')"
2093 hg files "set:grep('bob')"
2094
2094
2095 - search tracked file contents with xargs and grep::
2095 - search tracked file contents with xargs and grep::
2096
2096
2097 hg files -0 | xargs -0 grep foo
2097 hg files -0 | xargs -0 grep foo
2098
2098
2099 See :hg:`help patterns` and :hg:`help filesets` for more information
2099 See :hg:`help patterns` and :hg:`help filesets` for more information
2100 on specifying file patterns.
2100 on specifying file patterns.
2101
2101
2102 Returns 0 if a match is found, 1 otherwise.
2102 Returns 0 if a match is found, 1 otherwise.
2103
2103
2104 """
2104 """
2105
2105
2106 opts = pycompat.byteskwargs(opts)
2106 opts = pycompat.byteskwargs(opts)
2107 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2107 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2108
2108
2109 end = '\n'
2109 end = '\n'
2110 if opts.get('print0'):
2110 if opts.get('print0'):
2111 end = '\0'
2111 end = '\0'
2112 fmt = '%s' + end
2112 fmt = '%s' + end
2113
2113
2114 m = scmutil.match(ctx, pats, opts)
2114 m = scmutil.match(ctx, pats, opts)
2115 ui.pager('files')
2115 ui.pager('files')
2116 with ui.formatter('files', opts) as fm:
2116 with ui.formatter('files', opts) as fm:
2117 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2117 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2118
2118
2119 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2119 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2120 def forget(ui, repo, *pats, **opts):
2120 def forget(ui, repo, *pats, **opts):
2121 """forget the specified files on the next commit
2121 """forget the specified files on the next commit
2122
2122
2123 Mark the specified files so they will no longer be tracked
2123 Mark the specified files so they will no longer be tracked
2124 after the next commit.
2124 after the next commit.
2125
2125
2126 This only removes files from the current branch, not from the
2126 This only removes files from the current branch, not from the
2127 entire project history, and it does not delete them from the
2127 entire project history, and it does not delete them from the
2128 working directory.
2128 working directory.
2129
2129
2130 To delete the file from the working directory, see :hg:`remove`.
2130 To delete the file from the working directory, see :hg:`remove`.
2131
2131
2132 To undo a forget before the next commit, see :hg:`add`.
2132 To undo a forget before the next commit, see :hg:`add`.
2133
2133
2134 .. container:: verbose
2134 .. container:: verbose
2135
2135
2136 Examples:
2136 Examples:
2137
2137
2138 - forget newly-added binary files::
2138 - forget newly-added binary files::
2139
2139
2140 hg forget "set:added() and binary()"
2140 hg forget "set:added() and binary()"
2141
2141
2142 - forget files that would be excluded by .hgignore::
2142 - forget files that would be excluded by .hgignore::
2143
2143
2144 hg forget "set:hgignore()"
2144 hg forget "set:hgignore()"
2145
2145
2146 Returns 0 on success.
2146 Returns 0 on success.
2147 """
2147 """
2148
2148
2149 opts = pycompat.byteskwargs(opts)
2149 opts = pycompat.byteskwargs(opts)
2150 if not pats:
2150 if not pats:
2151 raise error.Abort(_('no files specified'))
2151 raise error.Abort(_('no files specified'))
2152
2152
2153 m = scmutil.match(repo[None], pats, opts)
2153 m = scmutil.match(repo[None], pats, opts)
2154 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2154 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2155 return rejected and 1 or 0
2155 return rejected and 1 or 0
2156
2156
2157 @command(
2157 @command(
2158 'graft',
2158 'graft',
2159 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2159 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2160 ('c', 'continue', False, _('resume interrupted graft')),
2160 ('c', 'continue', False, _('resume interrupted graft')),
2161 ('e', 'edit', False, _('invoke editor on commit messages')),
2161 ('e', 'edit', False, _('invoke editor on commit messages')),
2162 ('', 'log', None, _('append graft info to log message')),
2162 ('', 'log', None, _('append graft info to log message')),
2163 ('f', 'force', False, _('force graft')),
2163 ('f', 'force', False, _('force graft')),
2164 ('D', 'currentdate', False,
2164 ('D', 'currentdate', False,
2165 _('record the current date as commit date')),
2165 _('record the current date as commit date')),
2166 ('U', 'currentuser', False,
2166 ('U', 'currentuser', False,
2167 _('record the current user as committer'), _('DATE'))]
2167 _('record the current user as committer'), _('DATE'))]
2168 + commitopts2 + mergetoolopts + dryrunopts,
2168 + commitopts2 + mergetoolopts + dryrunopts,
2169 _('[OPTION]... [-r REV]... REV...'))
2169 _('[OPTION]... [-r REV]... REV...'))
2170 def graft(ui, repo, *revs, **opts):
2170 def graft(ui, repo, *revs, **opts):
2171 '''copy changes from other branches onto the current branch
2171 '''copy changes from other branches onto the current branch
2172
2172
2173 This command uses Mercurial's merge logic to copy individual
2173 This command uses Mercurial's merge logic to copy individual
2174 changes from other branches without merging branches in the
2174 changes from other branches without merging branches in the
2175 history graph. This is sometimes known as 'backporting' or
2175 history graph. This is sometimes known as 'backporting' or
2176 'cherry-picking'. By default, graft will copy user, date, and
2176 'cherry-picking'. By default, graft will copy user, date, and
2177 description from the source changesets.
2177 description from the source changesets.
2178
2178
2179 Changesets that are ancestors of the current revision, that have
2179 Changesets that are ancestors of the current revision, that have
2180 already been grafted, or that are merges will be skipped.
2180 already been grafted, or that are merges will be skipped.
2181
2181
2182 If --log is specified, log messages will have a comment appended
2182 If --log is specified, log messages will have a comment appended
2183 of the form::
2183 of the form::
2184
2184
2185 (grafted from CHANGESETHASH)
2185 (grafted from CHANGESETHASH)
2186
2186
2187 If --force is specified, revisions will be grafted even if they
2187 If --force is specified, revisions will be grafted even if they
2188 are already ancestors of or have been grafted to the destination.
2188 are already ancestors of or have been grafted to the destination.
2189 This is useful when the revisions have since been backed out.
2189 This is useful when the revisions have since been backed out.
2190
2190
2191 If a graft merge results in conflicts, the graft process is
2191 If a graft merge results in conflicts, the graft process is
2192 interrupted so that the current merge can be manually resolved.
2192 interrupted so that the current merge can be manually resolved.
2193 Once all conflicts are addressed, the graft process can be
2193 Once all conflicts are addressed, the graft process can be
2194 continued with the -c/--continue option.
2194 continued with the -c/--continue option.
2195
2195
2196 .. note::
2196 .. note::
2197
2197
2198 The -c/--continue option does not reapply earlier options, except
2198 The -c/--continue option does not reapply earlier options, except
2199 for --force.
2199 for --force.
2200
2200
2201 .. container:: verbose
2201 .. container:: verbose
2202
2202
2203 Examples:
2203 Examples:
2204
2204
2205 - copy a single change to the stable branch and edit its description::
2205 - copy a single change to the stable branch and edit its description::
2206
2206
2207 hg update stable
2207 hg update stable
2208 hg graft --edit 9393
2208 hg graft --edit 9393
2209
2209
2210 - graft a range of changesets with one exception, updating dates::
2210 - graft a range of changesets with one exception, updating dates::
2211
2211
2212 hg graft -D "2085::2093 and not 2091"
2212 hg graft -D "2085::2093 and not 2091"
2213
2213
2214 - continue a graft after resolving conflicts::
2214 - continue a graft after resolving conflicts::
2215
2215
2216 hg graft -c
2216 hg graft -c
2217
2217
2218 - show the source of a grafted changeset::
2218 - show the source of a grafted changeset::
2219
2219
2220 hg log --debug -r .
2220 hg log --debug -r .
2221
2221
2222 - show revisions sorted by date::
2222 - show revisions sorted by date::
2223
2223
2224 hg log -r "sort(all(), date)"
2224 hg log -r "sort(all(), date)"
2225
2225
2226 See :hg:`help revisions` for more about specifying revisions.
2226 See :hg:`help revisions` for more about specifying revisions.
2227
2227
2228 Returns 0 on successful completion.
2228 Returns 0 on successful completion.
2229 '''
2229 '''
2230 with repo.wlock():
2230 with repo.wlock():
2231 return _dograft(ui, repo, *revs, **opts)
2231 return _dograft(ui, repo, *revs, **opts)
2232
2232
2233 def _dograft(ui, repo, *revs, **opts):
2233 def _dograft(ui, repo, *revs, **opts):
2234 opts = pycompat.byteskwargs(opts)
2234 opts = pycompat.byteskwargs(opts)
2235 if revs and opts.get('rev'):
2235 if revs and opts.get('rev'):
2236 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2236 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2237 'revision ordering!\n'))
2237 'revision ordering!\n'))
2238
2238
2239 revs = list(revs)
2239 revs = list(revs)
2240 revs.extend(opts.get('rev'))
2240 revs.extend(opts.get('rev'))
2241
2241
2242 if not opts.get('user') and opts.get('currentuser'):
2242 if not opts.get('user') and opts.get('currentuser'):
2243 opts['user'] = ui.username()
2243 opts['user'] = ui.username()
2244 if not opts.get('date') and opts.get('currentdate'):
2244 if not opts.get('date') and opts.get('currentdate'):
2245 opts['date'] = "%d %d" % util.makedate()
2245 opts['date'] = "%d %d" % util.makedate()
2246
2246
2247 editor = cmdutil.getcommiteditor(editform='graft',
2247 editor = cmdutil.getcommiteditor(editform='graft',
2248 **pycompat.strkwargs(opts))
2248 **pycompat.strkwargs(opts))
2249
2249
2250 cont = False
2250 cont = False
2251 if opts.get('continue'):
2251 if opts.get('continue'):
2252 cont = True
2252 cont = True
2253 if revs:
2253 if revs:
2254 raise error.Abort(_("can't specify --continue and revisions"))
2254 raise error.Abort(_("can't specify --continue and revisions"))
2255 # read in unfinished revisions
2255 # read in unfinished revisions
2256 try:
2256 try:
2257 nodes = repo.vfs.read('graftstate').splitlines()
2257 nodes = repo.vfs.read('graftstate').splitlines()
2258 revs = [repo[node].rev() for node in nodes]
2258 revs = [repo[node].rev() for node in nodes]
2259 except IOError as inst:
2259 except IOError as inst:
2260 if inst.errno != errno.ENOENT:
2260 if inst.errno != errno.ENOENT:
2261 raise
2261 raise
2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2263 else:
2263 else:
2264 cmdutil.checkunfinished(repo)
2264 cmdutil.checkunfinished(repo)
2265 cmdutil.bailifchanged(repo)
2265 cmdutil.bailifchanged(repo)
2266 if not revs:
2266 if not revs:
2267 raise error.Abort(_('no revisions specified'))
2267 raise error.Abort(_('no revisions specified'))
2268 revs = scmutil.revrange(repo, revs)
2268 revs = scmutil.revrange(repo, revs)
2269
2269
2270 skipped = set()
2270 skipped = set()
2271 # check for merges
2271 # check for merges
2272 for rev in repo.revs('%ld and merge()', revs):
2272 for rev in repo.revs('%ld and merge()', revs):
2273 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2273 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2274 skipped.add(rev)
2274 skipped.add(rev)
2275 revs = [r for r in revs if r not in skipped]
2275 revs = [r for r in revs if r not in skipped]
2276 if not revs:
2276 if not revs:
2277 return -1
2277 return -1
2278
2278
2279 # Don't check in the --continue case, in effect retaining --force across
2279 # Don't check in the --continue case, in effect retaining --force across
2280 # --continues. That's because without --force, any revisions we decided to
2280 # --continues. That's because without --force, any revisions we decided to
2281 # skip would have been filtered out here, so they wouldn't have made their
2281 # skip would have been filtered out here, so they wouldn't have made their
2282 # way to the graftstate. With --force, any revisions we would have otherwise
2282 # way to the graftstate. With --force, any revisions we would have otherwise
2283 # skipped would not have been filtered out, and if they hadn't been applied
2283 # skipped would not have been filtered out, and if they hadn't been applied
2284 # already, they'd have been in the graftstate.
2284 # already, they'd have been in the graftstate.
2285 if not (cont or opts.get('force')):
2285 if not (cont or opts.get('force')):
2286 # check for ancestors of dest branch
2286 # check for ancestors of dest branch
2287 crev = repo['.'].rev()
2287 crev = repo['.'].rev()
2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2289 # XXX make this lazy in the future
2289 # XXX make this lazy in the future
2290 # don't mutate while iterating, create a copy
2290 # don't mutate while iterating, create a copy
2291 for rev in list(revs):
2291 for rev in list(revs):
2292 if rev in ancestors:
2292 if rev in ancestors:
2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2294 (rev, repo[rev]))
2294 (rev, repo[rev]))
2295 # XXX remove on list is slow
2295 # XXX remove on list is slow
2296 revs.remove(rev)
2296 revs.remove(rev)
2297 if not revs:
2297 if not revs:
2298 return -1
2298 return -1
2299
2299
2300 # analyze revs for earlier grafts
2300 # analyze revs for earlier grafts
2301 ids = {}
2301 ids = {}
2302 for ctx in repo.set("%ld", revs):
2302 for ctx in repo.set("%ld", revs):
2303 ids[ctx.hex()] = ctx.rev()
2303 ids[ctx.hex()] = ctx.rev()
2304 n = ctx.extra().get('source')
2304 n = ctx.extra().get('source')
2305 if n:
2305 if n:
2306 ids[n] = ctx.rev()
2306 ids[n] = ctx.rev()
2307
2307
2308 # check ancestors for earlier grafts
2308 # check ancestors for earlier grafts
2309 ui.debug('scanning for duplicate grafts\n')
2309 ui.debug('scanning for duplicate grafts\n')
2310
2310
2311 for rev in repo.changelog.findmissingrevs(revs, [crev]):
2311 # The only changesets we can be sure doesn't contain grafts of any
2312 # revs, are the ones that are common ancestors of *all* revs:
2313 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2312 ctx = repo[rev]
2314 ctx = repo[rev]
2313 n = ctx.extra().get('source')
2315 n = ctx.extra().get('source')
2314 if n in ids:
2316 if n in ids:
2315 try:
2317 try:
2316 r = repo[n].rev()
2318 r = repo[n].rev()
2317 except error.RepoLookupError:
2319 except error.RepoLookupError:
2318 r = None
2320 r = None
2319 if r in revs:
2321 if r in revs:
2320 ui.warn(_('skipping revision %d:%s '
2322 ui.warn(_('skipping revision %d:%s '
2321 '(already grafted to %d:%s)\n')
2323 '(already grafted to %d:%s)\n')
2322 % (r, repo[r], rev, ctx))
2324 % (r, repo[r], rev, ctx))
2323 revs.remove(r)
2325 revs.remove(r)
2324 elif ids[n] in revs:
2326 elif ids[n] in revs:
2325 if r is None:
2327 if r is None:
2326 ui.warn(_('skipping already grafted revision %d:%s '
2328 ui.warn(_('skipping already grafted revision %d:%s '
2327 '(%d:%s also has unknown origin %s)\n')
2329 '(%d:%s also has unknown origin %s)\n')
2328 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2330 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2329 else:
2331 else:
2330 ui.warn(_('skipping already grafted revision %d:%s '
2332 ui.warn(_('skipping already grafted revision %d:%s '
2331 '(%d:%s also has origin %d:%s)\n')
2333 '(%d:%s also has origin %d:%s)\n')
2332 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2334 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2333 revs.remove(ids[n])
2335 revs.remove(ids[n])
2334 elif ctx.hex() in ids:
2336 elif ctx.hex() in ids:
2335 r = ids[ctx.hex()]
2337 r = ids[ctx.hex()]
2336 ui.warn(_('skipping already grafted revision %d:%s '
2338 ui.warn(_('skipping already grafted revision %d:%s '
2337 '(was grafted from %d:%s)\n') %
2339 '(was grafted from %d:%s)\n') %
2338 (r, repo[r], rev, ctx))
2340 (r, repo[r], rev, ctx))
2339 revs.remove(r)
2341 revs.remove(r)
2340 if not revs:
2342 if not revs:
2341 return -1
2343 return -1
2342
2344
2343 for pos, ctx in enumerate(repo.set("%ld", revs)):
2345 for pos, ctx in enumerate(repo.set("%ld", revs)):
2344 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2346 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2345 ctx.description().split('\n', 1)[0])
2347 ctx.description().split('\n', 1)[0])
2346 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2348 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2347 if names:
2349 if names:
2348 desc += ' (%s)' % ' '.join(names)
2350 desc += ' (%s)' % ' '.join(names)
2349 ui.status(_('grafting %s\n') % desc)
2351 ui.status(_('grafting %s\n') % desc)
2350 if opts.get('dry_run'):
2352 if opts.get('dry_run'):
2351 continue
2353 continue
2352
2354
2353 source = ctx.extra().get('source')
2355 source = ctx.extra().get('source')
2354 extra = {}
2356 extra = {}
2355 if source:
2357 if source:
2356 extra['source'] = source
2358 extra['source'] = source
2357 extra['intermediate-source'] = ctx.hex()
2359 extra['intermediate-source'] = ctx.hex()
2358 else:
2360 else:
2359 extra['source'] = ctx.hex()
2361 extra['source'] = ctx.hex()
2360 user = ctx.user()
2362 user = ctx.user()
2361 if opts.get('user'):
2363 if opts.get('user'):
2362 user = opts['user']
2364 user = opts['user']
2363 date = ctx.date()
2365 date = ctx.date()
2364 if opts.get('date'):
2366 if opts.get('date'):
2365 date = opts['date']
2367 date = opts['date']
2366 message = ctx.description()
2368 message = ctx.description()
2367 if opts.get('log'):
2369 if opts.get('log'):
2368 message += '\n(grafted from %s)' % ctx.hex()
2370 message += '\n(grafted from %s)' % ctx.hex()
2369
2371
2370 # we don't merge the first commit when continuing
2372 # we don't merge the first commit when continuing
2371 if not cont:
2373 if not cont:
2372 # perform the graft merge with p1(rev) as 'ancestor'
2374 # perform the graft merge with p1(rev) as 'ancestor'
2373 try:
2375 try:
2374 # ui.forcemerge is an internal variable, do not document
2376 # ui.forcemerge is an internal variable, do not document
2375 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2377 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2376 'graft')
2378 'graft')
2377 stats = mergemod.graft(repo, ctx, ctx.p1(),
2379 stats = mergemod.graft(repo, ctx, ctx.p1(),
2378 ['local', 'graft'])
2380 ['local', 'graft'])
2379 finally:
2381 finally:
2380 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2382 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2381 # report any conflicts
2383 # report any conflicts
2382 if stats and stats[3] > 0:
2384 if stats and stats[3] > 0:
2383 # write out state for --continue
2385 # write out state for --continue
2384 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2386 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2385 repo.vfs.write('graftstate', ''.join(nodelines))
2387 repo.vfs.write('graftstate', ''.join(nodelines))
2386 extra = ''
2388 extra = ''
2387 if opts.get('user'):
2389 if opts.get('user'):
2388 extra += ' --user %s' % util.shellquote(opts['user'])
2390 extra += ' --user %s' % util.shellquote(opts['user'])
2389 if opts.get('date'):
2391 if opts.get('date'):
2390 extra += ' --date %s' % util.shellquote(opts['date'])
2392 extra += ' --date %s' % util.shellquote(opts['date'])
2391 if opts.get('log'):
2393 if opts.get('log'):
2392 extra += ' --log'
2394 extra += ' --log'
2393 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2395 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2394 raise error.Abort(
2396 raise error.Abort(
2395 _("unresolved conflicts, can't continue"),
2397 _("unresolved conflicts, can't continue"),
2396 hint=hint)
2398 hint=hint)
2397 else:
2399 else:
2398 cont = False
2400 cont = False
2399
2401
2400 # commit
2402 # commit
2401 node = repo.commit(text=message, user=user,
2403 node = repo.commit(text=message, user=user,
2402 date=date, extra=extra, editor=editor)
2404 date=date, extra=extra, editor=editor)
2403 if node is None:
2405 if node is None:
2404 ui.warn(
2406 ui.warn(
2405 _('note: graft of %d:%s created no changes to commit\n') %
2407 _('note: graft of %d:%s created no changes to commit\n') %
2406 (ctx.rev(), ctx))
2408 (ctx.rev(), ctx))
2407
2409
2408 # remove state when we complete successfully
2410 # remove state when we complete successfully
2409 if not opts.get('dry_run'):
2411 if not opts.get('dry_run'):
2410 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2412 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2411
2413
2412 return 0
2414 return 0
2413
2415
2414 @command('grep',
2416 @command('grep',
2415 [('0', 'print0', None, _('end fields with NUL')),
2417 [('0', 'print0', None, _('end fields with NUL')),
2416 ('', 'all', None, _('print all revisions that match')),
2418 ('', 'all', None, _('print all revisions that match')),
2417 ('a', 'text', None, _('treat all files as text')),
2419 ('a', 'text', None, _('treat all files as text')),
2418 ('f', 'follow', None,
2420 ('f', 'follow', None,
2419 _('follow changeset history,'
2421 _('follow changeset history,'
2420 ' or file history across copies and renames')),
2422 ' or file history across copies and renames')),
2421 ('i', 'ignore-case', None, _('ignore case when matching')),
2423 ('i', 'ignore-case', None, _('ignore case when matching')),
2422 ('l', 'files-with-matches', None,
2424 ('l', 'files-with-matches', None,
2423 _('print only filenames and revisions that match')),
2425 _('print only filenames and revisions that match')),
2424 ('n', 'line-number', None, _('print matching line numbers')),
2426 ('n', 'line-number', None, _('print matching line numbers')),
2425 ('r', 'rev', [],
2427 ('r', 'rev', [],
2426 _('only search files changed within revision range'), _('REV')),
2428 _('only search files changed within revision range'), _('REV')),
2427 ('u', 'user', None, _('list the author (long with -v)')),
2429 ('u', 'user', None, _('list the author (long with -v)')),
2428 ('d', 'date', None, _('list the date (short with -q)')),
2430 ('d', 'date', None, _('list the date (short with -q)')),
2429 ] + formatteropts + walkopts,
2431 ] + formatteropts + walkopts,
2430 _('[OPTION]... PATTERN [FILE]...'),
2432 _('[OPTION]... PATTERN [FILE]...'),
2431 inferrepo=True)
2433 inferrepo=True)
2432 def grep(ui, repo, pattern, *pats, **opts):
2434 def grep(ui, repo, pattern, *pats, **opts):
2433 """search revision history for a pattern in specified files
2435 """search revision history for a pattern in specified files
2434
2436
2435 Search revision history for a regular expression in the specified
2437 Search revision history for a regular expression in the specified
2436 files or the entire project.
2438 files or the entire project.
2437
2439
2438 By default, grep prints the most recent revision number for each
2440 By default, grep prints the most recent revision number for each
2439 file in which it finds a match. To get it to print every revision
2441 file in which it finds a match. To get it to print every revision
2440 that contains a change in match status ("-" for a match that becomes
2442 that contains a change in match status ("-" for a match that becomes
2441 a non-match, or "+" for a non-match that becomes a match), use the
2443 a non-match, or "+" for a non-match that becomes a match), use the
2442 --all flag.
2444 --all flag.
2443
2445
2444 PATTERN can be any Python (roughly Perl-compatible) regular
2446 PATTERN can be any Python (roughly Perl-compatible) regular
2445 expression.
2447 expression.
2446
2448
2447 If no FILEs are specified (and -f/--follow isn't set), all files in
2449 If no FILEs are specified (and -f/--follow isn't set), all files in
2448 the repository are searched, including those that don't exist in the
2450 the repository are searched, including those that don't exist in the
2449 current branch or have been deleted in a prior changeset.
2451 current branch or have been deleted in a prior changeset.
2450
2452
2451 Returns 0 if a match is found, 1 otherwise.
2453 Returns 0 if a match is found, 1 otherwise.
2452 """
2454 """
2453 opts = pycompat.byteskwargs(opts)
2455 opts = pycompat.byteskwargs(opts)
2454 reflags = re.M
2456 reflags = re.M
2455 if opts.get('ignore_case'):
2457 if opts.get('ignore_case'):
2456 reflags |= re.I
2458 reflags |= re.I
2457 try:
2459 try:
2458 regexp = util.re.compile(pattern, reflags)
2460 regexp = util.re.compile(pattern, reflags)
2459 except re.error as inst:
2461 except re.error as inst:
2460 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2462 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2461 return 1
2463 return 1
2462 sep, eol = ':', '\n'
2464 sep, eol = ':', '\n'
2463 if opts.get('print0'):
2465 if opts.get('print0'):
2464 sep = eol = '\0'
2466 sep = eol = '\0'
2465
2467
2466 getfile = util.lrucachefunc(repo.file)
2468 getfile = util.lrucachefunc(repo.file)
2467
2469
2468 def matchlines(body):
2470 def matchlines(body):
2469 begin = 0
2471 begin = 0
2470 linenum = 0
2472 linenum = 0
2471 while begin < len(body):
2473 while begin < len(body):
2472 match = regexp.search(body, begin)
2474 match = regexp.search(body, begin)
2473 if not match:
2475 if not match:
2474 break
2476 break
2475 mstart, mend = match.span()
2477 mstart, mend = match.span()
2476 linenum += body.count('\n', begin, mstart) + 1
2478 linenum += body.count('\n', begin, mstart) + 1
2477 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2479 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2478 begin = body.find('\n', mend) + 1 or len(body) + 1
2480 begin = body.find('\n', mend) + 1 or len(body) + 1
2479 lend = begin - 1
2481 lend = begin - 1
2480 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2482 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2481
2483
2482 class linestate(object):
2484 class linestate(object):
2483 def __init__(self, line, linenum, colstart, colend):
2485 def __init__(self, line, linenum, colstart, colend):
2484 self.line = line
2486 self.line = line
2485 self.linenum = linenum
2487 self.linenum = linenum
2486 self.colstart = colstart
2488 self.colstart = colstart
2487 self.colend = colend
2489 self.colend = colend
2488
2490
2489 def __hash__(self):
2491 def __hash__(self):
2490 return hash((self.linenum, self.line))
2492 return hash((self.linenum, self.line))
2491
2493
2492 def __eq__(self, other):
2494 def __eq__(self, other):
2493 return self.line == other.line
2495 return self.line == other.line
2494
2496
2495 def findpos(self):
2497 def findpos(self):
2496 """Iterate all (start, end) indices of matches"""
2498 """Iterate all (start, end) indices of matches"""
2497 yield self.colstart, self.colend
2499 yield self.colstart, self.colend
2498 p = self.colend
2500 p = self.colend
2499 while p < len(self.line):
2501 while p < len(self.line):
2500 m = regexp.search(self.line, p)
2502 m = regexp.search(self.line, p)
2501 if not m:
2503 if not m:
2502 break
2504 break
2503 yield m.span()
2505 yield m.span()
2504 p = m.end()
2506 p = m.end()
2505
2507
2506 matches = {}
2508 matches = {}
2507 copies = {}
2509 copies = {}
2508 def grepbody(fn, rev, body):
2510 def grepbody(fn, rev, body):
2509 matches[rev].setdefault(fn, [])
2511 matches[rev].setdefault(fn, [])
2510 m = matches[rev][fn]
2512 m = matches[rev][fn]
2511 for lnum, cstart, cend, line in matchlines(body):
2513 for lnum, cstart, cend, line in matchlines(body):
2512 s = linestate(line, lnum, cstart, cend)
2514 s = linestate(line, lnum, cstart, cend)
2513 m.append(s)
2515 m.append(s)
2514
2516
2515 def difflinestates(a, b):
2517 def difflinestates(a, b):
2516 sm = difflib.SequenceMatcher(None, a, b)
2518 sm = difflib.SequenceMatcher(None, a, b)
2517 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2519 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2518 if tag == 'insert':
2520 if tag == 'insert':
2519 for i in xrange(blo, bhi):
2521 for i in xrange(blo, bhi):
2520 yield ('+', b[i])
2522 yield ('+', b[i])
2521 elif tag == 'delete':
2523 elif tag == 'delete':
2522 for i in xrange(alo, ahi):
2524 for i in xrange(alo, ahi):
2523 yield ('-', a[i])
2525 yield ('-', a[i])
2524 elif tag == 'replace':
2526 elif tag == 'replace':
2525 for i in xrange(alo, ahi):
2527 for i in xrange(alo, ahi):
2526 yield ('-', a[i])
2528 yield ('-', a[i])
2527 for i in xrange(blo, bhi):
2529 for i in xrange(blo, bhi):
2528 yield ('+', b[i])
2530 yield ('+', b[i])
2529
2531
2530 def display(fm, fn, ctx, pstates, states):
2532 def display(fm, fn, ctx, pstates, states):
2531 rev = ctx.rev()
2533 rev = ctx.rev()
2532 if fm.isplain():
2534 if fm.isplain():
2533 formatuser = ui.shortuser
2535 formatuser = ui.shortuser
2534 else:
2536 else:
2535 formatuser = str
2537 formatuser = str
2536 if ui.quiet:
2538 if ui.quiet:
2537 datefmt = '%Y-%m-%d'
2539 datefmt = '%Y-%m-%d'
2538 else:
2540 else:
2539 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2541 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2540 found = False
2542 found = False
2541 @util.cachefunc
2543 @util.cachefunc
2542 def binary():
2544 def binary():
2543 flog = getfile(fn)
2545 flog = getfile(fn)
2544 return util.binary(flog.read(ctx.filenode(fn)))
2546 return util.binary(flog.read(ctx.filenode(fn)))
2545
2547
2546 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2548 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2547 if opts.get('all'):
2549 if opts.get('all'):
2548 iter = difflinestates(pstates, states)
2550 iter = difflinestates(pstates, states)
2549 else:
2551 else:
2550 iter = [('', l) for l in states]
2552 iter = [('', l) for l in states]
2551 for change, l in iter:
2553 for change, l in iter:
2552 fm.startitem()
2554 fm.startitem()
2553 fm.data(node=fm.hexfunc(ctx.node()))
2555 fm.data(node=fm.hexfunc(ctx.node()))
2554 cols = [
2556 cols = [
2555 ('filename', fn, True),
2557 ('filename', fn, True),
2556 ('rev', rev, True),
2558 ('rev', rev, True),
2557 ('linenumber', l.linenum, opts.get('line_number')),
2559 ('linenumber', l.linenum, opts.get('line_number')),
2558 ]
2560 ]
2559 if opts.get('all'):
2561 if opts.get('all'):
2560 cols.append(('change', change, True))
2562 cols.append(('change', change, True))
2561 cols.extend([
2563 cols.extend([
2562 ('user', formatuser(ctx.user()), opts.get('user')),
2564 ('user', formatuser(ctx.user()), opts.get('user')),
2563 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2565 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2564 ])
2566 ])
2565 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2567 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2566 for name, data, cond in cols:
2568 for name, data, cond in cols:
2567 field = fieldnamemap.get(name, name)
2569 field = fieldnamemap.get(name, name)
2568 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2570 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2569 if cond and name != lastcol:
2571 if cond and name != lastcol:
2570 fm.plain(sep, label='grep.sep')
2572 fm.plain(sep, label='grep.sep')
2571 if not opts.get('files_with_matches'):
2573 if not opts.get('files_with_matches'):
2572 fm.plain(sep, label='grep.sep')
2574 fm.plain(sep, label='grep.sep')
2573 if not opts.get('text') and binary():
2575 if not opts.get('text') and binary():
2574 fm.plain(_(" Binary file matches"))
2576 fm.plain(_(" Binary file matches"))
2575 else:
2577 else:
2576 displaymatches(fm.nested('texts'), l)
2578 displaymatches(fm.nested('texts'), l)
2577 fm.plain(eol)
2579 fm.plain(eol)
2578 found = True
2580 found = True
2579 if opts.get('files_with_matches'):
2581 if opts.get('files_with_matches'):
2580 break
2582 break
2581 return found
2583 return found
2582
2584
2583 def displaymatches(fm, l):
2585 def displaymatches(fm, l):
2584 p = 0
2586 p = 0
2585 for s, e in l.findpos():
2587 for s, e in l.findpos():
2586 if p < s:
2588 if p < s:
2587 fm.startitem()
2589 fm.startitem()
2588 fm.write('text', '%s', l.line[p:s])
2590 fm.write('text', '%s', l.line[p:s])
2589 fm.data(matched=False)
2591 fm.data(matched=False)
2590 fm.startitem()
2592 fm.startitem()
2591 fm.write('text', '%s', l.line[s:e], label='grep.match')
2593 fm.write('text', '%s', l.line[s:e], label='grep.match')
2592 fm.data(matched=True)
2594 fm.data(matched=True)
2593 p = e
2595 p = e
2594 if p < len(l.line):
2596 if p < len(l.line):
2595 fm.startitem()
2597 fm.startitem()
2596 fm.write('text', '%s', l.line[p:])
2598 fm.write('text', '%s', l.line[p:])
2597 fm.data(matched=False)
2599 fm.data(matched=False)
2598 fm.end()
2600 fm.end()
2599
2601
2600 skip = {}
2602 skip = {}
2601 revfiles = {}
2603 revfiles = {}
2602 matchfn = scmutil.match(repo[None], pats, opts)
2604 matchfn = scmutil.match(repo[None], pats, opts)
2603 found = False
2605 found = False
2604 follow = opts.get('follow')
2606 follow = opts.get('follow')
2605
2607
2606 def prep(ctx, fns):
2608 def prep(ctx, fns):
2607 rev = ctx.rev()
2609 rev = ctx.rev()
2608 pctx = ctx.p1()
2610 pctx = ctx.p1()
2609 parent = pctx.rev()
2611 parent = pctx.rev()
2610 matches.setdefault(rev, {})
2612 matches.setdefault(rev, {})
2611 matches.setdefault(parent, {})
2613 matches.setdefault(parent, {})
2612 files = revfiles.setdefault(rev, [])
2614 files = revfiles.setdefault(rev, [])
2613 for fn in fns:
2615 for fn in fns:
2614 flog = getfile(fn)
2616 flog = getfile(fn)
2615 try:
2617 try:
2616 fnode = ctx.filenode(fn)
2618 fnode = ctx.filenode(fn)
2617 except error.LookupError:
2619 except error.LookupError:
2618 continue
2620 continue
2619
2621
2620 copied = flog.renamed(fnode)
2622 copied = flog.renamed(fnode)
2621 copy = follow and copied and copied[0]
2623 copy = follow and copied and copied[0]
2622 if copy:
2624 if copy:
2623 copies.setdefault(rev, {})[fn] = copy
2625 copies.setdefault(rev, {})[fn] = copy
2624 if fn in skip:
2626 if fn in skip:
2625 if copy:
2627 if copy:
2626 skip[copy] = True
2628 skip[copy] = True
2627 continue
2629 continue
2628 files.append(fn)
2630 files.append(fn)
2629
2631
2630 if fn not in matches[rev]:
2632 if fn not in matches[rev]:
2631 grepbody(fn, rev, flog.read(fnode))
2633 grepbody(fn, rev, flog.read(fnode))
2632
2634
2633 pfn = copy or fn
2635 pfn = copy or fn
2634 if pfn not in matches[parent]:
2636 if pfn not in matches[parent]:
2635 try:
2637 try:
2636 fnode = pctx.filenode(pfn)
2638 fnode = pctx.filenode(pfn)
2637 grepbody(pfn, parent, flog.read(fnode))
2639 grepbody(pfn, parent, flog.read(fnode))
2638 except error.LookupError:
2640 except error.LookupError:
2639 pass
2641 pass
2640
2642
2641 ui.pager('grep')
2643 ui.pager('grep')
2642 fm = ui.formatter('grep', opts)
2644 fm = ui.formatter('grep', opts)
2643 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2645 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2644 rev = ctx.rev()
2646 rev = ctx.rev()
2645 parent = ctx.p1().rev()
2647 parent = ctx.p1().rev()
2646 for fn in sorted(revfiles.get(rev, [])):
2648 for fn in sorted(revfiles.get(rev, [])):
2647 states = matches[rev][fn]
2649 states = matches[rev][fn]
2648 copy = copies.get(rev, {}).get(fn)
2650 copy = copies.get(rev, {}).get(fn)
2649 if fn in skip:
2651 if fn in skip:
2650 if copy:
2652 if copy:
2651 skip[copy] = True
2653 skip[copy] = True
2652 continue
2654 continue
2653 pstates = matches.get(parent, {}).get(copy or fn, [])
2655 pstates = matches.get(parent, {}).get(copy or fn, [])
2654 if pstates or states:
2656 if pstates or states:
2655 r = display(fm, fn, ctx, pstates, states)
2657 r = display(fm, fn, ctx, pstates, states)
2656 found = found or r
2658 found = found or r
2657 if r and not opts.get('all'):
2659 if r and not opts.get('all'):
2658 skip[fn] = True
2660 skip[fn] = True
2659 if copy:
2661 if copy:
2660 skip[copy] = True
2662 skip[copy] = True
2661 del matches[rev]
2663 del matches[rev]
2662 del revfiles[rev]
2664 del revfiles[rev]
2663 fm.end()
2665 fm.end()
2664
2666
2665 return not found
2667 return not found
2666
2668
2667 @command('heads',
2669 @command('heads',
2668 [('r', 'rev', '',
2670 [('r', 'rev', '',
2669 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2671 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2670 ('t', 'topo', False, _('show topological heads only')),
2672 ('t', 'topo', False, _('show topological heads only')),
2671 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2673 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2672 ('c', 'closed', False, _('show normal and closed branch heads')),
2674 ('c', 'closed', False, _('show normal and closed branch heads')),
2673 ] + templateopts,
2675 ] + templateopts,
2674 _('[-ct] [-r STARTREV] [REV]...'))
2676 _('[-ct] [-r STARTREV] [REV]...'))
2675 def heads(ui, repo, *branchrevs, **opts):
2677 def heads(ui, repo, *branchrevs, **opts):
2676 """show branch heads
2678 """show branch heads
2677
2679
2678 With no arguments, show all open branch heads in the repository.
2680 With no arguments, show all open branch heads in the repository.
2679 Branch heads are changesets that have no descendants on the
2681 Branch heads are changesets that have no descendants on the
2680 same branch. They are where development generally takes place and
2682 same branch. They are where development generally takes place and
2681 are the usual targets for update and merge operations.
2683 are the usual targets for update and merge operations.
2682
2684
2683 If one or more REVs are given, only open branch heads on the
2685 If one or more REVs are given, only open branch heads on the
2684 branches associated with the specified changesets are shown. This
2686 branches associated with the specified changesets are shown. This
2685 means that you can use :hg:`heads .` to see the heads on the
2687 means that you can use :hg:`heads .` to see the heads on the
2686 currently checked-out branch.
2688 currently checked-out branch.
2687
2689
2688 If -c/--closed is specified, also show branch heads marked closed
2690 If -c/--closed is specified, also show branch heads marked closed
2689 (see :hg:`commit --close-branch`).
2691 (see :hg:`commit --close-branch`).
2690
2692
2691 If STARTREV is specified, only those heads that are descendants of
2693 If STARTREV is specified, only those heads that are descendants of
2692 STARTREV will be displayed.
2694 STARTREV will be displayed.
2693
2695
2694 If -t/--topo is specified, named branch mechanics will be ignored and only
2696 If -t/--topo is specified, named branch mechanics will be ignored and only
2695 topological heads (changesets with no children) will be shown.
2697 topological heads (changesets with no children) will be shown.
2696
2698
2697 Returns 0 if matching heads are found, 1 if not.
2699 Returns 0 if matching heads are found, 1 if not.
2698 """
2700 """
2699
2701
2700 opts = pycompat.byteskwargs(opts)
2702 opts = pycompat.byteskwargs(opts)
2701 start = None
2703 start = None
2702 if 'rev' in opts:
2704 if 'rev' in opts:
2703 start = scmutil.revsingle(repo, opts['rev'], None).node()
2705 start = scmutil.revsingle(repo, opts['rev'], None).node()
2704
2706
2705 if opts.get('topo'):
2707 if opts.get('topo'):
2706 heads = [repo[h] for h in repo.heads(start)]
2708 heads = [repo[h] for h in repo.heads(start)]
2707 else:
2709 else:
2708 heads = []
2710 heads = []
2709 for branch in repo.branchmap():
2711 for branch in repo.branchmap():
2710 heads += repo.branchheads(branch, start, opts.get('closed'))
2712 heads += repo.branchheads(branch, start, opts.get('closed'))
2711 heads = [repo[h] for h in heads]
2713 heads = [repo[h] for h in heads]
2712
2714
2713 if branchrevs:
2715 if branchrevs:
2714 branches = set(repo[br].branch() for br in branchrevs)
2716 branches = set(repo[br].branch() for br in branchrevs)
2715 heads = [h for h in heads if h.branch() in branches]
2717 heads = [h for h in heads if h.branch() in branches]
2716
2718
2717 if opts.get('active') and branchrevs:
2719 if opts.get('active') and branchrevs:
2718 dagheads = repo.heads(start)
2720 dagheads = repo.heads(start)
2719 heads = [h for h in heads if h.node() in dagheads]
2721 heads = [h for h in heads if h.node() in dagheads]
2720
2722
2721 if branchrevs:
2723 if branchrevs:
2722 haveheads = set(h.branch() for h in heads)
2724 haveheads = set(h.branch() for h in heads)
2723 if branches - haveheads:
2725 if branches - haveheads:
2724 headless = ', '.join(b for b in branches - haveheads)
2726 headless = ', '.join(b for b in branches - haveheads)
2725 msg = _('no open branch heads found on branches %s')
2727 msg = _('no open branch heads found on branches %s')
2726 if opts.get('rev'):
2728 if opts.get('rev'):
2727 msg += _(' (started at %s)') % opts['rev']
2729 msg += _(' (started at %s)') % opts['rev']
2728 ui.warn((msg + '\n') % headless)
2730 ui.warn((msg + '\n') % headless)
2729
2731
2730 if not heads:
2732 if not heads:
2731 return 1
2733 return 1
2732
2734
2733 ui.pager('heads')
2735 ui.pager('heads')
2734 heads = sorted(heads, key=lambda x: -x.rev())
2736 heads = sorted(heads, key=lambda x: -x.rev())
2735 displayer = cmdutil.show_changeset(ui, repo, opts)
2737 displayer = cmdutil.show_changeset(ui, repo, opts)
2736 for ctx in heads:
2738 for ctx in heads:
2737 displayer.show(ctx)
2739 displayer.show(ctx)
2738 displayer.close()
2740 displayer.close()
2739
2741
2740 @command('help',
2742 @command('help',
2741 [('e', 'extension', None, _('show only help for extensions')),
2743 [('e', 'extension', None, _('show only help for extensions')),
2742 ('c', 'command', None, _('show only help for commands')),
2744 ('c', 'command', None, _('show only help for commands')),
2743 ('k', 'keyword', None, _('show topics matching keyword')),
2745 ('k', 'keyword', None, _('show topics matching keyword')),
2744 ('s', 'system', [], _('show help for specific platform(s)')),
2746 ('s', 'system', [], _('show help for specific platform(s)')),
2745 ],
2747 ],
2746 _('[-ecks] [TOPIC]'),
2748 _('[-ecks] [TOPIC]'),
2747 norepo=True)
2749 norepo=True)
2748 def help_(ui, name=None, **opts):
2750 def help_(ui, name=None, **opts):
2749 """show help for a given topic or a help overview
2751 """show help for a given topic or a help overview
2750
2752
2751 With no arguments, print a list of commands with short help messages.
2753 With no arguments, print a list of commands with short help messages.
2752
2754
2753 Given a topic, extension, or command name, print help for that
2755 Given a topic, extension, or command name, print help for that
2754 topic.
2756 topic.
2755
2757
2756 Returns 0 if successful.
2758 Returns 0 if successful.
2757 """
2759 """
2758
2760
2759 keep = opts.get(r'system') or []
2761 keep = opts.get(r'system') or []
2760 if len(keep) == 0:
2762 if len(keep) == 0:
2761 if pycompat.sysplatform.startswith('win'):
2763 if pycompat.sysplatform.startswith('win'):
2762 keep.append('windows')
2764 keep.append('windows')
2763 elif pycompat.sysplatform == 'OpenVMS':
2765 elif pycompat.sysplatform == 'OpenVMS':
2764 keep.append('vms')
2766 keep.append('vms')
2765 elif pycompat.sysplatform == 'plan9':
2767 elif pycompat.sysplatform == 'plan9':
2766 keep.append('plan9')
2768 keep.append('plan9')
2767 else:
2769 else:
2768 keep.append('unix')
2770 keep.append('unix')
2769 keep.append(pycompat.sysplatform.lower())
2771 keep.append(pycompat.sysplatform.lower())
2770 if ui.verbose:
2772 if ui.verbose:
2771 keep.append('verbose')
2773 keep.append('verbose')
2772
2774
2773 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2775 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2774 ui.pager('help')
2776 ui.pager('help')
2775 ui.write(formatted)
2777 ui.write(formatted)
2776
2778
2777
2779
2778 @command('identify|id',
2780 @command('identify|id',
2779 [('r', 'rev', '',
2781 [('r', 'rev', '',
2780 _('identify the specified revision'), _('REV')),
2782 _('identify the specified revision'), _('REV')),
2781 ('n', 'num', None, _('show local revision number')),
2783 ('n', 'num', None, _('show local revision number')),
2782 ('i', 'id', None, _('show global revision id')),
2784 ('i', 'id', None, _('show global revision id')),
2783 ('b', 'branch', None, _('show branch')),
2785 ('b', 'branch', None, _('show branch')),
2784 ('t', 'tags', None, _('show tags')),
2786 ('t', 'tags', None, _('show tags')),
2785 ('B', 'bookmarks', None, _('show bookmarks')),
2787 ('B', 'bookmarks', None, _('show bookmarks')),
2786 ] + remoteopts,
2788 ] + remoteopts,
2787 _('[-nibtB] [-r REV] [SOURCE]'),
2789 _('[-nibtB] [-r REV] [SOURCE]'),
2788 optionalrepo=True)
2790 optionalrepo=True)
2789 def identify(ui, repo, source=None, rev=None,
2791 def identify(ui, repo, source=None, rev=None,
2790 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2792 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2791 """identify the working directory or specified revision
2793 """identify the working directory or specified revision
2792
2794
2793 Print a summary identifying the repository state at REV using one or
2795 Print a summary identifying the repository state at REV using one or
2794 two parent hash identifiers, followed by a "+" if the working
2796 two parent hash identifiers, followed by a "+" if the working
2795 directory has uncommitted changes, the branch name (if not default),
2797 directory has uncommitted changes, the branch name (if not default),
2796 a list of tags, and a list of bookmarks.
2798 a list of tags, and a list of bookmarks.
2797
2799
2798 When REV is not given, print a summary of the current state of the
2800 When REV is not given, print a summary of the current state of the
2799 repository.
2801 repository.
2800
2802
2801 Specifying a path to a repository root or Mercurial bundle will
2803 Specifying a path to a repository root or Mercurial bundle will
2802 cause lookup to operate on that repository/bundle.
2804 cause lookup to operate on that repository/bundle.
2803
2805
2804 .. container:: verbose
2806 .. container:: verbose
2805
2807
2806 Examples:
2808 Examples:
2807
2809
2808 - generate a build identifier for the working directory::
2810 - generate a build identifier for the working directory::
2809
2811
2810 hg id --id > build-id.dat
2812 hg id --id > build-id.dat
2811
2813
2812 - find the revision corresponding to a tag::
2814 - find the revision corresponding to a tag::
2813
2815
2814 hg id -n -r 1.3
2816 hg id -n -r 1.3
2815
2817
2816 - check the most recent revision of a remote repository::
2818 - check the most recent revision of a remote repository::
2817
2819
2818 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2820 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2819
2821
2820 See :hg:`log` for generating more information about specific revisions,
2822 See :hg:`log` for generating more information about specific revisions,
2821 including full hash identifiers.
2823 including full hash identifiers.
2822
2824
2823 Returns 0 if successful.
2825 Returns 0 if successful.
2824 """
2826 """
2825
2827
2826 opts = pycompat.byteskwargs(opts)
2828 opts = pycompat.byteskwargs(opts)
2827 if not repo and not source:
2829 if not repo and not source:
2828 raise error.Abort(_("there is no Mercurial repository here "
2830 raise error.Abort(_("there is no Mercurial repository here "
2829 "(.hg not found)"))
2831 "(.hg not found)"))
2830
2832
2831 if ui.debugflag:
2833 if ui.debugflag:
2832 hexfunc = hex
2834 hexfunc = hex
2833 else:
2835 else:
2834 hexfunc = short
2836 hexfunc = short
2835 default = not (num or id or branch or tags or bookmarks)
2837 default = not (num or id or branch or tags or bookmarks)
2836 output = []
2838 output = []
2837 revs = []
2839 revs = []
2838
2840
2839 if source:
2841 if source:
2840 source, branches = hg.parseurl(ui.expandpath(source))
2842 source, branches = hg.parseurl(ui.expandpath(source))
2841 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2843 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2842 repo = peer.local()
2844 repo = peer.local()
2843 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2845 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2844
2846
2845 if not repo:
2847 if not repo:
2846 if num or branch or tags:
2848 if num or branch or tags:
2847 raise error.Abort(
2849 raise error.Abort(
2848 _("can't query remote revision number, branch, or tags"))
2850 _("can't query remote revision number, branch, or tags"))
2849 if not rev and revs:
2851 if not rev and revs:
2850 rev = revs[0]
2852 rev = revs[0]
2851 if not rev:
2853 if not rev:
2852 rev = "tip"
2854 rev = "tip"
2853
2855
2854 remoterev = peer.lookup(rev)
2856 remoterev = peer.lookup(rev)
2855 if default or id:
2857 if default or id:
2856 output = [hexfunc(remoterev)]
2858 output = [hexfunc(remoterev)]
2857
2859
2858 def getbms():
2860 def getbms():
2859 bms = []
2861 bms = []
2860
2862
2861 if 'bookmarks' in peer.listkeys('namespaces'):
2863 if 'bookmarks' in peer.listkeys('namespaces'):
2862 hexremoterev = hex(remoterev)
2864 hexremoterev = hex(remoterev)
2863 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2865 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2864 if bmr == hexremoterev]
2866 if bmr == hexremoterev]
2865
2867
2866 return sorted(bms)
2868 return sorted(bms)
2867
2869
2868 if bookmarks:
2870 if bookmarks:
2869 output.extend(getbms())
2871 output.extend(getbms())
2870 elif default and not ui.quiet:
2872 elif default and not ui.quiet:
2871 # multiple bookmarks for a single parent separated by '/'
2873 # multiple bookmarks for a single parent separated by '/'
2872 bm = '/'.join(getbms())
2874 bm = '/'.join(getbms())
2873 if bm:
2875 if bm:
2874 output.append(bm)
2876 output.append(bm)
2875 else:
2877 else:
2876 ctx = scmutil.revsingle(repo, rev, None)
2878 ctx = scmutil.revsingle(repo, rev, None)
2877
2879
2878 if ctx.rev() is None:
2880 if ctx.rev() is None:
2879 ctx = repo[None]
2881 ctx = repo[None]
2880 parents = ctx.parents()
2882 parents = ctx.parents()
2881 taglist = []
2883 taglist = []
2882 for p in parents:
2884 for p in parents:
2883 taglist.extend(p.tags())
2885 taglist.extend(p.tags())
2884
2886
2885 changed = ""
2887 changed = ""
2886 if default or id or num:
2888 if default or id or num:
2887 if (any(repo.status())
2889 if (any(repo.status())
2888 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2890 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2889 changed = '+'
2891 changed = '+'
2890 if default or id:
2892 if default or id:
2891 output = ["%s%s" %
2893 output = ["%s%s" %
2892 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2894 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2893 if num:
2895 if num:
2894 output.append("%s%s" %
2896 output.append("%s%s" %
2895 ('+'.join([str(p.rev()) for p in parents]), changed))
2897 ('+'.join([str(p.rev()) for p in parents]), changed))
2896 else:
2898 else:
2897 if default or id:
2899 if default or id:
2898 output = [hexfunc(ctx.node())]
2900 output = [hexfunc(ctx.node())]
2899 if num:
2901 if num:
2900 output.append(str(ctx.rev()))
2902 output.append(str(ctx.rev()))
2901 taglist = ctx.tags()
2903 taglist = ctx.tags()
2902
2904
2903 if default and not ui.quiet:
2905 if default and not ui.quiet:
2904 b = ctx.branch()
2906 b = ctx.branch()
2905 if b != 'default':
2907 if b != 'default':
2906 output.append("(%s)" % b)
2908 output.append("(%s)" % b)
2907
2909
2908 # multiple tags for a single parent separated by '/'
2910 # multiple tags for a single parent separated by '/'
2909 t = '/'.join(taglist)
2911 t = '/'.join(taglist)
2910 if t:
2912 if t:
2911 output.append(t)
2913 output.append(t)
2912
2914
2913 # multiple bookmarks for a single parent separated by '/'
2915 # multiple bookmarks for a single parent separated by '/'
2914 bm = '/'.join(ctx.bookmarks())
2916 bm = '/'.join(ctx.bookmarks())
2915 if bm:
2917 if bm:
2916 output.append(bm)
2918 output.append(bm)
2917 else:
2919 else:
2918 if branch:
2920 if branch:
2919 output.append(ctx.branch())
2921 output.append(ctx.branch())
2920
2922
2921 if tags:
2923 if tags:
2922 output.extend(taglist)
2924 output.extend(taglist)
2923
2925
2924 if bookmarks:
2926 if bookmarks:
2925 output.extend(ctx.bookmarks())
2927 output.extend(ctx.bookmarks())
2926
2928
2927 ui.write("%s\n" % ' '.join(output))
2929 ui.write("%s\n" % ' '.join(output))
2928
2930
2929 @command('import|patch',
2931 @command('import|patch',
2930 [('p', 'strip', 1,
2932 [('p', 'strip', 1,
2931 _('directory strip option for patch. This has the same '
2933 _('directory strip option for patch. This has the same '
2932 'meaning as the corresponding patch option'), _('NUM')),
2934 'meaning as the corresponding patch option'), _('NUM')),
2933 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2935 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2934 ('e', 'edit', False, _('invoke editor on commit messages')),
2936 ('e', 'edit', False, _('invoke editor on commit messages')),
2935 ('f', 'force', None,
2937 ('f', 'force', None,
2936 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2938 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2937 ('', 'no-commit', None,
2939 ('', 'no-commit', None,
2938 _("don't commit, just update the working directory")),
2940 _("don't commit, just update the working directory")),
2939 ('', 'bypass', None,
2941 ('', 'bypass', None,
2940 _("apply patch without touching the working directory")),
2942 _("apply patch without touching the working directory")),
2941 ('', 'partial', None,
2943 ('', 'partial', None,
2942 _('commit even if some hunks fail')),
2944 _('commit even if some hunks fail')),
2943 ('', 'exact', None,
2945 ('', 'exact', None,
2944 _('abort if patch would apply lossily')),
2946 _('abort if patch would apply lossily')),
2945 ('', 'prefix', '',
2947 ('', 'prefix', '',
2946 _('apply patch to subdirectory'), _('DIR')),
2948 _('apply patch to subdirectory'), _('DIR')),
2947 ('', 'import-branch', None,
2949 ('', 'import-branch', None,
2948 _('use any branch information in patch (implied by --exact)'))] +
2950 _('use any branch information in patch (implied by --exact)'))] +
2949 commitopts + commitopts2 + similarityopts,
2951 commitopts + commitopts2 + similarityopts,
2950 _('[OPTION]... PATCH...'))
2952 _('[OPTION]... PATCH...'))
2951 def import_(ui, repo, patch1=None, *patches, **opts):
2953 def import_(ui, repo, patch1=None, *patches, **opts):
2952 """import an ordered set of patches
2954 """import an ordered set of patches
2953
2955
2954 Import a list of patches and commit them individually (unless
2956 Import a list of patches and commit them individually (unless
2955 --no-commit is specified).
2957 --no-commit is specified).
2956
2958
2957 To read a patch from standard input (stdin), use "-" as the patch
2959 To read a patch from standard input (stdin), use "-" as the patch
2958 name. If a URL is specified, the patch will be downloaded from
2960 name. If a URL is specified, the patch will be downloaded from
2959 there.
2961 there.
2960
2962
2961 Import first applies changes to the working directory (unless
2963 Import first applies changes to the working directory (unless
2962 --bypass is specified), import will abort if there are outstanding
2964 --bypass is specified), import will abort if there are outstanding
2963 changes.
2965 changes.
2964
2966
2965 Use --bypass to apply and commit patches directly to the
2967 Use --bypass to apply and commit patches directly to the
2966 repository, without affecting the working directory. Without
2968 repository, without affecting the working directory. Without
2967 --exact, patches will be applied on top of the working directory
2969 --exact, patches will be applied on top of the working directory
2968 parent revision.
2970 parent revision.
2969
2971
2970 You can import a patch straight from a mail message. Even patches
2972 You can import a patch straight from a mail message. Even patches
2971 as attachments work (to use the body part, it must have type
2973 as attachments work (to use the body part, it must have type
2972 text/plain or text/x-patch). From and Subject headers of email
2974 text/plain or text/x-patch). From and Subject headers of email
2973 message are used as default committer and commit message. All
2975 message are used as default committer and commit message. All
2974 text/plain body parts before first diff are added to the commit
2976 text/plain body parts before first diff are added to the commit
2975 message.
2977 message.
2976
2978
2977 If the imported patch was generated by :hg:`export`, user and
2979 If the imported patch was generated by :hg:`export`, user and
2978 description from patch override values from message headers and
2980 description from patch override values from message headers and
2979 body. Values given on command line with -m/--message and -u/--user
2981 body. Values given on command line with -m/--message and -u/--user
2980 override these.
2982 override these.
2981
2983
2982 If --exact is specified, import will set the working directory to
2984 If --exact is specified, import will set the working directory to
2983 the parent of each patch before applying it, and will abort if the
2985 the parent of each patch before applying it, and will abort if the
2984 resulting changeset has a different ID than the one recorded in
2986 resulting changeset has a different ID than the one recorded in
2985 the patch. This will guard against various ways that portable
2987 the patch. This will guard against various ways that portable
2986 patch formats and mail systems might fail to transfer Mercurial
2988 patch formats and mail systems might fail to transfer Mercurial
2987 data or metadata. See :hg:`bundle` for lossless transmission.
2989 data or metadata. See :hg:`bundle` for lossless transmission.
2988
2990
2989 Use --partial to ensure a changeset will be created from the patch
2991 Use --partial to ensure a changeset will be created from the patch
2990 even if some hunks fail to apply. Hunks that fail to apply will be
2992 even if some hunks fail to apply. Hunks that fail to apply will be
2991 written to a <target-file>.rej file. Conflicts can then be resolved
2993 written to a <target-file>.rej file. Conflicts can then be resolved
2992 by hand before :hg:`commit --amend` is run to update the created
2994 by hand before :hg:`commit --amend` is run to update the created
2993 changeset. This flag exists to let people import patches that
2995 changeset. This flag exists to let people import patches that
2994 partially apply without losing the associated metadata (author,
2996 partially apply without losing the associated metadata (author,
2995 date, description, ...).
2997 date, description, ...).
2996
2998
2997 .. note::
2999 .. note::
2998
3000
2999 When no hunks apply cleanly, :hg:`import --partial` will create
3001 When no hunks apply cleanly, :hg:`import --partial` will create
3000 an empty changeset, importing only the patch metadata.
3002 an empty changeset, importing only the patch metadata.
3001
3003
3002 With -s/--similarity, hg will attempt to discover renames and
3004 With -s/--similarity, hg will attempt to discover renames and
3003 copies in the patch in the same way as :hg:`addremove`.
3005 copies in the patch in the same way as :hg:`addremove`.
3004
3006
3005 It is possible to use external patch programs to perform the patch
3007 It is possible to use external patch programs to perform the patch
3006 by setting the ``ui.patch`` configuration option. For the default
3008 by setting the ``ui.patch`` configuration option. For the default
3007 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3009 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3008 See :hg:`help config` for more information about configuration
3010 See :hg:`help config` for more information about configuration
3009 files and how to use these options.
3011 files and how to use these options.
3010
3012
3011 See :hg:`help dates` for a list of formats valid for -d/--date.
3013 See :hg:`help dates` for a list of formats valid for -d/--date.
3012
3014
3013 .. container:: verbose
3015 .. container:: verbose
3014
3016
3015 Examples:
3017 Examples:
3016
3018
3017 - import a traditional patch from a website and detect renames::
3019 - import a traditional patch from a website and detect renames::
3018
3020
3019 hg import -s 80 http://example.com/bugfix.patch
3021 hg import -s 80 http://example.com/bugfix.patch
3020
3022
3021 - import a changeset from an hgweb server::
3023 - import a changeset from an hgweb server::
3022
3024
3023 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3025 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3024
3026
3025 - import all the patches in an Unix-style mbox::
3027 - import all the patches in an Unix-style mbox::
3026
3028
3027 hg import incoming-patches.mbox
3029 hg import incoming-patches.mbox
3028
3030
3029 - import patches from stdin::
3031 - import patches from stdin::
3030
3032
3031 hg import -
3033 hg import -
3032
3034
3033 - attempt to exactly restore an exported changeset (not always
3035 - attempt to exactly restore an exported changeset (not always
3034 possible)::
3036 possible)::
3035
3037
3036 hg import --exact proposed-fix.patch
3038 hg import --exact proposed-fix.patch
3037
3039
3038 - use an external tool to apply a patch which is too fuzzy for
3040 - use an external tool to apply a patch which is too fuzzy for
3039 the default internal tool.
3041 the default internal tool.
3040
3042
3041 hg import --config ui.patch="patch --merge" fuzzy.patch
3043 hg import --config ui.patch="patch --merge" fuzzy.patch
3042
3044
3043 - change the default fuzzing from 2 to a less strict 7
3045 - change the default fuzzing from 2 to a less strict 7
3044
3046
3045 hg import --config ui.fuzz=7 fuzz.patch
3047 hg import --config ui.fuzz=7 fuzz.patch
3046
3048
3047 Returns 0 on success, 1 on partial success (see --partial).
3049 Returns 0 on success, 1 on partial success (see --partial).
3048 """
3050 """
3049
3051
3050 opts = pycompat.byteskwargs(opts)
3052 opts = pycompat.byteskwargs(opts)
3051 if not patch1:
3053 if not patch1:
3052 raise error.Abort(_('need at least one patch to import'))
3054 raise error.Abort(_('need at least one patch to import'))
3053
3055
3054 patches = (patch1,) + patches
3056 patches = (patch1,) + patches
3055
3057
3056 date = opts.get('date')
3058 date = opts.get('date')
3057 if date:
3059 if date:
3058 opts['date'] = util.parsedate(date)
3060 opts['date'] = util.parsedate(date)
3059
3061
3060 exact = opts.get('exact')
3062 exact = opts.get('exact')
3061 update = not opts.get('bypass')
3063 update = not opts.get('bypass')
3062 if not update and opts.get('no_commit'):
3064 if not update and opts.get('no_commit'):
3063 raise error.Abort(_('cannot use --no-commit with --bypass'))
3065 raise error.Abort(_('cannot use --no-commit with --bypass'))
3064 try:
3066 try:
3065 sim = float(opts.get('similarity') or 0)
3067 sim = float(opts.get('similarity') or 0)
3066 except ValueError:
3068 except ValueError:
3067 raise error.Abort(_('similarity must be a number'))
3069 raise error.Abort(_('similarity must be a number'))
3068 if sim < 0 or sim > 100:
3070 if sim < 0 or sim > 100:
3069 raise error.Abort(_('similarity must be between 0 and 100'))
3071 raise error.Abort(_('similarity must be between 0 and 100'))
3070 if sim and not update:
3072 if sim and not update:
3071 raise error.Abort(_('cannot use --similarity with --bypass'))
3073 raise error.Abort(_('cannot use --similarity with --bypass'))
3072 if exact:
3074 if exact:
3073 if opts.get('edit'):
3075 if opts.get('edit'):
3074 raise error.Abort(_('cannot use --exact with --edit'))
3076 raise error.Abort(_('cannot use --exact with --edit'))
3075 if opts.get('prefix'):
3077 if opts.get('prefix'):
3076 raise error.Abort(_('cannot use --exact with --prefix'))
3078 raise error.Abort(_('cannot use --exact with --prefix'))
3077
3079
3078 base = opts["base"]
3080 base = opts["base"]
3079 wlock = dsguard = lock = tr = None
3081 wlock = dsguard = lock = tr = None
3080 msgs = []
3082 msgs = []
3081 ret = 0
3083 ret = 0
3082
3084
3083
3085
3084 try:
3086 try:
3085 wlock = repo.wlock()
3087 wlock = repo.wlock()
3086
3088
3087 if update:
3089 if update:
3088 cmdutil.checkunfinished(repo)
3090 cmdutil.checkunfinished(repo)
3089 if (exact or not opts.get('force')):
3091 if (exact or not opts.get('force')):
3090 cmdutil.bailifchanged(repo)
3092 cmdutil.bailifchanged(repo)
3091
3093
3092 if not opts.get('no_commit'):
3094 if not opts.get('no_commit'):
3093 lock = repo.lock()
3095 lock = repo.lock()
3094 tr = repo.transaction('import')
3096 tr = repo.transaction('import')
3095 else:
3097 else:
3096 dsguard = dirstateguard.dirstateguard(repo, 'import')
3098 dsguard = dirstateguard.dirstateguard(repo, 'import')
3097 parents = repo[None].parents()
3099 parents = repo[None].parents()
3098 for patchurl in patches:
3100 for patchurl in patches:
3099 if patchurl == '-':
3101 if patchurl == '-':
3100 ui.status(_('applying patch from stdin\n'))
3102 ui.status(_('applying patch from stdin\n'))
3101 patchfile = ui.fin
3103 patchfile = ui.fin
3102 patchurl = 'stdin' # for error message
3104 patchurl = 'stdin' # for error message
3103 else:
3105 else:
3104 patchurl = os.path.join(base, patchurl)
3106 patchurl = os.path.join(base, patchurl)
3105 ui.status(_('applying %s\n') % patchurl)
3107 ui.status(_('applying %s\n') % patchurl)
3106 patchfile = hg.openpath(ui, patchurl)
3108 patchfile = hg.openpath(ui, patchurl)
3107
3109
3108 haspatch = False
3110 haspatch = False
3109 for hunk in patch.split(patchfile):
3111 for hunk in patch.split(patchfile):
3110 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3112 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3111 parents, opts,
3113 parents, opts,
3112 msgs, hg.clean)
3114 msgs, hg.clean)
3113 if msg:
3115 if msg:
3114 haspatch = True
3116 haspatch = True
3115 ui.note(msg + '\n')
3117 ui.note(msg + '\n')
3116 if update or exact:
3118 if update or exact:
3117 parents = repo[None].parents()
3119 parents = repo[None].parents()
3118 else:
3120 else:
3119 parents = [repo[node]]
3121 parents = [repo[node]]
3120 if rej:
3122 if rej:
3121 ui.write_err(_("patch applied partially\n"))
3123 ui.write_err(_("patch applied partially\n"))
3122 ui.write_err(_("(fix the .rej files and run "
3124 ui.write_err(_("(fix the .rej files and run "
3123 "`hg commit --amend`)\n"))
3125 "`hg commit --amend`)\n"))
3124 ret = 1
3126 ret = 1
3125 break
3127 break
3126
3128
3127 if not haspatch:
3129 if not haspatch:
3128 raise error.Abort(_('%s: no diffs found') % patchurl)
3130 raise error.Abort(_('%s: no diffs found') % patchurl)
3129
3131
3130 if tr:
3132 if tr:
3131 tr.close()
3133 tr.close()
3132 if msgs:
3134 if msgs:
3133 repo.savecommitmessage('\n* * *\n'.join(msgs))
3135 repo.savecommitmessage('\n* * *\n'.join(msgs))
3134 if dsguard:
3136 if dsguard:
3135 dsguard.close()
3137 dsguard.close()
3136 return ret
3138 return ret
3137 finally:
3139 finally:
3138 if tr:
3140 if tr:
3139 tr.release()
3141 tr.release()
3140 release(lock, dsguard, wlock)
3142 release(lock, dsguard, wlock)
3141
3143
3142 @command('incoming|in',
3144 @command('incoming|in',
3143 [('f', 'force', None,
3145 [('f', 'force', None,
3144 _('run even if remote repository is unrelated')),
3146 _('run even if remote repository is unrelated')),
3145 ('n', 'newest-first', None, _('show newest record first')),
3147 ('n', 'newest-first', None, _('show newest record first')),
3146 ('', 'bundle', '',
3148 ('', 'bundle', '',
3147 _('file to store the bundles into'), _('FILE')),
3149 _('file to store the bundles into'), _('FILE')),
3148 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3150 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3149 ('B', 'bookmarks', False, _("compare bookmarks")),
3151 ('B', 'bookmarks', False, _("compare bookmarks")),
3150 ('b', 'branch', [],
3152 ('b', 'branch', [],
3151 _('a specific branch you would like to pull'), _('BRANCH')),
3153 _('a specific branch you would like to pull'), _('BRANCH')),
3152 ] + logopts + remoteopts + subrepoopts,
3154 ] + logopts + remoteopts + subrepoopts,
3153 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3155 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3154 def incoming(ui, repo, source="default", **opts):
3156 def incoming(ui, repo, source="default", **opts):
3155 """show new changesets found in source
3157 """show new changesets found in source
3156
3158
3157 Show new changesets found in the specified path/URL or the default
3159 Show new changesets found in the specified path/URL or the default
3158 pull location. These are the changesets that would have been pulled
3160 pull location. These are the changesets that would have been pulled
3159 if a pull at the time you issued this command.
3161 if a pull at the time you issued this command.
3160
3162
3161 See pull for valid source format details.
3163 See pull for valid source format details.
3162
3164
3163 .. container:: verbose
3165 .. container:: verbose
3164
3166
3165 With -B/--bookmarks, the result of bookmark comparison between
3167 With -B/--bookmarks, the result of bookmark comparison between
3166 local and remote repositories is displayed. With -v/--verbose,
3168 local and remote repositories is displayed. With -v/--verbose,
3167 status is also displayed for each bookmark like below::
3169 status is also displayed for each bookmark like below::
3168
3170
3169 BM1 01234567890a added
3171 BM1 01234567890a added
3170 BM2 1234567890ab advanced
3172 BM2 1234567890ab advanced
3171 BM3 234567890abc diverged
3173 BM3 234567890abc diverged
3172 BM4 34567890abcd changed
3174 BM4 34567890abcd changed
3173
3175
3174 The action taken locally when pulling depends on the
3176 The action taken locally when pulling depends on the
3175 status of each bookmark:
3177 status of each bookmark:
3176
3178
3177 :``added``: pull will create it
3179 :``added``: pull will create it
3178 :``advanced``: pull will update it
3180 :``advanced``: pull will update it
3179 :``diverged``: pull will create a divergent bookmark
3181 :``diverged``: pull will create a divergent bookmark
3180 :``changed``: result depends on remote changesets
3182 :``changed``: result depends on remote changesets
3181
3183
3182 From the point of view of pulling behavior, bookmark
3184 From the point of view of pulling behavior, bookmark
3183 existing only in the remote repository are treated as ``added``,
3185 existing only in the remote repository are treated as ``added``,
3184 even if it is in fact locally deleted.
3186 even if it is in fact locally deleted.
3185
3187
3186 .. container:: verbose
3188 .. container:: verbose
3187
3189
3188 For remote repository, using --bundle avoids downloading the
3190 For remote repository, using --bundle avoids downloading the
3189 changesets twice if the incoming is followed by a pull.
3191 changesets twice if the incoming is followed by a pull.
3190
3192
3191 Examples:
3193 Examples:
3192
3194
3193 - show incoming changes with patches and full description::
3195 - show incoming changes with patches and full description::
3194
3196
3195 hg incoming -vp
3197 hg incoming -vp
3196
3198
3197 - show incoming changes excluding merges, store a bundle::
3199 - show incoming changes excluding merges, store a bundle::
3198
3200
3199 hg in -vpM --bundle incoming.hg
3201 hg in -vpM --bundle incoming.hg
3200 hg pull incoming.hg
3202 hg pull incoming.hg
3201
3203
3202 - briefly list changes inside a bundle::
3204 - briefly list changes inside a bundle::
3203
3205
3204 hg in changes.hg -T "{desc|firstline}\\n"
3206 hg in changes.hg -T "{desc|firstline}\\n"
3205
3207
3206 Returns 0 if there are incoming changes, 1 otherwise.
3208 Returns 0 if there are incoming changes, 1 otherwise.
3207 """
3209 """
3208 opts = pycompat.byteskwargs(opts)
3210 opts = pycompat.byteskwargs(opts)
3209 if opts.get('graph'):
3211 if opts.get('graph'):
3210 cmdutil.checkunsupportedgraphflags([], opts)
3212 cmdutil.checkunsupportedgraphflags([], opts)
3211 def display(other, chlist, displayer):
3213 def display(other, chlist, displayer):
3212 revdag = cmdutil.graphrevs(other, chlist, opts)
3214 revdag = cmdutil.graphrevs(other, chlist, opts)
3213 cmdutil.displaygraph(ui, repo, revdag, displayer,
3215 cmdutil.displaygraph(ui, repo, revdag, displayer,
3214 graphmod.asciiedges)
3216 graphmod.asciiedges)
3215
3217
3216 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3218 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3217 return 0
3219 return 0
3218
3220
3219 if opts.get('bundle') and opts.get('subrepos'):
3221 if opts.get('bundle') and opts.get('subrepos'):
3220 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3222 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3221
3223
3222 if opts.get('bookmarks'):
3224 if opts.get('bookmarks'):
3223 source, branches = hg.parseurl(ui.expandpath(source),
3225 source, branches = hg.parseurl(ui.expandpath(source),
3224 opts.get('branch'))
3226 opts.get('branch'))
3225 other = hg.peer(repo, opts, source)
3227 other = hg.peer(repo, opts, source)
3226 if 'bookmarks' not in other.listkeys('namespaces'):
3228 if 'bookmarks' not in other.listkeys('namespaces'):
3227 ui.warn(_("remote doesn't support bookmarks\n"))
3229 ui.warn(_("remote doesn't support bookmarks\n"))
3228 return 0
3230 return 0
3229 ui.pager('incoming')
3231 ui.pager('incoming')
3230 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3232 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3231 return bookmarks.incoming(ui, repo, other)
3233 return bookmarks.incoming(ui, repo, other)
3232
3234
3233 repo._subtoppath = ui.expandpath(source)
3235 repo._subtoppath = ui.expandpath(source)
3234 try:
3236 try:
3235 return hg.incoming(ui, repo, source, opts)
3237 return hg.incoming(ui, repo, source, opts)
3236 finally:
3238 finally:
3237 del repo._subtoppath
3239 del repo._subtoppath
3238
3240
3239
3241
3240 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3242 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3241 norepo=True)
3243 norepo=True)
3242 def init(ui, dest=".", **opts):
3244 def init(ui, dest=".", **opts):
3243 """create a new repository in the given directory
3245 """create a new repository in the given directory
3244
3246
3245 Initialize a new repository in the given directory. If the given
3247 Initialize a new repository in the given directory. If the given
3246 directory does not exist, it will be created.
3248 directory does not exist, it will be created.
3247
3249
3248 If no directory is given, the current directory is used.
3250 If no directory is given, the current directory is used.
3249
3251
3250 It is possible to specify an ``ssh://`` URL as the destination.
3252 It is possible to specify an ``ssh://`` URL as the destination.
3251 See :hg:`help urls` for more information.
3253 See :hg:`help urls` for more information.
3252
3254
3253 Returns 0 on success.
3255 Returns 0 on success.
3254 """
3256 """
3255 opts = pycompat.byteskwargs(opts)
3257 opts = pycompat.byteskwargs(opts)
3256 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3258 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3257
3259
3258 @command('locate',
3260 @command('locate',
3259 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3261 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3260 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3262 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3261 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3263 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3262 ] + walkopts,
3264 ] + walkopts,
3263 _('[OPTION]... [PATTERN]...'))
3265 _('[OPTION]... [PATTERN]...'))
3264 def locate(ui, repo, *pats, **opts):
3266 def locate(ui, repo, *pats, **opts):
3265 """locate files matching specific patterns (DEPRECATED)
3267 """locate files matching specific patterns (DEPRECATED)
3266
3268
3267 Print files under Mercurial control in the working directory whose
3269 Print files under Mercurial control in the working directory whose
3268 names match the given patterns.
3270 names match the given patterns.
3269
3271
3270 By default, this command searches all directories in the working
3272 By default, this command searches all directories in the working
3271 directory. To search just the current directory and its
3273 directory. To search just the current directory and its
3272 subdirectories, use "--include .".
3274 subdirectories, use "--include .".
3273
3275
3274 If no patterns are given to match, this command prints the names
3276 If no patterns are given to match, this command prints the names
3275 of all files under Mercurial control in the working directory.
3277 of all files under Mercurial control in the working directory.
3276
3278
3277 If you want to feed the output of this command into the "xargs"
3279 If you want to feed the output of this command into the "xargs"
3278 command, use the -0 option to both this command and "xargs". This
3280 command, use the -0 option to both this command and "xargs". This
3279 will avoid the problem of "xargs" treating single filenames that
3281 will avoid the problem of "xargs" treating single filenames that
3280 contain whitespace as multiple filenames.
3282 contain whitespace as multiple filenames.
3281
3283
3282 See :hg:`help files` for a more versatile command.
3284 See :hg:`help files` for a more versatile command.
3283
3285
3284 Returns 0 if a match is found, 1 otherwise.
3286 Returns 0 if a match is found, 1 otherwise.
3285 """
3287 """
3286 opts = pycompat.byteskwargs(opts)
3288 opts = pycompat.byteskwargs(opts)
3287 if opts.get('print0'):
3289 if opts.get('print0'):
3288 end = '\0'
3290 end = '\0'
3289 else:
3291 else:
3290 end = '\n'
3292 end = '\n'
3291 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3293 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3292
3294
3293 ret = 1
3295 ret = 1
3294 ctx = repo[rev]
3296 ctx = repo[rev]
3295 m = scmutil.match(ctx, pats, opts, default='relglob',
3297 m = scmutil.match(ctx, pats, opts, default='relglob',
3296 badfn=lambda x, y: False)
3298 badfn=lambda x, y: False)
3297
3299
3298 ui.pager('locate')
3300 ui.pager('locate')
3299 for abs in ctx.matches(m):
3301 for abs in ctx.matches(m):
3300 if opts.get('fullpath'):
3302 if opts.get('fullpath'):
3301 ui.write(repo.wjoin(abs), end)
3303 ui.write(repo.wjoin(abs), end)
3302 else:
3304 else:
3303 ui.write(((pats and m.rel(abs)) or abs), end)
3305 ui.write(((pats and m.rel(abs)) or abs), end)
3304 ret = 0
3306 ret = 0
3305
3307
3306 return ret
3308 return ret
3307
3309
3308 @command('^log|history',
3310 @command('^log|history',
3309 [('f', 'follow', None,
3311 [('f', 'follow', None,
3310 _('follow changeset history, or file history across copies and renames')),
3312 _('follow changeset history, or file history across copies and renames')),
3311 ('', 'follow-first', None,
3313 ('', 'follow-first', None,
3312 _('only follow the first parent of merge changesets (DEPRECATED)')),
3314 _('only follow the first parent of merge changesets (DEPRECATED)')),
3313 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3315 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3314 ('C', 'copies', None, _('show copied files')),
3316 ('C', 'copies', None, _('show copied files')),
3315 ('k', 'keyword', [],
3317 ('k', 'keyword', [],
3316 _('do case-insensitive search for a given text'), _('TEXT')),
3318 _('do case-insensitive search for a given text'), _('TEXT')),
3317 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3319 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3318 ('', 'removed', None, _('include revisions where files were removed')),
3320 ('', 'removed', None, _('include revisions where files were removed')),
3319 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3321 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3320 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3322 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3321 ('', 'only-branch', [],
3323 ('', 'only-branch', [],
3322 _('show only changesets within the given named branch (DEPRECATED)'),
3324 _('show only changesets within the given named branch (DEPRECATED)'),
3323 _('BRANCH')),
3325 _('BRANCH')),
3324 ('b', 'branch', [],
3326 ('b', 'branch', [],
3325 _('show changesets within the given named branch'), _('BRANCH')),
3327 _('show changesets within the given named branch'), _('BRANCH')),
3326 ('P', 'prune', [],
3328 ('P', 'prune', [],
3327 _('do not display revision or any of its ancestors'), _('REV')),
3329 _('do not display revision or any of its ancestors'), _('REV')),
3328 ] + logopts + walkopts,
3330 ] + logopts + walkopts,
3329 _('[OPTION]... [FILE]'),
3331 _('[OPTION]... [FILE]'),
3330 inferrepo=True)
3332 inferrepo=True)
3331 def log(ui, repo, *pats, **opts):
3333 def log(ui, repo, *pats, **opts):
3332 """show revision history of entire repository or files
3334 """show revision history of entire repository or files
3333
3335
3334 Print the revision history of the specified files or the entire
3336 Print the revision history of the specified files or the entire
3335 project.
3337 project.
3336
3338
3337 If no revision range is specified, the default is ``tip:0`` unless
3339 If no revision range is specified, the default is ``tip:0`` unless
3338 --follow is set, in which case the working directory parent is
3340 --follow is set, in which case the working directory parent is
3339 used as the starting revision.
3341 used as the starting revision.
3340
3342
3341 File history is shown without following rename or copy history of
3343 File history is shown without following rename or copy history of
3342 files. Use -f/--follow with a filename to follow history across
3344 files. Use -f/--follow with a filename to follow history across
3343 renames and copies. --follow without a filename will only show
3345 renames and copies. --follow without a filename will only show
3344 ancestors or descendants of the starting revision.
3346 ancestors or descendants of the starting revision.
3345
3347
3346 By default this command prints revision number and changeset id,
3348 By default this command prints revision number and changeset id,
3347 tags, non-trivial parents, user, date and time, and a summary for
3349 tags, non-trivial parents, user, date and time, and a summary for
3348 each commit. When the -v/--verbose switch is used, the list of
3350 each commit. When the -v/--verbose switch is used, the list of
3349 changed files and full commit message are shown.
3351 changed files and full commit message are shown.
3350
3352
3351 With --graph the revisions are shown as an ASCII art DAG with the most
3353 With --graph the revisions are shown as an ASCII art DAG with the most
3352 recent changeset at the top.
3354 recent changeset at the top.
3353 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3355 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3354 and '+' represents a fork where the changeset from the lines below is a
3356 and '+' represents a fork where the changeset from the lines below is a
3355 parent of the 'o' merge on the same line.
3357 parent of the 'o' merge on the same line.
3356 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3358 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3357 of a '|' indicates one or more revisions in a path are omitted.
3359 of a '|' indicates one or more revisions in a path are omitted.
3358
3360
3359 .. note::
3361 .. note::
3360
3362
3361 :hg:`log --patch` may generate unexpected diff output for merge
3363 :hg:`log --patch` may generate unexpected diff output for merge
3362 changesets, as it will only compare the merge changeset against
3364 changesets, as it will only compare the merge changeset against
3363 its first parent. Also, only files different from BOTH parents
3365 its first parent. Also, only files different from BOTH parents
3364 will appear in files:.
3366 will appear in files:.
3365
3367
3366 .. note::
3368 .. note::
3367
3369
3368 For performance reasons, :hg:`log FILE` may omit duplicate changes
3370 For performance reasons, :hg:`log FILE` may omit duplicate changes
3369 made on branches and will not show removals or mode changes. To
3371 made on branches and will not show removals or mode changes. To
3370 see all such changes, use the --removed switch.
3372 see all such changes, use the --removed switch.
3371
3373
3372 .. container:: verbose
3374 .. container:: verbose
3373
3375
3374 Some examples:
3376 Some examples:
3375
3377
3376 - changesets with full descriptions and file lists::
3378 - changesets with full descriptions and file lists::
3377
3379
3378 hg log -v
3380 hg log -v
3379
3381
3380 - changesets ancestral to the working directory::
3382 - changesets ancestral to the working directory::
3381
3383
3382 hg log -f
3384 hg log -f
3383
3385
3384 - last 10 commits on the current branch::
3386 - last 10 commits on the current branch::
3385
3387
3386 hg log -l 10 -b .
3388 hg log -l 10 -b .
3387
3389
3388 - changesets showing all modifications of a file, including removals::
3390 - changesets showing all modifications of a file, including removals::
3389
3391
3390 hg log --removed file.c
3392 hg log --removed file.c
3391
3393
3392 - all changesets that touch a directory, with diffs, excluding merges::
3394 - all changesets that touch a directory, with diffs, excluding merges::
3393
3395
3394 hg log -Mp lib/
3396 hg log -Mp lib/
3395
3397
3396 - all revision numbers that match a keyword::
3398 - all revision numbers that match a keyword::
3397
3399
3398 hg log -k bug --template "{rev}\\n"
3400 hg log -k bug --template "{rev}\\n"
3399
3401
3400 - the full hash identifier of the working directory parent::
3402 - the full hash identifier of the working directory parent::
3401
3403
3402 hg log -r . --template "{node}\\n"
3404 hg log -r . --template "{node}\\n"
3403
3405
3404 - list available log templates::
3406 - list available log templates::
3405
3407
3406 hg log -T list
3408 hg log -T list
3407
3409
3408 - check if a given changeset is included in a tagged release::
3410 - check if a given changeset is included in a tagged release::
3409
3411
3410 hg log -r "a21ccf and ancestor(1.9)"
3412 hg log -r "a21ccf and ancestor(1.9)"
3411
3413
3412 - find all changesets by some user in a date range::
3414 - find all changesets by some user in a date range::
3413
3415
3414 hg log -k alice -d "may 2008 to jul 2008"
3416 hg log -k alice -d "may 2008 to jul 2008"
3415
3417
3416 - summary of all changesets after the last tag::
3418 - summary of all changesets after the last tag::
3417
3419
3418 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3420 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3419
3421
3420 See :hg:`help dates` for a list of formats valid for -d/--date.
3422 See :hg:`help dates` for a list of formats valid for -d/--date.
3421
3423
3422 See :hg:`help revisions` for more about specifying and ordering
3424 See :hg:`help revisions` for more about specifying and ordering
3423 revisions.
3425 revisions.
3424
3426
3425 See :hg:`help templates` for more about pre-packaged styles and
3427 See :hg:`help templates` for more about pre-packaged styles and
3426 specifying custom templates.
3428 specifying custom templates.
3427
3429
3428 Returns 0 on success.
3430 Returns 0 on success.
3429
3431
3430 """
3432 """
3431 opts = pycompat.byteskwargs(opts)
3433 opts = pycompat.byteskwargs(opts)
3432 if opts.get('follow') and opts.get('rev'):
3434 if opts.get('follow') and opts.get('rev'):
3433 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3435 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3434 del opts['follow']
3436 del opts['follow']
3435
3437
3436 if opts.get('graph'):
3438 if opts.get('graph'):
3437 return cmdutil.graphlog(ui, repo, pats, opts)
3439 return cmdutil.graphlog(ui, repo, pats, opts)
3438
3440
3439 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3441 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3440 limit = cmdutil.loglimit(opts)
3442 limit = cmdutil.loglimit(opts)
3441 count = 0
3443 count = 0
3442
3444
3443 getrenamed = None
3445 getrenamed = None
3444 if opts.get('copies'):
3446 if opts.get('copies'):
3445 endrev = None
3447 endrev = None
3446 if opts.get('rev'):
3448 if opts.get('rev'):
3447 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3449 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3448 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3450 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3449
3451
3450 ui.pager('log')
3452 ui.pager('log')
3451 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3453 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3452 for rev in revs:
3454 for rev in revs:
3453 if count == limit:
3455 if count == limit:
3454 break
3456 break
3455 ctx = repo[rev]
3457 ctx = repo[rev]
3456 copies = None
3458 copies = None
3457 if getrenamed is not None and rev:
3459 if getrenamed is not None and rev:
3458 copies = []
3460 copies = []
3459 for fn in ctx.files():
3461 for fn in ctx.files():
3460 rename = getrenamed(fn, rev)
3462 rename = getrenamed(fn, rev)
3461 if rename:
3463 if rename:
3462 copies.append((fn, rename[0]))
3464 copies.append((fn, rename[0]))
3463 if filematcher:
3465 if filematcher:
3464 revmatchfn = filematcher(ctx.rev())
3466 revmatchfn = filematcher(ctx.rev())
3465 else:
3467 else:
3466 revmatchfn = None
3468 revmatchfn = None
3467 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3469 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3468 if displayer.flush(ctx):
3470 if displayer.flush(ctx):
3469 count += 1
3471 count += 1
3470
3472
3471 displayer.close()
3473 displayer.close()
3472
3474
3473 @command('manifest',
3475 @command('manifest',
3474 [('r', 'rev', '', _('revision to display'), _('REV')),
3476 [('r', 'rev', '', _('revision to display'), _('REV')),
3475 ('', 'all', False, _("list files from all revisions"))]
3477 ('', 'all', False, _("list files from all revisions"))]
3476 + formatteropts,
3478 + formatteropts,
3477 _('[-r REV]'))
3479 _('[-r REV]'))
3478 def manifest(ui, repo, node=None, rev=None, **opts):
3480 def manifest(ui, repo, node=None, rev=None, **opts):
3479 """output the current or given revision of the project manifest
3481 """output the current or given revision of the project manifest
3480
3482
3481 Print a list of version controlled files for the given revision.
3483 Print a list of version controlled files for the given revision.
3482 If no revision is given, the first parent of the working directory
3484 If no revision is given, the first parent of the working directory
3483 is used, or the null revision if no revision is checked out.
3485 is used, or the null revision if no revision is checked out.
3484
3486
3485 With -v, print file permissions, symlink and executable bits.
3487 With -v, print file permissions, symlink and executable bits.
3486 With --debug, print file revision hashes.
3488 With --debug, print file revision hashes.
3487
3489
3488 If option --all is specified, the list of all files from all revisions
3490 If option --all is specified, the list of all files from all revisions
3489 is printed. This includes deleted and renamed files.
3491 is printed. This includes deleted and renamed files.
3490
3492
3491 Returns 0 on success.
3493 Returns 0 on success.
3492 """
3494 """
3493 opts = pycompat.byteskwargs(opts)
3495 opts = pycompat.byteskwargs(opts)
3494 fm = ui.formatter('manifest', opts)
3496 fm = ui.formatter('manifest', opts)
3495
3497
3496 if opts.get('all'):
3498 if opts.get('all'):
3497 if rev or node:
3499 if rev or node:
3498 raise error.Abort(_("can't specify a revision with --all"))
3500 raise error.Abort(_("can't specify a revision with --all"))
3499
3501
3500 res = []
3502 res = []
3501 prefix = "data/"
3503 prefix = "data/"
3502 suffix = ".i"
3504 suffix = ".i"
3503 plen = len(prefix)
3505 plen = len(prefix)
3504 slen = len(suffix)
3506 slen = len(suffix)
3505 with repo.lock():
3507 with repo.lock():
3506 for fn, b, size in repo.store.datafiles():
3508 for fn, b, size in repo.store.datafiles():
3507 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3509 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3508 res.append(fn[plen:-slen])
3510 res.append(fn[plen:-slen])
3509 ui.pager('manifest')
3511 ui.pager('manifest')
3510 for f in res:
3512 for f in res:
3511 fm.startitem()
3513 fm.startitem()
3512 fm.write("path", '%s\n', f)
3514 fm.write("path", '%s\n', f)
3513 fm.end()
3515 fm.end()
3514 return
3516 return
3515
3517
3516 if rev and node:
3518 if rev and node:
3517 raise error.Abort(_("please specify just one revision"))
3519 raise error.Abort(_("please specify just one revision"))
3518
3520
3519 if not node:
3521 if not node:
3520 node = rev
3522 node = rev
3521
3523
3522 char = {'l': '@', 'x': '*', '': ''}
3524 char = {'l': '@', 'x': '*', '': ''}
3523 mode = {'l': '644', 'x': '755', '': '644'}
3525 mode = {'l': '644', 'x': '755', '': '644'}
3524 ctx = scmutil.revsingle(repo, node)
3526 ctx = scmutil.revsingle(repo, node)
3525 mf = ctx.manifest()
3527 mf = ctx.manifest()
3526 ui.pager('manifest')
3528 ui.pager('manifest')
3527 for f in ctx:
3529 for f in ctx:
3528 fm.startitem()
3530 fm.startitem()
3529 fl = ctx[f].flags()
3531 fl = ctx[f].flags()
3530 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3532 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3531 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3533 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3532 fm.write('path', '%s\n', f)
3534 fm.write('path', '%s\n', f)
3533 fm.end()
3535 fm.end()
3534
3536
3535 @command('^merge',
3537 @command('^merge',
3536 [('f', 'force', None,
3538 [('f', 'force', None,
3537 _('force a merge including outstanding changes (DEPRECATED)')),
3539 _('force a merge including outstanding changes (DEPRECATED)')),
3538 ('r', 'rev', '', _('revision to merge'), _('REV')),
3540 ('r', 'rev', '', _('revision to merge'), _('REV')),
3539 ('P', 'preview', None,
3541 ('P', 'preview', None,
3540 _('review revisions to merge (no merge is performed)'))
3542 _('review revisions to merge (no merge is performed)'))
3541 ] + mergetoolopts,
3543 ] + mergetoolopts,
3542 _('[-P] [[-r] REV]'))
3544 _('[-P] [[-r] REV]'))
3543 def merge(ui, repo, node=None, **opts):
3545 def merge(ui, repo, node=None, **opts):
3544 """merge another revision into working directory
3546 """merge another revision into working directory
3545
3547
3546 The current working directory is updated with all changes made in
3548 The current working directory is updated with all changes made in
3547 the requested revision since the last common predecessor revision.
3549 the requested revision since the last common predecessor revision.
3548
3550
3549 Files that changed between either parent are marked as changed for
3551 Files that changed between either parent are marked as changed for
3550 the next commit and a commit must be performed before any further
3552 the next commit and a commit must be performed before any further
3551 updates to the repository are allowed. The next commit will have
3553 updates to the repository are allowed. The next commit will have
3552 two parents.
3554 two parents.
3553
3555
3554 ``--tool`` can be used to specify the merge tool used for file
3556 ``--tool`` can be used to specify the merge tool used for file
3555 merges. It overrides the HGMERGE environment variable and your
3557 merges. It overrides the HGMERGE environment variable and your
3556 configuration files. See :hg:`help merge-tools` for options.
3558 configuration files. See :hg:`help merge-tools` for options.
3557
3559
3558 If no revision is specified, the working directory's parent is a
3560 If no revision is specified, the working directory's parent is a
3559 head revision, and the current branch contains exactly one other
3561 head revision, and the current branch contains exactly one other
3560 head, the other head is merged with by default. Otherwise, an
3562 head, the other head is merged with by default. Otherwise, an
3561 explicit revision with which to merge with must be provided.
3563 explicit revision with which to merge with must be provided.
3562
3564
3563 See :hg:`help resolve` for information on handling file conflicts.
3565 See :hg:`help resolve` for information on handling file conflicts.
3564
3566
3565 To undo an uncommitted merge, use :hg:`update --clean .` which
3567 To undo an uncommitted merge, use :hg:`update --clean .` which
3566 will check out a clean copy of the original merge parent, losing
3568 will check out a clean copy of the original merge parent, losing
3567 all changes.
3569 all changes.
3568
3570
3569 Returns 0 on success, 1 if there are unresolved files.
3571 Returns 0 on success, 1 if there are unresolved files.
3570 """
3572 """
3571
3573
3572 opts = pycompat.byteskwargs(opts)
3574 opts = pycompat.byteskwargs(opts)
3573 if opts.get('rev') and node:
3575 if opts.get('rev') and node:
3574 raise error.Abort(_("please specify just one revision"))
3576 raise error.Abort(_("please specify just one revision"))
3575 if not node:
3577 if not node:
3576 node = opts.get('rev')
3578 node = opts.get('rev')
3577
3579
3578 if node:
3580 if node:
3579 node = scmutil.revsingle(repo, node).node()
3581 node = scmutil.revsingle(repo, node).node()
3580
3582
3581 if not node:
3583 if not node:
3582 node = repo[destutil.destmerge(repo)].node()
3584 node = repo[destutil.destmerge(repo)].node()
3583
3585
3584 if opts.get('preview'):
3586 if opts.get('preview'):
3585 # find nodes that are ancestors of p2 but not of p1
3587 # find nodes that are ancestors of p2 but not of p1
3586 p1 = repo.lookup('.')
3588 p1 = repo.lookup('.')
3587 p2 = repo.lookup(node)
3589 p2 = repo.lookup(node)
3588 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3590 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3589
3591
3590 displayer = cmdutil.show_changeset(ui, repo, opts)
3592 displayer = cmdutil.show_changeset(ui, repo, opts)
3591 for node in nodes:
3593 for node in nodes:
3592 displayer.show(repo[node])
3594 displayer.show(repo[node])
3593 displayer.close()
3595 displayer.close()
3594 return 0
3596 return 0
3595
3597
3596 try:
3598 try:
3597 # ui.forcemerge is an internal variable, do not document
3599 # ui.forcemerge is an internal variable, do not document
3598 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3600 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3599 force = opts.get('force')
3601 force = opts.get('force')
3600 labels = ['working copy', 'merge rev']
3602 labels = ['working copy', 'merge rev']
3601 return hg.merge(repo, node, force=force, mergeforce=force,
3603 return hg.merge(repo, node, force=force, mergeforce=force,
3602 labels=labels)
3604 labels=labels)
3603 finally:
3605 finally:
3604 ui.setconfig('ui', 'forcemerge', '', 'merge')
3606 ui.setconfig('ui', 'forcemerge', '', 'merge')
3605
3607
3606 @command('outgoing|out',
3608 @command('outgoing|out',
3607 [('f', 'force', None, _('run even when the destination is unrelated')),
3609 [('f', 'force', None, _('run even when the destination is unrelated')),
3608 ('r', 'rev', [],
3610 ('r', 'rev', [],
3609 _('a changeset intended to be included in the destination'), _('REV')),
3611 _('a changeset intended to be included in the destination'), _('REV')),
3610 ('n', 'newest-first', None, _('show newest record first')),
3612 ('n', 'newest-first', None, _('show newest record first')),
3611 ('B', 'bookmarks', False, _('compare bookmarks')),
3613 ('B', 'bookmarks', False, _('compare bookmarks')),
3612 ('b', 'branch', [], _('a specific branch you would like to push'),
3614 ('b', 'branch', [], _('a specific branch you would like to push'),
3613 _('BRANCH')),
3615 _('BRANCH')),
3614 ] + logopts + remoteopts + subrepoopts,
3616 ] + logopts + remoteopts + subrepoopts,
3615 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3617 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3616 def outgoing(ui, repo, dest=None, **opts):
3618 def outgoing(ui, repo, dest=None, **opts):
3617 """show changesets not found in the destination
3619 """show changesets not found in the destination
3618
3620
3619 Show changesets not found in the specified destination repository
3621 Show changesets not found in the specified destination repository
3620 or the default push location. These are the changesets that would
3622 or the default push location. These are the changesets that would
3621 be pushed if a push was requested.
3623 be pushed if a push was requested.
3622
3624
3623 See pull for details of valid destination formats.
3625 See pull for details of valid destination formats.
3624
3626
3625 .. container:: verbose
3627 .. container:: verbose
3626
3628
3627 With -B/--bookmarks, the result of bookmark comparison between
3629 With -B/--bookmarks, the result of bookmark comparison between
3628 local and remote repositories is displayed. With -v/--verbose,
3630 local and remote repositories is displayed. With -v/--verbose,
3629 status is also displayed for each bookmark like below::
3631 status is also displayed for each bookmark like below::
3630
3632
3631 BM1 01234567890a added
3633 BM1 01234567890a added
3632 BM2 deleted
3634 BM2 deleted
3633 BM3 234567890abc advanced
3635 BM3 234567890abc advanced
3634 BM4 34567890abcd diverged
3636 BM4 34567890abcd diverged
3635 BM5 4567890abcde changed
3637 BM5 4567890abcde changed
3636
3638
3637 The action taken when pushing depends on the
3639 The action taken when pushing depends on the
3638 status of each bookmark:
3640 status of each bookmark:
3639
3641
3640 :``added``: push with ``-B`` will create it
3642 :``added``: push with ``-B`` will create it
3641 :``deleted``: push with ``-B`` will delete it
3643 :``deleted``: push with ``-B`` will delete it
3642 :``advanced``: push will update it
3644 :``advanced``: push will update it
3643 :``diverged``: push with ``-B`` will update it
3645 :``diverged``: push with ``-B`` will update it
3644 :``changed``: push with ``-B`` will update it
3646 :``changed``: push with ``-B`` will update it
3645
3647
3646 From the point of view of pushing behavior, bookmarks
3648 From the point of view of pushing behavior, bookmarks
3647 existing only in the remote repository are treated as
3649 existing only in the remote repository are treated as
3648 ``deleted``, even if it is in fact added remotely.
3650 ``deleted``, even if it is in fact added remotely.
3649
3651
3650 Returns 0 if there are outgoing changes, 1 otherwise.
3652 Returns 0 if there are outgoing changes, 1 otherwise.
3651 """
3653 """
3652 opts = pycompat.byteskwargs(opts)
3654 opts = pycompat.byteskwargs(opts)
3653 if opts.get('graph'):
3655 if opts.get('graph'):
3654 cmdutil.checkunsupportedgraphflags([], opts)
3656 cmdutil.checkunsupportedgraphflags([], opts)
3655 o, other = hg._outgoing(ui, repo, dest, opts)
3657 o, other = hg._outgoing(ui, repo, dest, opts)
3656 if not o:
3658 if not o:
3657 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3659 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3658 return
3660 return
3659
3661
3660 revdag = cmdutil.graphrevs(repo, o, opts)
3662 revdag = cmdutil.graphrevs(repo, o, opts)
3661 ui.pager('outgoing')
3663 ui.pager('outgoing')
3662 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3664 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3663 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3665 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3664 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3666 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3665 return 0
3667 return 0
3666
3668
3667 if opts.get('bookmarks'):
3669 if opts.get('bookmarks'):
3668 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3670 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3669 dest, branches = hg.parseurl(dest, opts.get('branch'))
3671 dest, branches = hg.parseurl(dest, opts.get('branch'))
3670 other = hg.peer(repo, opts, dest)
3672 other = hg.peer(repo, opts, dest)
3671 if 'bookmarks' not in other.listkeys('namespaces'):
3673 if 'bookmarks' not in other.listkeys('namespaces'):
3672 ui.warn(_("remote doesn't support bookmarks\n"))
3674 ui.warn(_("remote doesn't support bookmarks\n"))
3673 return 0
3675 return 0
3674 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3676 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3675 ui.pager('outgoing')
3677 ui.pager('outgoing')
3676 return bookmarks.outgoing(ui, repo, other)
3678 return bookmarks.outgoing(ui, repo, other)
3677
3679
3678 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3680 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3679 try:
3681 try:
3680 return hg.outgoing(ui, repo, dest, opts)
3682 return hg.outgoing(ui, repo, dest, opts)
3681 finally:
3683 finally:
3682 del repo._subtoppath
3684 del repo._subtoppath
3683
3685
3684 @command('parents',
3686 @command('parents',
3685 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3687 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3686 ] + templateopts,
3688 ] + templateopts,
3687 _('[-r REV] [FILE]'),
3689 _('[-r REV] [FILE]'),
3688 inferrepo=True)
3690 inferrepo=True)
3689 def parents(ui, repo, file_=None, **opts):
3691 def parents(ui, repo, file_=None, **opts):
3690 """show the parents of the working directory or revision (DEPRECATED)
3692 """show the parents of the working directory or revision (DEPRECATED)
3691
3693
3692 Print the working directory's parent revisions. If a revision is
3694 Print the working directory's parent revisions. If a revision is
3693 given via -r/--rev, the parent of that revision will be printed.
3695 given via -r/--rev, the parent of that revision will be printed.
3694 If a file argument is given, the revision in which the file was
3696 If a file argument is given, the revision in which the file was
3695 last changed (before the working directory revision or the
3697 last changed (before the working directory revision or the
3696 argument to --rev if given) is printed.
3698 argument to --rev if given) is printed.
3697
3699
3698 This command is equivalent to::
3700 This command is equivalent to::
3699
3701
3700 hg log -r "p1()+p2()" or
3702 hg log -r "p1()+p2()" or
3701 hg log -r "p1(REV)+p2(REV)" or
3703 hg log -r "p1(REV)+p2(REV)" or
3702 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3704 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3703 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3705 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3704
3706
3705 See :hg:`summary` and :hg:`help revsets` for related information.
3707 See :hg:`summary` and :hg:`help revsets` for related information.
3706
3708
3707 Returns 0 on success.
3709 Returns 0 on success.
3708 """
3710 """
3709
3711
3710 opts = pycompat.byteskwargs(opts)
3712 opts = pycompat.byteskwargs(opts)
3711 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3713 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3712
3714
3713 if file_:
3715 if file_:
3714 m = scmutil.match(ctx, (file_,), opts)
3716 m = scmutil.match(ctx, (file_,), opts)
3715 if m.anypats() or len(m.files()) != 1:
3717 if m.anypats() or len(m.files()) != 1:
3716 raise error.Abort(_('can only specify an explicit filename'))
3718 raise error.Abort(_('can only specify an explicit filename'))
3717 file_ = m.files()[0]
3719 file_ = m.files()[0]
3718 filenodes = []
3720 filenodes = []
3719 for cp in ctx.parents():
3721 for cp in ctx.parents():
3720 if not cp:
3722 if not cp:
3721 continue
3723 continue
3722 try:
3724 try:
3723 filenodes.append(cp.filenode(file_))
3725 filenodes.append(cp.filenode(file_))
3724 except error.LookupError:
3726 except error.LookupError:
3725 pass
3727 pass
3726 if not filenodes:
3728 if not filenodes:
3727 raise error.Abort(_("'%s' not found in manifest!") % file_)
3729 raise error.Abort(_("'%s' not found in manifest!") % file_)
3728 p = []
3730 p = []
3729 for fn in filenodes:
3731 for fn in filenodes:
3730 fctx = repo.filectx(file_, fileid=fn)
3732 fctx = repo.filectx(file_, fileid=fn)
3731 p.append(fctx.node())
3733 p.append(fctx.node())
3732 else:
3734 else:
3733 p = [cp.node() for cp in ctx.parents()]
3735 p = [cp.node() for cp in ctx.parents()]
3734
3736
3735 displayer = cmdutil.show_changeset(ui, repo, opts)
3737 displayer = cmdutil.show_changeset(ui, repo, opts)
3736 for n in p:
3738 for n in p:
3737 if n != nullid:
3739 if n != nullid:
3738 displayer.show(repo[n])
3740 displayer.show(repo[n])
3739 displayer.close()
3741 displayer.close()
3740
3742
3741 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3743 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3742 def paths(ui, repo, search=None, **opts):
3744 def paths(ui, repo, search=None, **opts):
3743 """show aliases for remote repositories
3745 """show aliases for remote repositories
3744
3746
3745 Show definition of symbolic path name NAME. If no name is given,
3747 Show definition of symbolic path name NAME. If no name is given,
3746 show definition of all available names.
3748 show definition of all available names.
3747
3749
3748 Option -q/--quiet suppresses all output when searching for NAME
3750 Option -q/--quiet suppresses all output when searching for NAME
3749 and shows only the path names when listing all definitions.
3751 and shows only the path names when listing all definitions.
3750
3752
3751 Path names are defined in the [paths] section of your
3753 Path names are defined in the [paths] section of your
3752 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3754 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3753 repository, ``.hg/hgrc`` is used, too.
3755 repository, ``.hg/hgrc`` is used, too.
3754
3756
3755 The path names ``default`` and ``default-push`` have a special
3757 The path names ``default`` and ``default-push`` have a special
3756 meaning. When performing a push or pull operation, they are used
3758 meaning. When performing a push or pull operation, they are used
3757 as fallbacks if no location is specified on the command-line.
3759 as fallbacks if no location is specified on the command-line.
3758 When ``default-push`` is set, it will be used for push and
3760 When ``default-push`` is set, it will be used for push and
3759 ``default`` will be used for pull; otherwise ``default`` is used
3761 ``default`` will be used for pull; otherwise ``default`` is used
3760 as the fallback for both. When cloning a repository, the clone
3762 as the fallback for both. When cloning a repository, the clone
3761 source is written as ``default`` in ``.hg/hgrc``.
3763 source is written as ``default`` in ``.hg/hgrc``.
3762
3764
3763 .. note::
3765 .. note::
3764
3766
3765 ``default`` and ``default-push`` apply to all inbound (e.g.
3767 ``default`` and ``default-push`` apply to all inbound (e.g.
3766 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3768 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3767 and :hg:`bundle`) operations.
3769 and :hg:`bundle`) operations.
3768
3770
3769 See :hg:`help urls` for more information.
3771 See :hg:`help urls` for more information.
3770
3772
3771 Returns 0 on success.
3773 Returns 0 on success.
3772 """
3774 """
3773
3775
3774 opts = pycompat.byteskwargs(opts)
3776 opts = pycompat.byteskwargs(opts)
3775 ui.pager('paths')
3777 ui.pager('paths')
3776 if search:
3778 if search:
3777 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3779 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3778 if name == search]
3780 if name == search]
3779 else:
3781 else:
3780 pathitems = sorted(ui.paths.iteritems())
3782 pathitems = sorted(ui.paths.iteritems())
3781
3783
3782 fm = ui.formatter('paths', opts)
3784 fm = ui.formatter('paths', opts)
3783 if fm.isplain():
3785 if fm.isplain():
3784 hidepassword = util.hidepassword
3786 hidepassword = util.hidepassword
3785 else:
3787 else:
3786 hidepassword = str
3788 hidepassword = str
3787 if ui.quiet:
3789 if ui.quiet:
3788 namefmt = '%s\n'
3790 namefmt = '%s\n'
3789 else:
3791 else:
3790 namefmt = '%s = '
3792 namefmt = '%s = '
3791 showsubopts = not search and not ui.quiet
3793 showsubopts = not search and not ui.quiet
3792
3794
3793 for name, path in pathitems:
3795 for name, path in pathitems:
3794 fm.startitem()
3796 fm.startitem()
3795 fm.condwrite(not search, 'name', namefmt, name)
3797 fm.condwrite(not search, 'name', namefmt, name)
3796 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3798 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3797 for subopt, value in sorted(path.suboptions.items()):
3799 for subopt, value in sorted(path.suboptions.items()):
3798 assert subopt not in ('name', 'url')
3800 assert subopt not in ('name', 'url')
3799 if showsubopts:
3801 if showsubopts:
3800 fm.plain('%s:%s = ' % (name, subopt))
3802 fm.plain('%s:%s = ' % (name, subopt))
3801 fm.condwrite(showsubopts, subopt, '%s\n', value)
3803 fm.condwrite(showsubopts, subopt, '%s\n', value)
3802
3804
3803 fm.end()
3805 fm.end()
3804
3806
3805 if search and not pathitems:
3807 if search and not pathitems:
3806 if not ui.quiet:
3808 if not ui.quiet:
3807 ui.warn(_("not found!\n"))
3809 ui.warn(_("not found!\n"))
3808 return 1
3810 return 1
3809 else:
3811 else:
3810 return 0
3812 return 0
3811
3813
3812 @command('phase',
3814 @command('phase',
3813 [('p', 'public', False, _('set changeset phase to public')),
3815 [('p', 'public', False, _('set changeset phase to public')),
3814 ('d', 'draft', False, _('set changeset phase to draft')),
3816 ('d', 'draft', False, _('set changeset phase to draft')),
3815 ('s', 'secret', False, _('set changeset phase to secret')),
3817 ('s', 'secret', False, _('set changeset phase to secret')),
3816 ('f', 'force', False, _('allow to move boundary backward')),
3818 ('f', 'force', False, _('allow to move boundary backward')),
3817 ('r', 'rev', [], _('target revision'), _('REV')),
3819 ('r', 'rev', [], _('target revision'), _('REV')),
3818 ],
3820 ],
3819 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3821 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3820 def phase(ui, repo, *revs, **opts):
3822 def phase(ui, repo, *revs, **opts):
3821 """set or show the current phase name
3823 """set or show the current phase name
3822
3824
3823 With no argument, show the phase name of the current revision(s).
3825 With no argument, show the phase name of the current revision(s).
3824
3826
3825 With one of -p/--public, -d/--draft or -s/--secret, change the
3827 With one of -p/--public, -d/--draft or -s/--secret, change the
3826 phase value of the specified revisions.
3828 phase value of the specified revisions.
3827
3829
3828 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3830 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3829 lower phase to an higher phase. Phases are ordered as follows::
3831 lower phase to an higher phase. Phases are ordered as follows::
3830
3832
3831 public < draft < secret
3833 public < draft < secret
3832
3834
3833 Returns 0 on success, 1 if some phases could not be changed.
3835 Returns 0 on success, 1 if some phases could not be changed.
3834
3836
3835 (For more information about the phases concept, see :hg:`help phases`.)
3837 (For more information about the phases concept, see :hg:`help phases`.)
3836 """
3838 """
3837 opts = pycompat.byteskwargs(opts)
3839 opts = pycompat.byteskwargs(opts)
3838 # search for a unique phase argument
3840 # search for a unique phase argument
3839 targetphase = None
3841 targetphase = None
3840 for idx, name in enumerate(phases.phasenames):
3842 for idx, name in enumerate(phases.phasenames):
3841 if opts[name]:
3843 if opts[name]:
3842 if targetphase is not None:
3844 if targetphase is not None:
3843 raise error.Abort(_('only one phase can be specified'))
3845 raise error.Abort(_('only one phase can be specified'))
3844 targetphase = idx
3846 targetphase = idx
3845
3847
3846 # look for specified revision
3848 # look for specified revision
3847 revs = list(revs)
3849 revs = list(revs)
3848 revs.extend(opts['rev'])
3850 revs.extend(opts['rev'])
3849 if not revs:
3851 if not revs:
3850 # display both parents as the second parent phase can influence
3852 # display both parents as the second parent phase can influence
3851 # the phase of a merge commit
3853 # the phase of a merge commit
3852 revs = [c.rev() for c in repo[None].parents()]
3854 revs = [c.rev() for c in repo[None].parents()]
3853
3855
3854 revs = scmutil.revrange(repo, revs)
3856 revs = scmutil.revrange(repo, revs)
3855
3857
3856 lock = None
3858 lock = None
3857 ret = 0
3859 ret = 0
3858 if targetphase is None:
3860 if targetphase is None:
3859 # display
3861 # display
3860 for r in revs:
3862 for r in revs:
3861 ctx = repo[r]
3863 ctx = repo[r]
3862 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3864 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3863 else:
3865 else:
3864 tr = None
3866 tr = None
3865 lock = repo.lock()
3867 lock = repo.lock()
3866 try:
3868 try:
3867 tr = repo.transaction("phase")
3869 tr = repo.transaction("phase")
3868 # set phase
3870 # set phase
3869 if not revs:
3871 if not revs:
3870 raise error.Abort(_('empty revision set'))
3872 raise error.Abort(_('empty revision set'))
3871 nodes = [repo[r].node() for r in revs]
3873 nodes = [repo[r].node() for r in revs]
3872 # moving revision from public to draft may hide them
3874 # moving revision from public to draft may hide them
3873 # We have to check result on an unfiltered repository
3875 # We have to check result on an unfiltered repository
3874 unfi = repo.unfiltered()
3876 unfi = repo.unfiltered()
3875 getphase = unfi._phasecache.phase
3877 getphase = unfi._phasecache.phase
3876 olddata = [getphase(unfi, r) for r in unfi]
3878 olddata = [getphase(unfi, r) for r in unfi]
3877 phases.advanceboundary(repo, tr, targetphase, nodes)
3879 phases.advanceboundary(repo, tr, targetphase, nodes)
3878 if opts['force']:
3880 if opts['force']:
3879 phases.retractboundary(repo, tr, targetphase, nodes)
3881 phases.retractboundary(repo, tr, targetphase, nodes)
3880 tr.close()
3882 tr.close()
3881 finally:
3883 finally:
3882 if tr is not None:
3884 if tr is not None:
3883 tr.release()
3885 tr.release()
3884 lock.release()
3886 lock.release()
3885 getphase = unfi._phasecache.phase
3887 getphase = unfi._phasecache.phase
3886 newdata = [getphase(unfi, r) for r in unfi]
3888 newdata = [getphase(unfi, r) for r in unfi]
3887 changes = sum(newdata[r] != olddata[r] for r in unfi)
3889 changes = sum(newdata[r] != olddata[r] for r in unfi)
3888 cl = unfi.changelog
3890 cl = unfi.changelog
3889 rejected = [n for n in nodes
3891 rejected = [n for n in nodes
3890 if newdata[cl.rev(n)] < targetphase]
3892 if newdata[cl.rev(n)] < targetphase]
3891 if rejected:
3893 if rejected:
3892 ui.warn(_('cannot move %i changesets to a higher '
3894 ui.warn(_('cannot move %i changesets to a higher '
3893 'phase, use --force\n') % len(rejected))
3895 'phase, use --force\n') % len(rejected))
3894 ret = 1
3896 ret = 1
3895 if changes:
3897 if changes:
3896 msg = _('phase changed for %i changesets\n') % changes
3898 msg = _('phase changed for %i changesets\n') % changes
3897 if ret:
3899 if ret:
3898 ui.status(msg)
3900 ui.status(msg)
3899 else:
3901 else:
3900 ui.note(msg)
3902 ui.note(msg)
3901 else:
3903 else:
3902 ui.warn(_('no phases changed\n'))
3904 ui.warn(_('no phases changed\n'))
3903 return ret
3905 return ret
3904
3906
3905 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3907 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3906 """Run after a changegroup has been added via pull/unbundle
3908 """Run after a changegroup has been added via pull/unbundle
3907
3909
3908 This takes arguments below:
3910 This takes arguments below:
3909
3911
3910 :modheads: change of heads by pull/unbundle
3912 :modheads: change of heads by pull/unbundle
3911 :optupdate: updating working directory is needed or not
3913 :optupdate: updating working directory is needed or not
3912 :checkout: update destination revision (or None to default destination)
3914 :checkout: update destination revision (or None to default destination)
3913 :brev: a name, which might be a bookmark to be activated after updating
3915 :brev: a name, which might be a bookmark to be activated after updating
3914 """
3916 """
3915 if modheads == 0:
3917 if modheads == 0:
3916 return
3918 return
3917 if optupdate:
3919 if optupdate:
3918 try:
3920 try:
3919 return hg.updatetotally(ui, repo, checkout, brev)
3921 return hg.updatetotally(ui, repo, checkout, brev)
3920 except error.UpdateAbort as inst:
3922 except error.UpdateAbort as inst:
3921 msg = _("not updating: %s") % str(inst)
3923 msg = _("not updating: %s") % str(inst)
3922 hint = inst.hint
3924 hint = inst.hint
3923 raise error.UpdateAbort(msg, hint=hint)
3925 raise error.UpdateAbort(msg, hint=hint)
3924 if modheads > 1:
3926 if modheads > 1:
3925 currentbranchheads = len(repo.branchheads())
3927 currentbranchheads = len(repo.branchheads())
3926 if currentbranchheads == modheads:
3928 if currentbranchheads == modheads:
3927 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3929 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3928 elif currentbranchheads > 1:
3930 elif currentbranchheads > 1:
3929 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3931 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3930 "merge)\n"))
3932 "merge)\n"))
3931 else:
3933 else:
3932 ui.status(_("(run 'hg heads' to see heads)\n"))
3934 ui.status(_("(run 'hg heads' to see heads)\n"))
3933 else:
3935 else:
3934 ui.status(_("(run 'hg update' to get a working copy)\n"))
3936 ui.status(_("(run 'hg update' to get a working copy)\n"))
3935
3937
3936 @command('^pull',
3938 @command('^pull',
3937 [('u', 'update', None,
3939 [('u', 'update', None,
3938 _('update to new branch head if changesets were pulled')),
3940 _('update to new branch head if changesets were pulled')),
3939 ('f', 'force', None, _('run even when remote repository is unrelated')),
3941 ('f', 'force', None, _('run even when remote repository is unrelated')),
3940 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3942 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3941 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3943 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3942 ('b', 'branch', [], _('a specific branch you would like to pull'),
3944 ('b', 'branch', [], _('a specific branch you would like to pull'),
3943 _('BRANCH')),
3945 _('BRANCH')),
3944 ] + remoteopts,
3946 ] + remoteopts,
3945 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3947 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3946 def pull(ui, repo, source="default", **opts):
3948 def pull(ui, repo, source="default", **opts):
3947 """pull changes from the specified source
3949 """pull changes from the specified source
3948
3950
3949 Pull changes from a remote repository to a local one.
3951 Pull changes from a remote repository to a local one.
3950
3952
3951 This finds all changes from the repository at the specified path
3953 This finds all changes from the repository at the specified path
3952 or URL and adds them to a local repository (the current one unless
3954 or URL and adds them to a local repository (the current one unless
3953 -R is specified). By default, this does not update the copy of the
3955 -R is specified). By default, this does not update the copy of the
3954 project in the working directory.
3956 project in the working directory.
3955
3957
3956 Use :hg:`incoming` if you want to see what would have been added
3958 Use :hg:`incoming` if you want to see what would have been added
3957 by a pull at the time you issued this command. If you then decide
3959 by a pull at the time you issued this command. If you then decide
3958 to add those changes to the repository, you should use :hg:`pull
3960 to add those changes to the repository, you should use :hg:`pull
3959 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3961 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3960
3962
3961 If SOURCE is omitted, the 'default' path will be used.
3963 If SOURCE is omitted, the 'default' path will be used.
3962 See :hg:`help urls` for more information.
3964 See :hg:`help urls` for more information.
3963
3965
3964 Specifying bookmark as ``.`` is equivalent to specifying the active
3966 Specifying bookmark as ``.`` is equivalent to specifying the active
3965 bookmark's name.
3967 bookmark's name.
3966
3968
3967 Returns 0 on success, 1 if an update had unresolved files.
3969 Returns 0 on success, 1 if an update had unresolved files.
3968 """
3970 """
3969
3971
3970 opts = pycompat.byteskwargs(opts)
3972 opts = pycompat.byteskwargs(opts)
3971 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3973 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3972 msg = _('update destination required by configuration')
3974 msg = _('update destination required by configuration')
3973 hint = _('use hg pull followed by hg update DEST')
3975 hint = _('use hg pull followed by hg update DEST')
3974 raise error.Abort(msg, hint=hint)
3976 raise error.Abort(msg, hint=hint)
3975
3977
3976 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3978 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3977 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3979 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3978 other = hg.peer(repo, opts, source)
3980 other = hg.peer(repo, opts, source)
3979 try:
3981 try:
3980 revs, checkout = hg.addbranchrevs(repo, other, branches,
3982 revs, checkout = hg.addbranchrevs(repo, other, branches,
3981 opts.get('rev'))
3983 opts.get('rev'))
3982
3984
3983
3985
3984 pullopargs = {}
3986 pullopargs = {}
3985 if opts.get('bookmark'):
3987 if opts.get('bookmark'):
3986 if not revs:
3988 if not revs:
3987 revs = []
3989 revs = []
3988 # The list of bookmark used here is not the one used to actually
3990 # The list of bookmark used here is not the one used to actually
3989 # update the bookmark name. This can result in the revision pulled
3991 # update the bookmark name. This can result in the revision pulled
3990 # not ending up with the name of the bookmark because of a race
3992 # not ending up with the name of the bookmark because of a race
3991 # condition on the server. (See issue 4689 for details)
3993 # condition on the server. (See issue 4689 for details)
3992 remotebookmarks = other.listkeys('bookmarks')
3994 remotebookmarks = other.listkeys('bookmarks')
3993 pullopargs['remotebookmarks'] = remotebookmarks
3995 pullopargs['remotebookmarks'] = remotebookmarks
3994 for b in opts['bookmark']:
3996 for b in opts['bookmark']:
3995 b = repo._bookmarks.expandname(b)
3997 b = repo._bookmarks.expandname(b)
3996 if b not in remotebookmarks:
3998 if b not in remotebookmarks:
3997 raise error.Abort(_('remote bookmark %s not found!') % b)
3999 raise error.Abort(_('remote bookmark %s not found!') % b)
3998 revs.append(remotebookmarks[b])
4000 revs.append(remotebookmarks[b])
3999
4001
4000 if revs:
4002 if revs:
4001 try:
4003 try:
4002 # When 'rev' is a bookmark name, we cannot guarantee that it
4004 # When 'rev' is a bookmark name, we cannot guarantee that it
4003 # will be updated with that name because of a race condition
4005 # will be updated with that name because of a race condition
4004 # server side. (See issue 4689 for details)
4006 # server side. (See issue 4689 for details)
4005 oldrevs = revs
4007 oldrevs = revs
4006 revs = [] # actually, nodes
4008 revs = [] # actually, nodes
4007 for r in oldrevs:
4009 for r in oldrevs:
4008 node = other.lookup(r)
4010 node = other.lookup(r)
4009 revs.append(node)
4011 revs.append(node)
4010 if r == checkout:
4012 if r == checkout:
4011 checkout = node
4013 checkout = node
4012 except error.CapabilityError:
4014 except error.CapabilityError:
4013 err = _("other repository doesn't support revision lookup, "
4015 err = _("other repository doesn't support revision lookup, "
4014 "so a rev cannot be specified.")
4016 "so a rev cannot be specified.")
4015 raise error.Abort(err)
4017 raise error.Abort(err)
4016
4018
4017 pullopargs.update(opts.get('opargs', {}))
4019 pullopargs.update(opts.get('opargs', {}))
4018 modheads = exchange.pull(repo, other, heads=revs,
4020 modheads = exchange.pull(repo, other, heads=revs,
4019 force=opts.get('force'),
4021 force=opts.get('force'),
4020 bookmarks=opts.get('bookmark', ()),
4022 bookmarks=opts.get('bookmark', ()),
4021 opargs=pullopargs).cgresult
4023 opargs=pullopargs).cgresult
4022
4024
4023 # brev is a name, which might be a bookmark to be activated at
4025 # brev is a name, which might be a bookmark to be activated at
4024 # the end of the update. In other words, it is an explicit
4026 # the end of the update. In other words, it is an explicit
4025 # destination of the update
4027 # destination of the update
4026 brev = None
4028 brev = None
4027
4029
4028 if checkout:
4030 if checkout:
4029 checkout = str(repo.changelog.rev(checkout))
4031 checkout = str(repo.changelog.rev(checkout))
4030
4032
4031 # order below depends on implementation of
4033 # order below depends on implementation of
4032 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4034 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4033 # because 'checkout' is determined without it.
4035 # because 'checkout' is determined without it.
4034 if opts.get('rev'):
4036 if opts.get('rev'):
4035 brev = opts['rev'][0]
4037 brev = opts['rev'][0]
4036 elif opts.get('branch'):
4038 elif opts.get('branch'):
4037 brev = opts['branch'][0]
4039 brev = opts['branch'][0]
4038 else:
4040 else:
4039 brev = branches[0]
4041 brev = branches[0]
4040 repo._subtoppath = source
4042 repo._subtoppath = source
4041 try:
4043 try:
4042 ret = postincoming(ui, repo, modheads, opts.get('update'),
4044 ret = postincoming(ui, repo, modheads, opts.get('update'),
4043 checkout, brev)
4045 checkout, brev)
4044
4046
4045 finally:
4047 finally:
4046 del repo._subtoppath
4048 del repo._subtoppath
4047
4049
4048 finally:
4050 finally:
4049 other.close()
4051 other.close()
4050 return ret
4052 return ret
4051
4053
4052 @command('^push',
4054 @command('^push',
4053 [('f', 'force', None, _('force push')),
4055 [('f', 'force', None, _('force push')),
4054 ('r', 'rev', [],
4056 ('r', 'rev', [],
4055 _('a changeset intended to be included in the destination'),
4057 _('a changeset intended to be included in the destination'),
4056 _('REV')),
4058 _('REV')),
4057 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4059 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4058 ('b', 'branch', [],
4060 ('b', 'branch', [],
4059 _('a specific branch you would like to push'), _('BRANCH')),
4061 _('a specific branch you would like to push'), _('BRANCH')),
4060 ('', 'new-branch', False, _('allow pushing a new branch')),
4062 ('', 'new-branch', False, _('allow pushing a new branch')),
4061 ] + remoteopts,
4063 ] + remoteopts,
4062 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4064 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4063 def push(ui, repo, dest=None, **opts):
4065 def push(ui, repo, dest=None, **opts):
4064 """push changes to the specified destination
4066 """push changes to the specified destination
4065
4067
4066 Push changesets from the local repository to the specified
4068 Push changesets from the local repository to the specified
4067 destination.
4069 destination.
4068
4070
4069 This operation is symmetrical to pull: it is identical to a pull
4071 This operation is symmetrical to pull: it is identical to a pull
4070 in the destination repository from the current one.
4072 in the destination repository from the current one.
4071
4073
4072 By default, push will not allow creation of new heads at the
4074 By default, push will not allow creation of new heads at the
4073 destination, since multiple heads would make it unclear which head
4075 destination, since multiple heads would make it unclear which head
4074 to use. In this situation, it is recommended to pull and merge
4076 to use. In this situation, it is recommended to pull and merge
4075 before pushing.
4077 before pushing.
4076
4078
4077 Use --new-branch if you want to allow push to create a new named
4079 Use --new-branch if you want to allow push to create a new named
4078 branch that is not present at the destination. This allows you to
4080 branch that is not present at the destination. This allows you to
4079 only create a new branch without forcing other changes.
4081 only create a new branch without forcing other changes.
4080
4082
4081 .. note::
4083 .. note::
4082
4084
4083 Extra care should be taken with the -f/--force option,
4085 Extra care should be taken with the -f/--force option,
4084 which will push all new heads on all branches, an action which will
4086 which will push all new heads on all branches, an action which will
4085 almost always cause confusion for collaborators.
4087 almost always cause confusion for collaborators.
4086
4088
4087 If -r/--rev is used, the specified revision and all its ancestors
4089 If -r/--rev is used, the specified revision and all its ancestors
4088 will be pushed to the remote repository.
4090 will be pushed to the remote repository.
4089
4091
4090 If -B/--bookmark is used, the specified bookmarked revision, its
4092 If -B/--bookmark is used, the specified bookmarked revision, its
4091 ancestors, and the bookmark will be pushed to the remote
4093 ancestors, and the bookmark will be pushed to the remote
4092 repository. Specifying ``.`` is equivalent to specifying the active
4094 repository. Specifying ``.`` is equivalent to specifying the active
4093 bookmark's name.
4095 bookmark's name.
4094
4096
4095 Please see :hg:`help urls` for important details about ``ssh://``
4097 Please see :hg:`help urls` for important details about ``ssh://``
4096 URLs. If DESTINATION is omitted, a default path will be used.
4098 URLs. If DESTINATION is omitted, a default path will be used.
4097
4099
4098 Returns 0 if push was successful, 1 if nothing to push.
4100 Returns 0 if push was successful, 1 if nothing to push.
4099 """
4101 """
4100
4102
4101 opts = pycompat.byteskwargs(opts)
4103 opts = pycompat.byteskwargs(opts)
4102 if opts.get('bookmark'):
4104 if opts.get('bookmark'):
4103 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4105 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4104 for b in opts['bookmark']:
4106 for b in opts['bookmark']:
4105 # translate -B options to -r so changesets get pushed
4107 # translate -B options to -r so changesets get pushed
4106 b = repo._bookmarks.expandname(b)
4108 b = repo._bookmarks.expandname(b)
4107 if b in repo._bookmarks:
4109 if b in repo._bookmarks:
4108 opts.setdefault('rev', []).append(b)
4110 opts.setdefault('rev', []).append(b)
4109 else:
4111 else:
4110 # if we try to push a deleted bookmark, translate it to null
4112 # if we try to push a deleted bookmark, translate it to null
4111 # this lets simultaneous -r, -b options continue working
4113 # this lets simultaneous -r, -b options continue working
4112 opts.setdefault('rev', []).append("null")
4114 opts.setdefault('rev', []).append("null")
4113
4115
4114 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4116 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4115 if not path:
4117 if not path:
4116 raise error.Abort(_('default repository not configured!'),
4118 raise error.Abort(_('default repository not configured!'),
4117 hint=_("see 'hg help config.paths'"))
4119 hint=_("see 'hg help config.paths'"))
4118 dest = path.pushloc or path.loc
4120 dest = path.pushloc or path.loc
4119 branches = (path.branch, opts.get('branch') or [])
4121 branches = (path.branch, opts.get('branch') or [])
4120 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4122 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4121 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4123 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4122 other = hg.peer(repo, opts, dest)
4124 other = hg.peer(repo, opts, dest)
4123
4125
4124 if revs:
4126 if revs:
4125 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4127 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4126 if not revs:
4128 if not revs:
4127 raise error.Abort(_("specified revisions evaluate to an empty set"),
4129 raise error.Abort(_("specified revisions evaluate to an empty set"),
4128 hint=_("use different revision arguments"))
4130 hint=_("use different revision arguments"))
4129 elif path.pushrev:
4131 elif path.pushrev:
4130 # It doesn't make any sense to specify ancestor revisions. So limit
4132 # It doesn't make any sense to specify ancestor revisions. So limit
4131 # to DAG heads to make discovery simpler.
4133 # to DAG heads to make discovery simpler.
4132 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4134 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4133 revs = scmutil.revrange(repo, [expr])
4135 revs = scmutil.revrange(repo, [expr])
4134 revs = [repo[rev].node() for rev in revs]
4136 revs = [repo[rev].node() for rev in revs]
4135 if not revs:
4137 if not revs:
4136 raise error.Abort(_('default push revset for path evaluates to an '
4138 raise error.Abort(_('default push revset for path evaluates to an '
4137 'empty set'))
4139 'empty set'))
4138
4140
4139 repo._subtoppath = dest
4141 repo._subtoppath = dest
4140 try:
4142 try:
4141 # push subrepos depth-first for coherent ordering
4143 # push subrepos depth-first for coherent ordering
4142 c = repo['']
4144 c = repo['']
4143 subs = c.substate # only repos that are committed
4145 subs = c.substate # only repos that are committed
4144 for s in sorted(subs):
4146 for s in sorted(subs):
4145 result = c.sub(s).push(opts)
4147 result = c.sub(s).push(opts)
4146 if result == 0:
4148 if result == 0:
4147 return not result
4149 return not result
4148 finally:
4150 finally:
4149 del repo._subtoppath
4151 del repo._subtoppath
4150 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4152 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4151 newbranch=opts.get('new_branch'),
4153 newbranch=opts.get('new_branch'),
4152 bookmarks=opts.get('bookmark', ()),
4154 bookmarks=opts.get('bookmark', ()),
4153 opargs=opts.get('opargs'))
4155 opargs=opts.get('opargs'))
4154
4156
4155 result = not pushop.cgresult
4157 result = not pushop.cgresult
4156
4158
4157 if pushop.bkresult is not None:
4159 if pushop.bkresult is not None:
4158 if pushop.bkresult == 2:
4160 if pushop.bkresult == 2:
4159 result = 2
4161 result = 2
4160 elif not result and pushop.bkresult:
4162 elif not result and pushop.bkresult:
4161 result = 2
4163 result = 2
4162
4164
4163 return result
4165 return result
4164
4166
4165 @command('recover', [])
4167 @command('recover', [])
4166 def recover(ui, repo):
4168 def recover(ui, repo):
4167 """roll back an interrupted transaction
4169 """roll back an interrupted transaction
4168
4170
4169 Recover from an interrupted commit or pull.
4171 Recover from an interrupted commit or pull.
4170
4172
4171 This command tries to fix the repository status after an
4173 This command tries to fix the repository status after an
4172 interrupted operation. It should only be necessary when Mercurial
4174 interrupted operation. It should only be necessary when Mercurial
4173 suggests it.
4175 suggests it.
4174
4176
4175 Returns 0 if successful, 1 if nothing to recover or verify fails.
4177 Returns 0 if successful, 1 if nothing to recover or verify fails.
4176 """
4178 """
4177 if repo.recover():
4179 if repo.recover():
4178 return hg.verify(repo)
4180 return hg.verify(repo)
4179 return 1
4181 return 1
4180
4182
4181 @command('^remove|rm',
4183 @command('^remove|rm',
4182 [('A', 'after', None, _('record delete for missing files')),
4184 [('A', 'after', None, _('record delete for missing files')),
4183 ('f', 'force', None,
4185 ('f', 'force', None,
4184 _('forget added files, delete modified files')),
4186 _('forget added files, delete modified files')),
4185 ] + subrepoopts + walkopts,
4187 ] + subrepoopts + walkopts,
4186 _('[OPTION]... FILE...'),
4188 _('[OPTION]... FILE...'),
4187 inferrepo=True)
4189 inferrepo=True)
4188 def remove(ui, repo, *pats, **opts):
4190 def remove(ui, repo, *pats, **opts):
4189 """remove the specified files on the next commit
4191 """remove the specified files on the next commit
4190
4192
4191 Schedule the indicated files for removal from the current branch.
4193 Schedule the indicated files for removal from the current branch.
4192
4194
4193 This command schedules the files to be removed at the next commit.
4195 This command schedules the files to be removed at the next commit.
4194 To undo a remove before that, see :hg:`revert`. To undo added
4196 To undo a remove before that, see :hg:`revert`. To undo added
4195 files, see :hg:`forget`.
4197 files, see :hg:`forget`.
4196
4198
4197 .. container:: verbose
4199 .. container:: verbose
4198
4200
4199 -A/--after can be used to remove only files that have already
4201 -A/--after can be used to remove only files that have already
4200 been deleted, -f/--force can be used to force deletion, and -Af
4202 been deleted, -f/--force can be used to force deletion, and -Af
4201 can be used to remove files from the next revision without
4203 can be used to remove files from the next revision without
4202 deleting them from the working directory.
4204 deleting them from the working directory.
4203
4205
4204 The following table details the behavior of remove for different
4206 The following table details the behavior of remove for different
4205 file states (columns) and option combinations (rows). The file
4207 file states (columns) and option combinations (rows). The file
4206 states are Added [A], Clean [C], Modified [M] and Missing [!]
4208 states are Added [A], Clean [C], Modified [M] and Missing [!]
4207 (as reported by :hg:`status`). The actions are Warn, Remove
4209 (as reported by :hg:`status`). The actions are Warn, Remove
4208 (from branch) and Delete (from disk):
4210 (from branch) and Delete (from disk):
4209
4211
4210 ========= == == == ==
4212 ========= == == == ==
4211 opt/state A C M !
4213 opt/state A C M !
4212 ========= == == == ==
4214 ========= == == == ==
4213 none W RD W R
4215 none W RD W R
4214 -f R RD RD R
4216 -f R RD RD R
4215 -A W W W R
4217 -A W W W R
4216 -Af R R R R
4218 -Af R R R R
4217 ========= == == == ==
4219 ========= == == == ==
4218
4220
4219 .. note::
4221 .. note::
4220
4222
4221 :hg:`remove` never deletes files in Added [A] state from the
4223 :hg:`remove` never deletes files in Added [A] state from the
4222 working directory, not even if ``--force`` is specified.
4224 working directory, not even if ``--force`` is specified.
4223
4225
4224 Returns 0 on success, 1 if any warnings encountered.
4226 Returns 0 on success, 1 if any warnings encountered.
4225 """
4227 """
4226
4228
4227 opts = pycompat.byteskwargs(opts)
4229 opts = pycompat.byteskwargs(opts)
4228 after, force = opts.get('after'), opts.get('force')
4230 after, force = opts.get('after'), opts.get('force')
4229 if not pats and not after:
4231 if not pats and not after:
4230 raise error.Abort(_('no files specified'))
4232 raise error.Abort(_('no files specified'))
4231
4233
4232 m = scmutil.match(repo[None], pats, opts)
4234 m = scmutil.match(repo[None], pats, opts)
4233 subrepos = opts.get('subrepos')
4235 subrepos = opts.get('subrepos')
4234 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4236 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4235
4237
4236 @command('rename|move|mv',
4238 @command('rename|move|mv',
4237 [('A', 'after', None, _('record a rename that has already occurred')),
4239 [('A', 'after', None, _('record a rename that has already occurred')),
4238 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4240 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4239 ] + walkopts + dryrunopts,
4241 ] + walkopts + dryrunopts,
4240 _('[OPTION]... SOURCE... DEST'))
4242 _('[OPTION]... SOURCE... DEST'))
4241 def rename(ui, repo, *pats, **opts):
4243 def rename(ui, repo, *pats, **opts):
4242 """rename files; equivalent of copy + remove
4244 """rename files; equivalent of copy + remove
4243
4245
4244 Mark dest as copies of sources; mark sources for deletion. If dest
4246 Mark dest as copies of sources; mark sources for deletion. If dest
4245 is a directory, copies are put in that directory. If dest is a
4247 is a directory, copies are put in that directory. If dest is a
4246 file, there can only be one source.
4248 file, there can only be one source.
4247
4249
4248 By default, this command copies the contents of files as they
4250 By default, this command copies the contents of files as they
4249 exist in the working directory. If invoked with -A/--after, the
4251 exist in the working directory. If invoked with -A/--after, the
4250 operation is recorded, but no copying is performed.
4252 operation is recorded, but no copying is performed.
4251
4253
4252 This command takes effect at the next commit. To undo a rename
4254 This command takes effect at the next commit. To undo a rename
4253 before that, see :hg:`revert`.
4255 before that, see :hg:`revert`.
4254
4256
4255 Returns 0 on success, 1 if errors are encountered.
4257 Returns 0 on success, 1 if errors are encountered.
4256 """
4258 """
4257 opts = pycompat.byteskwargs(opts)
4259 opts = pycompat.byteskwargs(opts)
4258 with repo.wlock(False):
4260 with repo.wlock(False):
4259 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4261 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4260
4262
4261 @command('resolve',
4263 @command('resolve',
4262 [('a', 'all', None, _('select all unresolved files')),
4264 [('a', 'all', None, _('select all unresolved files')),
4263 ('l', 'list', None, _('list state of files needing merge')),
4265 ('l', 'list', None, _('list state of files needing merge')),
4264 ('m', 'mark', None, _('mark files as resolved')),
4266 ('m', 'mark', None, _('mark files as resolved')),
4265 ('u', 'unmark', None, _('mark files as unresolved')),
4267 ('u', 'unmark', None, _('mark files as unresolved')),
4266 ('n', 'no-status', None, _('hide status prefix'))]
4268 ('n', 'no-status', None, _('hide status prefix'))]
4267 + mergetoolopts + walkopts + formatteropts,
4269 + mergetoolopts + walkopts + formatteropts,
4268 _('[OPTION]... [FILE]...'),
4270 _('[OPTION]... [FILE]...'),
4269 inferrepo=True)
4271 inferrepo=True)
4270 def resolve(ui, repo, *pats, **opts):
4272 def resolve(ui, repo, *pats, **opts):
4271 """redo merges or set/view the merge status of files
4273 """redo merges or set/view the merge status of files
4272
4274
4273 Merges with unresolved conflicts are often the result of
4275 Merges with unresolved conflicts are often the result of
4274 non-interactive merging using the ``internal:merge`` configuration
4276 non-interactive merging using the ``internal:merge`` configuration
4275 setting, or a command-line merge tool like ``diff3``. The resolve
4277 setting, or a command-line merge tool like ``diff3``. The resolve
4276 command is used to manage the files involved in a merge, after
4278 command is used to manage the files involved in a merge, after
4277 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4279 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4278 working directory must have two parents). See :hg:`help
4280 working directory must have two parents). See :hg:`help
4279 merge-tools` for information on configuring merge tools.
4281 merge-tools` for information on configuring merge tools.
4280
4282
4281 The resolve command can be used in the following ways:
4283 The resolve command can be used in the following ways:
4282
4284
4283 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4285 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4284 files, discarding any previous merge attempts. Re-merging is not
4286 files, discarding any previous merge attempts. Re-merging is not
4285 performed for files already marked as resolved. Use ``--all/-a``
4287 performed for files already marked as resolved. Use ``--all/-a``
4286 to select all unresolved files. ``--tool`` can be used to specify
4288 to select all unresolved files. ``--tool`` can be used to specify
4287 the merge tool used for the given files. It overrides the HGMERGE
4289 the merge tool used for the given files. It overrides the HGMERGE
4288 environment variable and your configuration files. Previous file
4290 environment variable and your configuration files. Previous file
4289 contents are saved with a ``.orig`` suffix.
4291 contents are saved with a ``.orig`` suffix.
4290
4292
4291 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4293 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4292 (e.g. after having manually fixed-up the files). The default is
4294 (e.g. after having manually fixed-up the files). The default is
4293 to mark all unresolved files.
4295 to mark all unresolved files.
4294
4296
4295 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4297 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4296 default is to mark all resolved files.
4298 default is to mark all resolved files.
4297
4299
4298 - :hg:`resolve -l`: list files which had or still have conflicts.
4300 - :hg:`resolve -l`: list files which had or still have conflicts.
4299 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4301 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4300 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4302 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4301 the list. See :hg:`help filesets` for details.
4303 the list. See :hg:`help filesets` for details.
4302
4304
4303 .. note::
4305 .. note::
4304
4306
4305 Mercurial will not let you commit files with unresolved merge
4307 Mercurial will not let you commit files with unresolved merge
4306 conflicts. You must use :hg:`resolve -m ...` before you can
4308 conflicts. You must use :hg:`resolve -m ...` before you can
4307 commit after a conflicting merge.
4309 commit after a conflicting merge.
4308
4310
4309 Returns 0 on success, 1 if any files fail a resolve attempt.
4311 Returns 0 on success, 1 if any files fail a resolve attempt.
4310 """
4312 """
4311
4313
4312 opts = pycompat.byteskwargs(opts)
4314 opts = pycompat.byteskwargs(opts)
4313 flaglist = 'all mark unmark list no_status'.split()
4315 flaglist = 'all mark unmark list no_status'.split()
4314 all, mark, unmark, show, nostatus = \
4316 all, mark, unmark, show, nostatus = \
4315 [opts.get(o) for o in flaglist]
4317 [opts.get(o) for o in flaglist]
4316
4318
4317 if (show and (mark or unmark)) or (mark and unmark):
4319 if (show and (mark or unmark)) or (mark and unmark):
4318 raise error.Abort(_("too many options specified"))
4320 raise error.Abort(_("too many options specified"))
4319 if pats and all:
4321 if pats and all:
4320 raise error.Abort(_("can't specify --all and patterns"))
4322 raise error.Abort(_("can't specify --all and patterns"))
4321 if not (all or pats or show or mark or unmark):
4323 if not (all or pats or show or mark or unmark):
4322 raise error.Abort(_('no files or directories specified'),
4324 raise error.Abort(_('no files or directories specified'),
4323 hint=('use --all to re-merge all unresolved files'))
4325 hint=('use --all to re-merge all unresolved files'))
4324
4326
4325 if show:
4327 if show:
4326 ui.pager('resolve')
4328 ui.pager('resolve')
4327 fm = ui.formatter('resolve', opts)
4329 fm = ui.formatter('resolve', opts)
4328 ms = mergemod.mergestate.read(repo)
4330 ms = mergemod.mergestate.read(repo)
4329 m = scmutil.match(repo[None], pats, opts)
4331 m = scmutil.match(repo[None], pats, opts)
4330 for f in ms:
4332 for f in ms:
4331 if not m(f):
4333 if not m(f):
4332 continue
4334 continue
4333 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4335 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4334 'd': 'driverresolved'}[ms[f]]
4336 'd': 'driverresolved'}[ms[f]]
4335 fm.startitem()
4337 fm.startitem()
4336 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4338 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4337 fm.write('path', '%s\n', f, label=l)
4339 fm.write('path', '%s\n', f, label=l)
4338 fm.end()
4340 fm.end()
4339 return 0
4341 return 0
4340
4342
4341 with repo.wlock():
4343 with repo.wlock():
4342 ms = mergemod.mergestate.read(repo)
4344 ms = mergemod.mergestate.read(repo)
4343
4345
4344 if not (ms.active() or repo.dirstate.p2() != nullid):
4346 if not (ms.active() or repo.dirstate.p2() != nullid):
4345 raise error.Abort(
4347 raise error.Abort(
4346 _('resolve command not applicable when not merging'))
4348 _('resolve command not applicable when not merging'))
4347
4349
4348 wctx = repo[None]
4350 wctx = repo[None]
4349
4351
4350 if ms.mergedriver and ms.mdstate() == 'u':
4352 if ms.mergedriver and ms.mdstate() == 'u':
4351 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4353 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4352 ms.commit()
4354 ms.commit()
4353 # allow mark and unmark to go through
4355 # allow mark and unmark to go through
4354 if not mark and not unmark and not proceed:
4356 if not mark and not unmark and not proceed:
4355 return 1
4357 return 1
4356
4358
4357 m = scmutil.match(wctx, pats, opts)
4359 m = scmutil.match(wctx, pats, opts)
4358 ret = 0
4360 ret = 0
4359 didwork = False
4361 didwork = False
4360 runconclude = False
4362 runconclude = False
4361
4363
4362 tocomplete = []
4364 tocomplete = []
4363 for f in ms:
4365 for f in ms:
4364 if not m(f):
4366 if not m(f):
4365 continue
4367 continue
4366
4368
4367 didwork = True
4369 didwork = True
4368
4370
4369 # don't let driver-resolved files be marked, and run the conclude
4371 # don't let driver-resolved files be marked, and run the conclude
4370 # step if asked to resolve
4372 # step if asked to resolve
4371 if ms[f] == "d":
4373 if ms[f] == "d":
4372 exact = m.exact(f)
4374 exact = m.exact(f)
4373 if mark:
4375 if mark:
4374 if exact:
4376 if exact:
4375 ui.warn(_('not marking %s as it is driver-resolved\n')
4377 ui.warn(_('not marking %s as it is driver-resolved\n')
4376 % f)
4378 % f)
4377 elif unmark:
4379 elif unmark:
4378 if exact:
4380 if exact:
4379 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4381 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4380 % f)
4382 % f)
4381 else:
4383 else:
4382 runconclude = True
4384 runconclude = True
4383 continue
4385 continue
4384
4386
4385 if mark:
4387 if mark:
4386 ms.mark(f, "r")
4388 ms.mark(f, "r")
4387 elif unmark:
4389 elif unmark:
4388 ms.mark(f, "u")
4390 ms.mark(f, "u")
4389 else:
4391 else:
4390 # backup pre-resolve (merge uses .orig for its own purposes)
4392 # backup pre-resolve (merge uses .orig for its own purposes)
4391 a = repo.wjoin(f)
4393 a = repo.wjoin(f)
4392 try:
4394 try:
4393 util.copyfile(a, a + ".resolve")
4395 util.copyfile(a, a + ".resolve")
4394 except (IOError, OSError) as inst:
4396 except (IOError, OSError) as inst:
4395 if inst.errno != errno.ENOENT:
4397 if inst.errno != errno.ENOENT:
4396 raise
4398 raise
4397
4399
4398 try:
4400 try:
4399 # preresolve file
4401 # preresolve file
4400 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4402 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4401 'resolve')
4403 'resolve')
4402 complete, r = ms.preresolve(f, wctx)
4404 complete, r = ms.preresolve(f, wctx)
4403 if not complete:
4405 if not complete:
4404 tocomplete.append(f)
4406 tocomplete.append(f)
4405 elif r:
4407 elif r:
4406 ret = 1
4408 ret = 1
4407 finally:
4409 finally:
4408 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4410 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4409 ms.commit()
4411 ms.commit()
4410
4412
4411 # replace filemerge's .orig file with our resolve file, but only
4413 # replace filemerge's .orig file with our resolve file, but only
4412 # for merges that are complete
4414 # for merges that are complete
4413 if complete:
4415 if complete:
4414 try:
4416 try:
4415 util.rename(a + ".resolve",
4417 util.rename(a + ".resolve",
4416 scmutil.origpath(ui, repo, a))
4418 scmutil.origpath(ui, repo, a))
4417 except OSError as inst:
4419 except OSError as inst:
4418 if inst.errno != errno.ENOENT:
4420 if inst.errno != errno.ENOENT:
4419 raise
4421 raise
4420
4422
4421 for f in tocomplete:
4423 for f in tocomplete:
4422 try:
4424 try:
4423 # resolve file
4425 # resolve file
4424 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4426 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4425 'resolve')
4427 'resolve')
4426 r = ms.resolve(f, wctx)
4428 r = ms.resolve(f, wctx)
4427 if r:
4429 if r:
4428 ret = 1
4430 ret = 1
4429 finally:
4431 finally:
4430 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4432 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4431 ms.commit()
4433 ms.commit()
4432
4434
4433 # replace filemerge's .orig file with our resolve file
4435 # replace filemerge's .orig file with our resolve file
4434 a = repo.wjoin(f)
4436 a = repo.wjoin(f)
4435 try:
4437 try:
4436 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4438 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4437 except OSError as inst:
4439 except OSError as inst:
4438 if inst.errno != errno.ENOENT:
4440 if inst.errno != errno.ENOENT:
4439 raise
4441 raise
4440
4442
4441 ms.commit()
4443 ms.commit()
4442 ms.recordactions()
4444 ms.recordactions()
4443
4445
4444 if not didwork and pats:
4446 if not didwork and pats:
4445 hint = None
4447 hint = None
4446 if not any([p for p in pats if p.find(':') >= 0]):
4448 if not any([p for p in pats if p.find(':') >= 0]):
4447 pats = ['path:%s' % p for p in pats]
4449 pats = ['path:%s' % p for p in pats]
4448 m = scmutil.match(wctx, pats, opts)
4450 m = scmutil.match(wctx, pats, opts)
4449 for f in ms:
4451 for f in ms:
4450 if not m(f):
4452 if not m(f):
4451 continue
4453 continue
4452 flags = ''.join(['-%s ' % o[0] for o in flaglist
4454 flags = ''.join(['-%s ' % o[0] for o in flaglist
4453 if opts.get(o)])
4455 if opts.get(o)])
4454 hint = _("(try: hg resolve %s%s)\n") % (
4456 hint = _("(try: hg resolve %s%s)\n") % (
4455 flags,
4457 flags,
4456 ' '.join(pats))
4458 ' '.join(pats))
4457 break
4459 break
4458 ui.warn(_("arguments do not match paths that need resolving\n"))
4460 ui.warn(_("arguments do not match paths that need resolving\n"))
4459 if hint:
4461 if hint:
4460 ui.warn(hint)
4462 ui.warn(hint)
4461 elif ms.mergedriver and ms.mdstate() != 's':
4463 elif ms.mergedriver and ms.mdstate() != 's':
4462 # run conclude step when either a driver-resolved file is requested
4464 # run conclude step when either a driver-resolved file is requested
4463 # or there are no driver-resolved files
4465 # or there are no driver-resolved files
4464 # we can't use 'ret' to determine whether any files are unresolved
4466 # we can't use 'ret' to determine whether any files are unresolved
4465 # because we might not have tried to resolve some
4467 # because we might not have tried to resolve some
4466 if ((runconclude or not list(ms.driverresolved()))
4468 if ((runconclude or not list(ms.driverresolved()))
4467 and not list(ms.unresolved())):
4469 and not list(ms.unresolved())):
4468 proceed = mergemod.driverconclude(repo, ms, wctx)
4470 proceed = mergemod.driverconclude(repo, ms, wctx)
4469 ms.commit()
4471 ms.commit()
4470 if not proceed:
4472 if not proceed:
4471 return 1
4473 return 1
4472
4474
4473 # Nudge users into finishing an unfinished operation
4475 # Nudge users into finishing an unfinished operation
4474 unresolvedf = list(ms.unresolved())
4476 unresolvedf = list(ms.unresolved())
4475 driverresolvedf = list(ms.driverresolved())
4477 driverresolvedf = list(ms.driverresolved())
4476 if not unresolvedf and not driverresolvedf:
4478 if not unresolvedf and not driverresolvedf:
4477 ui.status(_('(no more unresolved files)\n'))
4479 ui.status(_('(no more unresolved files)\n'))
4478 cmdutil.checkafterresolved(repo)
4480 cmdutil.checkafterresolved(repo)
4479 elif not unresolvedf:
4481 elif not unresolvedf:
4480 ui.status(_('(no more unresolved files -- '
4482 ui.status(_('(no more unresolved files -- '
4481 'run "hg resolve --all" to conclude)\n'))
4483 'run "hg resolve --all" to conclude)\n'))
4482
4484
4483 return ret
4485 return ret
4484
4486
4485 @command('revert',
4487 @command('revert',
4486 [('a', 'all', None, _('revert all changes when no arguments given')),
4488 [('a', 'all', None, _('revert all changes when no arguments given')),
4487 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4489 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4488 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4490 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4489 ('C', 'no-backup', None, _('do not save backup copies of files')),
4491 ('C', 'no-backup', None, _('do not save backup copies of files')),
4490 ('i', 'interactive', None,
4492 ('i', 'interactive', None,
4491 _('interactively select the changes (EXPERIMENTAL)')),
4493 _('interactively select the changes (EXPERIMENTAL)')),
4492 ] + walkopts + dryrunopts,
4494 ] + walkopts + dryrunopts,
4493 _('[OPTION]... [-r REV] [NAME]...'))
4495 _('[OPTION]... [-r REV] [NAME]...'))
4494 def revert(ui, repo, *pats, **opts):
4496 def revert(ui, repo, *pats, **opts):
4495 """restore files to their checkout state
4497 """restore files to their checkout state
4496
4498
4497 .. note::
4499 .. note::
4498
4500
4499 To check out earlier revisions, you should use :hg:`update REV`.
4501 To check out earlier revisions, you should use :hg:`update REV`.
4500 To cancel an uncommitted merge (and lose your changes),
4502 To cancel an uncommitted merge (and lose your changes),
4501 use :hg:`update --clean .`.
4503 use :hg:`update --clean .`.
4502
4504
4503 With no revision specified, revert the specified files or directories
4505 With no revision specified, revert the specified files or directories
4504 to the contents they had in the parent of the working directory.
4506 to the contents they had in the parent of the working directory.
4505 This restores the contents of files to an unmodified
4507 This restores the contents of files to an unmodified
4506 state and unschedules adds, removes, copies, and renames. If the
4508 state and unschedules adds, removes, copies, and renames. If the
4507 working directory has two parents, you must explicitly specify a
4509 working directory has two parents, you must explicitly specify a
4508 revision.
4510 revision.
4509
4511
4510 Using the -r/--rev or -d/--date options, revert the given files or
4512 Using the -r/--rev or -d/--date options, revert the given files or
4511 directories to their states as of a specific revision. Because
4513 directories to their states as of a specific revision. Because
4512 revert does not change the working directory parents, this will
4514 revert does not change the working directory parents, this will
4513 cause these files to appear modified. This can be helpful to "back
4515 cause these files to appear modified. This can be helpful to "back
4514 out" some or all of an earlier change. See :hg:`backout` for a
4516 out" some or all of an earlier change. See :hg:`backout` for a
4515 related method.
4517 related method.
4516
4518
4517 Modified files are saved with a .orig suffix before reverting.
4519 Modified files are saved with a .orig suffix before reverting.
4518 To disable these backups, use --no-backup. It is possible to store
4520 To disable these backups, use --no-backup. It is possible to store
4519 the backup files in a custom directory relative to the root of the
4521 the backup files in a custom directory relative to the root of the
4520 repository by setting the ``ui.origbackuppath`` configuration
4522 repository by setting the ``ui.origbackuppath`` configuration
4521 option.
4523 option.
4522
4524
4523 See :hg:`help dates` for a list of formats valid for -d/--date.
4525 See :hg:`help dates` for a list of formats valid for -d/--date.
4524
4526
4525 See :hg:`help backout` for a way to reverse the effect of an
4527 See :hg:`help backout` for a way to reverse the effect of an
4526 earlier changeset.
4528 earlier changeset.
4527
4529
4528 Returns 0 on success.
4530 Returns 0 on success.
4529 """
4531 """
4530
4532
4531 if opts.get("date"):
4533 if opts.get("date"):
4532 if opts.get("rev"):
4534 if opts.get("rev"):
4533 raise error.Abort(_("you can't specify a revision and a date"))
4535 raise error.Abort(_("you can't specify a revision and a date"))
4534 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4536 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4535
4537
4536 parent, p2 = repo.dirstate.parents()
4538 parent, p2 = repo.dirstate.parents()
4537 if not opts.get('rev') and p2 != nullid:
4539 if not opts.get('rev') and p2 != nullid:
4538 # revert after merge is a trap for new users (issue2915)
4540 # revert after merge is a trap for new users (issue2915)
4539 raise error.Abort(_('uncommitted merge with no revision specified'),
4541 raise error.Abort(_('uncommitted merge with no revision specified'),
4540 hint=_("use 'hg update' or see 'hg help revert'"))
4542 hint=_("use 'hg update' or see 'hg help revert'"))
4541
4543
4542 ctx = scmutil.revsingle(repo, opts.get('rev'))
4544 ctx = scmutil.revsingle(repo, opts.get('rev'))
4543
4545
4544 if (not (pats or opts.get('include') or opts.get('exclude') or
4546 if (not (pats or opts.get('include') or opts.get('exclude') or
4545 opts.get('all') or opts.get('interactive'))):
4547 opts.get('all') or opts.get('interactive'))):
4546 msg = _("no files or directories specified")
4548 msg = _("no files or directories specified")
4547 if p2 != nullid:
4549 if p2 != nullid:
4548 hint = _("uncommitted merge, use --all to discard all changes,"
4550 hint = _("uncommitted merge, use --all to discard all changes,"
4549 " or 'hg update -C .' to abort the merge")
4551 " or 'hg update -C .' to abort the merge")
4550 raise error.Abort(msg, hint=hint)
4552 raise error.Abort(msg, hint=hint)
4551 dirty = any(repo.status())
4553 dirty = any(repo.status())
4552 node = ctx.node()
4554 node = ctx.node()
4553 if node != parent:
4555 if node != parent:
4554 if dirty:
4556 if dirty:
4555 hint = _("uncommitted changes, use --all to discard all"
4557 hint = _("uncommitted changes, use --all to discard all"
4556 " changes, or 'hg update %s' to update") % ctx.rev()
4558 " changes, or 'hg update %s' to update") % ctx.rev()
4557 else:
4559 else:
4558 hint = _("use --all to revert all files,"
4560 hint = _("use --all to revert all files,"
4559 " or 'hg update %s' to update") % ctx.rev()
4561 " or 'hg update %s' to update") % ctx.rev()
4560 elif dirty:
4562 elif dirty:
4561 hint = _("uncommitted changes, use --all to discard all changes")
4563 hint = _("uncommitted changes, use --all to discard all changes")
4562 else:
4564 else:
4563 hint = _("use --all to revert all files")
4565 hint = _("use --all to revert all files")
4564 raise error.Abort(msg, hint=hint)
4566 raise error.Abort(msg, hint=hint)
4565
4567
4566 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4568 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4567
4569
4568 @command('rollback', dryrunopts +
4570 @command('rollback', dryrunopts +
4569 [('f', 'force', False, _('ignore safety measures'))])
4571 [('f', 'force', False, _('ignore safety measures'))])
4570 def rollback(ui, repo, **opts):
4572 def rollback(ui, repo, **opts):
4571 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4573 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4572
4574
4573 Please use :hg:`commit --amend` instead of rollback to correct
4575 Please use :hg:`commit --amend` instead of rollback to correct
4574 mistakes in the last commit.
4576 mistakes in the last commit.
4575
4577
4576 This command should be used with care. There is only one level of
4578 This command should be used with care. There is only one level of
4577 rollback, and there is no way to undo a rollback. It will also
4579 rollback, and there is no way to undo a rollback. It will also
4578 restore the dirstate at the time of the last transaction, losing
4580 restore the dirstate at the time of the last transaction, losing
4579 any dirstate changes since that time. This command does not alter
4581 any dirstate changes since that time. This command does not alter
4580 the working directory.
4582 the working directory.
4581
4583
4582 Transactions are used to encapsulate the effects of all commands
4584 Transactions are used to encapsulate the effects of all commands
4583 that create new changesets or propagate existing changesets into a
4585 that create new changesets or propagate existing changesets into a
4584 repository.
4586 repository.
4585
4587
4586 .. container:: verbose
4588 .. container:: verbose
4587
4589
4588 For example, the following commands are transactional, and their
4590 For example, the following commands are transactional, and their
4589 effects can be rolled back:
4591 effects can be rolled back:
4590
4592
4591 - commit
4593 - commit
4592 - import
4594 - import
4593 - pull
4595 - pull
4594 - push (with this repository as the destination)
4596 - push (with this repository as the destination)
4595 - unbundle
4597 - unbundle
4596
4598
4597 To avoid permanent data loss, rollback will refuse to rollback a
4599 To avoid permanent data loss, rollback will refuse to rollback a
4598 commit transaction if it isn't checked out. Use --force to
4600 commit transaction if it isn't checked out. Use --force to
4599 override this protection.
4601 override this protection.
4600
4602
4601 The rollback command can be entirely disabled by setting the
4603 The rollback command can be entirely disabled by setting the
4602 ``ui.rollback`` configuration setting to false. If you're here
4604 ``ui.rollback`` configuration setting to false. If you're here
4603 because you want to use rollback and it's disabled, you can
4605 because you want to use rollback and it's disabled, you can
4604 re-enable the command by setting ``ui.rollback`` to true.
4606 re-enable the command by setting ``ui.rollback`` to true.
4605
4607
4606 This command is not intended for use on public repositories. Once
4608 This command is not intended for use on public repositories. Once
4607 changes are visible for pull by other users, rolling a transaction
4609 changes are visible for pull by other users, rolling a transaction
4608 back locally is ineffective (someone else may already have pulled
4610 back locally is ineffective (someone else may already have pulled
4609 the changes). Furthermore, a race is possible with readers of the
4611 the changes). Furthermore, a race is possible with readers of the
4610 repository; for example an in-progress pull from the repository
4612 repository; for example an in-progress pull from the repository
4611 may fail if a rollback is performed.
4613 may fail if a rollback is performed.
4612
4614
4613 Returns 0 on success, 1 if no rollback data is available.
4615 Returns 0 on success, 1 if no rollback data is available.
4614 """
4616 """
4615 if not ui.configbool('ui', 'rollback', True):
4617 if not ui.configbool('ui', 'rollback', True):
4616 raise error.Abort(_('rollback is disabled because it is unsafe'),
4618 raise error.Abort(_('rollback is disabled because it is unsafe'),
4617 hint=('see `hg help -v rollback` for information'))
4619 hint=('see `hg help -v rollback` for information'))
4618 return repo.rollback(dryrun=opts.get(r'dry_run'),
4620 return repo.rollback(dryrun=opts.get(r'dry_run'),
4619 force=opts.get(r'force'))
4621 force=opts.get(r'force'))
4620
4622
4621 @command('root', [])
4623 @command('root', [])
4622 def root(ui, repo):
4624 def root(ui, repo):
4623 """print the root (top) of the current working directory
4625 """print the root (top) of the current working directory
4624
4626
4625 Print the root directory of the current repository.
4627 Print the root directory of the current repository.
4626
4628
4627 Returns 0 on success.
4629 Returns 0 on success.
4628 """
4630 """
4629 ui.write(repo.root + "\n")
4631 ui.write(repo.root + "\n")
4630
4632
4631 @command('^serve',
4633 @command('^serve',
4632 [('A', 'accesslog', '', _('name of access log file to write to'),
4634 [('A', 'accesslog', '', _('name of access log file to write to'),
4633 _('FILE')),
4635 _('FILE')),
4634 ('d', 'daemon', None, _('run server in background')),
4636 ('d', 'daemon', None, _('run server in background')),
4635 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4637 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4636 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4638 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4637 # use string type, then we can check if something was passed
4639 # use string type, then we can check if something was passed
4638 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4640 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4639 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4641 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4640 _('ADDR')),
4642 _('ADDR')),
4641 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4643 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4642 _('PREFIX')),
4644 _('PREFIX')),
4643 ('n', 'name', '',
4645 ('n', 'name', '',
4644 _('name to show in web pages (default: working directory)'), _('NAME')),
4646 _('name to show in web pages (default: working directory)'), _('NAME')),
4645 ('', 'web-conf', '',
4647 ('', 'web-conf', '',
4646 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4648 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4647 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4649 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4648 _('FILE')),
4650 _('FILE')),
4649 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4651 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4650 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4652 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4651 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4653 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4652 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4654 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4653 ('', 'style', '', _('template style to use'), _('STYLE')),
4655 ('', 'style', '', _('template style to use'), _('STYLE')),
4654 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4656 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4655 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4657 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4656 + subrepoopts,
4658 + subrepoopts,
4657 _('[OPTION]...'),
4659 _('[OPTION]...'),
4658 optionalrepo=True)
4660 optionalrepo=True)
4659 def serve(ui, repo, **opts):
4661 def serve(ui, repo, **opts):
4660 """start stand-alone webserver
4662 """start stand-alone webserver
4661
4663
4662 Start a local HTTP repository browser and pull server. You can use
4664 Start a local HTTP repository browser and pull server. You can use
4663 this for ad-hoc sharing and browsing of repositories. It is
4665 this for ad-hoc sharing and browsing of repositories. It is
4664 recommended to use a real web server to serve a repository for
4666 recommended to use a real web server to serve a repository for
4665 longer periods of time.
4667 longer periods of time.
4666
4668
4667 Please note that the server does not implement access control.
4669 Please note that the server does not implement access control.
4668 This means that, by default, anybody can read from the server and
4670 This means that, by default, anybody can read from the server and
4669 nobody can write to it by default. Set the ``web.allow_push``
4671 nobody can write to it by default. Set the ``web.allow_push``
4670 option to ``*`` to allow everybody to push to the server. You
4672 option to ``*`` to allow everybody to push to the server. You
4671 should use a real web server if you need to authenticate users.
4673 should use a real web server if you need to authenticate users.
4672
4674
4673 By default, the server logs accesses to stdout and errors to
4675 By default, the server logs accesses to stdout and errors to
4674 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4676 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4675 files.
4677 files.
4676
4678
4677 To have the server choose a free port number to listen on, specify
4679 To have the server choose a free port number to listen on, specify
4678 a port number of 0; in this case, the server will print the port
4680 a port number of 0; in this case, the server will print the port
4679 number it uses.
4681 number it uses.
4680
4682
4681 Returns 0 on success.
4683 Returns 0 on success.
4682 """
4684 """
4683
4685
4684 opts = pycompat.byteskwargs(opts)
4686 opts = pycompat.byteskwargs(opts)
4685 if opts["stdio"] and opts["cmdserver"]:
4687 if opts["stdio"] and opts["cmdserver"]:
4686 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4688 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4687
4689
4688 if opts["stdio"]:
4690 if opts["stdio"]:
4689 if repo is None:
4691 if repo is None:
4690 raise error.RepoError(_("there is no Mercurial repository here"
4692 raise error.RepoError(_("there is no Mercurial repository here"
4691 " (.hg not found)"))
4693 " (.hg not found)"))
4692 s = sshserver.sshserver(ui, repo)
4694 s = sshserver.sshserver(ui, repo)
4693 s.serve_forever()
4695 s.serve_forever()
4694
4696
4695 service = server.createservice(ui, repo, opts)
4697 service = server.createservice(ui, repo, opts)
4696 return server.runservice(opts, initfn=service.init, runfn=service.run)
4698 return server.runservice(opts, initfn=service.init, runfn=service.run)
4697
4699
4698 @command('^status|st',
4700 @command('^status|st',
4699 [('A', 'all', None, _('show status of all files')),
4701 [('A', 'all', None, _('show status of all files')),
4700 ('m', 'modified', None, _('show only modified files')),
4702 ('m', 'modified', None, _('show only modified files')),
4701 ('a', 'added', None, _('show only added files')),
4703 ('a', 'added', None, _('show only added files')),
4702 ('r', 'removed', None, _('show only removed files')),
4704 ('r', 'removed', None, _('show only removed files')),
4703 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4705 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4704 ('c', 'clean', None, _('show only files without changes')),
4706 ('c', 'clean', None, _('show only files without changes')),
4705 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4707 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4706 ('i', 'ignored', None, _('show only ignored files')),
4708 ('i', 'ignored', None, _('show only ignored files')),
4707 ('n', 'no-status', None, _('hide status prefix')),
4709 ('n', 'no-status', None, _('hide status prefix')),
4708 ('C', 'copies', None, _('show source of copied files')),
4710 ('C', 'copies', None, _('show source of copied files')),
4709 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4711 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4710 ('', 'rev', [], _('show difference from revision'), _('REV')),
4712 ('', 'rev', [], _('show difference from revision'), _('REV')),
4711 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4713 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4712 ] + walkopts + subrepoopts + formatteropts,
4714 ] + walkopts + subrepoopts + formatteropts,
4713 _('[OPTION]... [FILE]...'),
4715 _('[OPTION]... [FILE]...'),
4714 inferrepo=True)
4716 inferrepo=True)
4715 def status(ui, repo, *pats, **opts):
4717 def status(ui, repo, *pats, **opts):
4716 """show changed files in the working directory
4718 """show changed files in the working directory
4717
4719
4718 Show status of files in the repository. If names are given, only
4720 Show status of files in the repository. If names are given, only
4719 files that match are shown. Files that are clean or ignored or
4721 files that match are shown. Files that are clean or ignored or
4720 the source of a copy/move operation, are not listed unless
4722 the source of a copy/move operation, are not listed unless
4721 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4723 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4722 Unless options described with "show only ..." are given, the
4724 Unless options described with "show only ..." are given, the
4723 options -mardu are used.
4725 options -mardu are used.
4724
4726
4725 Option -q/--quiet hides untracked (unknown and ignored) files
4727 Option -q/--quiet hides untracked (unknown and ignored) files
4726 unless explicitly requested with -u/--unknown or -i/--ignored.
4728 unless explicitly requested with -u/--unknown or -i/--ignored.
4727
4729
4728 .. note::
4730 .. note::
4729
4731
4730 :hg:`status` may appear to disagree with diff if permissions have
4732 :hg:`status` may appear to disagree with diff if permissions have
4731 changed or a merge has occurred. The standard diff format does
4733 changed or a merge has occurred. The standard diff format does
4732 not report permission changes and diff only reports changes
4734 not report permission changes and diff only reports changes
4733 relative to one merge parent.
4735 relative to one merge parent.
4734
4736
4735 If one revision is given, it is used as the base revision.
4737 If one revision is given, it is used as the base revision.
4736 If two revisions are given, the differences between them are
4738 If two revisions are given, the differences between them are
4737 shown. The --change option can also be used as a shortcut to list
4739 shown. The --change option can also be used as a shortcut to list
4738 the changed files of a revision from its first parent.
4740 the changed files of a revision from its first parent.
4739
4741
4740 The codes used to show the status of files are::
4742 The codes used to show the status of files are::
4741
4743
4742 M = modified
4744 M = modified
4743 A = added
4745 A = added
4744 R = removed
4746 R = removed
4745 C = clean
4747 C = clean
4746 ! = missing (deleted by non-hg command, but still tracked)
4748 ! = missing (deleted by non-hg command, but still tracked)
4747 ? = not tracked
4749 ? = not tracked
4748 I = ignored
4750 I = ignored
4749 = origin of the previous file (with --copies)
4751 = origin of the previous file (with --copies)
4750
4752
4751 .. container:: verbose
4753 .. container:: verbose
4752
4754
4753 Examples:
4755 Examples:
4754
4756
4755 - show changes in the working directory relative to a
4757 - show changes in the working directory relative to a
4756 changeset::
4758 changeset::
4757
4759
4758 hg status --rev 9353
4760 hg status --rev 9353
4759
4761
4760 - show changes in the working directory relative to the
4762 - show changes in the working directory relative to the
4761 current directory (see :hg:`help patterns` for more information)::
4763 current directory (see :hg:`help patterns` for more information)::
4762
4764
4763 hg status re:
4765 hg status re:
4764
4766
4765 - show all changes including copies in an existing changeset::
4767 - show all changes including copies in an existing changeset::
4766
4768
4767 hg status --copies --change 9353
4769 hg status --copies --change 9353
4768
4770
4769 - get a NUL separated list of added files, suitable for xargs::
4771 - get a NUL separated list of added files, suitable for xargs::
4770
4772
4771 hg status -an0
4773 hg status -an0
4772
4774
4773 Returns 0 on success.
4775 Returns 0 on success.
4774 """
4776 """
4775
4777
4776 opts = pycompat.byteskwargs(opts)
4778 opts = pycompat.byteskwargs(opts)
4777 revs = opts.get('rev')
4779 revs = opts.get('rev')
4778 change = opts.get('change')
4780 change = opts.get('change')
4779
4781
4780 if revs and change:
4782 if revs and change:
4781 msg = _('cannot specify --rev and --change at the same time')
4783 msg = _('cannot specify --rev and --change at the same time')
4782 raise error.Abort(msg)
4784 raise error.Abort(msg)
4783 elif change:
4785 elif change:
4784 node2 = scmutil.revsingle(repo, change, None).node()
4786 node2 = scmutil.revsingle(repo, change, None).node()
4785 node1 = repo[node2].p1().node()
4787 node1 = repo[node2].p1().node()
4786 else:
4788 else:
4787 node1, node2 = scmutil.revpair(repo, revs)
4789 node1, node2 = scmutil.revpair(repo, revs)
4788
4790
4789 if pats or ui.configbool('commands', 'status.relative'):
4791 if pats or ui.configbool('commands', 'status.relative'):
4790 cwd = repo.getcwd()
4792 cwd = repo.getcwd()
4791 else:
4793 else:
4792 cwd = ''
4794 cwd = ''
4793
4795
4794 if opts.get('print0'):
4796 if opts.get('print0'):
4795 end = '\0'
4797 end = '\0'
4796 else:
4798 else:
4797 end = '\n'
4799 end = '\n'
4798 copy = {}
4800 copy = {}
4799 states = 'modified added removed deleted unknown ignored clean'.split()
4801 states = 'modified added removed deleted unknown ignored clean'.split()
4800 show = [k for k in states if opts.get(k)]
4802 show = [k for k in states if opts.get(k)]
4801 if opts.get('all'):
4803 if opts.get('all'):
4802 show += ui.quiet and (states[:4] + ['clean']) or states
4804 show += ui.quiet and (states[:4] + ['clean']) or states
4803 if not show:
4805 if not show:
4804 if ui.quiet:
4806 if ui.quiet:
4805 show = states[:4]
4807 show = states[:4]
4806 else:
4808 else:
4807 show = states[:5]
4809 show = states[:5]
4808
4810
4809 m = scmutil.match(repo[node2], pats, opts)
4811 m = scmutil.match(repo[node2], pats, opts)
4810 stat = repo.status(node1, node2, m,
4812 stat = repo.status(node1, node2, m,
4811 'ignored' in show, 'clean' in show, 'unknown' in show,
4813 'ignored' in show, 'clean' in show, 'unknown' in show,
4812 opts.get('subrepos'))
4814 opts.get('subrepos'))
4813 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4815 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4814
4816
4815 if (opts.get('all') or opts.get('copies')
4817 if (opts.get('all') or opts.get('copies')
4816 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4818 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4817 copy = copies.pathcopies(repo[node1], repo[node2], m)
4819 copy = copies.pathcopies(repo[node1], repo[node2], m)
4818
4820
4819 ui.pager('status')
4821 ui.pager('status')
4820 fm = ui.formatter('status', opts)
4822 fm = ui.formatter('status', opts)
4821 fmt = '%s' + end
4823 fmt = '%s' + end
4822 showchar = not opts.get('no_status')
4824 showchar = not opts.get('no_status')
4823
4825
4824 for state, char, files in changestates:
4826 for state, char, files in changestates:
4825 if state in show:
4827 if state in show:
4826 label = 'status.' + state
4828 label = 'status.' + state
4827 for f in files:
4829 for f in files:
4828 fm.startitem()
4830 fm.startitem()
4829 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4831 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4830 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4832 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4831 if f in copy:
4833 if f in copy:
4832 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4834 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4833 label='status.copied')
4835 label='status.copied')
4834 fm.end()
4836 fm.end()
4835
4837
4836 @command('^summary|sum',
4838 @command('^summary|sum',
4837 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4839 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4838 def summary(ui, repo, **opts):
4840 def summary(ui, repo, **opts):
4839 """summarize working directory state
4841 """summarize working directory state
4840
4842
4841 This generates a brief summary of the working directory state,
4843 This generates a brief summary of the working directory state,
4842 including parents, branch, commit status, phase and available updates.
4844 including parents, branch, commit status, phase and available updates.
4843
4845
4844 With the --remote option, this will check the default paths for
4846 With the --remote option, this will check the default paths for
4845 incoming and outgoing changes. This can be time-consuming.
4847 incoming and outgoing changes. This can be time-consuming.
4846
4848
4847 Returns 0 on success.
4849 Returns 0 on success.
4848 """
4850 """
4849
4851
4850 opts = pycompat.byteskwargs(opts)
4852 opts = pycompat.byteskwargs(opts)
4851 ui.pager('summary')
4853 ui.pager('summary')
4852 ctx = repo[None]
4854 ctx = repo[None]
4853 parents = ctx.parents()
4855 parents = ctx.parents()
4854 pnode = parents[0].node()
4856 pnode = parents[0].node()
4855 marks = []
4857 marks = []
4856
4858
4857 ms = None
4859 ms = None
4858 try:
4860 try:
4859 ms = mergemod.mergestate.read(repo)
4861 ms = mergemod.mergestate.read(repo)
4860 except error.UnsupportedMergeRecords as e:
4862 except error.UnsupportedMergeRecords as e:
4861 s = ' '.join(e.recordtypes)
4863 s = ' '.join(e.recordtypes)
4862 ui.warn(
4864 ui.warn(
4863 _('warning: merge state has unsupported record types: %s\n') % s)
4865 _('warning: merge state has unsupported record types: %s\n') % s)
4864 unresolved = 0
4866 unresolved = 0
4865 else:
4867 else:
4866 unresolved = [f for f in ms if ms[f] == 'u']
4868 unresolved = [f for f in ms if ms[f] == 'u']
4867
4869
4868 for p in parents:
4870 for p in parents:
4869 # label with log.changeset (instead of log.parent) since this
4871 # label with log.changeset (instead of log.parent) since this
4870 # shows a working directory parent *changeset*:
4872 # shows a working directory parent *changeset*:
4871 # i18n: column positioning for "hg summary"
4873 # i18n: column positioning for "hg summary"
4872 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4874 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4873 label=cmdutil._changesetlabels(p))
4875 label=cmdutil._changesetlabels(p))
4874 ui.write(' '.join(p.tags()), label='log.tag')
4876 ui.write(' '.join(p.tags()), label='log.tag')
4875 if p.bookmarks():
4877 if p.bookmarks():
4876 marks.extend(p.bookmarks())
4878 marks.extend(p.bookmarks())
4877 if p.rev() == -1:
4879 if p.rev() == -1:
4878 if not len(repo):
4880 if not len(repo):
4879 ui.write(_(' (empty repository)'))
4881 ui.write(_(' (empty repository)'))
4880 else:
4882 else:
4881 ui.write(_(' (no revision checked out)'))
4883 ui.write(_(' (no revision checked out)'))
4882 if p.obsolete():
4884 if p.obsolete():
4883 ui.write(_(' (obsolete)'))
4885 ui.write(_(' (obsolete)'))
4884 if p.troubled():
4886 if p.troubled():
4885 ui.write(' ('
4887 ui.write(' ('
4886 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4888 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4887 for trouble in p.troubles())
4889 for trouble in p.troubles())
4888 + ')')
4890 + ')')
4889 ui.write('\n')
4891 ui.write('\n')
4890 if p.description():
4892 if p.description():
4891 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4893 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4892 label='log.summary')
4894 label='log.summary')
4893
4895
4894 branch = ctx.branch()
4896 branch = ctx.branch()
4895 bheads = repo.branchheads(branch)
4897 bheads = repo.branchheads(branch)
4896 # i18n: column positioning for "hg summary"
4898 # i18n: column positioning for "hg summary"
4897 m = _('branch: %s\n') % branch
4899 m = _('branch: %s\n') % branch
4898 if branch != 'default':
4900 if branch != 'default':
4899 ui.write(m, label='log.branch')
4901 ui.write(m, label='log.branch')
4900 else:
4902 else:
4901 ui.status(m, label='log.branch')
4903 ui.status(m, label='log.branch')
4902
4904
4903 if marks:
4905 if marks:
4904 active = repo._activebookmark
4906 active = repo._activebookmark
4905 # i18n: column positioning for "hg summary"
4907 # i18n: column positioning for "hg summary"
4906 ui.write(_('bookmarks:'), label='log.bookmark')
4908 ui.write(_('bookmarks:'), label='log.bookmark')
4907 if active is not None:
4909 if active is not None:
4908 if active in marks:
4910 if active in marks:
4909 ui.write(' *' + active, label=activebookmarklabel)
4911 ui.write(' *' + active, label=activebookmarklabel)
4910 marks.remove(active)
4912 marks.remove(active)
4911 else:
4913 else:
4912 ui.write(' [%s]' % active, label=activebookmarklabel)
4914 ui.write(' [%s]' % active, label=activebookmarklabel)
4913 for m in marks:
4915 for m in marks:
4914 ui.write(' ' + m, label='log.bookmark')
4916 ui.write(' ' + m, label='log.bookmark')
4915 ui.write('\n', label='log.bookmark')
4917 ui.write('\n', label='log.bookmark')
4916
4918
4917 status = repo.status(unknown=True)
4919 status = repo.status(unknown=True)
4918
4920
4919 c = repo.dirstate.copies()
4921 c = repo.dirstate.copies()
4920 copied, renamed = [], []
4922 copied, renamed = [], []
4921 for d, s in c.iteritems():
4923 for d, s in c.iteritems():
4922 if s in status.removed:
4924 if s in status.removed:
4923 status.removed.remove(s)
4925 status.removed.remove(s)
4924 renamed.append(d)
4926 renamed.append(d)
4925 else:
4927 else:
4926 copied.append(d)
4928 copied.append(d)
4927 if d in status.added:
4929 if d in status.added:
4928 status.added.remove(d)
4930 status.added.remove(d)
4929
4931
4930 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4932 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4931
4933
4932 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4934 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4933 (ui.label(_('%d added'), 'status.added'), status.added),
4935 (ui.label(_('%d added'), 'status.added'), status.added),
4934 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4936 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4935 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4937 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4936 (ui.label(_('%d copied'), 'status.copied'), copied),
4938 (ui.label(_('%d copied'), 'status.copied'), copied),
4937 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4939 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4938 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4940 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4939 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4941 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4940 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4942 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4941 t = []
4943 t = []
4942 for l, s in labels:
4944 for l, s in labels:
4943 if s:
4945 if s:
4944 t.append(l % len(s))
4946 t.append(l % len(s))
4945
4947
4946 t = ', '.join(t)
4948 t = ', '.join(t)
4947 cleanworkdir = False
4949 cleanworkdir = False
4948
4950
4949 if repo.vfs.exists('graftstate'):
4951 if repo.vfs.exists('graftstate'):
4950 t += _(' (graft in progress)')
4952 t += _(' (graft in progress)')
4951 if repo.vfs.exists('updatestate'):
4953 if repo.vfs.exists('updatestate'):
4952 t += _(' (interrupted update)')
4954 t += _(' (interrupted update)')
4953 elif len(parents) > 1:
4955 elif len(parents) > 1:
4954 t += _(' (merge)')
4956 t += _(' (merge)')
4955 elif branch != parents[0].branch():
4957 elif branch != parents[0].branch():
4956 t += _(' (new branch)')
4958 t += _(' (new branch)')
4957 elif (parents[0].closesbranch() and
4959 elif (parents[0].closesbranch() and
4958 pnode in repo.branchheads(branch, closed=True)):
4960 pnode in repo.branchheads(branch, closed=True)):
4959 t += _(' (head closed)')
4961 t += _(' (head closed)')
4960 elif not (status.modified or status.added or status.removed or renamed or
4962 elif not (status.modified or status.added or status.removed or renamed or
4961 copied or subs):
4963 copied or subs):
4962 t += _(' (clean)')
4964 t += _(' (clean)')
4963 cleanworkdir = True
4965 cleanworkdir = True
4964 elif pnode not in bheads:
4966 elif pnode not in bheads:
4965 t += _(' (new branch head)')
4967 t += _(' (new branch head)')
4966
4968
4967 if parents:
4969 if parents:
4968 pendingphase = max(p.phase() for p in parents)
4970 pendingphase = max(p.phase() for p in parents)
4969 else:
4971 else:
4970 pendingphase = phases.public
4972 pendingphase = phases.public
4971
4973
4972 if pendingphase > phases.newcommitphase(ui):
4974 if pendingphase > phases.newcommitphase(ui):
4973 t += ' (%s)' % phases.phasenames[pendingphase]
4975 t += ' (%s)' % phases.phasenames[pendingphase]
4974
4976
4975 if cleanworkdir:
4977 if cleanworkdir:
4976 # i18n: column positioning for "hg summary"
4978 # i18n: column positioning for "hg summary"
4977 ui.status(_('commit: %s\n') % t.strip())
4979 ui.status(_('commit: %s\n') % t.strip())
4978 else:
4980 else:
4979 # i18n: column positioning for "hg summary"
4981 # i18n: column positioning for "hg summary"
4980 ui.write(_('commit: %s\n') % t.strip())
4982 ui.write(_('commit: %s\n') % t.strip())
4981
4983
4982 # all ancestors of branch heads - all ancestors of parent = new csets
4984 # all ancestors of branch heads - all ancestors of parent = new csets
4983 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4985 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4984 bheads))
4986 bheads))
4985
4987
4986 if new == 0:
4988 if new == 0:
4987 # i18n: column positioning for "hg summary"
4989 # i18n: column positioning for "hg summary"
4988 ui.status(_('update: (current)\n'))
4990 ui.status(_('update: (current)\n'))
4989 elif pnode not in bheads:
4991 elif pnode not in bheads:
4990 # i18n: column positioning for "hg summary"
4992 # i18n: column positioning for "hg summary"
4991 ui.write(_('update: %d new changesets (update)\n') % new)
4993 ui.write(_('update: %d new changesets (update)\n') % new)
4992 else:
4994 else:
4993 # i18n: column positioning for "hg summary"
4995 # i18n: column positioning for "hg summary"
4994 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4996 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4995 (new, len(bheads)))
4997 (new, len(bheads)))
4996
4998
4997 t = []
4999 t = []
4998 draft = len(repo.revs('draft()'))
5000 draft = len(repo.revs('draft()'))
4999 if draft:
5001 if draft:
5000 t.append(_('%d draft') % draft)
5002 t.append(_('%d draft') % draft)
5001 secret = len(repo.revs('secret()'))
5003 secret = len(repo.revs('secret()'))
5002 if secret:
5004 if secret:
5003 t.append(_('%d secret') % secret)
5005 t.append(_('%d secret') % secret)
5004
5006
5005 if draft or secret:
5007 if draft or secret:
5006 ui.status(_('phases: %s\n') % ', '.join(t))
5008 ui.status(_('phases: %s\n') % ', '.join(t))
5007
5009
5008 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5010 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5009 for trouble in ("unstable", "divergent", "bumped"):
5011 for trouble in ("unstable", "divergent", "bumped"):
5010 numtrouble = len(repo.revs(trouble + "()"))
5012 numtrouble = len(repo.revs(trouble + "()"))
5011 # We write all the possibilities to ease translation
5013 # We write all the possibilities to ease translation
5012 troublemsg = {
5014 troublemsg = {
5013 "unstable": _("unstable: %d changesets"),
5015 "unstable": _("unstable: %d changesets"),
5014 "divergent": _("divergent: %d changesets"),
5016 "divergent": _("divergent: %d changesets"),
5015 "bumped": _("bumped: %d changesets"),
5017 "bumped": _("bumped: %d changesets"),
5016 }
5018 }
5017 if numtrouble > 0:
5019 if numtrouble > 0:
5018 ui.status(troublemsg[trouble] % numtrouble + "\n")
5020 ui.status(troublemsg[trouble] % numtrouble + "\n")
5019
5021
5020 cmdutil.summaryhooks(ui, repo)
5022 cmdutil.summaryhooks(ui, repo)
5021
5023
5022 if opts.get('remote'):
5024 if opts.get('remote'):
5023 needsincoming, needsoutgoing = True, True
5025 needsincoming, needsoutgoing = True, True
5024 else:
5026 else:
5025 needsincoming, needsoutgoing = False, False
5027 needsincoming, needsoutgoing = False, False
5026 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5028 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5027 if i:
5029 if i:
5028 needsincoming = True
5030 needsincoming = True
5029 if o:
5031 if o:
5030 needsoutgoing = True
5032 needsoutgoing = True
5031 if not needsincoming and not needsoutgoing:
5033 if not needsincoming and not needsoutgoing:
5032 return
5034 return
5033
5035
5034 def getincoming():
5036 def getincoming():
5035 source, branches = hg.parseurl(ui.expandpath('default'))
5037 source, branches = hg.parseurl(ui.expandpath('default'))
5036 sbranch = branches[0]
5038 sbranch = branches[0]
5037 try:
5039 try:
5038 other = hg.peer(repo, {}, source)
5040 other = hg.peer(repo, {}, source)
5039 except error.RepoError:
5041 except error.RepoError:
5040 if opts.get('remote'):
5042 if opts.get('remote'):
5041 raise
5043 raise
5042 return source, sbranch, None, None, None
5044 return source, sbranch, None, None, None
5043 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5045 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5044 if revs:
5046 if revs:
5045 revs = [other.lookup(rev) for rev in revs]
5047 revs = [other.lookup(rev) for rev in revs]
5046 ui.debug('comparing with %s\n' % util.hidepassword(source))
5048 ui.debug('comparing with %s\n' % util.hidepassword(source))
5047 repo.ui.pushbuffer()
5049 repo.ui.pushbuffer()
5048 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5050 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5049 repo.ui.popbuffer()
5051 repo.ui.popbuffer()
5050 return source, sbranch, other, commoninc, commoninc[1]
5052 return source, sbranch, other, commoninc, commoninc[1]
5051
5053
5052 if needsincoming:
5054 if needsincoming:
5053 source, sbranch, sother, commoninc, incoming = getincoming()
5055 source, sbranch, sother, commoninc, incoming = getincoming()
5054 else:
5056 else:
5055 source = sbranch = sother = commoninc = incoming = None
5057 source = sbranch = sother = commoninc = incoming = None
5056
5058
5057 def getoutgoing():
5059 def getoutgoing():
5058 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5060 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5059 dbranch = branches[0]
5061 dbranch = branches[0]
5060 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5062 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5061 if source != dest:
5063 if source != dest:
5062 try:
5064 try:
5063 dother = hg.peer(repo, {}, dest)
5065 dother = hg.peer(repo, {}, dest)
5064 except error.RepoError:
5066 except error.RepoError:
5065 if opts.get('remote'):
5067 if opts.get('remote'):
5066 raise
5068 raise
5067 return dest, dbranch, None, None
5069 return dest, dbranch, None, None
5068 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5070 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5069 elif sother is None:
5071 elif sother is None:
5070 # there is no explicit destination peer, but source one is invalid
5072 # there is no explicit destination peer, but source one is invalid
5071 return dest, dbranch, None, None
5073 return dest, dbranch, None, None
5072 else:
5074 else:
5073 dother = sother
5075 dother = sother
5074 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5076 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5075 common = None
5077 common = None
5076 else:
5078 else:
5077 common = commoninc
5079 common = commoninc
5078 if revs:
5080 if revs:
5079 revs = [repo.lookup(rev) for rev in revs]
5081 revs = [repo.lookup(rev) for rev in revs]
5080 repo.ui.pushbuffer()
5082 repo.ui.pushbuffer()
5081 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5083 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5082 commoninc=common)
5084 commoninc=common)
5083 repo.ui.popbuffer()
5085 repo.ui.popbuffer()
5084 return dest, dbranch, dother, outgoing
5086 return dest, dbranch, dother, outgoing
5085
5087
5086 if needsoutgoing:
5088 if needsoutgoing:
5087 dest, dbranch, dother, outgoing = getoutgoing()
5089 dest, dbranch, dother, outgoing = getoutgoing()
5088 else:
5090 else:
5089 dest = dbranch = dother = outgoing = None
5091 dest = dbranch = dother = outgoing = None
5090
5092
5091 if opts.get('remote'):
5093 if opts.get('remote'):
5092 t = []
5094 t = []
5093 if incoming:
5095 if incoming:
5094 t.append(_('1 or more incoming'))
5096 t.append(_('1 or more incoming'))
5095 o = outgoing.missing
5097 o = outgoing.missing
5096 if o:
5098 if o:
5097 t.append(_('%d outgoing') % len(o))
5099 t.append(_('%d outgoing') % len(o))
5098 other = dother or sother
5100 other = dother or sother
5099 if 'bookmarks' in other.listkeys('namespaces'):
5101 if 'bookmarks' in other.listkeys('namespaces'):
5100 counts = bookmarks.summary(repo, other)
5102 counts = bookmarks.summary(repo, other)
5101 if counts[0] > 0:
5103 if counts[0] > 0:
5102 t.append(_('%d incoming bookmarks') % counts[0])
5104 t.append(_('%d incoming bookmarks') % counts[0])
5103 if counts[1] > 0:
5105 if counts[1] > 0:
5104 t.append(_('%d outgoing bookmarks') % counts[1])
5106 t.append(_('%d outgoing bookmarks') % counts[1])
5105
5107
5106 if t:
5108 if t:
5107 # i18n: column positioning for "hg summary"
5109 # i18n: column positioning for "hg summary"
5108 ui.write(_('remote: %s\n') % (', '.join(t)))
5110 ui.write(_('remote: %s\n') % (', '.join(t)))
5109 else:
5111 else:
5110 # i18n: column positioning for "hg summary"
5112 # i18n: column positioning for "hg summary"
5111 ui.status(_('remote: (synced)\n'))
5113 ui.status(_('remote: (synced)\n'))
5112
5114
5113 cmdutil.summaryremotehooks(ui, repo, opts,
5115 cmdutil.summaryremotehooks(ui, repo, opts,
5114 ((source, sbranch, sother, commoninc),
5116 ((source, sbranch, sother, commoninc),
5115 (dest, dbranch, dother, outgoing)))
5117 (dest, dbranch, dother, outgoing)))
5116
5118
5117 @command('tag',
5119 @command('tag',
5118 [('f', 'force', None, _('force tag')),
5120 [('f', 'force', None, _('force tag')),
5119 ('l', 'local', None, _('make the tag local')),
5121 ('l', 'local', None, _('make the tag local')),
5120 ('r', 'rev', '', _('revision to tag'), _('REV')),
5122 ('r', 'rev', '', _('revision to tag'), _('REV')),
5121 ('', 'remove', None, _('remove a tag')),
5123 ('', 'remove', None, _('remove a tag')),
5122 # -l/--local is already there, commitopts cannot be used
5124 # -l/--local is already there, commitopts cannot be used
5123 ('e', 'edit', None, _('invoke editor on commit messages')),
5125 ('e', 'edit', None, _('invoke editor on commit messages')),
5124 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5126 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5125 ] + commitopts2,
5127 ] + commitopts2,
5126 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5128 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5127 def tag(ui, repo, name1, *names, **opts):
5129 def tag(ui, repo, name1, *names, **opts):
5128 """add one or more tags for the current or given revision
5130 """add one or more tags for the current or given revision
5129
5131
5130 Name a particular revision using <name>.
5132 Name a particular revision using <name>.
5131
5133
5132 Tags are used to name particular revisions of the repository and are
5134 Tags are used to name particular revisions of the repository and are
5133 very useful to compare different revisions, to go back to significant
5135 very useful to compare different revisions, to go back to significant
5134 earlier versions or to mark branch points as releases, etc. Changing
5136 earlier versions or to mark branch points as releases, etc. Changing
5135 an existing tag is normally disallowed; use -f/--force to override.
5137 an existing tag is normally disallowed; use -f/--force to override.
5136
5138
5137 If no revision is given, the parent of the working directory is
5139 If no revision is given, the parent of the working directory is
5138 used.
5140 used.
5139
5141
5140 To facilitate version control, distribution, and merging of tags,
5142 To facilitate version control, distribution, and merging of tags,
5141 they are stored as a file named ".hgtags" which is managed similarly
5143 they are stored as a file named ".hgtags" which is managed similarly
5142 to other project files and can be hand-edited if necessary. This
5144 to other project files and can be hand-edited if necessary. This
5143 also means that tagging creates a new commit. The file
5145 also means that tagging creates a new commit. The file
5144 ".hg/localtags" is used for local tags (not shared among
5146 ".hg/localtags" is used for local tags (not shared among
5145 repositories).
5147 repositories).
5146
5148
5147 Tag commits are usually made at the head of a branch. If the parent
5149 Tag commits are usually made at the head of a branch. If the parent
5148 of the working directory is not a branch head, :hg:`tag` aborts; use
5150 of the working directory is not a branch head, :hg:`tag` aborts; use
5149 -f/--force to force the tag commit to be based on a non-head
5151 -f/--force to force the tag commit to be based on a non-head
5150 changeset.
5152 changeset.
5151
5153
5152 See :hg:`help dates` for a list of formats valid for -d/--date.
5154 See :hg:`help dates` for a list of formats valid for -d/--date.
5153
5155
5154 Since tag names have priority over branch names during revision
5156 Since tag names have priority over branch names during revision
5155 lookup, using an existing branch name as a tag name is discouraged.
5157 lookup, using an existing branch name as a tag name is discouraged.
5156
5158
5157 Returns 0 on success.
5159 Returns 0 on success.
5158 """
5160 """
5159 opts = pycompat.byteskwargs(opts)
5161 opts = pycompat.byteskwargs(opts)
5160 wlock = lock = None
5162 wlock = lock = None
5161 try:
5163 try:
5162 wlock = repo.wlock()
5164 wlock = repo.wlock()
5163 lock = repo.lock()
5165 lock = repo.lock()
5164 rev_ = "."
5166 rev_ = "."
5165 names = [t.strip() for t in (name1,) + names]
5167 names = [t.strip() for t in (name1,) + names]
5166 if len(names) != len(set(names)):
5168 if len(names) != len(set(names)):
5167 raise error.Abort(_('tag names must be unique'))
5169 raise error.Abort(_('tag names must be unique'))
5168 for n in names:
5170 for n in names:
5169 scmutil.checknewlabel(repo, n, 'tag')
5171 scmutil.checknewlabel(repo, n, 'tag')
5170 if not n:
5172 if not n:
5171 raise error.Abort(_('tag names cannot consist entirely of '
5173 raise error.Abort(_('tag names cannot consist entirely of '
5172 'whitespace'))
5174 'whitespace'))
5173 if opts.get('rev') and opts.get('remove'):
5175 if opts.get('rev') and opts.get('remove'):
5174 raise error.Abort(_("--rev and --remove are incompatible"))
5176 raise error.Abort(_("--rev and --remove are incompatible"))
5175 if opts.get('rev'):
5177 if opts.get('rev'):
5176 rev_ = opts['rev']
5178 rev_ = opts['rev']
5177 message = opts.get('message')
5179 message = opts.get('message')
5178 if opts.get('remove'):
5180 if opts.get('remove'):
5179 if opts.get('local'):
5181 if opts.get('local'):
5180 expectedtype = 'local'
5182 expectedtype = 'local'
5181 else:
5183 else:
5182 expectedtype = 'global'
5184 expectedtype = 'global'
5183
5185
5184 for n in names:
5186 for n in names:
5185 if not repo.tagtype(n):
5187 if not repo.tagtype(n):
5186 raise error.Abort(_("tag '%s' does not exist") % n)
5188 raise error.Abort(_("tag '%s' does not exist") % n)
5187 if repo.tagtype(n) != expectedtype:
5189 if repo.tagtype(n) != expectedtype:
5188 if expectedtype == 'global':
5190 if expectedtype == 'global':
5189 raise error.Abort(_("tag '%s' is not a global tag") % n)
5191 raise error.Abort(_("tag '%s' is not a global tag") % n)
5190 else:
5192 else:
5191 raise error.Abort(_("tag '%s' is not a local tag") % n)
5193 raise error.Abort(_("tag '%s' is not a local tag") % n)
5192 rev_ = 'null'
5194 rev_ = 'null'
5193 if not message:
5195 if not message:
5194 # we don't translate commit messages
5196 # we don't translate commit messages
5195 message = 'Removed tag %s' % ', '.join(names)
5197 message = 'Removed tag %s' % ', '.join(names)
5196 elif not opts.get('force'):
5198 elif not opts.get('force'):
5197 for n in names:
5199 for n in names:
5198 if n in repo.tags():
5200 if n in repo.tags():
5199 raise error.Abort(_("tag '%s' already exists "
5201 raise error.Abort(_("tag '%s' already exists "
5200 "(use -f to force)") % n)
5202 "(use -f to force)") % n)
5201 if not opts.get('local'):
5203 if not opts.get('local'):
5202 p1, p2 = repo.dirstate.parents()
5204 p1, p2 = repo.dirstate.parents()
5203 if p2 != nullid:
5205 if p2 != nullid:
5204 raise error.Abort(_('uncommitted merge'))
5206 raise error.Abort(_('uncommitted merge'))
5205 bheads = repo.branchheads()
5207 bheads = repo.branchheads()
5206 if not opts.get('force') and bheads and p1 not in bheads:
5208 if not opts.get('force') and bheads and p1 not in bheads:
5207 raise error.Abort(_('working directory is not at a branch head '
5209 raise error.Abort(_('working directory is not at a branch head '
5208 '(use -f to force)'))
5210 '(use -f to force)'))
5209 r = scmutil.revsingle(repo, rev_).node()
5211 r = scmutil.revsingle(repo, rev_).node()
5210
5212
5211 if not message:
5213 if not message:
5212 # we don't translate commit messages
5214 # we don't translate commit messages
5213 message = ('Added tag %s for changeset %s' %
5215 message = ('Added tag %s for changeset %s' %
5214 (', '.join(names), short(r)))
5216 (', '.join(names), short(r)))
5215
5217
5216 date = opts.get('date')
5218 date = opts.get('date')
5217 if date:
5219 if date:
5218 date = util.parsedate(date)
5220 date = util.parsedate(date)
5219
5221
5220 if opts.get('remove'):
5222 if opts.get('remove'):
5221 editform = 'tag.remove'
5223 editform = 'tag.remove'
5222 else:
5224 else:
5223 editform = 'tag.add'
5225 editform = 'tag.add'
5224 editor = cmdutil.getcommiteditor(editform=editform,
5226 editor = cmdutil.getcommiteditor(editform=editform,
5225 **pycompat.strkwargs(opts))
5227 **pycompat.strkwargs(opts))
5226
5228
5227 # don't allow tagging the null rev
5229 # don't allow tagging the null rev
5228 if (not opts.get('remove') and
5230 if (not opts.get('remove') and
5229 scmutil.revsingle(repo, rev_).rev() == nullrev):
5231 scmutil.revsingle(repo, rev_).rev() == nullrev):
5230 raise error.Abort(_("cannot tag null revision"))
5232 raise error.Abort(_("cannot tag null revision"))
5231
5233
5232 tagsmod.tag(repo, names, r, message, opts.get('local'),
5234 tagsmod.tag(repo, names, r, message, opts.get('local'),
5233 opts.get('user'), date, editor=editor)
5235 opts.get('user'), date, editor=editor)
5234 finally:
5236 finally:
5235 release(lock, wlock)
5237 release(lock, wlock)
5236
5238
5237 @command('tags', formatteropts, '')
5239 @command('tags', formatteropts, '')
5238 def tags(ui, repo, **opts):
5240 def tags(ui, repo, **opts):
5239 """list repository tags
5241 """list repository tags
5240
5242
5241 This lists both regular and local tags. When the -v/--verbose
5243 This lists both regular and local tags. When the -v/--verbose
5242 switch is used, a third column "local" is printed for local tags.
5244 switch is used, a third column "local" is printed for local tags.
5243 When the -q/--quiet switch is used, only the tag name is printed.
5245 When the -q/--quiet switch is used, only the tag name is printed.
5244
5246
5245 Returns 0 on success.
5247 Returns 0 on success.
5246 """
5248 """
5247
5249
5248 opts = pycompat.byteskwargs(opts)
5250 opts = pycompat.byteskwargs(opts)
5249 ui.pager('tags')
5251 ui.pager('tags')
5250 fm = ui.formatter('tags', opts)
5252 fm = ui.formatter('tags', opts)
5251 hexfunc = fm.hexfunc
5253 hexfunc = fm.hexfunc
5252 tagtype = ""
5254 tagtype = ""
5253
5255
5254 for t, n in reversed(repo.tagslist()):
5256 for t, n in reversed(repo.tagslist()):
5255 hn = hexfunc(n)
5257 hn = hexfunc(n)
5256 label = 'tags.normal'
5258 label = 'tags.normal'
5257 tagtype = ''
5259 tagtype = ''
5258 if repo.tagtype(t) == 'local':
5260 if repo.tagtype(t) == 'local':
5259 label = 'tags.local'
5261 label = 'tags.local'
5260 tagtype = 'local'
5262 tagtype = 'local'
5261
5263
5262 fm.startitem()
5264 fm.startitem()
5263 fm.write('tag', '%s', t, label=label)
5265 fm.write('tag', '%s', t, label=label)
5264 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5266 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5265 fm.condwrite(not ui.quiet, 'rev node', fmt,
5267 fm.condwrite(not ui.quiet, 'rev node', fmt,
5266 repo.changelog.rev(n), hn, label=label)
5268 repo.changelog.rev(n), hn, label=label)
5267 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5269 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5268 tagtype, label=label)
5270 tagtype, label=label)
5269 fm.plain('\n')
5271 fm.plain('\n')
5270 fm.end()
5272 fm.end()
5271
5273
5272 @command('tip',
5274 @command('tip',
5273 [('p', 'patch', None, _('show patch')),
5275 [('p', 'patch', None, _('show patch')),
5274 ('g', 'git', None, _('use git extended diff format')),
5276 ('g', 'git', None, _('use git extended diff format')),
5275 ] + templateopts,
5277 ] + templateopts,
5276 _('[-p] [-g]'))
5278 _('[-p] [-g]'))
5277 def tip(ui, repo, **opts):
5279 def tip(ui, repo, **opts):
5278 """show the tip revision (DEPRECATED)
5280 """show the tip revision (DEPRECATED)
5279
5281
5280 The tip revision (usually just called the tip) is the changeset
5282 The tip revision (usually just called the tip) is the changeset
5281 most recently added to the repository (and therefore the most
5283 most recently added to the repository (and therefore the most
5282 recently changed head).
5284 recently changed head).
5283
5285
5284 If you have just made a commit, that commit will be the tip. If
5286 If you have just made a commit, that commit will be the tip. If
5285 you have just pulled changes from another repository, the tip of
5287 you have just pulled changes from another repository, the tip of
5286 that repository becomes the current tip. The "tip" tag is special
5288 that repository becomes the current tip. The "tip" tag is special
5287 and cannot be renamed or assigned to a different changeset.
5289 and cannot be renamed or assigned to a different changeset.
5288
5290
5289 This command is deprecated, please use :hg:`heads` instead.
5291 This command is deprecated, please use :hg:`heads` instead.
5290
5292
5291 Returns 0 on success.
5293 Returns 0 on success.
5292 """
5294 """
5293 opts = pycompat.byteskwargs(opts)
5295 opts = pycompat.byteskwargs(opts)
5294 displayer = cmdutil.show_changeset(ui, repo, opts)
5296 displayer = cmdutil.show_changeset(ui, repo, opts)
5295 displayer.show(repo['tip'])
5297 displayer.show(repo['tip'])
5296 displayer.close()
5298 displayer.close()
5297
5299
5298 @command('unbundle',
5300 @command('unbundle',
5299 [('u', 'update', None,
5301 [('u', 'update', None,
5300 _('update to new branch head if changesets were unbundled'))],
5302 _('update to new branch head if changesets were unbundled'))],
5301 _('[-u] FILE...'))
5303 _('[-u] FILE...'))
5302 def unbundle(ui, repo, fname1, *fnames, **opts):
5304 def unbundle(ui, repo, fname1, *fnames, **opts):
5303 """apply one or more bundle files
5305 """apply one or more bundle files
5304
5306
5305 Apply one or more bundle files generated by :hg:`bundle`.
5307 Apply one or more bundle files generated by :hg:`bundle`.
5306
5308
5307 Returns 0 on success, 1 if an update has unresolved files.
5309 Returns 0 on success, 1 if an update has unresolved files.
5308 """
5310 """
5309 fnames = (fname1,) + fnames
5311 fnames = (fname1,) + fnames
5310
5312
5311 with repo.lock():
5313 with repo.lock():
5312 for fname in fnames:
5314 for fname in fnames:
5313 f = hg.openpath(ui, fname)
5315 f = hg.openpath(ui, fname)
5314 gen = exchange.readbundle(ui, f, fname)
5316 gen = exchange.readbundle(ui, f, fname)
5315 if isinstance(gen, bundle2.unbundle20):
5317 if isinstance(gen, bundle2.unbundle20):
5316 tr = repo.transaction('unbundle')
5318 tr = repo.transaction('unbundle')
5317 try:
5319 try:
5318 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5320 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5319 url='bundle:' + fname)
5321 url='bundle:' + fname)
5320 tr.close()
5322 tr.close()
5321 except error.BundleUnknownFeatureError as exc:
5323 except error.BundleUnknownFeatureError as exc:
5322 raise error.Abort(_('%s: unknown bundle feature, %s')
5324 raise error.Abort(_('%s: unknown bundle feature, %s')
5323 % (fname, exc),
5325 % (fname, exc),
5324 hint=_("see https://mercurial-scm.org/"
5326 hint=_("see https://mercurial-scm.org/"
5325 "wiki/BundleFeature for more "
5327 "wiki/BundleFeature for more "
5326 "information"))
5328 "information"))
5327 finally:
5329 finally:
5328 if tr:
5330 if tr:
5329 tr.release()
5331 tr.release()
5330 changes = [r.get('return', 0)
5332 changes = [r.get('return', 0)
5331 for r in op.records['changegroup']]
5333 for r in op.records['changegroup']]
5332 modheads = changegroup.combineresults(changes)
5334 modheads = changegroup.combineresults(changes)
5333 elif isinstance(gen, streamclone.streamcloneapplier):
5335 elif isinstance(gen, streamclone.streamcloneapplier):
5334 raise error.Abort(
5336 raise error.Abort(
5335 _('packed bundles cannot be applied with '
5337 _('packed bundles cannot be applied with '
5336 '"hg unbundle"'),
5338 '"hg unbundle"'),
5337 hint=_('use "hg debugapplystreamclonebundle"'))
5339 hint=_('use "hg debugapplystreamclonebundle"'))
5338 else:
5340 else:
5339 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5341 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5340
5342
5341 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5343 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5342
5344
5343 @command('^update|up|checkout|co',
5345 @command('^update|up|checkout|co',
5344 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5346 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5345 ('c', 'check', None, _('require clean working directory')),
5347 ('c', 'check', None, _('require clean working directory')),
5346 ('m', 'merge', None, _('merge uncommitted changes')),
5348 ('m', 'merge', None, _('merge uncommitted changes')),
5347 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5349 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5348 ('r', 'rev', '', _('revision'), _('REV'))
5350 ('r', 'rev', '', _('revision'), _('REV'))
5349 ] + mergetoolopts,
5351 ] + mergetoolopts,
5350 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5352 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5351 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5353 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5352 merge=None, tool=None):
5354 merge=None, tool=None):
5353 """update working directory (or switch revisions)
5355 """update working directory (or switch revisions)
5354
5356
5355 Update the repository's working directory to the specified
5357 Update the repository's working directory to the specified
5356 changeset. If no changeset is specified, update to the tip of the
5358 changeset. If no changeset is specified, update to the tip of the
5357 current named branch and move the active bookmark (see :hg:`help
5359 current named branch and move the active bookmark (see :hg:`help
5358 bookmarks`).
5360 bookmarks`).
5359
5361
5360 Update sets the working directory's parent revision to the specified
5362 Update sets the working directory's parent revision to the specified
5361 changeset (see :hg:`help parents`).
5363 changeset (see :hg:`help parents`).
5362
5364
5363 If the changeset is not a descendant or ancestor of the working
5365 If the changeset is not a descendant or ancestor of the working
5364 directory's parent and there are uncommitted changes, the update is
5366 directory's parent and there are uncommitted changes, the update is
5365 aborted. With the -c/--check option, the working directory is checked
5367 aborted. With the -c/--check option, the working directory is checked
5366 for uncommitted changes; if none are found, the working directory is
5368 for uncommitted changes; if none are found, the working directory is
5367 updated to the specified changeset.
5369 updated to the specified changeset.
5368
5370
5369 .. container:: verbose
5371 .. container:: verbose
5370
5372
5371 The -C/--clean, -c/--check, and -m/--merge options control what
5373 The -C/--clean, -c/--check, and -m/--merge options control what
5372 happens if the working directory contains uncommitted changes.
5374 happens if the working directory contains uncommitted changes.
5373 At most of one of them can be specified.
5375 At most of one of them can be specified.
5374
5376
5375 1. If no option is specified, and if
5377 1. If no option is specified, and if
5376 the requested changeset is an ancestor or descendant of
5378 the requested changeset is an ancestor or descendant of
5377 the working directory's parent, the uncommitted changes
5379 the working directory's parent, the uncommitted changes
5378 are merged into the requested changeset and the merged
5380 are merged into the requested changeset and the merged
5379 result is left uncommitted. If the requested changeset is
5381 result is left uncommitted. If the requested changeset is
5380 not an ancestor or descendant (that is, it is on another
5382 not an ancestor or descendant (that is, it is on another
5381 branch), the update is aborted and the uncommitted changes
5383 branch), the update is aborted and the uncommitted changes
5382 are preserved.
5384 are preserved.
5383
5385
5384 2. With the -m/--merge option, the update is allowed even if the
5386 2. With the -m/--merge option, the update is allowed even if the
5385 requested changeset is not an ancestor or descendant of
5387 requested changeset is not an ancestor or descendant of
5386 the working directory's parent.
5388 the working directory's parent.
5387
5389
5388 3. With the -c/--check option, the update is aborted and the
5390 3. With the -c/--check option, the update is aborted and the
5389 uncommitted changes are preserved.
5391 uncommitted changes are preserved.
5390
5392
5391 4. With the -C/--clean option, uncommitted changes are discarded and
5393 4. With the -C/--clean option, uncommitted changes are discarded and
5392 the working directory is updated to the requested changeset.
5394 the working directory is updated to the requested changeset.
5393
5395
5394 To cancel an uncommitted merge (and lose your changes), use
5396 To cancel an uncommitted merge (and lose your changes), use
5395 :hg:`update --clean .`.
5397 :hg:`update --clean .`.
5396
5398
5397 Use null as the changeset to remove the working directory (like
5399 Use null as the changeset to remove the working directory (like
5398 :hg:`clone -U`).
5400 :hg:`clone -U`).
5399
5401
5400 If you want to revert just one file to an older revision, use
5402 If you want to revert just one file to an older revision, use
5401 :hg:`revert [-r REV] NAME`.
5403 :hg:`revert [-r REV] NAME`.
5402
5404
5403 See :hg:`help dates` for a list of formats valid for -d/--date.
5405 See :hg:`help dates` for a list of formats valid for -d/--date.
5404
5406
5405 Returns 0 on success, 1 if there are unresolved files.
5407 Returns 0 on success, 1 if there are unresolved files.
5406 """
5408 """
5407 if rev and node:
5409 if rev and node:
5408 raise error.Abort(_("please specify just one revision"))
5410 raise error.Abort(_("please specify just one revision"))
5409
5411
5410 if ui.configbool('commands', 'update.requiredest'):
5412 if ui.configbool('commands', 'update.requiredest'):
5411 if not node and not rev and not date:
5413 if not node and not rev and not date:
5412 raise error.Abort(_('you must specify a destination'),
5414 raise error.Abort(_('you must specify a destination'),
5413 hint=_('for example: hg update ".::"'))
5415 hint=_('for example: hg update ".::"'))
5414
5416
5415 if rev is None or rev == '':
5417 if rev is None or rev == '':
5416 rev = node
5418 rev = node
5417
5419
5418 if date and rev is not None:
5420 if date and rev is not None:
5419 raise error.Abort(_("you can't specify a revision and a date"))
5421 raise error.Abort(_("you can't specify a revision and a date"))
5420
5422
5421 if len([x for x in (clean, check, merge) if x]) > 1:
5423 if len([x for x in (clean, check, merge) if x]) > 1:
5422 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5424 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5423 "or -m/merge"))
5425 "or -m/merge"))
5424
5426
5425 updatecheck = None
5427 updatecheck = None
5426 if check:
5428 if check:
5427 updatecheck = 'abort'
5429 updatecheck = 'abort'
5428 elif merge:
5430 elif merge:
5429 updatecheck = 'none'
5431 updatecheck = 'none'
5430
5432
5431 with repo.wlock():
5433 with repo.wlock():
5432 cmdutil.clearunfinished(repo)
5434 cmdutil.clearunfinished(repo)
5433
5435
5434 if date:
5436 if date:
5435 rev = cmdutil.finddate(ui, repo, date)
5437 rev = cmdutil.finddate(ui, repo, date)
5436
5438
5437 # if we defined a bookmark, we have to remember the original name
5439 # if we defined a bookmark, we have to remember the original name
5438 brev = rev
5440 brev = rev
5439 rev = scmutil.revsingle(repo, rev, rev).rev()
5441 rev = scmutil.revsingle(repo, rev, rev).rev()
5440
5442
5441 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5443 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5442
5444
5443 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5445 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5444 updatecheck=updatecheck)
5446 updatecheck=updatecheck)
5445
5447
5446 @command('verify', [])
5448 @command('verify', [])
5447 def verify(ui, repo):
5449 def verify(ui, repo):
5448 """verify the integrity of the repository
5450 """verify the integrity of the repository
5449
5451
5450 Verify the integrity of the current repository.
5452 Verify the integrity of the current repository.
5451
5453
5452 This will perform an extensive check of the repository's
5454 This will perform an extensive check of the repository's
5453 integrity, validating the hashes and checksums of each entry in
5455 integrity, validating the hashes and checksums of each entry in
5454 the changelog, manifest, and tracked files, as well as the
5456 the changelog, manifest, and tracked files, as well as the
5455 integrity of their crosslinks and indices.
5457 integrity of their crosslinks and indices.
5456
5458
5457 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5459 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5458 for more information about recovery from corruption of the
5460 for more information about recovery from corruption of the
5459 repository.
5461 repository.
5460
5462
5461 Returns 0 on success, 1 if errors are encountered.
5463 Returns 0 on success, 1 if errors are encountered.
5462 """
5464 """
5463 return hg.verify(repo)
5465 return hg.verify(repo)
5464
5466
5465 @command('version', [] + formatteropts, norepo=True)
5467 @command('version', [] + formatteropts, norepo=True)
5466 def version_(ui, **opts):
5468 def version_(ui, **opts):
5467 """output version and copyright information"""
5469 """output version and copyright information"""
5468 opts = pycompat.byteskwargs(opts)
5470 opts = pycompat.byteskwargs(opts)
5469 if ui.verbose:
5471 if ui.verbose:
5470 ui.pager('version')
5472 ui.pager('version')
5471 fm = ui.formatter("version", opts)
5473 fm = ui.formatter("version", opts)
5472 fm.startitem()
5474 fm.startitem()
5473 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5475 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5474 util.version())
5476 util.version())
5475 license = _(
5477 license = _(
5476 "(see https://mercurial-scm.org for more information)\n"
5478 "(see https://mercurial-scm.org for more information)\n"
5477 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5479 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5478 "This is free software; see the source for copying conditions. "
5480 "This is free software; see the source for copying conditions. "
5479 "There is NO\nwarranty; "
5481 "There is NO\nwarranty; "
5480 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5482 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5481 )
5483 )
5482 if not ui.quiet:
5484 if not ui.quiet:
5483 fm.plain(license)
5485 fm.plain(license)
5484
5486
5485 if ui.verbose:
5487 if ui.verbose:
5486 fm.plain(_("\nEnabled extensions:\n\n"))
5488 fm.plain(_("\nEnabled extensions:\n\n"))
5487 # format names and versions into columns
5489 # format names and versions into columns
5488 names = []
5490 names = []
5489 vers = []
5491 vers = []
5490 isinternals = []
5492 isinternals = []
5491 for name, module in extensions.extensions():
5493 for name, module in extensions.extensions():
5492 names.append(name)
5494 names.append(name)
5493 vers.append(extensions.moduleversion(module) or None)
5495 vers.append(extensions.moduleversion(module) or None)
5494 isinternals.append(extensions.ismoduleinternal(module))
5496 isinternals.append(extensions.ismoduleinternal(module))
5495 fn = fm.nested("extensions")
5497 fn = fm.nested("extensions")
5496 if names:
5498 if names:
5497 namefmt = " %%-%ds " % max(len(n) for n in names)
5499 namefmt = " %%-%ds " % max(len(n) for n in names)
5498 places = [_("external"), _("internal")]
5500 places = [_("external"), _("internal")]
5499 for n, v, p in zip(names, vers, isinternals):
5501 for n, v, p in zip(names, vers, isinternals):
5500 fn.startitem()
5502 fn.startitem()
5501 fn.condwrite(ui.verbose, "name", namefmt, n)
5503 fn.condwrite(ui.verbose, "name", namefmt, n)
5502 if ui.verbose:
5504 if ui.verbose:
5503 fn.plain("%s " % places[p])
5505 fn.plain("%s " % places[p])
5504 fn.data(bundled=p)
5506 fn.data(bundled=p)
5505 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5507 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5506 if ui.verbose:
5508 if ui.verbose:
5507 fn.plain("\n")
5509 fn.plain("\n")
5508 fn.end()
5510 fn.end()
5509 fm.end()
5511 fm.end()
5510
5512
5511 def loadcmdtable(ui, name, cmdtable):
5513 def loadcmdtable(ui, name, cmdtable):
5512 """Load command functions from specified cmdtable
5514 """Load command functions from specified cmdtable
5513 """
5515 """
5514 overrides = [cmd for cmd in cmdtable if cmd in table]
5516 overrides = [cmd for cmd in cmdtable if cmd in table]
5515 if overrides:
5517 if overrides:
5516 ui.warn(_("extension '%s' overrides commands: %s\n")
5518 ui.warn(_("extension '%s' overrides commands: %s\n")
5517 % (name, " ".join(overrides)))
5519 % (name, " ".join(overrides)))
5518 table.update(cmdtable)
5520 table.update(cmdtable)
@@ -1,85 +1,85 b''
1 To merge files Mercurial uses merge tools.
1 To merge files Mercurial uses merge tools.
2
2
3 A merge tool combines two different versions of a file into a merged
3 A merge tool combines two different versions of a file into a merged
4 file. Merge tools are given the two files and the greatest common
4 file. Merge tools are given the two files and the greatest common
5 ancestor of the two file versions, so they can determine the changes
5 ancestor of the two file versions, so they can determine the changes
6 made on both branches.
6 made on both branches.
7
7
8 Merge tools are used both for :hg:`resolve`, :hg:`merge`, :hg:`update`,
8 Merge tools are used both for :hg:`resolve`, :hg:`merge`, :hg:`update`,
9 :hg:`backout` and in several extensions.
9 :hg:`backout` and in several extensions.
10
10
11 Usually, the merge tool tries to automatically reconcile the files by
11 Usually, the merge tool tries to automatically reconcile the files by
12 combining all non-overlapping changes that occurred separately in
12 combining all non-overlapping changes that occurred separately in
13 the two different evolutions of the same initial base file. Furthermore, some
13 the two different evolutions of the same initial base file. Furthermore, some
14 interactive merge programs make it easier to manually resolve
14 interactive merge programs make it easier to manually resolve
15 conflicting merges, either in a graphical way, or by inserting some
15 conflicting merges, either in a graphical way, or by inserting some
16 conflict markers. Mercurial does not include any interactive merge
16 conflict markers. Mercurial does not include any interactive merge
17 programs but relies on external tools for that.
17 programs but relies on external tools for that.
18
18
19 Available merge tools
19 Available merge tools
20 =====================
20 =====================
21
21
22 External merge tools and their properties are configured in the
22 External merge tools and their properties are configured in the
23 merge-tools configuration section - see hgrc(5) - but they can often just
23 merge-tools configuration section - see hgrc(5) - but they can often just
24 be named by their executable.
24 be named by their executable.
25
25
26 A merge tool is generally usable if its executable can be found on the
26 A merge tool is generally usable if its executable can be found on the
27 system and if it can handle the merge. The executable is found if it
27 system and if it can handle the merge. The executable is found if it
28 is an absolute or relative executable path or the name of an
28 is an absolute or relative executable path or the name of an
29 application in the executable search path. The tool is assumed to be
29 application in the executable search path. The tool is assumed to be
30 able to handle the merge if it can handle symlinks if the file is a
30 able to handle the merge if it can handle symlinks if the file is a
31 symlink, if it can handle binary files if the file is binary, and if a
31 symlink, if it can handle binary files if the file is binary, and if a
32 GUI is available if the tool requires a GUI.
32 GUI is available if the tool requires a GUI.
33
33
34 There are some internal merge tools which can be used. The internal
34 There are some internal merge tools which can be used. The internal
35 merge tools are:
35 merge tools are:
36
36
37 .. internaltoolsmarker
37 .. internaltoolsmarker
38
38
39 Internal tools are always available and do not require a GUI but will by default
39 Internal tools are always available and do not require a GUI but will by default
40 not handle symlinks or binary files.
40 not handle symlinks or binary files.
41
41
42 Choosing a merge tool
42 Choosing a merge tool
43 =====================
43 =====================
44
44
45 Mercurial uses these rules when deciding which merge tool to use:
45 Mercurial uses these rules when deciding which merge tool to use:
46
46
47 1. If a tool has been specified with the --tool option to merge or resolve, it
47 1. If a tool has been specified with the --tool option to merge or resolve, it
48 is used. If it is the name of a tool in the merge-tools configuration, its
48 is used. If it is the name of a tool in the merge-tools configuration, its
49 configuration is used. Otherwise the specified tool must be executable by
49 configuration is used. Otherwise the specified tool must be executable by
50 the shell.
50 the shell.
51
51
52 2. If the ``HGMERGE`` environment variable is present, its value is used and
52 2. If the ``HGMERGE`` environment variable is present, its value is used and
53 must be executable by the shell.
53 must be executable by the shell.
54
54
55 3. If the filename of the file to be merged matches any of the patterns in the
55 3. If the filename of the file to be merged matches any of the patterns in the
56 merge-patterns configuration section, the first usable merge tool
56 merge-patterns configuration section, the first usable merge tool
57 corresponding to a matching pattern is used. Here, binary capabilities of the
57 corresponding to a matching pattern is used. Here, binary capabilities of the
58 merge tool are not considered.
58 merge tool are not considered.
59
59
60 4. If ui.merge is set it will be considered next. If the value is not the name
60 4. If ui.merge is set it will be considered next. If the value is not the name
61 of a configured tool, the specified value is used and must be executable by
61 of a configured tool, the specified value is used and must be executable by
62 the shell. Otherwise the named tool is used if it is usable.
62 the shell. Otherwise the named tool is used if it is usable.
63
63
64 5. If any usable merge tools are present in the merge-tools configuration
64 5. If any usable merge tools are present in the merge-tools configuration
65 section, the one with the highest priority is used.
65 section, the one with the highest priority is used.
66
66
67 6. If a program named ``hgmerge`` can be found on the system, it is used - but
67 6. If a program named ``hgmerge`` can be found on the system, it is used - but
68 it will by default not be used for symlinks and binary files.
68 it will by default not be used for symlinks and binary files.
69
69
70 7. If the file to be merged is not binary and is not a symlink, then
70 7. If the file to be merged is not binary and is not a symlink, then
71 internal ``:merge`` is used.
71 internal ``:merge`` is used.
72
72
73 8. The merge of the file fails and must be resolved before commit.
73 8. Otherwise, ``:prompt`` is used.
74
74
75 .. note::
75 .. note::
76
76
77 After selecting a merge program, Mercurial will by default attempt
77 After selecting a merge program, Mercurial will by default attempt
78 to merge the files using a simple merge algorithm first. Only if it doesn't
78 to merge the files using a simple merge algorithm first. Only if it doesn't
79 succeed because of conflicting changes Mercurial will actually execute the
79 succeed because of conflicting changes Mercurial will actually execute the
80 merge program. Whether to use the simple merge algorithm first can be
80 merge program. Whether to use the simple merge algorithm first can be
81 controlled by the premerge setting of the merge tool. Premerge is enabled by
81 controlled by the premerge setting of the merge tool. Premerge is enabled by
82 default unless the file is binary or a symlink.
82 default unless the file is binary or a symlink.
83
83
84 See the merge-tools and ui sections of hgrc(5) for details on the
84 See the merge-tools and ui sections of hgrc(5) for details on the
85 configuration of merge tools.
85 configuration of merge tools.
@@ -1,860 +1,860 b''
1 # sslutil.py - SSL handling for mercurial
1 # sslutil.py - SSL handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import re
14 import re
15 import ssl
15 import ssl
16 import sys
16 import sys
17
17
18 from .i18n import _
18 from .i18n import _
19 from . import (
19 from . import (
20 error,
20 error,
21 pycompat,
21 pycompat,
22 util,
22 util,
23 )
23 )
24
24
25 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
25 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
26 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
26 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
27 # all exposed via the "ssl" module.
27 # all exposed via the "ssl" module.
28 #
28 #
29 # Depending on the version of Python being used, SSL/TLS support is either
29 # Depending on the version of Python being used, SSL/TLS support is either
30 # modern/secure or legacy/insecure. Many operations in this module have
30 # modern/secure or legacy/insecure. Many operations in this module have
31 # separate code paths depending on support in Python.
31 # separate code paths depending on support in Python.
32
32
33 configprotocols = set([
33 configprotocols = set([
34 'tls1.0',
34 'tls1.0',
35 'tls1.1',
35 'tls1.1',
36 'tls1.2',
36 'tls1.2',
37 ])
37 ])
38
38
39 hassni = getattr(ssl, 'HAS_SNI', False)
39 hassni = getattr(ssl, 'HAS_SNI', False)
40
40
41 # TLS 1.1 and 1.2 may not be supported if the OpenSSL Python is compiled
41 # TLS 1.1 and 1.2 may not be supported if the OpenSSL Python is compiled
42 # against doesn't support them.
42 # against doesn't support them.
43 supportedprotocols = set(['tls1.0'])
43 supportedprotocols = set(['tls1.0'])
44 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_1'):
44 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_1'):
45 supportedprotocols.add('tls1.1')
45 supportedprotocols.add('tls1.1')
46 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_2'):
46 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_2'):
47 supportedprotocols.add('tls1.2')
47 supportedprotocols.add('tls1.2')
48
48
49 try:
49 try:
50 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
50 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
51 # SSL/TLS features are available.
51 # SSL/TLS features are available.
52 SSLContext = ssl.SSLContext
52 SSLContext = ssl.SSLContext
53 modernssl = True
53 modernssl = True
54 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
54 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
55 except AttributeError:
55 except AttributeError:
56 modernssl = False
56 modernssl = False
57 _canloaddefaultcerts = False
57 _canloaddefaultcerts = False
58
58
59 # We implement SSLContext using the interface from the standard library.
59 # We implement SSLContext using the interface from the standard library.
60 class SSLContext(object):
60 class SSLContext(object):
61 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
61 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
62 _supportsciphers = sys.version_info >= (2, 7)
62 _supportsciphers = sys.version_info >= (2, 7)
63
63
64 def __init__(self, protocol):
64 def __init__(self, protocol):
65 # From the public interface of SSLContext
65 # From the public interface of SSLContext
66 self.protocol = protocol
66 self.protocol = protocol
67 self.check_hostname = False
67 self.check_hostname = False
68 self.options = 0
68 self.options = 0
69 self.verify_mode = ssl.CERT_NONE
69 self.verify_mode = ssl.CERT_NONE
70
70
71 # Used by our implementation.
71 # Used by our implementation.
72 self._certfile = None
72 self._certfile = None
73 self._keyfile = None
73 self._keyfile = None
74 self._certpassword = None
74 self._certpassword = None
75 self._cacerts = None
75 self._cacerts = None
76 self._ciphers = None
76 self._ciphers = None
77
77
78 def load_cert_chain(self, certfile, keyfile=None, password=None):
78 def load_cert_chain(self, certfile, keyfile=None, password=None):
79 self._certfile = certfile
79 self._certfile = certfile
80 self._keyfile = keyfile
80 self._keyfile = keyfile
81 self._certpassword = password
81 self._certpassword = password
82
82
83 def load_default_certs(self, purpose=None):
83 def load_default_certs(self, purpose=None):
84 pass
84 pass
85
85
86 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
86 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
87 if capath:
87 if capath:
88 raise error.Abort(_('capath not supported'))
88 raise error.Abort(_('capath not supported'))
89 if cadata:
89 if cadata:
90 raise error.Abort(_('cadata not supported'))
90 raise error.Abort(_('cadata not supported'))
91
91
92 self._cacerts = cafile
92 self._cacerts = cafile
93
93
94 def set_ciphers(self, ciphers):
94 def set_ciphers(self, ciphers):
95 if not self._supportsciphers:
95 if not self._supportsciphers:
96 raise error.Abort(_('setting ciphers in [hostsecurity] is not '
96 raise error.Abort(_('setting ciphers in [hostsecurity] is not '
97 'supported by this version of Python'),
97 'supported by this version of Python'),
98 hint=_('remove the config option or run '
98 hint=_('remove the config option or run '
99 'Mercurial with a modern Python '
99 'Mercurial with a modern Python '
100 'version (preferred)'))
100 'version (preferred)'))
101
101
102 self._ciphers = ciphers
102 self._ciphers = ciphers
103
103
104 def wrap_socket(self, socket, server_hostname=None, server_side=False):
104 def wrap_socket(self, socket, server_hostname=None, server_side=False):
105 # server_hostname is unique to SSLContext.wrap_socket and is used
105 # server_hostname is unique to SSLContext.wrap_socket and is used
106 # for SNI in that context. So there's nothing for us to do with it
106 # for SNI in that context. So there's nothing for us to do with it
107 # in this legacy code since we don't support SNI.
107 # in this legacy code since we don't support SNI.
108
108
109 args = {
109 args = {
110 'keyfile': self._keyfile,
110 'keyfile': self._keyfile,
111 'certfile': self._certfile,
111 'certfile': self._certfile,
112 'server_side': server_side,
112 'server_side': server_side,
113 'cert_reqs': self.verify_mode,
113 'cert_reqs': self.verify_mode,
114 'ssl_version': self.protocol,
114 'ssl_version': self.protocol,
115 'ca_certs': self._cacerts,
115 'ca_certs': self._cacerts,
116 }
116 }
117
117
118 if self._supportsciphers:
118 if self._supportsciphers:
119 args['ciphers'] = self._ciphers
119 args['ciphers'] = self._ciphers
120
120
121 return ssl.wrap_socket(socket, **args)
121 return ssl.wrap_socket(socket, **args)
122
122
123 def _hostsettings(ui, hostname):
123 def _hostsettings(ui, hostname):
124 """Obtain security settings for a hostname.
124 """Obtain security settings for a hostname.
125
125
126 Returns a dict of settings relevant to that hostname.
126 Returns a dict of settings relevant to that hostname.
127 """
127 """
128 s = {
128 s = {
129 # Whether we should attempt to load default/available CA certs
129 # Whether we should attempt to load default/available CA certs
130 # if an explicit ``cafile`` is not defined.
130 # if an explicit ``cafile`` is not defined.
131 'allowloaddefaultcerts': True,
131 'allowloaddefaultcerts': True,
132 # List of 2-tuple of (hash algorithm, hash).
132 # List of 2-tuple of (hash algorithm, hash).
133 'certfingerprints': [],
133 'certfingerprints': [],
134 # Path to file containing concatenated CA certs. Used by
134 # Path to file containing concatenated CA certs. Used by
135 # SSLContext.load_verify_locations().
135 # SSLContext.load_verify_locations().
136 'cafile': None,
136 'cafile': None,
137 # Whether certificate verification should be disabled.
137 # Whether certificate verification should be disabled.
138 'disablecertverification': False,
138 'disablecertverification': False,
139 # Whether the legacy [hostfingerprints] section has data for this host.
139 # Whether the legacy [hostfingerprints] section has data for this host.
140 'legacyfingerprint': False,
140 'legacyfingerprint': False,
141 # PROTOCOL_* constant to use for SSLContext.__init__.
141 # PROTOCOL_* constant to use for SSLContext.__init__.
142 'protocol': None,
142 'protocol': None,
143 # String representation of minimum protocol to be used for UI
143 # String representation of minimum protocol to be used for UI
144 # presentation.
144 # presentation.
145 'protocolui': None,
145 'protocolui': None,
146 # ssl.CERT_* constant used by SSLContext.verify_mode.
146 # ssl.CERT_* constant used by SSLContext.verify_mode.
147 'verifymode': None,
147 'verifymode': None,
148 # Defines extra ssl.OP* bitwise options to set.
148 # Defines extra ssl.OP* bitwise options to set.
149 'ctxoptions': None,
149 'ctxoptions': None,
150 # OpenSSL Cipher List to use (instead of default).
150 # OpenSSL Cipher List to use (instead of default).
151 'ciphers': None,
151 'ciphers': None,
152 }
152 }
153
153
154 # Allow minimum TLS protocol to be specified in the config.
154 # Allow minimum TLS protocol to be specified in the config.
155 def validateprotocol(protocol, key):
155 def validateprotocol(protocol, key):
156 if protocol not in configprotocols:
156 if protocol not in configprotocols:
157 raise error.Abort(
157 raise error.Abort(
158 _('unsupported protocol from hostsecurity.%s: %s') %
158 _('unsupported protocol from hostsecurity.%s: %s') %
159 (key, protocol),
159 (key, protocol),
160 hint=_('valid protocols: %s') %
160 hint=_('valid protocols: %s') %
161 ' '.join(sorted(configprotocols)))
161 ' '.join(sorted(configprotocols)))
162
162
163 # We default to TLS 1.1+ where we can because TLS 1.0 has known
163 # We default to TLS 1.1+ where we can because TLS 1.0 has known
164 # vulnerabilities (like BEAST and POODLE). We allow users to downgrade to
164 # vulnerabilities (like BEAST and POODLE). We allow users to downgrade to
165 # TLS 1.0+ via config options in case a legacy server is encountered.
165 # TLS 1.0+ via config options in case a legacy server is encountered.
166 if 'tls1.1' in supportedprotocols:
166 if 'tls1.1' in supportedprotocols:
167 defaultprotocol = 'tls1.1'
167 defaultprotocol = 'tls1.1'
168 else:
168 else:
169 # Let people know they are borderline secure.
169 # Let people know they are borderline secure.
170 # We don't document this config option because we want people to see
170 # We don't document this config option because we want people to see
171 # the bold warnings on the web site.
171 # the bold warnings on the web site.
172 # internal config: hostsecurity.disabletls10warning
172 # internal config: hostsecurity.disabletls10warning
173 if not ui.configbool('hostsecurity', 'disabletls10warning'):
173 if not ui.configbool('hostsecurity', 'disabletls10warning'):
174 ui.warn(_('warning: connecting to %s using legacy security '
174 ui.warn(_('warning: connecting to %s using legacy security '
175 'technology (TLS 1.0); see '
175 'technology (TLS 1.0); see '
176 'https://mercurial-scm.org/wiki/SecureConnections for '
176 'https://mercurial-scm.org/wiki/SecureConnections for '
177 'more info\n') % hostname)
177 'more info\n') % hostname)
178 defaultprotocol = 'tls1.0'
178 defaultprotocol = 'tls1.0'
179
179
180 key = 'minimumprotocol'
180 key = 'minimumprotocol'
181 protocol = ui.config('hostsecurity', key, defaultprotocol)
181 protocol = ui.config('hostsecurity', key, defaultprotocol)
182 validateprotocol(protocol, key)
182 validateprotocol(protocol, key)
183
183
184 key = '%s:minimumprotocol' % hostname
184 key = '%s:minimumprotocol' % hostname
185 protocol = ui.config('hostsecurity', key, protocol)
185 protocol = ui.config('hostsecurity', key, protocol)
186 validateprotocol(protocol, key)
186 validateprotocol(protocol, key)
187
187
188 # If --insecure is used, we allow the use of TLS 1.0 despite config options.
188 # If --insecure is used, we allow the use of TLS 1.0 despite config options.
189 # We always print a "connection security to %s is disabled..." message when
189 # We always print a "connection security to %s is disabled..." message when
190 # --insecure is used. So no need to print anything more here.
190 # --insecure is used. So no need to print anything more here.
191 if ui.insecureconnections:
191 if ui.insecureconnections:
192 protocol = 'tls1.0'
192 protocol = 'tls1.0'
193
193
194 s['protocol'], s['ctxoptions'], s['protocolui'] = protocolsettings(protocol)
194 s['protocol'], s['ctxoptions'], s['protocolui'] = protocolsettings(protocol)
195
195
196 ciphers = ui.config('hostsecurity', 'ciphers')
196 ciphers = ui.config('hostsecurity', 'ciphers')
197 ciphers = ui.config('hostsecurity', '%s:ciphers' % hostname, ciphers)
197 ciphers = ui.config('hostsecurity', '%s:ciphers' % hostname, ciphers)
198 s['ciphers'] = ciphers
198 s['ciphers'] = ciphers
199
199
200 # Look for fingerprints in [hostsecurity] section. Value is a list
200 # Look for fingerprints in [hostsecurity] section. Value is a list
201 # of <alg>:<fingerprint> strings.
201 # of <alg>:<fingerprint> strings.
202 fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
202 fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
203 [])
203 [])
204 for fingerprint in fingerprints:
204 for fingerprint in fingerprints:
205 if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
205 if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
206 raise error.Abort(_('invalid fingerprint for %s: %s') % (
206 raise error.Abort(_('invalid fingerprint for %s: %s') % (
207 hostname, fingerprint),
207 hostname, fingerprint),
208 hint=_('must begin with "sha1:", "sha256:", '
208 hint=_('must begin with "sha1:", "sha256:", '
209 'or "sha512:"'))
209 'or "sha512:"'))
210
210
211 alg, fingerprint = fingerprint.split(':', 1)
211 alg, fingerprint = fingerprint.split(':', 1)
212 fingerprint = fingerprint.replace(':', '').lower()
212 fingerprint = fingerprint.replace(':', '').lower()
213 s['certfingerprints'].append((alg, fingerprint))
213 s['certfingerprints'].append((alg, fingerprint))
214
214
215 # Fingerprints from [hostfingerprints] are always SHA-1.
215 # Fingerprints from [hostfingerprints] are always SHA-1.
216 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
216 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
217 fingerprint = fingerprint.replace(':', '').lower()
217 fingerprint = fingerprint.replace(':', '').lower()
218 s['certfingerprints'].append(('sha1', fingerprint))
218 s['certfingerprints'].append(('sha1', fingerprint))
219 s['legacyfingerprint'] = True
219 s['legacyfingerprint'] = True
220
220
221 # If a host cert fingerprint is defined, it is the only thing that
221 # If a host cert fingerprint is defined, it is the only thing that
222 # matters. No need to validate CA certs.
222 # matters. No need to validate CA certs.
223 if s['certfingerprints']:
223 if s['certfingerprints']:
224 s['verifymode'] = ssl.CERT_NONE
224 s['verifymode'] = ssl.CERT_NONE
225 s['allowloaddefaultcerts'] = False
225 s['allowloaddefaultcerts'] = False
226
226
227 # If --insecure is used, don't take CAs into consideration.
227 # If --insecure is used, don't take CAs into consideration.
228 elif ui.insecureconnections:
228 elif ui.insecureconnections:
229 s['disablecertverification'] = True
229 s['disablecertverification'] = True
230 s['verifymode'] = ssl.CERT_NONE
230 s['verifymode'] = ssl.CERT_NONE
231 s['allowloaddefaultcerts'] = False
231 s['allowloaddefaultcerts'] = False
232
232
233 if ui.configbool('devel', 'disableloaddefaultcerts'):
233 if ui.configbool('devel', 'disableloaddefaultcerts'):
234 s['allowloaddefaultcerts'] = False
234 s['allowloaddefaultcerts'] = False
235
235
236 # If both fingerprints and a per-host ca file are specified, issue a warning
236 # If both fingerprints and a per-host ca file are specified, issue a warning
237 # because users should not be surprised about what security is or isn't
237 # because users should not be surprised about what security is or isn't
238 # being performed.
238 # being performed.
239 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname)
239 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname)
240 if s['certfingerprints'] and cafile:
240 if s['certfingerprints'] and cafile:
241 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host '
241 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host '
242 'fingerprints defined; using host fingerprints for '
242 'fingerprints defined; using host fingerprints for '
243 'verification)\n') % hostname)
243 'verification)\n') % hostname)
244
244
245 # Try to hook up CA certificate validation unless something above
245 # Try to hook up CA certificate validation unless something above
246 # makes it not necessary.
246 # makes it not necessary.
247 if s['verifymode'] is None:
247 if s['verifymode'] is None:
248 # Look at per-host ca file first.
248 # Look at per-host ca file first.
249 if cafile:
249 if cafile:
250 cafile = util.expandpath(cafile)
250 cafile = util.expandpath(cafile)
251 if not os.path.exists(cafile):
251 if not os.path.exists(cafile):
252 raise error.Abort(_('path specified by %s does not exist: %s') %
252 raise error.Abort(_('path specified by %s does not exist: %s') %
253 ('hostsecurity.%s:verifycertsfile' % hostname,
253 ('hostsecurity.%s:verifycertsfile' % hostname,
254 cafile))
254 cafile))
255 s['cafile'] = cafile
255 s['cafile'] = cafile
256 else:
256 else:
257 # Find global certificates file in config.
257 # Find global certificates file in config.
258 cafile = ui.config('web', 'cacerts')
258 cafile = ui.config('web', 'cacerts')
259
259
260 if cafile:
260 if cafile:
261 cafile = util.expandpath(cafile)
261 cafile = util.expandpath(cafile)
262 if not os.path.exists(cafile):
262 if not os.path.exists(cafile):
263 raise error.Abort(_('could not find web.cacerts: %s') %
263 raise error.Abort(_('could not find web.cacerts: %s') %
264 cafile)
264 cafile)
265 elif s['allowloaddefaultcerts']:
265 elif s['allowloaddefaultcerts']:
266 # CAs not defined in config. Try to find system bundles.
266 # CAs not defined in config. Try to find system bundles.
267 cafile = _defaultcacerts(ui)
267 cafile = _defaultcacerts(ui)
268 if cafile:
268 if cafile:
269 ui.debug('using %s for CA file\n' % cafile)
269 ui.debug('using %s for CA file\n' % cafile)
270
270
271 s['cafile'] = cafile
271 s['cafile'] = cafile
272
272
273 # Require certificate validation if CA certs are being loaded and
273 # Require certificate validation if CA certs are being loaded and
274 # verification hasn't been disabled above.
274 # verification hasn't been disabled above.
275 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
275 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
276 s['verifymode'] = ssl.CERT_REQUIRED
276 s['verifymode'] = ssl.CERT_REQUIRED
277 else:
277 else:
278 # At this point we don't have a fingerprint, aren't being
278 # At this point we don't have a fingerprint, aren't being
279 # explicitly insecure, and can't load CA certs. Connecting
279 # explicitly insecure, and can't load CA certs. Connecting
280 # is insecure. We allow the connection and abort during
280 # is insecure. We allow the connection and abort during
281 # validation (once we have the fingerprint to print to the
281 # validation (once we have the fingerprint to print to the
282 # user).
282 # user).
283 s['verifymode'] = ssl.CERT_NONE
283 s['verifymode'] = ssl.CERT_NONE
284
284
285 assert s['protocol'] is not None
285 assert s['protocol'] is not None
286 assert s['ctxoptions'] is not None
286 assert s['ctxoptions'] is not None
287 assert s['verifymode'] is not None
287 assert s['verifymode'] is not None
288
288
289 return s
289 return s
290
290
291 def protocolsettings(protocol):
291 def protocolsettings(protocol):
292 """Resolve the protocol for a config value.
292 """Resolve the protocol for a config value.
293
293
294 Returns a 3-tuple of (protocol, options, ui value) where the first
294 Returns a 3-tuple of (protocol, options, ui value) where the first
295 2 items are values used by SSLContext and the last is a string value
295 2 items are values used by SSLContext and the last is a string value
296 of the ``minimumprotocol`` config option equivalent.
296 of the ``minimumprotocol`` config option equivalent.
297 """
297 """
298 if protocol not in configprotocols:
298 if protocol not in configprotocols:
299 raise ValueError('protocol value not supported: %s' % protocol)
299 raise ValueError('protocol value not supported: %s' % protocol)
300
300
301 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
301 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
302 # that both ends support, including TLS protocols. On legacy stacks,
302 # that both ends support, including TLS protocols. On legacy stacks,
303 # the highest it likely goes is TLS 1.0. On modern stacks, it can
303 # the highest it likely goes is TLS 1.0. On modern stacks, it can
304 # support TLS 1.2.
304 # support TLS 1.2.
305 #
305 #
306 # The PROTOCOL_TLSv* constants select a specific TLS version
306 # The PROTOCOL_TLSv* constants select a specific TLS version
307 # only (as opposed to multiple versions). So the method for
307 # only (as opposed to multiple versions). So the method for
308 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
308 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
309 # disable protocols via SSLContext.options and OP_NO_* constants.
309 # disable protocols via SSLContext.options and OP_NO_* constants.
310 # However, SSLContext.options doesn't work unless we have the
310 # However, SSLContext.options doesn't work unless we have the
311 # full/real SSLContext available to us.
311 # full/real SSLContext available to us.
312 if supportedprotocols == set(['tls1.0']):
312 if supportedprotocols == set(['tls1.0']):
313 if protocol != 'tls1.0':
313 if protocol != 'tls1.0':
314 raise error.Abort(_('current Python does not support protocol '
314 raise error.Abort(_('current Python does not support protocol '
315 'setting %s') % protocol,
315 'setting %s') % protocol,
316 hint=_('upgrade Python or disable setting since '
316 hint=_('upgrade Python or disable setting since '
317 'only TLS 1.0 is supported'))
317 'only TLS 1.0 is supported'))
318
318
319 return ssl.PROTOCOL_TLSv1, 0, 'tls1.0'
319 return ssl.PROTOCOL_TLSv1, 0, 'tls1.0'
320
320
321 # WARNING: returned options don't work unless the modern ssl module
321 # WARNING: returned options don't work unless the modern ssl module
322 # is available. Be careful when adding options here.
322 # is available. Be careful when adding options here.
323
323
324 # SSLv2 and SSLv3 are broken. We ban them outright.
324 # SSLv2 and SSLv3 are broken. We ban them outright.
325 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
325 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
326
326
327 if protocol == 'tls1.0':
327 if protocol == 'tls1.0':
328 # Defaults above are to use TLS 1.0+
328 # Defaults above are to use TLS 1.0+
329 pass
329 pass
330 elif protocol == 'tls1.1':
330 elif protocol == 'tls1.1':
331 options |= ssl.OP_NO_TLSv1
331 options |= ssl.OP_NO_TLSv1
332 elif protocol == 'tls1.2':
332 elif protocol == 'tls1.2':
333 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
333 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
334 else:
334 else:
335 raise error.Abort(_('this should not happen'))
335 raise error.Abort(_('this should not happen'))
336
336
337 # Prevent CRIME.
337 # Prevent CRIME.
338 # There is no guarantee this attribute is defined on the module.
338 # There is no guarantee this attribute is defined on the module.
339 options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
339 options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
340
340
341 return ssl.PROTOCOL_SSLv23, options, protocol
341 return ssl.PROTOCOL_SSLv23, options, protocol
342
342
343 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
343 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
344 """Add SSL/TLS to a socket.
344 """Add SSL/TLS to a socket.
345
345
346 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
346 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
347 choices based on what security options are available.
347 choices based on what security options are available.
348
348
349 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
349 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
350 the following additional arguments:
350 the following additional arguments:
351
351
352 * serverhostname - The expected hostname of the remote server. If the
352 * serverhostname - The expected hostname of the remote server. If the
353 server (and client) support SNI, this tells the server which certificate
353 server (and client) support SNI, this tells the server which certificate
354 to use.
354 to use.
355 """
355 """
356 if not serverhostname:
356 if not serverhostname:
357 raise error.Abort(_('serverhostname argument is required'))
357 raise error.Abort(_('serverhostname argument is required'))
358
358
359 settings = _hostsettings(ui, serverhostname)
359 settings = _hostsettings(ui, serverhostname)
360
360
361 # We can't use ssl.create_default_context() because it calls
361 # We can't use ssl.create_default_context() because it calls
362 # load_default_certs() unless CA arguments are passed to it. We want to
362 # load_default_certs() unless CA arguments are passed to it. We want to
363 # have explicit control over CA loading because implicitly loading
363 # have explicit control over CA loading because implicitly loading
364 # CAs may undermine the user's intent. For example, a user may define a CA
364 # CAs may undermine the user's intent. For example, a user may define a CA
365 # bundle with a specific CA cert removed. If the system/default CA bundle
365 # bundle with a specific CA cert removed. If the system/default CA bundle
366 # is loaded and contains that removed CA, you've just undone the user's
366 # is loaded and contains that removed CA, you've just undone the user's
367 # choice.
367 # choice.
368 sslcontext = SSLContext(settings['protocol'])
368 sslcontext = SSLContext(settings['protocol'])
369
369
370 # This is a no-op unless using modern ssl.
370 # This is a no-op unless using modern ssl.
371 sslcontext.options |= settings['ctxoptions']
371 sslcontext.options |= settings['ctxoptions']
372
372
373 # This still works on our fake SSLContext.
373 # This still works on our fake SSLContext.
374 sslcontext.verify_mode = settings['verifymode']
374 sslcontext.verify_mode = settings['verifymode']
375
375
376 if settings['ciphers']:
376 if settings['ciphers']:
377 try:
377 try:
378 sslcontext.set_ciphers(settings['ciphers'])
378 sslcontext.set_ciphers(settings['ciphers'])
379 except ssl.SSLError as e:
379 except ssl.SSLError as e:
380 raise error.Abort(_('could not set ciphers: %s') % e.args[0],
380 raise error.Abort(_('could not set ciphers: %s') % e.args[0],
381 hint=_('change cipher string (%s) in config') %
381 hint=_('change cipher string (%s) in config') %
382 settings['ciphers'])
382 settings['ciphers'])
383
383
384 if certfile is not None:
384 if certfile is not None:
385 def password():
385 def password():
386 f = keyfile or certfile
386 f = keyfile or certfile
387 return ui.getpass(_('passphrase for %s: ') % f, '')
387 return ui.getpass(_('passphrase for %s: ') % f, '')
388 sslcontext.load_cert_chain(certfile, keyfile, password)
388 sslcontext.load_cert_chain(certfile, keyfile, password)
389
389
390 if settings['cafile'] is not None:
390 if settings['cafile'] is not None:
391 try:
391 try:
392 sslcontext.load_verify_locations(cafile=settings['cafile'])
392 sslcontext.load_verify_locations(cafile=settings['cafile'])
393 except ssl.SSLError as e:
393 except ssl.SSLError as e:
394 if len(e.args) == 1: # pypy has different SSLError args
394 if len(e.args) == 1: # pypy has different SSLError args
395 msg = e.args[0]
395 msg = e.args[0]
396 else:
396 else:
397 msg = e.args[1]
397 msg = e.args[1]
398 raise error.Abort(_('error loading CA file %s: %s') % (
398 raise error.Abort(_('error loading CA file %s: %s') % (
399 settings['cafile'], msg),
399 settings['cafile'], msg),
400 hint=_('file is empty or malformed?'))
400 hint=_('file is empty or malformed?'))
401 caloaded = True
401 caloaded = True
402 elif settings['allowloaddefaultcerts']:
402 elif settings['allowloaddefaultcerts']:
403 # This is a no-op on old Python.
403 # This is a no-op on old Python.
404 sslcontext.load_default_certs()
404 sslcontext.load_default_certs()
405 caloaded = True
405 caloaded = True
406 else:
406 else:
407 caloaded = False
407 caloaded = False
408
408
409 try:
409 try:
410 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
410 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
411 except ssl.SSLError as e:
411 except ssl.SSLError as e:
412 # If we're doing certificate verification and no CA certs are loaded,
412 # If we're doing certificate verification and no CA certs are loaded,
413 # that is almost certainly the reason why verification failed. Provide
413 # that is almost certainly the reason why verification failed. Provide
414 # a hint to the user.
414 # a hint to the user.
415 # Only modern ssl module exposes SSLContext.get_ca_certs() so we can
415 # Only modern ssl module exposes SSLContext.get_ca_certs() so we can
416 # only show this warning if modern ssl is available.
416 # only show this warning if modern ssl is available.
417 # The exception handler is here to handle bugs around cert attributes:
417 # The exception handler is here to handle bugs around cert attributes:
418 # https://bugs.python.org/issue20916#msg213479. (See issues5313.)
418 # https://bugs.python.org/issue20916#msg213479. (See issues5313.)
419 # When the main 20916 bug occurs, 'sslcontext.get_ca_certs()' is a
419 # When the main 20916 bug occurs, 'sslcontext.get_ca_certs()' is a
420 # non-empty list, but the following conditional is otherwise True.
420 # non-empty list, but the following conditional is otherwise True.
421 try:
421 try:
422 if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
422 if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
423 modernssl and not sslcontext.get_ca_certs()):
423 modernssl and not sslcontext.get_ca_certs()):
424 ui.warn(_('(an attempt was made to load CA certificates but '
424 ui.warn(_('(an attempt was made to load CA certificates but '
425 'none were loaded; see '
425 'none were loaded; see '
426 'https://mercurial-scm.org/wiki/SecureConnections '
426 'https://mercurial-scm.org/wiki/SecureConnections '
427 'for how to configure Mercurial to avoid this '
427 'for how to configure Mercurial to avoid this '
428 'error)\n'))
428 'error)\n'))
429 except ssl.SSLError:
429 except ssl.SSLError:
430 pass
430 pass
431 # Try to print more helpful error messages for known failures.
431 # Try to print more helpful error messages for known failures.
432 if util.safehasattr(e, 'reason'):
432 if util.safehasattr(e, 'reason'):
433 # This error occurs when the client and server don't share a
433 # This error occurs when the client and server don't share a
434 # common/supported SSL/TLS protocol. We've disabled SSLv2 and SSLv3
434 # common/supported SSL/TLS protocol. We've disabled SSLv2 and SSLv3
435 # outright. Hopefully the reason for this error is that we require
435 # outright. Hopefully the reason for this error is that we require
436 # TLS 1.1+ and the server only supports TLS 1.0. Whatever the
436 # TLS 1.1+ and the server only supports TLS 1.0. Whatever the
437 # reason, try to emit an actionable warning.
437 # reason, try to emit an actionable warning.
438 if e.reason == 'UNSUPPORTED_PROTOCOL':
438 if e.reason == 'UNSUPPORTED_PROTOCOL':
439 # We attempted TLS 1.0+.
439 # We attempted TLS 1.0+.
440 if settings['protocolui'] == 'tls1.0':
440 if settings['protocolui'] == 'tls1.0':
441 # We support more than just TLS 1.0+. If this happens,
441 # We support more than just TLS 1.0+. If this happens,
442 # the likely scenario is either the client or the server
442 # the likely scenario is either the client or the server
443 # is really old. (e.g. server doesn't support TLS 1.0+ or
443 # is really old. (e.g. server doesn't support TLS 1.0+ or
444 # client doesn't support modern TLS versions introduced
444 # client doesn't support modern TLS versions introduced
445 # several years from when this comment was written).
445 # several years from when this comment was written).
446 if supportedprotocols != set(['tls1.0']):
446 if supportedprotocols != set(['tls1.0']):
447 ui.warn(_(
447 ui.warn(_(
448 '(could not communicate with %s using security '
448 '(could not communicate with %s using security '
449 'protocols %s; if you are using a modern Mercurial '
449 'protocols %s; if you are using a modern Mercurial '
450 'version, consider contacting the operator of this '
450 'version, consider contacting the operator of this '
451 'server; see '
451 'server; see '
452 'https://mercurial-scm.org/wiki/SecureConnections '
452 'https://mercurial-scm.org/wiki/SecureConnections '
453 'for more info)\n') % (
453 'for more info)\n') % (
454 serverhostname,
454 serverhostname,
455 ', '.join(sorted(supportedprotocols))))
455 ', '.join(sorted(supportedprotocols))))
456 else:
456 else:
457 ui.warn(_(
457 ui.warn(_(
458 '(could not communicate with %s using TLS 1.0; the '
458 '(could not communicate with %s using TLS 1.0; the '
459 'likely cause of this is the server no longer '
459 'likely cause of this is the server no longer '
460 'supports TLS 1.0 because it has known security '
460 'supports TLS 1.0 because it has known security '
461 'vulnerabilities; see '
461 'vulnerabilities; see '
462 'https://mercurial-scm.org/wiki/SecureConnections '
462 'https://mercurial-scm.org/wiki/SecureConnections '
463 'for more info)\n') % serverhostname)
463 'for more info)\n') % serverhostname)
464 else:
464 else:
465 # We attempted TLS 1.1+. We can only get here if the client
465 # We attempted TLS 1.1+. We can only get here if the client
466 # supports the configured protocol. So the likely reason is
466 # supports the configured protocol. So the likely reason is
467 # the client wants better security than the server can
467 # the client wants better security than the server can
468 # offer.
468 # offer.
469 ui.warn(_(
469 ui.warn(_(
470 '(could not negotiate a common security protocol (%s+) '
470 '(could not negotiate a common security protocol (%s+) '
471 'with %s; the likely cause is Mercurial is configured '
471 'with %s; the likely cause is Mercurial is configured '
472 'to be more secure than the server can support)\n') % (
472 'to be more secure than the server can support)\n') % (
473 settings['protocolui'], serverhostname))
473 settings['protocolui'], serverhostname))
474 ui.warn(_('(consider contacting the operator of this '
474 ui.warn(_('(consider contacting the operator of this '
475 'server and ask them to support modern TLS '
475 'server and ask them to support modern TLS '
476 'protocol versions; or, set '
476 'protocol versions; or, set '
477 'hostsecurity.%s:minimumprotocol=tls1.0 to allow '
477 'hostsecurity.%s:minimumprotocol=tls1.0 to allow '
478 'use of legacy, less secure protocols when '
478 'use of legacy, less secure protocols when '
479 'communicating with this server)\n') %
479 'communicating with this server)\n') %
480 serverhostname)
480 serverhostname)
481 ui.warn(_(
481 ui.warn(_(
482 '(see https://mercurial-scm.org/wiki/SecureConnections '
482 '(see https://mercurial-scm.org/wiki/SecureConnections '
483 'for more info)\n'))
483 'for more info)\n'))
484 raise
484 raise
485
485
486 # check if wrap_socket failed silently because socket had been
486 # check if wrap_socket failed silently because socket had been
487 # closed
487 # closed
488 # - see http://bugs.python.org/issue13721
488 # - see http://bugs.python.org/issue13721
489 if not sslsocket.cipher():
489 if not sslsocket.cipher():
490 raise error.Abort(_('ssl connection failed'))
490 raise error.Abort(_('ssl connection failed'))
491
491
492 sslsocket._hgstate = {
492 sslsocket._hgstate = {
493 'caloaded': caloaded,
493 'caloaded': caloaded,
494 'hostname': serverhostname,
494 'hostname': serverhostname,
495 'settings': settings,
495 'settings': settings,
496 'ui': ui,
496 'ui': ui,
497 }
497 }
498
498
499 return sslsocket
499 return sslsocket
500
500
501 def wrapserversocket(sock, ui, certfile=None, keyfile=None, cafile=None,
501 def wrapserversocket(sock, ui, certfile=None, keyfile=None, cafile=None,
502 requireclientcert=False):
502 requireclientcert=False):
503 """Wrap a socket for use by servers.
503 """Wrap a socket for use by servers.
504
504
505 ``certfile`` and ``keyfile`` specify the files containing the certificate's
505 ``certfile`` and ``keyfile`` specify the files containing the certificate's
506 public and private keys, respectively. Both keys can be defined in the same
506 public and private keys, respectively. Both keys can be defined in the same
507 file via ``certfile`` (the private key must come first in the file).
507 file via ``certfile`` (the private key must come first in the file).
508
508
509 ``cafile`` defines the path to certificate authorities.
509 ``cafile`` defines the path to certificate authorities.
510
510
511 ``requireclientcert`` specifies whether to require client certificates.
511 ``requireclientcert`` specifies whether to require client certificates.
512
512
513 Typically ``cafile`` is only defined if ``requireclientcert`` is true.
513 Typically ``cafile`` is only defined if ``requireclientcert`` is true.
514 """
514 """
515 protocol, options, _protocolui = protocolsettings('tls1.0')
515 protocol, options, _protocolui = protocolsettings('tls1.0')
516
516
517 # This config option is intended for use in tests only. It is a giant
517 # This config option is intended for use in tests only. It is a giant
518 # footgun to kill security. Don't define it.
518 # footgun to kill security. Don't define it.
519 exactprotocol = ui.config('devel', 'serverexactprotocol')
519 exactprotocol = ui.config('devel', 'serverexactprotocol')
520 if exactprotocol == 'tls1.0':
520 if exactprotocol == 'tls1.0':
521 protocol = ssl.PROTOCOL_TLSv1
521 protocol = ssl.PROTOCOL_TLSv1
522 elif exactprotocol == 'tls1.1':
522 elif exactprotocol == 'tls1.1':
523 if 'tls1.1' not in supportedprotocols:
523 if 'tls1.1' not in supportedprotocols:
524 raise error.Abort(_('TLS 1.1 not supported by this Python'))
524 raise error.Abort(_('TLS 1.1 not supported by this Python'))
525 protocol = ssl.PROTOCOL_TLSv1_1
525 protocol = ssl.PROTOCOL_TLSv1_1
526 elif exactprotocol == 'tls1.2':
526 elif exactprotocol == 'tls1.2':
527 if 'tls1.2' not in supportedprotocols:
527 if 'tls1.2' not in supportedprotocols:
528 raise error.Abort(_('TLS 1.2 not supported by this Python'))
528 raise error.Abort(_('TLS 1.2 not supported by this Python'))
529 protocol = ssl.PROTOCOL_TLSv1_2
529 protocol = ssl.PROTOCOL_TLSv1_2
530 elif exactprotocol:
530 elif exactprotocol:
531 raise error.Abort(_('invalid value for serverexactprotocol: %s') %
531 raise error.Abort(_('invalid value for serverexactprotocol: %s') %
532 exactprotocol)
532 exactprotocol)
533
533
534 if modernssl:
534 if modernssl:
535 # We /could/ use create_default_context() here since it doesn't load
535 # We /could/ use create_default_context() here since it doesn't load
536 # CAs when configured for client auth. However, it is hard-coded to
536 # CAs when configured for client auth. However, it is hard-coded to
537 # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
537 # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
538 sslcontext = SSLContext(protocol)
538 sslcontext = SSLContext(protocol)
539 sslcontext.options |= options
539 sslcontext.options |= options
540
540
541 # Improve forward secrecy.
541 # Improve forward secrecy.
542 sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)
542 sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)
543 sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0)
543 sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0)
544
544
545 # Use the list of more secure ciphers if found in the ssl module.
545 # Use the list of more secure ciphers if found in the ssl module.
546 if util.safehasattr(ssl, '_RESTRICTED_SERVER_CIPHERS'):
546 if util.safehasattr(ssl, '_RESTRICTED_SERVER_CIPHERS'):
547 sslcontext.options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0)
547 sslcontext.options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0)
548 sslcontext.set_ciphers(ssl._RESTRICTED_SERVER_CIPHERS)
548 sslcontext.set_ciphers(ssl._RESTRICTED_SERVER_CIPHERS)
549 else:
549 else:
550 sslcontext = SSLContext(ssl.PROTOCOL_TLSv1)
550 sslcontext = SSLContext(ssl.PROTOCOL_TLSv1)
551
551
552 if requireclientcert:
552 if requireclientcert:
553 sslcontext.verify_mode = ssl.CERT_REQUIRED
553 sslcontext.verify_mode = ssl.CERT_REQUIRED
554 else:
554 else:
555 sslcontext.verify_mode = ssl.CERT_NONE
555 sslcontext.verify_mode = ssl.CERT_NONE
556
556
557 if certfile or keyfile:
557 if certfile or keyfile:
558 sslcontext.load_cert_chain(certfile=certfile, keyfile=keyfile)
558 sslcontext.load_cert_chain(certfile=certfile, keyfile=keyfile)
559
559
560 if cafile:
560 if cafile:
561 sslcontext.load_verify_locations(cafile=cafile)
561 sslcontext.load_verify_locations(cafile=cafile)
562
562
563 return sslcontext.wrap_socket(sock, server_side=True)
563 return sslcontext.wrap_socket(sock, server_side=True)
564
564
565 class wildcarderror(Exception):
565 class wildcarderror(Exception):
566 """Represents an error parsing wildcards in DNS name."""
566 """Represents an error parsing wildcards in DNS name."""
567
567
568 def _dnsnamematch(dn, hostname, maxwildcards=1):
568 def _dnsnamematch(dn, hostname, maxwildcards=1):
569 """Match DNS names according RFC 6125 section 6.4.3.
569 """Match DNS names according RFC 6125 section 6.4.3.
570
570
571 This code is effectively copied from CPython's ssl._dnsname_match.
571 This code is effectively copied from CPython's ssl._dnsname_match.
572
572
573 Returns a bool indicating whether the expected hostname matches
573 Returns a bool indicating whether the expected hostname matches
574 the value in ``dn``.
574 the value in ``dn``.
575 """
575 """
576 pats = []
576 pats = []
577 if not dn:
577 if not dn:
578 return False
578 return False
579
579
580 pieces = dn.split(r'.')
580 pieces = dn.split(r'.')
581 leftmost = pieces[0]
581 leftmost = pieces[0]
582 remainder = pieces[1:]
582 remainder = pieces[1:]
583 wildcards = leftmost.count('*')
583 wildcards = leftmost.count('*')
584 if wildcards > maxwildcards:
584 if wildcards > maxwildcards:
585 raise wildcarderror(
585 raise wildcarderror(
586 _('too many wildcards in certificate DNS name: %s') % dn)
586 _('too many wildcards in certificate DNS name: %s') % dn)
587
587
588 # speed up common case w/o wildcards
588 # speed up common case w/o wildcards
589 if not wildcards:
589 if not wildcards:
590 return dn.lower() == hostname.lower()
590 return dn.lower() == hostname.lower()
591
591
592 # RFC 6125, section 6.4.3, subitem 1.
592 # RFC 6125, section 6.4.3, subitem 1.
593 # The client SHOULD NOT attempt to match a presented identifier in which
593 # The client SHOULD NOT attempt to match a presented identifier in which
594 # the wildcard character comprises a label other than the left-most label.
594 # the wildcard character comprises a label other than the left-most label.
595 if leftmost == '*':
595 if leftmost == '*':
596 # When '*' is a fragment by itself, it matches a non-empty dotless
596 # When '*' is a fragment by itself, it matches a non-empty dotless
597 # fragment.
597 # fragment.
598 pats.append('[^.]+')
598 pats.append('[^.]+')
599 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
599 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
600 # RFC 6125, section 6.4.3, subitem 3.
600 # RFC 6125, section 6.4.3, subitem 3.
601 # The client SHOULD NOT attempt to match a presented identifier
601 # The client SHOULD NOT attempt to match a presented identifier
602 # where the wildcard character is embedded within an A-label or
602 # where the wildcard character is embedded within an A-label or
603 # U-label of an internationalized domain name.
603 # U-label of an internationalized domain name.
604 pats.append(re.escape(leftmost))
604 pats.append(re.escape(leftmost))
605 else:
605 else:
606 # Otherwise, '*' matches any dotless string, e.g. www*
606 # Otherwise, '*' matches any dotless string, e.g. www*
607 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
607 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
608
608
609 # add the remaining fragments, ignore any wildcards
609 # add the remaining fragments, ignore any wildcards
610 for frag in remainder:
610 for frag in remainder:
611 pats.append(re.escape(frag))
611 pats.append(re.escape(frag))
612
612
613 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
613 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
614 return pat.match(hostname) is not None
614 return pat.match(hostname) is not None
615
615
616 def _verifycert(cert, hostname):
616 def _verifycert(cert, hostname):
617 '''Verify that cert (in socket.getpeercert() format) matches hostname.
617 '''Verify that cert (in socket.getpeercert() format) matches hostname.
618 CRLs is not handled.
618 CRLs is not handled.
619
619
620 Returns error message if any problems are found and None on success.
620 Returns error message if any problems are found and None on success.
621 '''
621 '''
622 if not cert:
622 if not cert:
623 return _('no certificate received')
623 return _('no certificate received')
624
624
625 dnsnames = []
625 dnsnames = []
626 san = cert.get('subjectAltName', [])
626 san = cert.get('subjectAltName', [])
627 for key, value in san:
627 for key, value in san:
628 if key == 'DNS':
628 if key == 'DNS':
629 try:
629 try:
630 if _dnsnamematch(value, hostname):
630 if _dnsnamematch(value, hostname):
631 return
631 return
632 except wildcarderror as e:
632 except wildcarderror as e:
633 return e.args[0]
633 return e.args[0]
634
634
635 dnsnames.append(value)
635 dnsnames.append(value)
636
636
637 if not dnsnames:
637 if not dnsnames:
638 # The subject is only checked when there is no DNS in subjectAltName.
638 # The subject is only checked when there is no DNS in subjectAltName.
639 for sub in cert.get('subject', []):
639 for sub in cert.get('subject', []):
640 for key, value in sub:
640 for key, value in sub:
641 # According to RFC 2818 the most specific Common Name must
641 # According to RFC 2818 the most specific Common Name must
642 # be used.
642 # be used.
643 if key == 'commonName':
643 if key == 'commonName':
644 # 'subject' entries are unicode.
644 # 'subject' entries are unicode.
645 try:
645 try:
646 value = value.encode('ascii')
646 value = value.encode('ascii')
647 except UnicodeEncodeError:
647 except UnicodeEncodeError:
648 return _('IDN in certificate not supported')
648 return _('IDN in certificate not supported')
649
649
650 try:
650 try:
651 if _dnsnamematch(value, hostname):
651 if _dnsnamematch(value, hostname):
652 return
652 return
653 except wildcarderror as e:
653 except wildcarderror as e:
654 return e.args[0]
654 return e.args[0]
655
655
656 dnsnames.append(value)
656 dnsnames.append(value)
657
657
658 if len(dnsnames) > 1:
658 if len(dnsnames) > 1:
659 return _('certificate is for %s') % ', '.join(dnsnames)
659 return _('certificate is for %s') % ', '.join(dnsnames)
660 elif len(dnsnames) == 1:
660 elif len(dnsnames) == 1:
661 return _('certificate is for %s') % dnsnames[0]
661 return _('certificate is for %s') % dnsnames[0]
662 else:
662 else:
663 return _('no commonName or subjectAltName found in certificate')
663 return _('no commonName or subjectAltName found in certificate')
664
664
665 def _plainapplepython():
665 def _plainapplepython():
666 """return true if this seems to be a pure Apple Python that
666 """return true if this seems to be a pure Apple Python that
667 * is unfrozen and presumably has the whole mercurial module in the file
667 * is unfrozen and presumably has the whole mercurial module in the file
668 system
668 system
669 * presumably is an Apple Python that uses Apple OpenSSL which has patches
669 * presumably is an Apple Python that uses Apple OpenSSL which has patches
670 for using system certificate store CAs in addition to the provided
670 for using system certificate store CAs in addition to the provided
671 cacerts file
671 cacerts file
672 """
672 """
673 if (pycompat.sysplatform != 'darwin' or
673 if (pycompat.sysplatform != 'darwin' or
674 util.mainfrozen() or not pycompat.sysexecutable):
674 util.mainfrozen() or not pycompat.sysexecutable):
675 return False
675 return False
676 exe = os.path.realpath(pycompat.sysexecutable).lower()
676 exe = os.path.realpath(pycompat.sysexecutable).lower()
677 return (exe.startswith('/usr/bin/python') or
677 return (exe.startswith('/usr/bin/python') or
678 exe.startswith('/system/library/frameworks/python.framework/'))
678 exe.startswith('/system/library/frameworks/python.framework/'))
679
679
680 _systemcacertpaths = [
680 _systemcacertpaths = [
681 # RHEL, CentOS, and Fedora
681 # RHEL, CentOS, and Fedora
682 '/etc/pki/tls/certs/ca-bundle.trust.crt',
682 '/etc/pki/tls/certs/ca-bundle.trust.crt',
683 # Debian, Ubuntu, Gentoo
683 # Debian, Ubuntu, Gentoo
684 '/etc/ssl/certs/ca-certificates.crt',
684 '/etc/ssl/certs/ca-certificates.crt',
685 ]
685 ]
686
686
687 def _defaultcacerts(ui):
687 def _defaultcacerts(ui):
688 """return path to default CA certificates or None.
688 """return path to default CA certificates or None.
689
689
690 It is assumed this function is called when the returned certificates
690 It is assumed this function is called when the returned certificates
691 file will actually be used to validate connections. Therefore this
691 file will actually be used to validate connections. Therefore this
692 function may print warnings or debug messages assuming this usage.
692 function may print warnings or debug messages assuming this usage.
693
693
694 We don't print a message when the Python is able to load default
694 We don't print a message when the Python is able to load default
695 CA certs because this scenario is detected at socket connect time.
695 CA certs because this scenario is detected at socket connect time.
696 """
696 """
697 # The "certifi" Python package provides certificates. If it is installed
697 # The "certifi" Python package provides certificates. If it is installed
698 # and usable, assume the user intends it to be used and use it.
698 # and usable, assume the user intends it to be used and use it.
699 try:
699 try:
700 import certifi
700 import certifi
701 certs = certifi.where()
701 certs = certifi.where()
702 if os.path.exists(certs):
702 if os.path.exists(certs):
703 ui.debug('using ca certificates from certifi\n')
703 ui.debug('using ca certificates from certifi\n')
704 return certs
704 return certs
705 except (ImportError, AttributeError):
705 except (ImportError, AttributeError):
706 pass
706 pass
707
707
708 # On Windows, only the modern ssl module is capable of loading the system
708 # On Windows, only the modern ssl module is capable of loading the system
709 # CA certificates. If we're not capable of doing that, emit a warning
709 # CA certificates. If we're not capable of doing that, emit a warning
710 # because we'll get a certificate verification error later and the lack
710 # because we'll get a certificate verification error later and the lack
711 # of loaded CA certificates will be the reason why.
711 # of loaded CA certificates will be the reason why.
712 # Assertion: this code is only called if certificates are being verified.
712 # Assertion: this code is only called if certificates are being verified.
713 if pycompat.osname == 'nt':
713 if pycompat.osname == 'nt':
714 if not _canloaddefaultcerts:
714 if not _canloaddefaultcerts:
715 ui.warn(_('(unable to load Windows CA certificates; see '
715 ui.warn(_('(unable to load Windows CA certificates; see '
716 'https://mercurial-scm.org/wiki/SecureConnections for '
716 'https://mercurial-scm.org/wiki/SecureConnections for '
717 'how to configure Mercurial to avoid this message)\n'))
717 'how to configure Mercurial to avoid this message)\n'))
718
718
719 return None
719 return None
720
720
721 # Apple's OpenSSL has patches that allow a specially constructed certificate
721 # Apple's OpenSSL has patches that allow a specially constructed certificate
722 # to load the system CA store. If we're running on Apple Python, use this
722 # to load the system CA store. If we're running on Apple Python, use this
723 # trick.
723 # trick.
724 if _plainapplepython():
724 if _plainapplepython():
725 dummycert = os.path.join(
725 dummycert = os.path.join(
726 os.path.dirname(pycompat.fsencode(__file__)), 'dummycert.pem')
726 os.path.dirname(pycompat.fsencode(__file__)), 'dummycert.pem')
727 if os.path.exists(dummycert):
727 if os.path.exists(dummycert):
728 return dummycert
728 return dummycert
729
729
730 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
730 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
731 # load system certs, we're out of luck.
731 # load system certs, we're out of luck.
732 if pycompat.sysplatform == 'darwin':
732 if pycompat.sysplatform == 'darwin':
733 # FUTURE Consider looking for Homebrew or MacPorts installed certs
733 # FUTURE Consider looking for Homebrew or MacPorts installed certs
734 # files. Also consider exporting the keychain certs to a file during
734 # files. Also consider exporting the keychain certs to a file during
735 # Mercurial install.
735 # Mercurial install.
736 if not _canloaddefaultcerts:
736 if not _canloaddefaultcerts:
737 ui.warn(_('(unable to load CA certificates; see '
737 ui.warn(_('(unable to load CA certificates; see '
738 'https://mercurial-scm.org/wiki/SecureConnections for '
738 'https://mercurial-scm.org/wiki/SecureConnections for '
739 'how to configure Mercurial to avoid this message)\n'))
739 'how to configure Mercurial to avoid this message)\n'))
740 return None
740 return None
741
741
742 # / is writable on Windows. Out of an abundance of caution make sure
742 # / is writable on Windows. Out of an abundance of caution make sure
743 # we're not on Windows because paths from _systemcacerts could be installed
743 # we're not on Windows because paths from _systemcacerts could be installed
744 # by non-admin users.
744 # by non-admin users.
745 assert pycompat.osname != 'nt'
745 assert pycompat.osname != 'nt'
746
746
747 # Try to find CA certificates in well-known locations. We print a warning
747 # Try to find CA certificates in well-known locations. We print a warning
748 # when using a found file because we don't want too much silent magic
748 # when using a found file because we don't want too much silent magic
749 # for security settings. The expectation is that proper Mercurial
749 # for security settings. The expectation is that proper Mercurial
750 # installs will have the CA certs path defined at install time and the
750 # installs will have the CA certs path defined at install time and the
751 # installer/packager will make an appropriate decision on the user's
751 # installer/packager will make an appropriate decision on the user's
752 # behalf. We only get here and perform this setting as a feature of
752 # behalf. We only get here and perform this setting as a feature of
753 # last resort.
753 # last resort.
754 if not _canloaddefaultcerts:
754 if not _canloaddefaultcerts:
755 for path in _systemcacertpaths:
755 for path in _systemcacertpaths:
756 if os.path.isfile(path):
756 if os.path.isfile(path):
757 ui.warn(_('(using CA certificates from %s; if you see this '
757 ui.warn(_('(using CA certificates from %s; if you see this '
758 'message, your Mercurial install is not properly '
758 'message, your Mercurial install is not properly '
759 'configured; see '
759 'configured; see '
760 'https://mercurial-scm.org/wiki/SecureConnections '
760 'https://mercurial-scm.org/wiki/SecureConnections '
761 'for how to configure Mercurial to avoid this '
761 'for how to configure Mercurial to avoid this '
762 'message)\n') % path)
762 'message)\n') % path)
763 return path
763 return path
764
764
765 ui.warn(_('(unable to load CA certificates; see '
765 ui.warn(_('(unable to load CA certificates; see '
766 'https://mercurial-scm.org/wiki/SecureConnections for '
766 'https://mercurial-scm.org/wiki/SecureConnections for '
767 'how to configure Mercurial to avoid this message)\n'))
767 'how to configure Mercurial to avoid this message)\n'))
768
768
769 return None
769 return None
770
770
771 def validatesocket(sock):
771 def validatesocket(sock):
772 """Validate a socket meets security requirements.
772 """Validate a socket meets security requirements.
773
773
774 The passed socket must have been created with ``wrapsocket()``.
774 The passed socket must have been created with ``wrapsocket()``.
775 """
775 """
776 host = sock._hgstate['hostname']
776 host = sock._hgstate['hostname']
777 ui = sock._hgstate['ui']
777 ui = sock._hgstate['ui']
778 settings = sock._hgstate['settings']
778 settings = sock._hgstate['settings']
779
779
780 try:
780 try:
781 peercert = sock.getpeercert(True)
781 peercert = sock.getpeercert(True)
782 peercert2 = sock.getpeercert()
782 peercert2 = sock.getpeercert()
783 except AttributeError:
783 except AttributeError:
784 raise error.Abort(_('%s ssl connection error') % host)
784 raise error.Abort(_('%s ssl connection error') % host)
785
785
786 if not peercert:
786 if not peercert:
787 raise error.Abort(_('%s certificate error: '
787 raise error.Abort(_('%s certificate error: '
788 'no certificate received') % host)
788 'no certificate received') % host)
789
789
790 if settings['disablecertverification']:
790 if settings['disablecertverification']:
791 # We don't print the certificate fingerprint because it shouldn't
791 # We don't print the certificate fingerprint because it shouldn't
792 # be necessary: if the user requested certificate verification be
792 # be necessary: if the user requested certificate verification be
793 # disabled, they presumably already saw a message about the inability
793 # disabled, they presumably already saw a message about the inability
794 # to verify the certificate and this message would have printed the
794 # to verify the certificate and this message would have printed the
795 # fingerprint. So printing the fingerprint here adds little to no
795 # fingerprint. So printing the fingerprint here adds little to no
796 # value.
796 # value.
797 ui.warn(_('warning: connection security to %s is disabled per current '
797 ui.warn(_('warning: connection security to %s is disabled per current '
798 'settings; communication is susceptible to eavesdropping '
798 'settings; communication is susceptible to eavesdropping '
799 'and tampering\n') % host)
799 'and tampering\n') % host)
800 return
800 return
801
801
802 # If a certificate fingerprint is pinned, use it and only it to
802 # If a certificate fingerprint is pinned, use it and only it to
803 # validate the remote cert.
803 # validate the remote cert.
804 peerfingerprints = {
804 peerfingerprints = {
805 'sha1': hashlib.sha1(peercert).hexdigest(),
805 'sha1': hashlib.sha1(peercert).hexdigest(),
806 'sha256': hashlib.sha256(peercert).hexdigest(),
806 'sha256': hashlib.sha256(peercert).hexdigest(),
807 'sha512': hashlib.sha512(peercert).hexdigest(),
807 'sha512': hashlib.sha512(peercert).hexdigest(),
808 }
808 }
809
809
810 def fmtfingerprint(s):
810 def fmtfingerprint(s):
811 return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
811 return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
812
812
813 nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
813 nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
814
814
815 if settings['certfingerprints']:
815 if settings['certfingerprints']:
816 for hash, fingerprint in settings['certfingerprints']:
816 for hash, fingerprint in settings['certfingerprints']:
817 if peerfingerprints[hash].lower() == fingerprint:
817 if peerfingerprints[hash].lower() == fingerprint:
818 ui.debug('%s certificate matched fingerprint %s:%s\n' %
818 ui.debug('%s certificate matched fingerprint %s:%s\n' %
819 (host, hash, fmtfingerprint(fingerprint)))
819 (host, hash, fmtfingerprint(fingerprint)))
820 if settings['legacyfingerprint']:
820 if settings['legacyfingerprint']:
821 ui.warn(_('(SHA-1 fingerprint for %s found in legacy '
821 ui.warn(_('(SHA-1 fingerprint for %s found in legacy '
822 '[hostfingerprints] section; '
822 '[hostfingerprints] section; '
823 'if you trust this fingerprint, set the '
823 'if you trust this fingerprint, set the '
824 'following config value in [hostsecurity] and '
824 'following config value in [hostsecurity] and '
825 'remove the old one from [hostfingerprints] '
825 'remove the old one from [hostfingerprints] '
826 'to upgrade to a more secure SHA-256 '
826 'to upgrade to a more secure SHA-256 '
827 'fingerprint: '
827 'fingerprint: '
828 '%s.fingerprints=%s)\n') % (
828 '%s:fingerprints=%s)\n') % (
829 host, host, nicefingerprint))
829 host, host, nicefingerprint))
830 return
830 return
831
831
832 # Pinned fingerprint didn't match. This is a fatal error.
832 # Pinned fingerprint didn't match. This is a fatal error.
833 if settings['legacyfingerprint']:
833 if settings['legacyfingerprint']:
834 section = 'hostfingerprint'
834 section = 'hostfingerprint'
835 nice = fmtfingerprint(peerfingerprints['sha1'])
835 nice = fmtfingerprint(peerfingerprints['sha1'])
836 else:
836 else:
837 section = 'hostsecurity'
837 section = 'hostsecurity'
838 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
838 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
839 raise error.Abort(_('certificate for %s has unexpected '
839 raise error.Abort(_('certificate for %s has unexpected '
840 'fingerprint %s') % (host, nice),
840 'fingerprint %s') % (host, nice),
841 hint=_('check %s configuration') % section)
841 hint=_('check %s configuration') % section)
842
842
843 # Security is enabled but no CAs are loaded. We can't establish trust
843 # Security is enabled but no CAs are loaded. We can't establish trust
844 # for the cert so abort.
844 # for the cert so abort.
845 if not sock._hgstate['caloaded']:
845 if not sock._hgstate['caloaded']:
846 raise error.Abort(
846 raise error.Abort(
847 _('unable to verify security of %s (no loaded CA certificates); '
847 _('unable to verify security of %s (no loaded CA certificates); '
848 'refusing to connect') % host,
848 'refusing to connect') % host,
849 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
849 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
850 'how to configure Mercurial to avoid this error or set '
850 'how to configure Mercurial to avoid this error or set '
851 'hostsecurity.%s:fingerprints=%s to trust this server') %
851 'hostsecurity.%s:fingerprints=%s to trust this server') %
852 (host, nicefingerprint))
852 (host, nicefingerprint))
853
853
854 msg = _verifycert(peercert2, host)
854 msg = _verifycert(peercert2, host)
855 if msg:
855 if msg:
856 raise error.Abort(_('%s certificate error: %s') % (host, msg),
856 raise error.Abort(_('%s certificate error: %s') % (host, msg),
857 hint=_('set hostsecurity.%s:certfingerprints=%s '
857 hint=_('set hostsecurity.%s:certfingerprints=%s '
858 'config setting or use --insecure to connect '
858 'config setting or use --insecure to connect '
859 'insecurely') %
859 'insecurely') %
860 (host, nicefingerprint))
860 (host, nicefingerprint))
@@ -1,636 +1,636 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import errno
3 import errno
4 import os
4 import os
5 import re
5 import re
6 import socket
6 import socket
7 import stat
7 import stat
8 import subprocess
8 import subprocess
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 checks = {
14 checks = {
15 "true": (lambda: True, "yak shaving"),
15 "true": (lambda: True, "yak shaving"),
16 "false": (lambda: False, "nail clipper"),
16 "false": (lambda: False, "nail clipper"),
17 }
17 }
18
18
19 def check(name, desc):
19 def check(name, desc):
20 """Registers a check function for a feature."""
20 """Registers a check function for a feature."""
21 def decorator(func):
21 def decorator(func):
22 checks[name] = (func, desc)
22 checks[name] = (func, desc)
23 return func
23 return func
24 return decorator
24 return decorator
25
25
26 def checkvers(name, desc, vers):
26 def checkvers(name, desc, vers):
27 """Registers a check function for each of a series of versions.
27 """Registers a check function for each of a series of versions.
28
28
29 vers can be a list or an iterator"""
29 vers can be a list or an iterator"""
30 def decorator(func):
30 def decorator(func):
31 def funcv(v):
31 def funcv(v):
32 def f():
32 def f():
33 return func(v)
33 return func(v)
34 return f
34 return f
35 for v in vers:
35 for v in vers:
36 v = str(v)
36 v = str(v)
37 f = funcv(v)
37 f = funcv(v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
39 return func
39 return func
40 return decorator
40 return decorator
41
41
42 def checkfeatures(features):
42 def checkfeatures(features):
43 result = {
43 result = {
44 'error': [],
44 'error': [],
45 'missing': [],
45 'missing': [],
46 'skipped': [],
46 'skipped': [],
47 }
47 }
48
48
49 for feature in features:
49 for feature in features:
50 negate = feature.startswith('no-')
50 negate = feature.startswith('no-')
51 if negate:
51 if negate:
52 feature = feature[3:]
52 feature = feature[3:]
53
53
54 if feature not in checks:
54 if feature not in checks:
55 result['missing'].append(feature)
55 result['missing'].append(feature)
56 continue
56 continue
57
57
58 check, desc = checks[feature]
58 check, desc = checks[feature]
59 try:
59 try:
60 available = check()
60 available = check()
61 except Exception:
61 except Exception:
62 result['error'].append('hghave check failed: %s' % feature)
62 result['error'].append('hghave check failed: %s' % feature)
63 continue
63 continue
64
64
65 if not negate and not available:
65 if not negate and not available:
66 result['skipped'].append('missing feature: %s' % desc)
66 result['skipped'].append('missing feature: %s' % desc)
67 elif negate and available:
67 elif negate and available:
68 result['skipped'].append('system supports %s' % desc)
68 result['skipped'].append('system supports %s' % desc)
69
69
70 return result
70 return result
71
71
72 def require(features):
72 def require(features):
73 """Require that features are available, exiting if not."""
73 """Require that features are available, exiting if not."""
74 result = checkfeatures(features)
74 result = checkfeatures(features)
75
75
76 for missing in result['missing']:
76 for missing in result['missing']:
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
78 for msg in result['skipped']:
78 for msg in result['skipped']:
79 sys.stderr.write('skipped: %s\n' % msg)
79 sys.stderr.write('skipped: %s\n' % msg)
80 for msg in result['error']:
80 for msg in result['error']:
81 sys.stderr.write('%s\n' % msg)
81 sys.stderr.write('%s\n' % msg)
82
82
83 if result['missing']:
83 if result['missing']:
84 sys.exit(2)
84 sys.exit(2)
85
85
86 if result['skipped'] or result['error']:
86 if result['skipped'] or result['error']:
87 sys.exit(1)
87 sys.exit(1)
88
88
89 def matchoutput(cmd, regexp, ignorestatus=False):
89 def matchoutput(cmd, regexp, ignorestatus=False):
90 """Return the match object if cmd executes successfully and its output
90 """Return the match object if cmd executes successfully and its output
91 is matched by the supplied regular expression.
91 is matched by the supplied regular expression.
92 """
92 """
93 r = re.compile(regexp)
93 r = re.compile(regexp)
94 try:
94 try:
95 p = subprocess.Popen(
95 p = subprocess.Popen(
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
97 except OSError as e:
97 except OSError as e:
98 if e.errno != errno.ENOENT:
98 if e.errno != errno.ENOENT:
99 raise
99 raise
100 ret = -1
100 ret = -1
101 ret = p.wait()
101 ret = p.wait()
102 s = p.stdout.read()
102 s = p.stdout.read()
103 return (ignorestatus or not ret) and r.search(s)
103 return (ignorestatus or not ret) and r.search(s)
104
104
105 @check("baz", "GNU Arch baz client")
105 @check("baz", "GNU Arch baz client")
106 def has_baz():
106 def has_baz():
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
108
108
109 @check("bzr", "Canonical's Bazaar client")
109 @check("bzr", "Canonical's Bazaar client")
110 def has_bzr():
110 def has_bzr():
111 try:
111 try:
112 import bzrlib
112 import bzrlib
113 import bzrlib.bzrdir
113 import bzrlib.bzrdir
114 import bzrlib.errors
114 import bzrlib.errors
115 import bzrlib.revision
115 import bzrlib.revision
116 import bzrlib.revisionspec
116 import bzrlib.revisionspec
117 bzrlib.revisionspec.RevisionSpec
117 bzrlib.revisionspec.RevisionSpec
118 return bzrlib.__doc__ is not None
118 return bzrlib.__doc__ is not None
119 except (AttributeError, ImportError):
119 except (AttributeError, ImportError):
120 return False
120 return False
121
121
122 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
122 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
123 def has_bzr_range(v):
123 def has_bzr_range(v):
124 major, minor = v.split('.')[0:2]
124 major, minor = v.split('.')[0:2]
125 try:
125 try:
126 import bzrlib
126 import bzrlib
127 return (bzrlib.__doc__ is not None
127 return (bzrlib.__doc__ is not None
128 and bzrlib.version_info[:2] >= (int(major), int(minor)))
128 and bzrlib.version_info[:2] >= (int(major), int(minor)))
129 except ImportError:
129 except ImportError:
130 return False
130 return False
131
131
132 @check("chg", "running with chg")
132 @check("chg", "running with chg")
133 def has_chg():
133 def has_chg():
134 return 'CHGHG' in os.environ
134 return 'CHGHG' in os.environ
135
135
136 @check("cvs", "cvs client/server")
136 @check("cvs", "cvs client/server")
137 def has_cvs():
137 def has_cvs():
138 re = br'Concurrent Versions System.*?server'
138 re = br'Concurrent Versions System.*?server'
139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
140
140
141 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
141 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
142 def has_cvs112():
142 def has_cvs112():
143 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
143 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
144 return matchoutput('cvs --version 2>&1', re) and not has_msys()
144 return matchoutput('cvs --version 2>&1', re) and not has_msys()
145
145
146 @check("cvsnt", "cvsnt client/server")
146 @check("cvsnt", "cvsnt client/server")
147 def has_cvsnt():
147 def has_cvsnt():
148 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
148 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
149 return matchoutput('cvsnt --version 2>&1', re)
149 return matchoutput('cvsnt --version 2>&1', re)
150
150
151 @check("darcs", "darcs client")
151 @check("darcs", "darcs client")
152 def has_darcs():
152 def has_darcs():
153 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
153 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
154
154
155 @check("mtn", "monotone client (>= 1.0)")
155 @check("mtn", "monotone client (>= 1.0)")
156 def has_mtn():
156 def has_mtn():
157 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
157 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
158 'mtn --version', br'monotone 0\.', True)
158 'mtn --version', br'monotone 0\.', True)
159
159
160 @check("eol-in-paths", "end-of-lines in paths")
160 @check("eol-in-paths", "end-of-lines in paths")
161 def has_eol_in_paths():
161 def has_eol_in_paths():
162 try:
162 try:
163 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
163 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
164 os.close(fd)
164 os.close(fd)
165 os.remove(path)
165 os.remove(path)
166 return True
166 return True
167 except (IOError, OSError):
167 except (IOError, OSError):
168 return False
168 return False
169
169
170 @check("execbit", "executable bit")
170 @check("execbit", "executable bit")
171 def has_executablebit():
171 def has_executablebit():
172 try:
172 try:
173 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
173 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
174 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
174 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
175 try:
175 try:
176 os.close(fh)
176 os.close(fh)
177 m = os.stat(fn).st_mode & 0o777
177 m = os.stat(fn).st_mode & 0o777
178 new_file_has_exec = m & EXECFLAGS
178 new_file_has_exec = m & EXECFLAGS
179 os.chmod(fn, m ^ EXECFLAGS)
179 os.chmod(fn, m ^ EXECFLAGS)
180 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
180 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
181 finally:
181 finally:
182 os.unlink(fn)
182 os.unlink(fn)
183 except (IOError, OSError):
183 except (IOError, OSError):
184 # we don't care, the user probably won't be able to commit anyway
184 # we don't care, the user probably won't be able to commit anyway
185 return False
185 return False
186 return not (new_file_has_exec or exec_flags_cannot_flip)
186 return not (new_file_has_exec or exec_flags_cannot_flip)
187
187
188 @check("icasefs", "case insensitive file system")
188 @check("icasefs", "case insensitive file system")
189 def has_icasefs():
189 def has_icasefs():
190 # Stolen from mercurial.util
190 # Stolen from mercurial.util
191 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
191 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
192 os.close(fd)
192 os.close(fd)
193 try:
193 try:
194 s1 = os.stat(path)
194 s1 = os.stat(path)
195 d, b = os.path.split(path)
195 d, b = os.path.split(path)
196 p2 = os.path.join(d, b.upper())
196 p2 = os.path.join(d, b.upper())
197 if path == p2:
197 if path == p2:
198 p2 = os.path.join(d, b.lower())
198 p2 = os.path.join(d, b.lower())
199 try:
199 try:
200 s2 = os.stat(p2)
200 s2 = os.stat(p2)
201 return s2 == s1
201 return s2 == s1
202 except OSError:
202 except OSError:
203 return False
203 return False
204 finally:
204 finally:
205 os.remove(path)
205 os.remove(path)
206
206
207 @check("fifo", "named pipes")
207 @check("fifo", "named pipes")
208 def has_fifo():
208 def has_fifo():
209 if getattr(os, "mkfifo", None) is None:
209 if getattr(os, "mkfifo", None) is None:
210 return False
210 return False
211 name = tempfile.mktemp(dir='.', prefix=tempprefix)
211 name = tempfile.mktemp(dir='.', prefix=tempprefix)
212 try:
212 try:
213 os.mkfifo(name)
213 os.mkfifo(name)
214 os.unlink(name)
214 os.unlink(name)
215 return True
215 return True
216 except OSError:
216 except OSError:
217 return False
217 return False
218
218
219 @check("killdaemons", 'killdaemons.py support')
219 @check("killdaemons", 'killdaemons.py support')
220 def has_killdaemons():
220 def has_killdaemons():
221 return True
221 return True
222
222
223 @check("cacheable", "cacheable filesystem")
223 @check("cacheable", "cacheable filesystem")
224 def has_cacheable_fs():
224 def has_cacheable_fs():
225 from mercurial import util
225 from mercurial import util
226
226
227 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
227 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
228 os.close(fd)
228 os.close(fd)
229 try:
229 try:
230 return util.cachestat(path).cacheable()
230 return util.cachestat(path).cacheable()
231 finally:
231 finally:
232 os.remove(path)
232 os.remove(path)
233
233
234 @check("lsprof", "python lsprof module")
234 @check("lsprof", "python lsprof module")
235 def has_lsprof():
235 def has_lsprof():
236 try:
236 try:
237 import _lsprof
237 import _lsprof
238 _lsprof.Profiler # silence unused import warning
238 _lsprof.Profiler # silence unused import warning
239 return True
239 return True
240 except ImportError:
240 except ImportError:
241 return False
241 return False
242
242
243 def gethgversion():
243 def gethgversion():
244 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
244 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
245 if not m:
245 if not m:
246 return (0, 0)
246 return (0, 0)
247 return (int(m.group(1)), int(m.group(2)))
247 return (int(m.group(1)), int(m.group(2)))
248
248
249 @checkvers("hg", "Mercurial >= %s",
249 @checkvers("hg", "Mercurial >= %s",
250 list([(1.0 * x) / 10 for x in range(9, 40)]))
250 list([(1.0 * x) / 10 for x in range(9, 99)]))
251 def has_hg_range(v):
251 def has_hg_range(v):
252 major, minor = v.split('.')[0:2]
252 major, minor = v.split('.')[0:2]
253 return gethgversion() >= (int(major), int(minor))
253 return gethgversion() >= (int(major), int(minor))
254
254
255 @check("hg08", "Mercurial >= 0.8")
255 @check("hg08", "Mercurial >= 0.8")
256 def has_hg08():
256 def has_hg08():
257 if checks["hg09"][0]():
257 if checks["hg09"][0]():
258 return True
258 return True
259 return matchoutput('hg help annotate 2>&1', '--date')
259 return matchoutput('hg help annotate 2>&1', '--date')
260
260
261 @check("hg07", "Mercurial >= 0.7")
261 @check("hg07", "Mercurial >= 0.7")
262 def has_hg07():
262 def has_hg07():
263 if checks["hg08"][0]():
263 if checks["hg08"][0]():
264 return True
264 return True
265 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
265 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
266
266
267 @check("hg06", "Mercurial >= 0.6")
267 @check("hg06", "Mercurial >= 0.6")
268 def has_hg06():
268 def has_hg06():
269 if checks["hg07"][0]():
269 if checks["hg07"][0]():
270 return True
270 return True
271 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
271 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
272
272
273 @check("gettext", "GNU Gettext (msgfmt)")
273 @check("gettext", "GNU Gettext (msgfmt)")
274 def has_gettext():
274 def has_gettext():
275 return matchoutput('msgfmt --version', br'GNU gettext-tools')
275 return matchoutput('msgfmt --version', br'GNU gettext-tools')
276
276
277 @check("git", "git command line client")
277 @check("git", "git command line client")
278 def has_git():
278 def has_git():
279 return matchoutput('git --version 2>&1', br'^git version')
279 return matchoutput('git --version 2>&1', br'^git version')
280
280
281 @check("docutils", "Docutils text processing library")
281 @check("docutils", "Docutils text processing library")
282 def has_docutils():
282 def has_docutils():
283 try:
283 try:
284 import docutils.core
284 import docutils.core
285 docutils.core.publish_cmdline # silence unused import
285 docutils.core.publish_cmdline # silence unused import
286 return True
286 return True
287 except ImportError:
287 except ImportError:
288 return False
288 return False
289
289
290 def getsvnversion():
290 def getsvnversion():
291 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
291 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
292 if not m:
292 if not m:
293 return (0, 0)
293 return (0, 0)
294 return (int(m.group(1)), int(m.group(2)))
294 return (int(m.group(1)), int(m.group(2)))
295
295
296 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
296 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
297 def has_svn_range(v):
297 def has_svn_range(v):
298 major, minor = v.split('.')[0:2]
298 major, minor = v.split('.')[0:2]
299 return getsvnversion() >= (int(major), int(minor))
299 return getsvnversion() >= (int(major), int(minor))
300
300
301 @check("svn", "subversion client and admin tools")
301 @check("svn", "subversion client and admin tools")
302 def has_svn():
302 def has_svn():
303 return matchoutput('svn --version 2>&1', br'^svn, version') and \
303 return matchoutput('svn --version 2>&1', br'^svn, version') and \
304 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
304 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
305
305
306 @check("svn-bindings", "subversion python bindings")
306 @check("svn-bindings", "subversion python bindings")
307 def has_svn_bindings():
307 def has_svn_bindings():
308 try:
308 try:
309 import svn.core
309 import svn.core
310 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
310 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
311 if version < (1, 4):
311 if version < (1, 4):
312 return False
312 return False
313 return True
313 return True
314 except ImportError:
314 except ImportError:
315 return False
315 return False
316
316
317 @check("p4", "Perforce server and client")
317 @check("p4", "Perforce server and client")
318 def has_p4():
318 def has_p4():
319 return (matchoutput('p4 -V', br'Rev\. P4/') and
319 return (matchoutput('p4 -V', br'Rev\. P4/') and
320 matchoutput('p4d -V', br'Rev\. P4D/'))
320 matchoutput('p4d -V', br'Rev\. P4D/'))
321
321
322 @check("symlink", "symbolic links")
322 @check("symlink", "symbolic links")
323 def has_symlink():
323 def has_symlink():
324 if getattr(os, "symlink", None) is None:
324 if getattr(os, "symlink", None) is None:
325 return False
325 return False
326 name = tempfile.mktemp(dir='.', prefix=tempprefix)
326 name = tempfile.mktemp(dir='.', prefix=tempprefix)
327 try:
327 try:
328 os.symlink(".", name)
328 os.symlink(".", name)
329 os.unlink(name)
329 os.unlink(name)
330 return True
330 return True
331 except (OSError, AttributeError):
331 except (OSError, AttributeError):
332 return False
332 return False
333
333
334 @check("hardlink", "hardlinks")
334 @check("hardlink", "hardlinks")
335 def has_hardlink():
335 def has_hardlink():
336 from mercurial import util
336 from mercurial import util
337 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
337 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
338 os.close(fh)
338 os.close(fh)
339 name = tempfile.mktemp(dir='.', prefix=tempprefix)
339 name = tempfile.mktemp(dir='.', prefix=tempprefix)
340 try:
340 try:
341 util.oslink(fn, name)
341 util.oslink(fn, name)
342 os.unlink(name)
342 os.unlink(name)
343 return True
343 return True
344 except OSError:
344 except OSError:
345 return False
345 return False
346 finally:
346 finally:
347 os.unlink(fn)
347 os.unlink(fn)
348
348
349 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
349 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
350 def has_hardlink_whitelisted():
350 def has_hardlink_whitelisted():
351 from mercurial import util
351 from mercurial import util
352 try:
352 try:
353 fstype = util.getfstype('.')
353 fstype = util.getfstype('.')
354 except OSError:
354 except OSError:
355 return False
355 return False
356 return fstype in util._hardlinkfswhitelist
356 return fstype in util._hardlinkfswhitelist
357
357
358 @check("rmcwd", "can remove current working directory")
358 @check("rmcwd", "can remove current working directory")
359 def has_rmcwd():
359 def has_rmcwd():
360 ocwd = os.getcwd()
360 ocwd = os.getcwd()
361 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
361 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
362 try:
362 try:
363 os.chdir(temp)
363 os.chdir(temp)
364 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
364 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
365 # On Solaris and Windows, the cwd can't be removed by any names.
365 # On Solaris and Windows, the cwd can't be removed by any names.
366 os.rmdir(os.getcwd())
366 os.rmdir(os.getcwd())
367 return True
367 return True
368 except OSError:
368 except OSError:
369 return False
369 return False
370 finally:
370 finally:
371 os.chdir(ocwd)
371 os.chdir(ocwd)
372 # clean up temp dir on platforms where cwd can't be removed
372 # clean up temp dir on platforms where cwd can't be removed
373 try:
373 try:
374 os.rmdir(temp)
374 os.rmdir(temp)
375 except OSError:
375 except OSError:
376 pass
376 pass
377
377
378 @check("tla", "GNU Arch tla client")
378 @check("tla", "GNU Arch tla client")
379 def has_tla():
379 def has_tla():
380 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
380 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
381
381
382 @check("gpg", "gpg client")
382 @check("gpg", "gpg client")
383 def has_gpg():
383 def has_gpg():
384 return matchoutput('gpg --version 2>&1', br'GnuPG')
384 return matchoutput('gpg --version 2>&1', br'GnuPG')
385
385
386 @check("gpg2", "gpg client v2")
386 @check("gpg2", "gpg client v2")
387 def has_gpg2():
387 def has_gpg2():
388 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
388 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
389
389
390 @check("gpg21", "gpg client v2.1+")
390 @check("gpg21", "gpg client v2.1+")
391 def has_gpg21():
391 def has_gpg21():
392 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
392 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
393
393
394 @check("unix-permissions", "unix-style permissions")
394 @check("unix-permissions", "unix-style permissions")
395 def has_unix_permissions():
395 def has_unix_permissions():
396 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
396 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
397 try:
397 try:
398 fname = os.path.join(d, 'foo')
398 fname = os.path.join(d, 'foo')
399 for umask in (0o77, 0o07, 0o22):
399 for umask in (0o77, 0o07, 0o22):
400 os.umask(umask)
400 os.umask(umask)
401 f = open(fname, 'w')
401 f = open(fname, 'w')
402 f.close()
402 f.close()
403 mode = os.stat(fname).st_mode
403 mode = os.stat(fname).st_mode
404 os.unlink(fname)
404 os.unlink(fname)
405 if mode & 0o777 != ~umask & 0o666:
405 if mode & 0o777 != ~umask & 0o666:
406 return False
406 return False
407 return True
407 return True
408 finally:
408 finally:
409 os.rmdir(d)
409 os.rmdir(d)
410
410
411 @check("unix-socket", "AF_UNIX socket family")
411 @check("unix-socket", "AF_UNIX socket family")
412 def has_unix_socket():
412 def has_unix_socket():
413 return getattr(socket, 'AF_UNIX', None) is not None
413 return getattr(socket, 'AF_UNIX', None) is not None
414
414
415 @check("root", "root permissions")
415 @check("root", "root permissions")
416 def has_root():
416 def has_root():
417 return getattr(os, 'geteuid', None) and os.geteuid() == 0
417 return getattr(os, 'geteuid', None) and os.geteuid() == 0
418
418
419 @check("pyflakes", "Pyflakes python linter")
419 @check("pyflakes", "Pyflakes python linter")
420 def has_pyflakes():
420 def has_pyflakes():
421 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
421 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
422 br"<stdin>:1: 're' imported but unused",
422 br"<stdin>:1: 're' imported but unused",
423 True)
423 True)
424
424
425 @check("pylint", "Pylint python linter")
425 @check("pylint", "Pylint python linter")
426 def has_pylint():
426 def has_pylint():
427 return matchoutput("pylint --help",
427 return matchoutput("pylint --help",
428 br"Usage: pylint",
428 br"Usage: pylint",
429 True)
429 True)
430
430
431 @check("pygments", "Pygments source highlighting library")
431 @check("pygments", "Pygments source highlighting library")
432 def has_pygments():
432 def has_pygments():
433 try:
433 try:
434 import pygments
434 import pygments
435 pygments.highlight # silence unused import warning
435 pygments.highlight # silence unused import warning
436 return True
436 return True
437 except ImportError:
437 except ImportError:
438 return False
438 return False
439
439
440 @check("outer-repo", "outer repo")
440 @check("outer-repo", "outer repo")
441 def has_outer_repo():
441 def has_outer_repo():
442 # failing for other reasons than 'no repo' imply that there is a repo
442 # failing for other reasons than 'no repo' imply that there is a repo
443 return not matchoutput('hg root 2>&1',
443 return not matchoutput('hg root 2>&1',
444 br'abort: no repository found', True)
444 br'abort: no repository found', True)
445
445
446 @check("ssl", "ssl module available")
446 @check("ssl", "ssl module available")
447 def has_ssl():
447 def has_ssl():
448 try:
448 try:
449 import ssl
449 import ssl
450 ssl.CERT_NONE
450 ssl.CERT_NONE
451 return True
451 return True
452 except ImportError:
452 except ImportError:
453 return False
453 return False
454
454
455 @check("sslcontext", "python >= 2.7.9 ssl")
455 @check("sslcontext", "python >= 2.7.9 ssl")
456 def has_sslcontext():
456 def has_sslcontext():
457 try:
457 try:
458 import ssl
458 import ssl
459 ssl.SSLContext
459 ssl.SSLContext
460 return True
460 return True
461 except (ImportError, AttributeError):
461 except (ImportError, AttributeError):
462 return False
462 return False
463
463
464 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
464 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
465 def has_defaultcacerts():
465 def has_defaultcacerts():
466 from mercurial import sslutil, ui as uimod
466 from mercurial import sslutil, ui as uimod
467 ui = uimod.ui.load()
467 ui = uimod.ui.load()
468 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
468 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
469
469
470 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
470 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
471 def has_defaultcacertsloaded():
471 def has_defaultcacertsloaded():
472 import ssl
472 import ssl
473 from mercurial import sslutil, ui as uimod
473 from mercurial import sslutil, ui as uimod
474
474
475 if not has_defaultcacerts():
475 if not has_defaultcacerts():
476 return False
476 return False
477 if not has_sslcontext():
477 if not has_sslcontext():
478 return False
478 return False
479
479
480 ui = uimod.ui.load()
480 ui = uimod.ui.load()
481 cafile = sslutil._defaultcacerts(ui)
481 cafile = sslutil._defaultcacerts(ui)
482 ctx = ssl.create_default_context()
482 ctx = ssl.create_default_context()
483 if cafile:
483 if cafile:
484 ctx.load_verify_locations(cafile=cafile)
484 ctx.load_verify_locations(cafile=cafile)
485 else:
485 else:
486 ctx.load_default_certs()
486 ctx.load_default_certs()
487
487
488 return len(ctx.get_ca_certs()) > 0
488 return len(ctx.get_ca_certs()) > 0
489
489
490 @check("tls1.2", "TLS 1.2 protocol support")
490 @check("tls1.2", "TLS 1.2 protocol support")
491 def has_tls1_2():
491 def has_tls1_2():
492 from mercurial import sslutil
492 from mercurial import sslutil
493 return 'tls1.2' in sslutil.supportedprotocols
493 return 'tls1.2' in sslutil.supportedprotocols
494
494
495 @check("windows", "Windows")
495 @check("windows", "Windows")
496 def has_windows():
496 def has_windows():
497 return os.name == 'nt'
497 return os.name == 'nt'
498
498
499 @check("system-sh", "system() uses sh")
499 @check("system-sh", "system() uses sh")
500 def has_system_sh():
500 def has_system_sh():
501 return os.name != 'nt'
501 return os.name != 'nt'
502
502
503 @check("serve", "platform and python can manage 'hg serve -d'")
503 @check("serve", "platform and python can manage 'hg serve -d'")
504 def has_serve():
504 def has_serve():
505 return os.name != 'nt' # gross approximation
505 return os.name != 'nt' # gross approximation
506
506
507 @check("test-repo", "running tests from repository")
507 @check("test-repo", "running tests from repository")
508 def has_test_repo():
508 def has_test_repo():
509 t = os.environ["TESTDIR"]
509 t = os.environ["TESTDIR"]
510 return os.path.isdir(os.path.join(t, "..", ".hg"))
510 return os.path.isdir(os.path.join(t, "..", ".hg"))
511
511
512 @check("tic", "terminfo compiler and curses module")
512 @check("tic", "terminfo compiler and curses module")
513 def has_tic():
513 def has_tic():
514 try:
514 try:
515 import curses
515 import curses
516 curses.COLOR_BLUE
516 curses.COLOR_BLUE
517 return matchoutput('test -x "`which tic`"', br'')
517 return matchoutput('test -x "`which tic`"', br'')
518 except ImportError:
518 except ImportError:
519 return False
519 return False
520
520
521 @check("msys", "Windows with MSYS")
521 @check("msys", "Windows with MSYS")
522 def has_msys():
522 def has_msys():
523 return os.getenv('MSYSTEM')
523 return os.getenv('MSYSTEM')
524
524
525 @check("aix", "AIX")
525 @check("aix", "AIX")
526 def has_aix():
526 def has_aix():
527 return sys.platform.startswith("aix")
527 return sys.platform.startswith("aix")
528
528
529 @check("osx", "OS X")
529 @check("osx", "OS X")
530 def has_osx():
530 def has_osx():
531 return sys.platform == 'darwin'
531 return sys.platform == 'darwin'
532
532
533 @check("osxpackaging", "OS X packaging tools")
533 @check("osxpackaging", "OS X packaging tools")
534 def has_osxpackaging():
534 def has_osxpackaging():
535 try:
535 try:
536 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
536 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
537 and matchoutput(
537 and matchoutput(
538 'productbuild', br'Usage: productbuild ',
538 'productbuild', br'Usage: productbuild ',
539 ignorestatus=1)
539 ignorestatus=1)
540 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
540 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
541 and matchoutput(
541 and matchoutput(
542 'xar --help', br'Usage: xar', ignorestatus=1))
542 'xar --help', br'Usage: xar', ignorestatus=1))
543 except ImportError:
543 except ImportError:
544 return False
544 return False
545
545
546 @check("docker", "docker support")
546 @check("docker", "docker support")
547 def has_docker():
547 def has_docker():
548 pat = br'A self-sufficient runtime for'
548 pat = br'A self-sufficient runtime for'
549 if matchoutput('docker --help', pat):
549 if matchoutput('docker --help', pat):
550 if 'linux' not in sys.platform:
550 if 'linux' not in sys.platform:
551 # TODO: in theory we should be able to test docker-based
551 # TODO: in theory we should be able to test docker-based
552 # package creation on non-linux using boot2docker, but in
552 # package creation on non-linux using boot2docker, but in
553 # practice that requires extra coordination to make sure
553 # practice that requires extra coordination to make sure
554 # $TESTTEMP is going to be visible at the same path to the
554 # $TESTTEMP is going to be visible at the same path to the
555 # boot2docker VM. If we figure out how to verify that, we
555 # boot2docker VM. If we figure out how to verify that, we
556 # can use the following instead of just saying False:
556 # can use the following instead of just saying False:
557 # return 'DOCKER_HOST' in os.environ
557 # return 'DOCKER_HOST' in os.environ
558 return False
558 return False
559
559
560 return True
560 return True
561 return False
561 return False
562
562
563 @check("debhelper", "debian packaging tools")
563 @check("debhelper", "debian packaging tools")
564 def has_debhelper():
564 def has_debhelper():
565 dpkg = matchoutput('dpkg --version',
565 dpkg = matchoutput('dpkg --version',
566 br"Debian `dpkg' package management program")
566 br"Debian `dpkg' package management program")
567 dh = matchoutput('dh --help',
567 dh = matchoutput('dh --help',
568 br'dh is a part of debhelper.', ignorestatus=True)
568 br'dh is a part of debhelper.', ignorestatus=True)
569 dh_py2 = matchoutput('dh_python2 --help',
569 dh_py2 = matchoutput('dh_python2 --help',
570 br'other supported Python versions')
570 br'other supported Python versions')
571 return dpkg and dh and dh_py2
571 return dpkg and dh and dh_py2
572
572
573 @check("demandimport", "demandimport enabled")
573 @check("demandimport", "demandimport enabled")
574 def has_demandimport():
574 def has_demandimport():
575 return os.environ.get('HGDEMANDIMPORT') != 'disable'
575 return os.environ.get('HGDEMANDIMPORT') != 'disable'
576
576
577 @check("absimport", "absolute_import in __future__")
577 @check("absimport", "absolute_import in __future__")
578 def has_absimport():
578 def has_absimport():
579 import __future__
579 import __future__
580 from mercurial import util
580 from mercurial import util
581 return util.safehasattr(__future__, "absolute_import")
581 return util.safehasattr(__future__, "absolute_import")
582
582
583 @check("py3k", "running with Python 3.x")
583 @check("py3k", "running with Python 3.x")
584 def has_py3k():
584 def has_py3k():
585 return 3 == sys.version_info[0]
585 return 3 == sys.version_info[0]
586
586
587 @check("py3exe", "a Python 3.x interpreter is available")
587 @check("py3exe", "a Python 3.x interpreter is available")
588 def has_python3exe():
588 def has_python3exe():
589 return 'PYTHON3' in os.environ
589 return 'PYTHON3' in os.environ
590
590
591 @check("py3pygments", "Pygments available on Python 3.x")
591 @check("py3pygments", "Pygments available on Python 3.x")
592 def has_py3pygments():
592 def has_py3pygments():
593 if has_py3k():
593 if has_py3k():
594 return has_pygments()
594 return has_pygments()
595 elif has_python3exe():
595 elif has_python3exe():
596 # just check exit status (ignoring output)
596 # just check exit status (ignoring output)
597 py3 = os.environ['PYTHON3']
597 py3 = os.environ['PYTHON3']
598 return matchoutput('%s -c "import pygments"' % py3, br'')
598 return matchoutput('%s -c "import pygments"' % py3, br'')
599 return False
599 return False
600
600
601 @check("pure", "running with pure Python code")
601 @check("pure", "running with pure Python code")
602 def has_pure():
602 def has_pure():
603 return any([
603 return any([
604 os.environ.get("HGMODULEPOLICY") == "py",
604 os.environ.get("HGMODULEPOLICY") == "py",
605 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
605 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
606 ])
606 ])
607
607
608 @check("slow", "allow slow tests")
608 @check("slow", "allow slow tests")
609 def has_slow():
609 def has_slow():
610 return os.environ.get('HGTEST_SLOW') == 'slow'
610 return os.environ.get('HGTEST_SLOW') == 'slow'
611
611
612 @check("hypothesis", "Hypothesis automated test generation")
612 @check("hypothesis", "Hypothesis automated test generation")
613 def has_hypothesis():
613 def has_hypothesis():
614 try:
614 try:
615 import hypothesis
615 import hypothesis
616 hypothesis.given
616 hypothesis.given
617 return True
617 return True
618 except ImportError:
618 except ImportError:
619 return False
619 return False
620
620
621 @check("unziplinks", "unzip(1) understands and extracts symlinks")
621 @check("unziplinks", "unzip(1) understands and extracts symlinks")
622 def unzip_understands_symlinks():
622 def unzip_understands_symlinks():
623 return matchoutput('unzip --help', br'Info-ZIP')
623 return matchoutput('unzip --help', br'Info-ZIP')
624
624
625 @check("zstd", "zstd Python module available")
625 @check("zstd", "zstd Python module available")
626 def has_zstd():
626 def has_zstd():
627 try:
627 try:
628 import mercurial.zstd
628 import mercurial.zstd
629 mercurial.zstd.__version__
629 mercurial.zstd.__version__
630 return True
630 return True
631 except ImportError:
631 except ImportError:
632 return False
632 return False
633
633
634 @check("devfull", "/dev/full special file")
634 @check("devfull", "/dev/full special file")
635 def has_dev_full():
635 def has_dev_full():
636 return os.path.exists('/dev/full')
636 return os.path.exists('/dev/full')
@@ -1,1310 +1,1354 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extdiff]
2 > [extdiff]
3 > # for portability:
3 > # for portability:
4 > pdiff = sh "$RUNTESTDIR/pdiff"
4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 > EOF
5 > EOF
6
6
7 Create a repo with some stuff in it:
7 Create a repo with some stuff in it:
8
8
9 $ hg init a
9 $ hg init a
10 $ cd a
10 $ cd a
11 $ echo a > a
11 $ echo a > a
12 $ echo a > d
12 $ echo a > d
13 $ echo a > e
13 $ echo a > e
14 $ hg ci -qAm0
14 $ hg ci -qAm0
15 $ echo b > a
15 $ echo b > a
16 $ hg ci -m1 -u bar
16 $ hg ci -m1 -u bar
17 $ hg mv a b
17 $ hg mv a b
18 $ hg ci -m2
18 $ hg ci -m2
19 $ hg cp b c
19 $ hg cp b c
20 $ hg ci -m3 -u baz
20 $ hg ci -m3 -u baz
21 $ echo b > d
21 $ echo b > d
22 $ echo f > e
22 $ echo f > e
23 $ hg ci -m4
23 $ hg ci -m4
24 $ hg up -q 3
24 $ hg up -q 3
25 $ echo b > e
25 $ echo b > e
26 $ hg branch -q stable
26 $ hg branch -q stable
27 $ hg ci -m5
27 $ hg ci -m5
28 $ hg merge -q default --tool internal:local
28 $ hg merge -q default --tool internal:local
29 $ hg branch -q default
29 $ hg branch -q default
30 $ hg ci -m6
30 $ hg ci -m6
31 $ hg phase --public 3
31 $ hg phase --public 3
32 $ hg phase --force --secret 6
32 $ hg phase --force --secret 6
33
33
34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 @ test@6.secret: 6
35 @ test@6.secret: 6
36 |\
36 |\
37 | o test@5.draft: 5
37 | o test@5.draft: 5
38 | |
38 | |
39 o | test@4.draft: 4
39 o | test@4.draft: 4
40 |/
40 |/
41 o baz@3.public: 3
41 o baz@3.public: 3
42 |
42 |
43 o test@2.public: 2
43 o test@2.public: 2
44 |
44 |
45 o bar@1.public: 1
45 o bar@1.public: 1
46 |
46 |
47 o test@0.public: 0
47 o test@0.public: 0
48
48
49 Can't continue without starting:
49 Can't continue without starting:
50
50
51 $ hg rm -q e
51 $ hg rm -q e
52 $ hg graft --continue
52 $ hg graft --continue
53 abort: no graft in progress
53 abort: no graft in progress
54 [255]
54 [255]
55 $ hg revert -r . -q e
55 $ hg revert -r . -q e
56
56
57 Need to specify a rev:
57 Need to specify a rev:
58
58
59 $ hg graft
59 $ hg graft
60 abort: no revisions specified
60 abort: no revisions specified
61 [255]
61 [255]
62
62
63 Can't graft ancestor:
63 Can't graft ancestor:
64
64
65 $ hg graft 1 2
65 $ hg graft 1 2
66 skipping ancestor revision 1:5d205f8b35b6
66 skipping ancestor revision 1:5d205f8b35b6
67 skipping ancestor revision 2:5c095ad7e90f
67 skipping ancestor revision 2:5c095ad7e90f
68 [255]
68 [255]
69
69
70 Specify revisions with -r:
70 Specify revisions with -r:
71
71
72 $ hg graft -r 1 -r 2
72 $ hg graft -r 1 -r 2
73 skipping ancestor revision 1:5d205f8b35b6
73 skipping ancestor revision 1:5d205f8b35b6
74 skipping ancestor revision 2:5c095ad7e90f
74 skipping ancestor revision 2:5c095ad7e90f
75 [255]
75 [255]
76
76
77 $ hg graft -r 1 2
77 $ hg graft -r 1 2
78 warning: inconsistent use of --rev might give unexpected revision ordering!
78 warning: inconsistent use of --rev might give unexpected revision ordering!
79 skipping ancestor revision 2:5c095ad7e90f
79 skipping ancestor revision 2:5c095ad7e90f
80 skipping ancestor revision 1:5d205f8b35b6
80 skipping ancestor revision 1:5d205f8b35b6
81 [255]
81 [255]
82
82
83 Can't graft with dirty wd:
83 Can't graft with dirty wd:
84
84
85 $ hg up -q 0
85 $ hg up -q 0
86 $ echo foo > a
86 $ echo foo > a
87 $ hg graft 1
87 $ hg graft 1
88 abort: uncommitted changes
88 abort: uncommitted changes
89 [255]
89 [255]
90 $ hg revert a
90 $ hg revert a
91
91
92 Graft a rename:
92 Graft a rename:
93 (this also tests that editor is invoked if '--edit' is specified)
93 (this also tests that editor is invoked if '--edit' is specified)
94
94
95 $ hg status --rev "2^1" --rev 2
95 $ hg status --rev "2^1" --rev 2
96 A b
96 A b
97 R a
97 R a
98 $ HGEDITOR=cat hg graft 2 -u foo --edit
98 $ HGEDITOR=cat hg graft 2 -u foo --edit
99 grafting 2:5c095ad7e90f "2"
99 grafting 2:5c095ad7e90f "2"
100 merging a and b to b
100 merging a and b to b
101 2
101 2
102
102
103
103
104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
105 HG: Leave message empty to abort commit.
105 HG: Leave message empty to abort commit.
106 HG: --
106 HG: --
107 HG: user: foo
107 HG: user: foo
108 HG: branch 'default'
108 HG: branch 'default'
109 HG: added b
109 HG: added b
110 HG: removed a
110 HG: removed a
111 $ hg export tip --git
111 $ hg export tip --git
112 # HG changeset patch
112 # HG changeset patch
113 # User foo
113 # User foo
114 # Date 0 0
114 # Date 0 0
115 # Thu Jan 01 00:00:00 1970 +0000
115 # Thu Jan 01 00:00:00 1970 +0000
116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
118 2
118 2
119
119
120 diff --git a/a b/b
120 diff --git a/a b/b
121 rename from a
121 rename from a
122 rename to b
122 rename to b
123
123
124 Look for extra:source
124 Look for extra:source
125
125
126 $ hg log --debug -r tip
126 $ hg log --debug -r tip
127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
128 tag: tip
128 tag: tip
129 phase: draft
129 phase: draft
130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
131 parent: -1:0000000000000000000000000000000000000000
131 parent: -1:0000000000000000000000000000000000000000
132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
133 user: foo
133 user: foo
134 date: Thu Jan 01 00:00:00 1970 +0000
134 date: Thu Jan 01 00:00:00 1970 +0000
135 files+: b
135 files+: b
136 files-: a
136 files-: a
137 extra: branch=default
137 extra: branch=default
138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
139 description:
139 description:
140 2
140 2
141
141
142
142
143
143
144 Graft out of order, skipping a merge and a duplicate
144 Graft out of order, skipping a merge and a duplicate
145 (this also tests that editor is not invoked if '--edit' is not specified)
145 (this also tests that editor is not invoked if '--edit' is not specified)
146
146
147 $ hg graft 1 5 4 3 'merge()' 2 -n
147 $ hg graft 1 5 4 3 'merge()' 2 -n
148 skipping ungraftable merge revision 6
148 skipping ungraftable merge revision 6
149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
150 grafting 1:5d205f8b35b6 "1"
150 grafting 1:5d205f8b35b6 "1"
151 grafting 5:97f8bfe72746 "5"
151 grafting 5:97f8bfe72746 "5"
152 grafting 4:9c233e8e184d "4"
152 grafting 4:9c233e8e184d "4"
153 grafting 3:4c60f11aa304 "3"
153 grafting 3:4c60f11aa304 "3"
154
154
155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
156 skipping ungraftable merge revision 6
156 skipping ungraftable merge revision 6
157 scanning for duplicate grafts
157 scanning for duplicate grafts
158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
159 grafting 1:5d205f8b35b6 "1"
159 grafting 1:5d205f8b35b6 "1"
160 searching for copies back to rev 1
160 searching for copies back to rev 1
161 unmatched files in local:
161 unmatched files in local:
162 b
162 b
163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 src: 'a' -> dst: 'b' *
164 src: 'a' -> dst: 'b' *
165 checking for directory renames
165 checking for directory renames
166 resolving manifests
166 resolving manifests
167 branchmerge: True, force: True, partial: False
167 branchmerge: True, force: True, partial: False
168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
169 preserving b for resolve of b
169 preserving b for resolve of b
170 starting 4 threads for background file closing (?)
170 starting 4 threads for background file closing (?)
171 b: local copied/moved from a -> m (premerge)
171 b: local copied/moved from a -> m (premerge)
172 picked tool ':merge' for b (binary False symlink False changedelete False)
172 picked tool ':merge' for b (binary False symlink False changedelete False)
173 merging b and a to b
173 merging b and a to b
174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
175 premerge successful
175 premerge successful
176 committing files:
176 committing files:
177 b
177 b
178 committing manifest
178 committing manifest
179 committing changelog
179 committing changelog
180 grafting 5:97f8bfe72746 "5"
180 grafting 5:97f8bfe72746 "5"
181 searching for copies back to rev 1
181 searching for copies back to rev 1
182 unmatched files in other (from topological common ancestor):
182 unmatched files in other (from topological common ancestor):
183 c
183 c
184 resolving manifests
184 resolving manifests
185 branchmerge: True, force: True, partial: False
185 branchmerge: True, force: True, partial: False
186 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
186 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
187 e: remote is newer -> g
187 e: remote is newer -> g
188 getting e
188 getting e
189 committing files:
189 committing files:
190 e
190 e
191 committing manifest
191 committing manifest
192 committing changelog
192 committing changelog
193 $ HGEDITOR=cat hg graft 4 3 --log --debug
193 $ HGEDITOR=cat hg graft 4 3 --log --debug
194 scanning for duplicate grafts
194 scanning for duplicate grafts
195 grafting 4:9c233e8e184d "4"
195 grafting 4:9c233e8e184d "4"
196 searching for copies back to rev 1
196 searching for copies back to rev 1
197 unmatched files in other (from topological common ancestor):
197 unmatched files in other (from topological common ancestor):
198 c
198 c
199 resolving manifests
199 resolving manifests
200 branchmerge: True, force: True, partial: False
200 branchmerge: True, force: True, partial: False
201 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
201 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
202 preserving e for resolve of e
202 preserving e for resolve of e
203 d: remote is newer -> g
203 d: remote is newer -> g
204 getting d
204 getting d
205 e: versions differ -> m (premerge)
205 e: versions differ -> m (premerge)
206 picked tool ':merge' for e (binary False symlink False changedelete False)
206 picked tool ':merge' for e (binary False symlink False changedelete False)
207 merging e
207 merging e
208 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
208 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
209 e: versions differ -> m (merge)
209 e: versions differ -> m (merge)
210 picked tool ':merge' for e (binary False symlink False changedelete False)
210 picked tool ':merge' for e (binary False symlink False changedelete False)
211 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
211 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
212 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
212 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
213 abort: unresolved conflicts, can't continue
213 abort: unresolved conflicts, can't continue
214 (use 'hg resolve' and 'hg graft --continue --log')
214 (use 'hg resolve' and 'hg graft --continue --log')
215 [255]
215 [255]
216
216
217 Summary should mention graft:
217 Summary should mention graft:
218
218
219 $ hg summary |grep graft
219 $ hg summary |grep graft
220 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
220 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
221
221
222 Commit while interrupted should fail:
222 Commit while interrupted should fail:
223
223
224 $ hg ci -m 'commit interrupted graft'
224 $ hg ci -m 'commit interrupted graft'
225 abort: graft in progress
225 abort: graft in progress
226 (use 'hg graft --continue' or 'hg update' to abort)
226 (use 'hg graft --continue' or 'hg update' to abort)
227 [255]
227 [255]
228
228
229 Abort the graft and try committing:
229 Abort the graft and try committing:
230
230
231 $ hg up -C .
231 $ hg up -C .
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 $ echo c >> e
233 $ echo c >> e
234 $ hg ci -mtest
234 $ hg ci -mtest
235
235
236 $ hg strip . --config extensions.strip=
236 $ hg strip . --config extensions.strip=
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
238 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
239
239
240 Graft again:
240 Graft again:
241
241
242 $ hg graft 1 5 4 3 'merge()' 2
242 $ hg graft 1 5 4 3 'merge()' 2
243 skipping ungraftable merge revision 6
243 skipping ungraftable merge revision 6
244 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
244 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
245 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
245 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
246 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
246 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
247 grafting 4:9c233e8e184d "4"
247 grafting 4:9c233e8e184d "4"
248 merging e
248 merging e
249 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
249 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
250 abort: unresolved conflicts, can't continue
250 abort: unresolved conflicts, can't continue
251 (use 'hg resolve' and 'hg graft --continue')
251 (use 'hg resolve' and 'hg graft --continue')
252 [255]
252 [255]
253
253
254 Continue without resolve should fail:
254 Continue without resolve should fail:
255
255
256 $ hg graft -c
256 $ hg graft -c
257 grafting 4:9c233e8e184d "4"
257 grafting 4:9c233e8e184d "4"
258 abort: unresolved merge conflicts (see 'hg help resolve')
258 abort: unresolved merge conflicts (see 'hg help resolve')
259 [255]
259 [255]
260
260
261 Fix up:
261 Fix up:
262
262
263 $ echo b > e
263 $ echo b > e
264 $ hg resolve -m e
264 $ hg resolve -m e
265 (no more unresolved files)
265 (no more unresolved files)
266 continue: hg graft --continue
266 continue: hg graft --continue
267
267
268 Continue with a revision should fail:
268 Continue with a revision should fail:
269
269
270 $ hg graft -c 6
270 $ hg graft -c 6
271 abort: can't specify --continue and revisions
271 abort: can't specify --continue and revisions
272 [255]
272 [255]
273
273
274 $ hg graft -c -r 6
274 $ hg graft -c -r 6
275 abort: can't specify --continue and revisions
275 abort: can't specify --continue and revisions
276 [255]
276 [255]
277
277
278 Continue for real, clobber usernames
278 Continue for real, clobber usernames
279
279
280 $ hg graft -c -U
280 $ hg graft -c -U
281 grafting 4:9c233e8e184d "4"
281 grafting 4:9c233e8e184d "4"
282 grafting 3:4c60f11aa304 "3"
282 grafting 3:4c60f11aa304 "3"
283
283
284 Compare with original:
284 Compare with original:
285
285
286 $ hg diff -r 6
286 $ hg diff -r 6
287 $ hg status --rev 0:. -C
287 $ hg status --rev 0:. -C
288 M d
288 M d
289 M e
289 M e
290 A b
290 A b
291 a
291 a
292 A c
292 A c
293 a
293 a
294 R a
294 R a
295
295
296 View graph:
296 View graph:
297
297
298 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
298 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
299 @ test@11.draft: 3
299 @ test@11.draft: 3
300 |
300 |
301 o test@10.draft: 4
301 o test@10.draft: 4
302 |
302 |
303 o test@9.draft: 5
303 o test@9.draft: 5
304 |
304 |
305 o bar@8.draft: 1
305 o bar@8.draft: 1
306 |
306 |
307 o foo@7.draft: 2
307 o foo@7.draft: 2
308 |
308 |
309 | o test@6.secret: 6
309 | o test@6.secret: 6
310 | |\
310 | |\
311 | | o test@5.draft: 5
311 | | o test@5.draft: 5
312 | | |
312 | | |
313 | o | test@4.draft: 4
313 | o | test@4.draft: 4
314 | |/
314 | |/
315 | o baz@3.public: 3
315 | o baz@3.public: 3
316 | |
316 | |
317 | o test@2.public: 2
317 | o test@2.public: 2
318 | |
318 | |
319 | o bar@1.public: 1
319 | o bar@1.public: 1
320 |/
320 |/
321 o test@0.public: 0
321 o test@0.public: 0
322
322
323 Graft again onto another branch should preserve the original source
323 Graft again onto another branch should preserve the original source
324 $ hg up -q 0
324 $ hg up -q 0
325 $ echo 'g'>g
325 $ echo 'g'>g
326 $ hg add g
326 $ hg add g
327 $ hg ci -m 7
327 $ hg ci -m 7
328 created new head
328 created new head
329 $ hg graft 7
329 $ hg graft 7
330 grafting 7:ef0ef43d49e7 "2"
330 grafting 7:ef0ef43d49e7 "2"
331
331
332 $ hg log -r 7 --template '{rev}:{node}\n'
332 $ hg log -r 7 --template '{rev}:{node}\n'
333 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
333 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
334 $ hg log -r 2 --template '{rev}:{node}\n'
334 $ hg log -r 2 --template '{rev}:{node}\n'
335 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
335 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
336
336
337 $ hg log --debug -r tip
337 $ hg log --debug -r tip
338 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
338 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
339 tag: tip
339 tag: tip
340 phase: draft
340 phase: draft
341 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
341 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
342 parent: -1:0000000000000000000000000000000000000000
342 parent: -1:0000000000000000000000000000000000000000
343 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
343 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
344 user: foo
344 user: foo
345 date: Thu Jan 01 00:00:00 1970 +0000
345 date: Thu Jan 01 00:00:00 1970 +0000
346 files+: b
346 files+: b
347 files-: a
347 files-: a
348 extra: branch=default
348 extra: branch=default
349 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
349 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
350 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
350 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
351 description:
351 description:
352 2
352 2
353
353
354
354
355 Disallow grafting an already grafted cset onto its original branch
355 Disallow grafting an already grafted cset onto its original branch
356 $ hg up -q 6
356 $ hg up -q 6
357 $ hg graft 7
357 $ hg graft 7
358 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
358 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
359 [255]
359 [255]
360
360
361 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
361 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
362 --- */hg-5c095ad7e90f.patch * (glob)
362 --- */hg-5c095ad7e90f.patch * (glob)
363 +++ */hg-7a4785234d87.patch * (glob)
363 +++ */hg-7a4785234d87.patch * (glob)
364 @@ -1,18 +1,18 @@
364 @@ -1,18 +1,18 @@
365 # HG changeset patch
365 # HG changeset patch
366 -# User test
366 -# User test
367 +# User foo
367 +# User foo
368 # Date 0 0
368 # Date 0 0
369 # Thu Jan 01 00:00:00 1970 +0000
369 # Thu Jan 01 00:00:00 1970 +0000
370 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
370 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
371 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
371 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
372 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
372 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
373 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
373 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
374 2
374 2
375
375
376 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
376 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
377 +diff -r b592ea63bb0c -r 7a4785234d87 a
377 +diff -r b592ea63bb0c -r 7a4785234d87 a
378 --- a/a Thu Jan 01 00:00:00 1970 +0000
378 --- a/a Thu Jan 01 00:00:00 1970 +0000
379 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
379 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
380 @@ -1,1 +0,0 @@
380 @@ -1,1 +0,0 @@
381 --b
381 --b
382 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
382 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
383 +-a
383 +-a
384 +diff -r b592ea63bb0c -r 7a4785234d87 b
384 +diff -r b592ea63bb0c -r 7a4785234d87 b
385 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
385 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
386 +++ b/b Thu Jan 01 00:00:00 1970 +0000
386 +++ b/b Thu Jan 01 00:00:00 1970 +0000
387 @@ -0,0 +1,1 @@
387 @@ -0,0 +1,1 @@
388 -+b
388 -+b
389 ++a
389 ++a
390 [1]
390 [1]
391
391
392 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
392 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
393 --- */hg-5c095ad7e90f.patch * (glob)
393 --- */hg-5c095ad7e90f.patch * (glob)
394 +++ */hg-7a4785234d87.patch * (glob)
394 +++ */hg-7a4785234d87.patch * (glob)
395 @@ -1,8 +1,8 @@
395 @@ -1,8 +1,8 @@
396 # HG changeset patch
396 # HG changeset patch
397 -# User test
397 -# User test
398 +# User foo
398 +# User foo
399 # Date 0 0
399 # Date 0 0
400 # Thu Jan 01 00:00:00 1970 +0000
400 # Thu Jan 01 00:00:00 1970 +0000
401 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
401 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
402 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
402 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
403 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
403 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
404 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
404 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
405 2
405 2
406
406
407 [1]
407 [1]
408
408
409 Disallow grafting already grafted csets with the same origin onto each other
409 Disallow grafting already grafted csets with the same origin onto each other
410 $ hg up -q 13
410 $ hg up -q 13
411 $ hg graft 2
411 $ hg graft 2
412 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
412 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
413 [255]
413 [255]
414 $ hg graft 7
414 $ hg graft 7
415 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
415 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
416 [255]
416 [255]
417
417
418 $ hg up -q 7
418 $ hg up -q 7
419 $ hg graft 2
419 $ hg graft 2
420 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
420 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
421 [255]
421 [255]
422 $ hg graft tip
422 $ hg graft tip
423 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
423 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
424 [255]
424 [255]
425
425
426 Graft with --log
426 Graft with --log
427
427
428 $ hg up -Cq 1
428 $ hg up -Cq 1
429 $ hg graft 3 --log -u foo
429 $ hg graft 3 --log -u foo
430 grafting 3:4c60f11aa304 "3"
430 grafting 3:4c60f11aa304 "3"
431 warning: can't find ancestor for 'c' copied from 'b'!
431 warning: can't find ancestor for 'c' copied from 'b'!
432 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
432 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
433 14:0c921c65ef1e 1:5d205f8b35b6 3
433 14:0c921c65ef1e 1:5d205f8b35b6 3
434 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
434 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
435
435
436 Resolve conflicted graft
436 Resolve conflicted graft
437 $ hg up -q 0
437 $ hg up -q 0
438 $ echo b > a
438 $ echo b > a
439 $ hg ci -m 8
439 $ hg ci -m 8
440 created new head
440 created new head
441 $ echo c > a
441 $ echo c > a
442 $ hg ci -m 9
442 $ hg ci -m 9
443 $ hg graft 1 --tool internal:fail
443 $ hg graft 1 --tool internal:fail
444 grafting 1:5d205f8b35b6 "1"
444 grafting 1:5d205f8b35b6 "1"
445 abort: unresolved conflicts, can't continue
445 abort: unresolved conflicts, can't continue
446 (use 'hg resolve' and 'hg graft --continue')
446 (use 'hg resolve' and 'hg graft --continue')
447 [255]
447 [255]
448 $ hg resolve --all
448 $ hg resolve --all
449 merging a
449 merging a
450 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
450 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
451 [1]
451 [1]
452 $ cat a
452 $ cat a
453 <<<<<<< local: aaa4406d4f0a - test: 9
453 <<<<<<< local: aaa4406d4f0a - test: 9
454 c
454 c
455 =======
455 =======
456 b
456 b
457 >>>>>>> graft: 5d205f8b35b6 - bar: 1
457 >>>>>>> graft: 5d205f8b35b6 - bar: 1
458 $ echo b > a
458 $ echo b > a
459 $ hg resolve -m a
459 $ hg resolve -m a
460 (no more unresolved files)
460 (no more unresolved files)
461 continue: hg graft --continue
461 continue: hg graft --continue
462 $ hg graft -c
462 $ hg graft -c
463 grafting 1:5d205f8b35b6 "1"
463 grafting 1:5d205f8b35b6 "1"
464 $ hg export tip --git
464 $ hg export tip --git
465 # HG changeset patch
465 # HG changeset patch
466 # User bar
466 # User bar
467 # Date 0 0
467 # Date 0 0
468 # Thu Jan 01 00:00:00 1970 +0000
468 # Thu Jan 01 00:00:00 1970 +0000
469 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
469 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
470 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
470 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
471 1
471 1
472
472
473 diff --git a/a b/a
473 diff --git a/a b/a
474 --- a/a
474 --- a/a
475 +++ b/a
475 +++ b/a
476 @@ -1,1 +1,1 @@
476 @@ -1,1 +1,1 @@
477 -c
477 -c
478 +b
478 +b
479
479
480 Resolve conflicted graft with rename
480 Resolve conflicted graft with rename
481 $ echo c > a
481 $ echo c > a
482 $ hg ci -m 10
482 $ hg ci -m 10
483 $ hg graft 2 --tool internal:fail
483 $ hg graft 2 --tool internal:fail
484 grafting 2:5c095ad7e90f "2"
484 grafting 2:5c095ad7e90f "2"
485 abort: unresolved conflicts, can't continue
485 abort: unresolved conflicts, can't continue
486 (use 'hg resolve' and 'hg graft --continue')
486 (use 'hg resolve' and 'hg graft --continue')
487 [255]
487 [255]
488 $ hg resolve --all
488 $ hg resolve --all
489 merging a and b to b
489 merging a and b to b
490 (no more unresolved files)
490 (no more unresolved files)
491 continue: hg graft --continue
491 continue: hg graft --continue
492 $ hg graft -c
492 $ hg graft -c
493 grafting 2:5c095ad7e90f "2"
493 grafting 2:5c095ad7e90f "2"
494 $ hg export tip --git
494 $ hg export tip --git
495 # HG changeset patch
495 # HG changeset patch
496 # User test
496 # User test
497 # Date 0 0
497 # Date 0 0
498 # Thu Jan 01 00:00:00 1970 +0000
498 # Thu Jan 01 00:00:00 1970 +0000
499 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
499 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
500 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
500 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
501 2
501 2
502
502
503 diff --git a/a b/b
503 diff --git a/a b/b
504 rename from a
504 rename from a
505 rename to b
505 rename to b
506
506
507 Test simple origin(), with and without args
507 Test simple origin(), with and without args
508 $ hg log -r 'origin()'
508 $ hg log -r 'origin()'
509 changeset: 1:5d205f8b35b6
509 changeset: 1:5d205f8b35b6
510 user: bar
510 user: bar
511 date: Thu Jan 01 00:00:00 1970 +0000
511 date: Thu Jan 01 00:00:00 1970 +0000
512 summary: 1
512 summary: 1
513
513
514 changeset: 2:5c095ad7e90f
514 changeset: 2:5c095ad7e90f
515 user: test
515 user: test
516 date: Thu Jan 01 00:00:00 1970 +0000
516 date: Thu Jan 01 00:00:00 1970 +0000
517 summary: 2
517 summary: 2
518
518
519 changeset: 3:4c60f11aa304
519 changeset: 3:4c60f11aa304
520 user: baz
520 user: baz
521 date: Thu Jan 01 00:00:00 1970 +0000
521 date: Thu Jan 01 00:00:00 1970 +0000
522 summary: 3
522 summary: 3
523
523
524 changeset: 4:9c233e8e184d
524 changeset: 4:9c233e8e184d
525 user: test
525 user: test
526 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
527 summary: 4
527 summary: 4
528
528
529 changeset: 5:97f8bfe72746
529 changeset: 5:97f8bfe72746
530 branch: stable
530 branch: stable
531 parent: 3:4c60f11aa304
531 parent: 3:4c60f11aa304
532 user: test
532 user: test
533 date: Thu Jan 01 00:00:00 1970 +0000
533 date: Thu Jan 01 00:00:00 1970 +0000
534 summary: 5
534 summary: 5
535
535
536 $ hg log -r 'origin(7)'
536 $ hg log -r 'origin(7)'
537 changeset: 2:5c095ad7e90f
537 changeset: 2:5c095ad7e90f
538 user: test
538 user: test
539 date: Thu Jan 01 00:00:00 1970 +0000
539 date: Thu Jan 01 00:00:00 1970 +0000
540 summary: 2
540 summary: 2
541
541
542 Now transplant a graft to test following through copies
542 Now transplant a graft to test following through copies
543 $ hg up -q 0
543 $ hg up -q 0
544 $ hg branch -q dev
544 $ hg branch -q dev
545 $ hg ci -qm "dev branch"
545 $ hg ci -qm "dev branch"
546 $ hg --config extensions.transplant= transplant -q 7
546 $ hg --config extensions.transplant= transplant -q 7
547 $ hg log -r 'origin(.)'
547 $ hg log -r 'origin(.)'
548 changeset: 2:5c095ad7e90f
548 changeset: 2:5c095ad7e90f
549 user: test
549 user: test
550 date: Thu Jan 01 00:00:00 1970 +0000
550 date: Thu Jan 01 00:00:00 1970 +0000
551 summary: 2
551 summary: 2
552
552
553 Test that the graft and transplant markers in extra are converted, allowing
553 Test that the graft and transplant markers in extra are converted, allowing
554 origin() to still work. Note that these recheck the immediately preceeding two
554 origin() to still work. Note that these recheck the immediately preceeding two
555 tests.
555 tests.
556 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
556 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
557
557
558 The graft case
558 The graft case
559 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
559 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
560 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
560 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
561 branch=default
561 branch=default
562 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
562 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
563 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
563 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
564 $ hg -R ../converted log -r 'origin(7)'
564 $ hg -R ../converted log -r 'origin(7)'
565 changeset: 2:e0213322b2c1
565 changeset: 2:e0213322b2c1
566 user: test
566 user: test
567 date: Thu Jan 01 00:00:00 1970 +0000
567 date: Thu Jan 01 00:00:00 1970 +0000
568 summary: 2
568 summary: 2
569
569
570 Test that template correctly expands more than one 'extra' (issue4362), and that
570 Test that template correctly expands more than one 'extra' (issue4362), and that
571 'intermediate-source' is converted.
571 'intermediate-source' is converted.
572 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
572 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
573 Extra: branch=default
573 Extra: branch=default
574 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
574 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
575 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
575 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
576 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
576 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
577
577
578 The transplant case
578 The transplant case
579 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
579 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
580 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
580 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
581 branch=dev
581 branch=dev
582 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
582 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
583 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
583 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
584 $ hg -R ../converted log -r 'origin(tip)'
584 $ hg -R ../converted log -r 'origin(tip)'
585 changeset: 2:e0213322b2c1
585 changeset: 2:e0213322b2c1
586 user: test
586 user: test
587 date: Thu Jan 01 00:00:00 1970 +0000
587 date: Thu Jan 01 00:00:00 1970 +0000
588 summary: 2
588 summary: 2
589
589
590
590
591 Test simple destination
591 Test simple destination
592 $ hg log -r 'destination()'
592 $ hg log -r 'destination()'
593 changeset: 7:ef0ef43d49e7
593 changeset: 7:ef0ef43d49e7
594 parent: 0:68795b066622
594 parent: 0:68795b066622
595 user: foo
595 user: foo
596 date: Thu Jan 01 00:00:00 1970 +0000
596 date: Thu Jan 01 00:00:00 1970 +0000
597 summary: 2
597 summary: 2
598
598
599 changeset: 8:6b9e5368ca4e
599 changeset: 8:6b9e5368ca4e
600 user: bar
600 user: bar
601 date: Thu Jan 01 00:00:00 1970 +0000
601 date: Thu Jan 01 00:00:00 1970 +0000
602 summary: 1
602 summary: 1
603
603
604 changeset: 9:1905859650ec
604 changeset: 9:1905859650ec
605 user: test
605 user: test
606 date: Thu Jan 01 00:00:00 1970 +0000
606 date: Thu Jan 01 00:00:00 1970 +0000
607 summary: 5
607 summary: 5
608
608
609 changeset: 10:52dc0b4c6907
609 changeset: 10:52dc0b4c6907
610 user: test
610 user: test
611 date: Thu Jan 01 00:00:00 1970 +0000
611 date: Thu Jan 01 00:00:00 1970 +0000
612 summary: 4
612 summary: 4
613
613
614 changeset: 11:882b35362a6b
614 changeset: 11:882b35362a6b
615 user: test
615 user: test
616 date: Thu Jan 01 00:00:00 1970 +0000
616 date: Thu Jan 01 00:00:00 1970 +0000
617 summary: 3
617 summary: 3
618
618
619 changeset: 13:7a4785234d87
619 changeset: 13:7a4785234d87
620 user: foo
620 user: foo
621 date: Thu Jan 01 00:00:00 1970 +0000
621 date: Thu Jan 01 00:00:00 1970 +0000
622 summary: 2
622 summary: 2
623
623
624 changeset: 14:0c921c65ef1e
624 changeset: 14:0c921c65ef1e
625 parent: 1:5d205f8b35b6
625 parent: 1:5d205f8b35b6
626 user: foo
626 user: foo
627 date: Thu Jan 01 00:00:00 1970 +0000
627 date: Thu Jan 01 00:00:00 1970 +0000
628 summary: 3
628 summary: 3
629
629
630 changeset: 17:f67661df0c48
630 changeset: 17:f67661df0c48
631 user: bar
631 user: bar
632 date: Thu Jan 01 00:00:00 1970 +0000
632 date: Thu Jan 01 00:00:00 1970 +0000
633 summary: 1
633 summary: 1
634
634
635 changeset: 19:9627f653b421
635 changeset: 19:9627f653b421
636 user: test
636 user: test
637 date: Thu Jan 01 00:00:00 1970 +0000
637 date: Thu Jan 01 00:00:00 1970 +0000
638 summary: 2
638 summary: 2
639
639
640 changeset: 21:7e61b508e709
640 changeset: 21:7e61b508e709
641 branch: dev
641 branch: dev
642 tag: tip
642 tag: tip
643 user: foo
643 user: foo
644 date: Thu Jan 01 00:00:00 1970 +0000
644 date: Thu Jan 01 00:00:00 1970 +0000
645 summary: 2
645 summary: 2
646
646
647 $ hg log -r 'destination(2)'
647 $ hg log -r 'destination(2)'
648 changeset: 7:ef0ef43d49e7
648 changeset: 7:ef0ef43d49e7
649 parent: 0:68795b066622
649 parent: 0:68795b066622
650 user: foo
650 user: foo
651 date: Thu Jan 01 00:00:00 1970 +0000
651 date: Thu Jan 01 00:00:00 1970 +0000
652 summary: 2
652 summary: 2
653
653
654 changeset: 13:7a4785234d87
654 changeset: 13:7a4785234d87
655 user: foo
655 user: foo
656 date: Thu Jan 01 00:00:00 1970 +0000
656 date: Thu Jan 01 00:00:00 1970 +0000
657 summary: 2
657 summary: 2
658
658
659 changeset: 19:9627f653b421
659 changeset: 19:9627f653b421
660 user: test
660 user: test
661 date: Thu Jan 01 00:00:00 1970 +0000
661 date: Thu Jan 01 00:00:00 1970 +0000
662 summary: 2
662 summary: 2
663
663
664 changeset: 21:7e61b508e709
664 changeset: 21:7e61b508e709
665 branch: dev
665 branch: dev
666 tag: tip
666 tag: tip
667 user: foo
667 user: foo
668 date: Thu Jan 01 00:00:00 1970 +0000
668 date: Thu Jan 01 00:00:00 1970 +0000
669 summary: 2
669 summary: 2
670
670
671 Transplants of grafts can find a destination...
671 Transplants of grafts can find a destination...
672 $ hg log -r 'destination(7)'
672 $ hg log -r 'destination(7)'
673 changeset: 21:7e61b508e709
673 changeset: 21:7e61b508e709
674 branch: dev
674 branch: dev
675 tag: tip
675 tag: tip
676 user: foo
676 user: foo
677 date: Thu Jan 01 00:00:00 1970 +0000
677 date: Thu Jan 01 00:00:00 1970 +0000
678 summary: 2
678 summary: 2
679
679
680 ... grafts of grafts unfortunately can't
680 ... grafts of grafts unfortunately can't
681 $ hg graft -q 13
681 $ hg graft -q 13
682 warning: can't find ancestor for 'b' copied from 'a'!
682 warning: can't find ancestor for 'b' copied from 'a'!
683 $ hg log -r 'destination(13)'
683 $ hg log -r 'destination(13)'
684 All copies of a cset
684 All copies of a cset
685 $ hg log -r 'origin(13) or destination(origin(13))'
685 $ hg log -r 'origin(13) or destination(origin(13))'
686 changeset: 2:5c095ad7e90f
686 changeset: 2:5c095ad7e90f
687 user: test
687 user: test
688 date: Thu Jan 01 00:00:00 1970 +0000
688 date: Thu Jan 01 00:00:00 1970 +0000
689 summary: 2
689 summary: 2
690
690
691 changeset: 7:ef0ef43d49e7
691 changeset: 7:ef0ef43d49e7
692 parent: 0:68795b066622
692 parent: 0:68795b066622
693 user: foo
693 user: foo
694 date: Thu Jan 01 00:00:00 1970 +0000
694 date: Thu Jan 01 00:00:00 1970 +0000
695 summary: 2
695 summary: 2
696
696
697 changeset: 13:7a4785234d87
697 changeset: 13:7a4785234d87
698 user: foo
698 user: foo
699 date: Thu Jan 01 00:00:00 1970 +0000
699 date: Thu Jan 01 00:00:00 1970 +0000
700 summary: 2
700 summary: 2
701
701
702 changeset: 19:9627f653b421
702 changeset: 19:9627f653b421
703 user: test
703 user: test
704 date: Thu Jan 01 00:00:00 1970 +0000
704 date: Thu Jan 01 00:00:00 1970 +0000
705 summary: 2
705 summary: 2
706
706
707 changeset: 21:7e61b508e709
707 changeset: 21:7e61b508e709
708 branch: dev
708 branch: dev
709 user: foo
709 user: foo
710 date: Thu Jan 01 00:00:00 1970 +0000
710 date: Thu Jan 01 00:00:00 1970 +0000
711 summary: 2
711 summary: 2
712
712
713 changeset: 22:d1cb6591fa4b
713 changeset: 22:d1cb6591fa4b
714 branch: dev
714 branch: dev
715 tag: tip
715 tag: tip
716 user: foo
716 user: foo
717 date: Thu Jan 01 00:00:00 1970 +0000
717 date: Thu Jan 01 00:00:00 1970 +0000
718 summary: 2
718 summary: 2
719
719
720
720
721 graft works on complex revset
721 graft works on complex revset
722
722
723 $ hg graft 'origin(13) or destination(origin(13))'
723 $ hg graft 'origin(13) or destination(origin(13))'
724 skipping ancestor revision 21:7e61b508e709
724 skipping ancestor revision 21:7e61b508e709
725 skipping ancestor revision 22:d1cb6591fa4b
725 skipping ancestor revision 22:d1cb6591fa4b
726 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
726 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
727 grafting 7:ef0ef43d49e7 "2"
727 grafting 7:ef0ef43d49e7 "2"
728 warning: can't find ancestor for 'b' copied from 'a'!
728 warning: can't find ancestor for 'b' copied from 'a'!
729 grafting 13:7a4785234d87 "2"
729 grafting 13:7a4785234d87 "2"
730 warning: can't find ancestor for 'b' copied from 'a'!
730 warning: can't find ancestor for 'b' copied from 'a'!
731 grafting 19:9627f653b421 "2"
731 grafting 19:9627f653b421 "2"
732 merging b
732 merging b
733 warning: can't find ancestor for 'b' copied from 'a'!
733 warning: can't find ancestor for 'b' copied from 'a'!
734
734
735 graft with --force (still doesn't graft merges)
735 graft with --force (still doesn't graft merges)
736
736
737 $ hg graft 19 0 6
737 $ hg graft 19 0 6
738 skipping ungraftable merge revision 6
738 skipping ungraftable merge revision 6
739 skipping ancestor revision 0:68795b066622
739 skipping ancestor revision 0:68795b066622
740 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
740 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
741 [255]
741 [255]
742 $ hg graft 19 0 6 --force
742 $ hg graft 19 0 6 --force
743 skipping ungraftable merge revision 6
743 skipping ungraftable merge revision 6
744 grafting 19:9627f653b421 "2"
744 grafting 19:9627f653b421 "2"
745 merging b
745 merging b
746 warning: can't find ancestor for 'b' copied from 'a'!
746 warning: can't find ancestor for 'b' copied from 'a'!
747 grafting 0:68795b066622 "0"
747 grafting 0:68795b066622 "0"
748
748
749 graft --force after backout
749 graft --force after backout
750
750
751 $ echo abc > a
751 $ echo abc > a
752 $ hg ci -m 28
752 $ hg ci -m 28
753 $ hg backout 28
753 $ hg backout 28
754 reverting a
754 reverting a
755 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
755 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
756 $ hg graft 28
756 $ hg graft 28
757 skipping ancestor revision 28:50a516bb8b57
757 skipping ancestor revision 28:50a516bb8b57
758 [255]
758 [255]
759 $ hg graft 28 --force
759 $ hg graft 28 --force
760 grafting 28:50a516bb8b57 "28"
760 grafting 28:50a516bb8b57 "28"
761 merging a
761 merging a
762 $ cat a
762 $ cat a
763 abc
763 abc
764
764
765 graft --continue after --force
765 graft --continue after --force
766
766
767 $ echo def > a
767 $ echo def > a
768 $ hg ci -m 31
768 $ hg ci -m 31
769 $ hg graft 28 --force --tool internal:fail
769 $ hg graft 28 --force --tool internal:fail
770 grafting 28:50a516bb8b57 "28"
770 grafting 28:50a516bb8b57 "28"
771 abort: unresolved conflicts, can't continue
771 abort: unresolved conflicts, can't continue
772 (use 'hg resolve' and 'hg graft --continue')
772 (use 'hg resolve' and 'hg graft --continue')
773 [255]
773 [255]
774 $ hg resolve --all
774 $ hg resolve --all
775 merging a
775 merging a
776 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
776 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
777 [1]
777 [1]
778 $ echo abc > a
778 $ echo abc > a
779 $ hg resolve -m a
779 $ hg resolve -m a
780 (no more unresolved files)
780 (no more unresolved files)
781 continue: hg graft --continue
781 continue: hg graft --continue
782 $ hg graft -c
782 $ hg graft -c
783 grafting 28:50a516bb8b57 "28"
783 grafting 28:50a516bb8b57 "28"
784 $ cat a
784 $ cat a
785 abc
785 abc
786
786
787 Continue testing same origin policy, using revision numbers from test above
787 Continue testing same origin policy, using revision numbers from test above
788 but do some destructive editing of the repo:
788 but do some destructive editing of the repo:
789
789
790 $ hg up -qC 7
790 $ hg up -qC 7
791 $ hg tag -l -r 13 tmp
791 $ hg tag -l -r 13 tmp
792 $ hg --config extensions.strip= strip 2
792 $ hg --config extensions.strip= strip 2
793 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
793 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
794 $ hg graft tmp
794 $ hg graft tmp
795 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
795 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
796 [255]
796 [255]
797
797
798 Empty graft
798 Empty graft
799
799
800 $ hg up -qr 26
800 $ hg up -qr 26
801 $ hg tag -f something
801 $ hg tag -f something
802 $ hg graft -qr 27
802 $ hg graft -qr 27
803 $ hg graft -f 27
803 $ hg graft -f 27
804 grafting 27:ed6c7e54e319 "28"
804 grafting 27:ed6c7e54e319 "28"
805 note: graft of 27:ed6c7e54e319 created no changes to commit
805 note: graft of 27:ed6c7e54e319 created no changes to commit
806
806
807 $ cd ..
807 $ cd ..
808
808
809 Graft to duplicate a commit
809 Graft to duplicate a commit
810
810
811 $ hg init graftsibling
811 $ hg init graftsibling
812 $ cd graftsibling
812 $ cd graftsibling
813 $ touch a
813 $ touch a
814 $ hg commit -qAm a
814 $ hg commit -qAm a
815 $ touch b
815 $ touch b
816 $ hg commit -qAm b
816 $ hg commit -qAm b
817 $ hg log -G -T '{rev}\n'
817 $ hg log -G -T '{rev}\n'
818 @ 1
818 @ 1
819 |
819 |
820 o 0
820 o 0
821
821
822 $ hg up -q 0
822 $ hg up -q 0
823 $ hg graft -r 1
823 $ hg graft -r 1
824 grafting 1:0e067c57feba "b" (tip)
824 grafting 1:0e067c57feba "b" (tip)
825 $ hg log -G -T '{rev}\n'
825 $ hg log -G -T '{rev}\n'
826 @ 2
826 @ 2
827 |
827 |
828 | o 1
828 | o 1
829 |/
829 |/
830 o 0
830 o 0
831
831
832 Graft to duplicate a commit twice
832 Graft to duplicate a commit twice
833
833
834 $ hg up -q 0
834 $ hg up -q 0
835 $ hg graft -r 2
835 $ hg graft -r 2
836 grafting 2:044ec77f6389 "b" (tip)
836 grafting 2:044ec77f6389 "b" (tip)
837 $ hg log -G -T '{rev}\n'
837 $ hg log -G -T '{rev}\n'
838 @ 3
838 @ 3
839 |
839 |
840 | o 2
840 | o 2
841 |/
841 |/
842 | o 1
842 | o 1
843 |/
843 |/
844 o 0
844 o 0
845
845
846 Graft from behind a move or rename
846 Graft from behind a move or rename
847 ==================================
847 ==================================
848
848
849 NOTE: This is affected by issue5343, and will need updating when it's fixed
849 NOTE: This is affected by issue5343, and will need updating when it's fixed
850
850
851 Possible cases during a regular graft (when ca is between cta and c2):
851 Possible cases during a regular graft (when ca is between cta and c2):
852
852
853 name | c1<-cta | cta<->ca | ca->c2
853 name | c1<-cta | cta<->ca | ca->c2
854 A.0 | | |
854 A.0 | | |
855 A.1 | X | |
855 A.1 | X | |
856 A.2 | | X |
856 A.2 | | X |
857 A.3 | | | X
857 A.3 | | | X
858 A.4 | X | X |
858 A.4 | X | X |
859 A.5 | X | | X
859 A.5 | X | | X
860 A.6 | | X | X
860 A.6 | | X | X
861 A.7 | X | X | X
861 A.7 | X | X | X
862
862
863 A.0 is trivial, and doesn't need copy tracking.
863 A.0 is trivial, and doesn't need copy tracking.
864 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
864 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
865 In A.2, the rename is recorded in the c2 pass and followed backwards.
865 In A.2, the rename is recorded in the c2 pass and followed backwards.
866 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
866 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
867 In A.4, both passes of checkcopies record incomplete renames, which are
867 In A.4, both passes of checkcopies record incomplete renames, which are
868 then joined in mergecopies to record a rename to be followed.
868 then joined in mergecopies to record a rename to be followed.
869 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
869 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
870 records an incomplete divergence. The incomplete rename is then joined to the
870 records an incomplete divergence. The incomplete rename is then joined to the
871 appropriate side of the incomplete divergence, and the result is recorded as a
871 appropriate side of the incomplete divergence, and the result is recorded as a
872 divergence. The code doesn't distinguish at all between these two cases, since
872 divergence. The code doesn't distinguish at all between these two cases, since
873 the end result of them is the same: an incomplete divergence joined with an
873 the end result of them is the same: an incomplete divergence joined with an
874 incomplete rename into a divergence.
874 incomplete rename into a divergence.
875 Finally, A.6 records a divergence entirely in the c2 pass.
875 Finally, A.6 records a divergence entirely in the c2 pass.
876
876
877 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
877 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
878 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
878 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
879 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
879 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
880 incomplete divergence, which is in fact complete. This is handled later in
880 incomplete divergence, which is in fact complete. This is handled later in
881 mergecopies.
881 mergecopies.
882 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
882 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
883 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
883 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
884 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
884 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
885 case, a<-b<-c->a is treated the same as a<-b<-b->a).
885 case, a<-b<-c->a is treated the same as a<-b<-b->a).
886
886
887 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
887 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
888 same name on both branches, then the rename is backed out on one branch, and
888 same name on both branches, then the rename is backed out on one branch, and
889 the backout is grafted to the other branch. This creates a challenging rename
889 the backout is grafted to the other branch. This creates a challenging rename
890 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
890 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
891 source, respectively. Since rename detection will run on the c1 side for such a
891 source, respectively. Since rename detection will run on the c1 side for such a
892 sequence (as for technical reasons, we split the c1 and c2 sides not at the
892 sequence (as for technical reasons, we split the c1 and c2 sides not at the
893 graft CA, but rather at the topological CA), it will pick up a false rename,
893 graft CA, but rather at the topological CA), it will pick up a false rename,
894 and cause a spurious merge conflict. This false rename is always exactly the
894 and cause a spurious merge conflict. This false rename is always exactly the
895 reverse of the true rename that would be detected on the c2 side, so we can
895 reverse of the true rename that would be detected on the c2 side, so we can
896 correct for it by detecting this condition and reversing as necessary.
896 correct for it by detecting this condition and reversing as necessary.
897
897
898 First, set up the repository with commits to be grafted
898 First, set up the repository with commits to be grafted
899
899
900 $ hg init ../graftmove
900 $ hg init ../graftmove
901 $ cd ../graftmove
901 $ cd ../graftmove
902 $ echo c1a > f1a
902 $ echo c1a > f1a
903 $ echo c2a > f2a
903 $ echo c2a > f2a
904 $ echo c3a > f3a
904 $ echo c3a > f3a
905 $ echo c4a > f4a
905 $ echo c4a > f4a
906 $ echo c5a > f5a
906 $ echo c5a > f5a
907 $ hg ci -qAm A0
907 $ hg ci -qAm A0
908 $ hg mv f1a f1b
908 $ hg mv f1a f1b
909 $ hg mv f3a f3b
909 $ hg mv f3a f3b
910 $ hg mv f5a f5b
910 $ hg mv f5a f5b
911 $ hg ci -qAm B0
911 $ hg ci -qAm B0
912 $ echo c1c > f1b
912 $ echo c1c > f1b
913 $ hg mv f2a f2c
913 $ hg mv f2a f2c
914 $ hg mv f5b f5a
914 $ hg mv f5b f5a
915 $ echo c5c > f5a
915 $ echo c5c > f5a
916 $ hg ci -qAm C0
916 $ hg ci -qAm C0
917 $ hg mv f3b f3d
917 $ hg mv f3b f3d
918 $ echo c4d > f4a
918 $ echo c4d > f4a
919 $ hg ci -qAm D0
919 $ hg ci -qAm D0
920 $ hg log -G
920 $ hg log -G
921 @ changeset: 3:b69f5839d2d9
921 @ changeset: 3:b69f5839d2d9
922 | tag: tip
922 | tag: tip
923 | user: test
923 | user: test
924 | date: Thu Jan 01 00:00:00 1970 +0000
924 | date: Thu Jan 01 00:00:00 1970 +0000
925 | summary: D0
925 | summary: D0
926 |
926 |
927 o changeset: 2:f58c7e2b28fa
927 o changeset: 2:f58c7e2b28fa
928 | user: test
928 | user: test
929 | date: Thu Jan 01 00:00:00 1970 +0000
929 | date: Thu Jan 01 00:00:00 1970 +0000
930 | summary: C0
930 | summary: C0
931 |
931 |
932 o changeset: 1:3d7bba921b5d
932 o changeset: 1:3d7bba921b5d
933 | user: test
933 | user: test
934 | date: Thu Jan 01 00:00:00 1970 +0000
934 | date: Thu Jan 01 00:00:00 1970 +0000
935 | summary: B0
935 | summary: B0
936 |
936 |
937 o changeset: 0:11f7a1b56675
937 o changeset: 0:11f7a1b56675
938 user: test
938 user: test
939 date: Thu Jan 01 00:00:00 1970 +0000
939 date: Thu Jan 01 00:00:00 1970 +0000
940 summary: A0
940 summary: A0
941
941
942
942
943 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
943 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
944 two renames actually converge to the same name (thus no actual divergence).
944 two renames actually converge to the same name (thus no actual divergence).
945
945
946 $ hg up -q 'desc("A0")'
946 $ hg up -q 'desc("A0")'
947 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
947 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
948 grafting 2:f58c7e2b28fa "C0"
948 grafting 2:f58c7e2b28fa "C0"
949 merging f1a and f1b to f1a
949 merging f1a and f1b to f1a
950 merging f5a
950 merging f5a
951 warning: can't find ancestor for 'f5a' copied from 'f5b'!
951 warning: can't find ancestor for 'f5a' copied from 'f5b'!
952 $ hg status --change .
952 $ hg status --change .
953 M f1a
953 M f1a
954 M f5a
954 M f5a
955 A f2c
955 A f2c
956 R f2a
956 R f2a
957 $ hg cat f1a
957 $ hg cat f1a
958 c1c
958 c1c
959 $ hg cat f1b
959 $ hg cat f1b
960 f1b: no such file in rev c9763722f9bd
960 f1b: no such file in rev c9763722f9bd
961 [1]
961 [1]
962
962
963 Test the cases A.0 (f4x) and A.6 (f3x)
963 Test the cases A.0 (f4x) and A.6 (f3x)
964
964
965 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
965 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
966 grafting 3:b69f5839d2d9 "D0"
966 grafting 3:b69f5839d2d9 "D0"
967 note: possible conflict - f3b was renamed multiple times to:
967 note: possible conflict - f3b was renamed multiple times to:
968 f3d
968 f3d
969 f3a
969 f3a
970 warning: can't find ancestor for 'f3d' copied from 'f3b'!
970 warning: can't find ancestor for 'f3d' copied from 'f3b'!
971
971
972 Set up the repository for some further tests
972 Set up the repository for some further tests
973
973
974 $ hg up -q "min(desc("A0"))"
974 $ hg up -q "min(desc("A0"))"
975 $ hg mv f1a f1e
975 $ hg mv f1a f1e
976 $ echo c2e > f2a
976 $ echo c2e > f2a
977 $ hg mv f3a f3e
977 $ hg mv f3a f3e
978 $ hg mv f4a f4e
978 $ hg mv f4a f4e
979 $ hg mv f5a f5b
979 $ hg mv f5a f5b
980 $ hg ci -qAm "E0"
980 $ hg ci -qAm "E0"
981 $ hg log -G
981 $ hg log -G
982 @ changeset: 6:6bd1736cab86
982 @ changeset: 6:6bd1736cab86
983 | tag: tip
983 | tag: tip
984 | parent: 0:11f7a1b56675
984 | parent: 0:11f7a1b56675
985 | user: test
985 | user: test
986 | date: Thu Jan 01 00:00:00 1970 +0000
986 | date: Thu Jan 01 00:00:00 1970 +0000
987 | summary: E0
987 | summary: E0
988 |
988 |
989 | o changeset: 5:560daee679da
989 | o changeset: 5:560daee679da
990 | | user: test
990 | | user: test
991 | | date: Thu Jan 01 00:00:00 1970 +0000
991 | | date: Thu Jan 01 00:00:00 1970 +0000
992 | | summary: D1
992 | | summary: D1
993 | |
993 | |
994 | o changeset: 4:c9763722f9bd
994 | o changeset: 4:c9763722f9bd
995 |/ parent: 0:11f7a1b56675
995 |/ parent: 0:11f7a1b56675
996 | user: test
996 | user: test
997 | date: Thu Jan 01 00:00:00 1970 +0000
997 | date: Thu Jan 01 00:00:00 1970 +0000
998 | summary: C1
998 | summary: C1
999 |
999 |
1000 | o changeset: 3:b69f5839d2d9
1000 | o changeset: 3:b69f5839d2d9
1001 | | user: test
1001 | | user: test
1002 | | date: Thu Jan 01 00:00:00 1970 +0000
1002 | | date: Thu Jan 01 00:00:00 1970 +0000
1003 | | summary: D0
1003 | | summary: D0
1004 | |
1004 | |
1005 | o changeset: 2:f58c7e2b28fa
1005 | o changeset: 2:f58c7e2b28fa
1006 | | user: test
1006 | | user: test
1007 | | date: Thu Jan 01 00:00:00 1970 +0000
1007 | | date: Thu Jan 01 00:00:00 1970 +0000
1008 | | summary: C0
1008 | | summary: C0
1009 | |
1009 | |
1010 | o changeset: 1:3d7bba921b5d
1010 | o changeset: 1:3d7bba921b5d
1011 |/ user: test
1011 |/ user: test
1012 | date: Thu Jan 01 00:00:00 1970 +0000
1012 | date: Thu Jan 01 00:00:00 1970 +0000
1013 | summary: B0
1013 | summary: B0
1014 |
1014 |
1015 o changeset: 0:11f7a1b56675
1015 o changeset: 0:11f7a1b56675
1016 user: test
1016 user: test
1017 date: Thu Jan 01 00:00:00 1970 +0000
1017 date: Thu Jan 01 00:00:00 1970 +0000
1018 summary: A0
1018 summary: A0
1019
1019
1020
1020
1021 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1021 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1022 and A.3 with a local content change to be preserved (f2x).
1022 and A.3 with a local content change to be preserved (f2x).
1023
1023
1024 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1024 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1025 grafting 2:f58c7e2b28fa "C0"
1025 grafting 2:f58c7e2b28fa "C0"
1026 merging f1e and f1b to f1e
1026 merging f1e and f1b to f1e
1027 merging f2a and f2c to f2c
1027 merging f2a and f2c to f2c
1028 merging f5b and f5a to f5a
1028 merging f5b and f5a to f5a
1029
1029
1030 Test the cases A.1 (f4x) and A.7 (f3x).
1030 Test the cases A.1 (f4x) and A.7 (f3x).
1031
1031
1032 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1032 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1033 grafting 3:b69f5839d2d9 "D0"
1033 grafting 3:b69f5839d2d9 "D0"
1034 note: possible conflict - f3b was renamed multiple times to:
1034 note: possible conflict - f3b was renamed multiple times to:
1035 f3e
1035 f3e
1036 f3d
1036 f3d
1037 merging f4e and f4a to f4e
1037 merging f4e and f4a to f4e
1038 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1038 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1039
1039
1040 Check the results of the grafts tested
1040 Check the results of the grafts tested
1041
1041
1042 $ hg log -CGv --patch --git
1042 $ hg log -CGv --patch --git
1043 @ changeset: 8:93ee502e8b0a
1043 @ changeset: 8:93ee502e8b0a
1044 | tag: tip
1044 | tag: tip
1045 | user: test
1045 | user: test
1046 | date: Thu Jan 01 00:00:00 1970 +0000
1046 | date: Thu Jan 01 00:00:00 1970 +0000
1047 | files: f3d f4e
1047 | files: f3d f4e
1048 | description:
1048 | description:
1049 | D2
1049 | D2
1050 |
1050 |
1051 |
1051 |
1052 | diff --git a/f3d b/f3d
1052 | diff --git a/f3d b/f3d
1053 | new file mode 100644
1053 | new file mode 100644
1054 | --- /dev/null
1054 | --- /dev/null
1055 | +++ b/f3d
1055 | +++ b/f3d
1056 | @@ -0,0 +1,1 @@
1056 | @@ -0,0 +1,1 @@
1057 | +c3a
1057 | +c3a
1058 | diff --git a/f4e b/f4e
1058 | diff --git a/f4e b/f4e
1059 | --- a/f4e
1059 | --- a/f4e
1060 | +++ b/f4e
1060 | +++ b/f4e
1061 | @@ -1,1 +1,1 @@
1061 | @@ -1,1 +1,1 @@
1062 | -c4a
1062 | -c4a
1063 | +c4d
1063 | +c4d
1064 |
1064 |
1065 o changeset: 7:539cf145f496
1065 o changeset: 7:539cf145f496
1066 | user: test
1066 | user: test
1067 | date: Thu Jan 01 00:00:00 1970 +0000
1067 | date: Thu Jan 01 00:00:00 1970 +0000
1068 | files: f1e f2a f2c f5a f5b
1068 | files: f1e f2a f2c f5a f5b
1069 | copies: f2c (f2a) f5a (f5b)
1069 | copies: f2c (f2a) f5a (f5b)
1070 | description:
1070 | description:
1071 | C2
1071 | C2
1072 |
1072 |
1073 |
1073 |
1074 | diff --git a/f1e b/f1e
1074 | diff --git a/f1e b/f1e
1075 | --- a/f1e
1075 | --- a/f1e
1076 | +++ b/f1e
1076 | +++ b/f1e
1077 | @@ -1,1 +1,1 @@
1077 | @@ -1,1 +1,1 @@
1078 | -c1a
1078 | -c1a
1079 | +c1c
1079 | +c1c
1080 | diff --git a/f2a b/f2c
1080 | diff --git a/f2a b/f2c
1081 | rename from f2a
1081 | rename from f2a
1082 | rename to f2c
1082 | rename to f2c
1083 | diff --git a/f5b b/f5a
1083 | diff --git a/f5b b/f5a
1084 | rename from f5b
1084 | rename from f5b
1085 | rename to f5a
1085 | rename to f5a
1086 | --- a/f5b
1086 | --- a/f5b
1087 | +++ b/f5a
1087 | +++ b/f5a
1088 | @@ -1,1 +1,1 @@
1088 | @@ -1,1 +1,1 @@
1089 | -c5a
1089 | -c5a
1090 | +c5c
1090 | +c5c
1091 |
1091 |
1092 o changeset: 6:6bd1736cab86
1092 o changeset: 6:6bd1736cab86
1093 | parent: 0:11f7a1b56675
1093 | parent: 0:11f7a1b56675
1094 | user: test
1094 | user: test
1095 | date: Thu Jan 01 00:00:00 1970 +0000
1095 | date: Thu Jan 01 00:00:00 1970 +0000
1096 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1096 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1097 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1097 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1098 | description:
1098 | description:
1099 | E0
1099 | E0
1100 |
1100 |
1101 |
1101 |
1102 | diff --git a/f1a b/f1e
1102 | diff --git a/f1a b/f1e
1103 | rename from f1a
1103 | rename from f1a
1104 | rename to f1e
1104 | rename to f1e
1105 | diff --git a/f2a b/f2a
1105 | diff --git a/f2a b/f2a
1106 | --- a/f2a
1106 | --- a/f2a
1107 | +++ b/f2a
1107 | +++ b/f2a
1108 | @@ -1,1 +1,1 @@
1108 | @@ -1,1 +1,1 @@
1109 | -c2a
1109 | -c2a
1110 | +c2e
1110 | +c2e
1111 | diff --git a/f3a b/f3e
1111 | diff --git a/f3a b/f3e
1112 | rename from f3a
1112 | rename from f3a
1113 | rename to f3e
1113 | rename to f3e
1114 | diff --git a/f4a b/f4e
1114 | diff --git a/f4a b/f4e
1115 | rename from f4a
1115 | rename from f4a
1116 | rename to f4e
1116 | rename to f4e
1117 | diff --git a/f5a b/f5b
1117 | diff --git a/f5a b/f5b
1118 | rename from f5a
1118 | rename from f5a
1119 | rename to f5b
1119 | rename to f5b
1120 |
1120 |
1121 | o changeset: 5:560daee679da
1121 | o changeset: 5:560daee679da
1122 | | user: test
1122 | | user: test
1123 | | date: Thu Jan 01 00:00:00 1970 +0000
1123 | | date: Thu Jan 01 00:00:00 1970 +0000
1124 | | files: f3d f4a
1124 | | files: f3d f4a
1125 | | description:
1125 | | description:
1126 | | D1
1126 | | D1
1127 | |
1127 | |
1128 | |
1128 | |
1129 | | diff --git a/f3d b/f3d
1129 | | diff --git a/f3d b/f3d
1130 | | new file mode 100644
1130 | | new file mode 100644
1131 | | --- /dev/null
1131 | | --- /dev/null
1132 | | +++ b/f3d
1132 | | +++ b/f3d
1133 | | @@ -0,0 +1,1 @@
1133 | | @@ -0,0 +1,1 @@
1134 | | +c3a
1134 | | +c3a
1135 | | diff --git a/f4a b/f4a
1135 | | diff --git a/f4a b/f4a
1136 | | --- a/f4a
1136 | | --- a/f4a
1137 | | +++ b/f4a
1137 | | +++ b/f4a
1138 | | @@ -1,1 +1,1 @@
1138 | | @@ -1,1 +1,1 @@
1139 | | -c4a
1139 | | -c4a
1140 | | +c4d
1140 | | +c4d
1141 | |
1141 | |
1142 | o changeset: 4:c9763722f9bd
1142 | o changeset: 4:c9763722f9bd
1143 |/ parent: 0:11f7a1b56675
1143 |/ parent: 0:11f7a1b56675
1144 | user: test
1144 | user: test
1145 | date: Thu Jan 01 00:00:00 1970 +0000
1145 | date: Thu Jan 01 00:00:00 1970 +0000
1146 | files: f1a f2a f2c f5a
1146 | files: f1a f2a f2c f5a
1147 | copies: f2c (f2a)
1147 | copies: f2c (f2a)
1148 | description:
1148 | description:
1149 | C1
1149 | C1
1150 |
1150 |
1151 |
1151 |
1152 | diff --git a/f1a b/f1a
1152 | diff --git a/f1a b/f1a
1153 | --- a/f1a
1153 | --- a/f1a
1154 | +++ b/f1a
1154 | +++ b/f1a
1155 | @@ -1,1 +1,1 @@
1155 | @@ -1,1 +1,1 @@
1156 | -c1a
1156 | -c1a
1157 | +c1c
1157 | +c1c
1158 | diff --git a/f2a b/f2c
1158 | diff --git a/f2a b/f2c
1159 | rename from f2a
1159 | rename from f2a
1160 | rename to f2c
1160 | rename to f2c
1161 | diff --git a/f5a b/f5a
1161 | diff --git a/f5a b/f5a
1162 | --- a/f5a
1162 | --- a/f5a
1163 | +++ b/f5a
1163 | +++ b/f5a
1164 | @@ -1,1 +1,1 @@
1164 | @@ -1,1 +1,1 @@
1165 | -c5a
1165 | -c5a
1166 | +c5c
1166 | +c5c
1167 |
1167 |
1168 | o changeset: 3:b69f5839d2d9
1168 | o changeset: 3:b69f5839d2d9
1169 | | user: test
1169 | | user: test
1170 | | date: Thu Jan 01 00:00:00 1970 +0000
1170 | | date: Thu Jan 01 00:00:00 1970 +0000
1171 | | files: f3b f3d f4a
1171 | | files: f3b f3d f4a
1172 | | copies: f3d (f3b)
1172 | | copies: f3d (f3b)
1173 | | description:
1173 | | description:
1174 | | D0
1174 | | D0
1175 | |
1175 | |
1176 | |
1176 | |
1177 | | diff --git a/f3b b/f3d
1177 | | diff --git a/f3b b/f3d
1178 | | rename from f3b
1178 | | rename from f3b
1179 | | rename to f3d
1179 | | rename to f3d
1180 | | diff --git a/f4a b/f4a
1180 | | diff --git a/f4a b/f4a
1181 | | --- a/f4a
1181 | | --- a/f4a
1182 | | +++ b/f4a
1182 | | +++ b/f4a
1183 | | @@ -1,1 +1,1 @@
1183 | | @@ -1,1 +1,1 @@
1184 | | -c4a
1184 | | -c4a
1185 | | +c4d
1185 | | +c4d
1186 | |
1186 | |
1187 | o changeset: 2:f58c7e2b28fa
1187 | o changeset: 2:f58c7e2b28fa
1188 | | user: test
1188 | | user: test
1189 | | date: Thu Jan 01 00:00:00 1970 +0000
1189 | | date: Thu Jan 01 00:00:00 1970 +0000
1190 | | files: f1b f2a f2c f5a f5b
1190 | | files: f1b f2a f2c f5a f5b
1191 | | copies: f2c (f2a) f5a (f5b)
1191 | | copies: f2c (f2a) f5a (f5b)
1192 | | description:
1192 | | description:
1193 | | C0
1193 | | C0
1194 | |
1194 | |
1195 | |
1195 | |
1196 | | diff --git a/f1b b/f1b
1196 | | diff --git a/f1b b/f1b
1197 | | --- a/f1b
1197 | | --- a/f1b
1198 | | +++ b/f1b
1198 | | +++ b/f1b
1199 | | @@ -1,1 +1,1 @@
1199 | | @@ -1,1 +1,1 @@
1200 | | -c1a
1200 | | -c1a
1201 | | +c1c
1201 | | +c1c
1202 | | diff --git a/f2a b/f2c
1202 | | diff --git a/f2a b/f2c
1203 | | rename from f2a
1203 | | rename from f2a
1204 | | rename to f2c
1204 | | rename to f2c
1205 | | diff --git a/f5b b/f5a
1205 | | diff --git a/f5b b/f5a
1206 | | rename from f5b
1206 | | rename from f5b
1207 | | rename to f5a
1207 | | rename to f5a
1208 | | --- a/f5b
1208 | | --- a/f5b
1209 | | +++ b/f5a
1209 | | +++ b/f5a
1210 | | @@ -1,1 +1,1 @@
1210 | | @@ -1,1 +1,1 @@
1211 | | -c5a
1211 | | -c5a
1212 | | +c5c
1212 | | +c5c
1213 | |
1213 | |
1214 | o changeset: 1:3d7bba921b5d
1214 | o changeset: 1:3d7bba921b5d
1215 |/ user: test
1215 |/ user: test
1216 | date: Thu Jan 01 00:00:00 1970 +0000
1216 | date: Thu Jan 01 00:00:00 1970 +0000
1217 | files: f1a f1b f3a f3b f5a f5b
1217 | files: f1a f1b f3a f3b f5a f5b
1218 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1218 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1219 | description:
1219 | description:
1220 | B0
1220 | B0
1221 |
1221 |
1222 |
1222 |
1223 | diff --git a/f1a b/f1b
1223 | diff --git a/f1a b/f1b
1224 | rename from f1a
1224 | rename from f1a
1225 | rename to f1b
1225 | rename to f1b
1226 | diff --git a/f3a b/f3b
1226 | diff --git a/f3a b/f3b
1227 | rename from f3a
1227 | rename from f3a
1228 | rename to f3b
1228 | rename to f3b
1229 | diff --git a/f5a b/f5b
1229 | diff --git a/f5a b/f5b
1230 | rename from f5a
1230 | rename from f5a
1231 | rename to f5b
1231 | rename to f5b
1232 |
1232 |
1233 o changeset: 0:11f7a1b56675
1233 o changeset: 0:11f7a1b56675
1234 user: test
1234 user: test
1235 date: Thu Jan 01 00:00:00 1970 +0000
1235 date: Thu Jan 01 00:00:00 1970 +0000
1236 files: f1a f2a f3a f4a f5a
1236 files: f1a f2a f3a f4a f5a
1237 description:
1237 description:
1238 A0
1238 A0
1239
1239
1240
1240
1241 diff --git a/f1a b/f1a
1241 diff --git a/f1a b/f1a
1242 new file mode 100644
1242 new file mode 100644
1243 --- /dev/null
1243 --- /dev/null
1244 +++ b/f1a
1244 +++ b/f1a
1245 @@ -0,0 +1,1 @@
1245 @@ -0,0 +1,1 @@
1246 +c1a
1246 +c1a
1247 diff --git a/f2a b/f2a
1247 diff --git a/f2a b/f2a
1248 new file mode 100644
1248 new file mode 100644
1249 --- /dev/null
1249 --- /dev/null
1250 +++ b/f2a
1250 +++ b/f2a
1251 @@ -0,0 +1,1 @@
1251 @@ -0,0 +1,1 @@
1252 +c2a
1252 +c2a
1253 diff --git a/f3a b/f3a
1253 diff --git a/f3a b/f3a
1254 new file mode 100644
1254 new file mode 100644
1255 --- /dev/null
1255 --- /dev/null
1256 +++ b/f3a
1256 +++ b/f3a
1257 @@ -0,0 +1,1 @@
1257 @@ -0,0 +1,1 @@
1258 +c3a
1258 +c3a
1259 diff --git a/f4a b/f4a
1259 diff --git a/f4a b/f4a
1260 new file mode 100644
1260 new file mode 100644
1261 --- /dev/null
1261 --- /dev/null
1262 +++ b/f4a
1262 +++ b/f4a
1263 @@ -0,0 +1,1 @@
1263 @@ -0,0 +1,1 @@
1264 +c4a
1264 +c4a
1265 diff --git a/f5a b/f5a
1265 diff --git a/f5a b/f5a
1266 new file mode 100644
1266 new file mode 100644
1267 --- /dev/null
1267 --- /dev/null
1268 +++ b/f5a
1268 +++ b/f5a
1269 @@ -0,0 +1,1 @@
1269 @@ -0,0 +1,1 @@
1270 +c5a
1270 +c5a
1271
1271
1272 $ hg cat f2c
1272 $ hg cat f2c
1273 c2e
1273 c2e
1274
1274
1275 Check superfluous filemerge of files renamed in the past but untouched by graft
1275 Check superfluous filemerge of files renamed in the past but untouched by graft
1276
1276
1277 $ echo a > a
1277 $ echo a > a
1278 $ hg ci -qAma
1278 $ hg ci -qAma
1279 $ hg mv a b
1279 $ hg mv a b
1280 $ echo b > b
1280 $ echo b > b
1281 $ hg ci -qAmb
1281 $ hg ci -qAmb
1282 $ echo c > c
1282 $ echo c > c
1283 $ hg ci -qAmc
1283 $ hg ci -qAmc
1284 $ hg up -q .~2
1284 $ hg up -q .~2
1285 $ hg graft tip -qt:fail
1285 $ hg graft tip -qt:fail
1286
1286
1287 $ cd ..
1287 $ cd ..
1288
1288
1289 Graft a change into a new file previously grafted into a renamed directory
1289 Graft a change into a new file previously grafted into a renamed directory
1290
1290
1291 $ hg init dirmovenewfile
1291 $ hg init dirmovenewfile
1292 $ cd dirmovenewfile
1292 $ cd dirmovenewfile
1293 $ mkdir a
1293 $ mkdir a
1294 $ echo a > a/a
1294 $ echo a > a/a
1295 $ hg ci -qAma
1295 $ hg ci -qAma
1296 $ echo x > a/x
1296 $ echo x > a/x
1297 $ hg ci -qAmx
1297 $ hg ci -qAmx
1298 $ hg up -q 0
1298 $ hg up -q 0
1299 $ hg mv -q a b
1299 $ hg mv -q a b
1300 $ hg ci -qAmb
1300 $ hg ci -qAmb
1301 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1301 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1302 $ hg up -q 1
1302 $ hg up -q 1
1303 $ echo y > a/x
1303 $ echo y > a/x
1304 $ hg ci -qAmy
1304 $ hg ci -qAmy
1305 $ hg up -q 3
1305 $ hg up -q 3
1306 $ hg graft -q 4
1306 $ hg graft -q 4
1307 $ hg status --change .
1307 $ hg status --change .
1308 M b/x
1308 M b/x
1309
1309
1310 Prepare for test of skipped changesets and how merges can influence it:
1311
1312 $ hg merge -q -r 1 --tool :local
1313 $ hg ci -m m
1314 $ echo xx >> b/x
1315 $ hg ci -m xx
1316
1317 $ hg log -G -T '{rev} {desc|firstline}'
1318 @ 7 xx
1319 |
1320 o 6 m
1321 |\
1322 | o 5 y
1323 | |
1324 +---o 4 y
1325 | |
1326 | o 3 x
1327 | |
1328 | o 2 b
1329 | |
1330 o | 1 x
1331 |/
1332 o 0 a
1333
1334 Grafting of plain changes correctly detects that 3 and 5 should be skipped:
1335
1336 $ hg up -qCr 4
1337 $ hg graft --tool :local -r 2::5
1338 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1339 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1340 grafting 2:42127f193bcd "b"
1341
1342 Extending the graft range to include a (skipped) merge of 3 will not prevent us from
1343 also detecting that both 3 and 5 should be skipped:
1344
1345 $ hg up -qCr 4
1346 $ hg graft --tool :local -r 2::7
1347 skipping ungraftable merge revision 6
1348 skipping already grafted revision 3:ca093ca2f1d9 (was grafted from 1:13ec5badbf2a)
1349 skipping already grafted revision 5:43e9eb70dab0 (was grafted from 4:6c9a1289e5f1)
1350 grafting 2:42127f193bcd "b"
1351 grafting 7:d3c3f2b38ecc "xx"
1352 note: graft of 7:d3c3f2b38ecc created no changes to commit
1353
1310 $ cd ..
1354 $ cd ..
@@ -1,3327 +1,3327 b''
1 Short help:
1 Short help:
2
2
3 $ hg
3 $ hg
4 Mercurial Distributed SCM
4 Mercurial Distributed SCM
5
5
6 basic commands:
6 basic commands:
7
7
8 add add the specified files on the next commit
8 add add the specified files on the next commit
9 annotate show changeset information by line for each file
9 annotate show changeset information by line for each file
10 clone make a copy of an existing repository
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
13 export dump the header and diffs for one or more changesets
14 forget forget the specified files on the next commit
14 forget forget the specified files on the next commit
15 init create a new repository in the given directory
15 init create a new repository in the given directory
16 log show revision history of entire repository or files
16 log show revision history of entire repository or files
17 merge merge another revision into working directory
17 merge merge another revision into working directory
18 pull pull changes from the specified source
18 pull pull changes from the specified source
19 push push changes to the specified destination
19 push push changes to the specified destination
20 remove remove the specified files on the next commit
20 remove remove the specified files on the next commit
21 serve start stand-alone webserver
21 serve start stand-alone webserver
22 status show changed files in the working directory
22 status show changed files in the working directory
23 summary summarize working directory state
23 summary summarize working directory state
24 update update working directory (or switch revisions)
24 update update working directory (or switch revisions)
25
25
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
26 (use 'hg help' for the full list of commands or 'hg -v' for details)
27
27
28 $ hg -q
28 $ hg -q
29 add add the specified files on the next commit
29 add add the specified files on the next commit
30 annotate show changeset information by line for each file
30 annotate show changeset information by line for each file
31 clone make a copy of an existing repository
31 clone make a copy of an existing repository
32 commit commit the specified files or all outstanding changes
32 commit commit the specified files or all outstanding changes
33 diff diff repository (or selected files)
33 diff diff repository (or selected files)
34 export dump the header and diffs for one or more changesets
34 export dump the header and diffs for one or more changesets
35 forget forget the specified files on the next commit
35 forget forget the specified files on the next commit
36 init create a new repository in the given directory
36 init create a new repository in the given directory
37 log show revision history of entire repository or files
37 log show revision history of entire repository or files
38 merge merge another revision into working directory
38 merge merge another revision into working directory
39 pull pull changes from the specified source
39 pull pull changes from the specified source
40 push push changes to the specified destination
40 push push changes to the specified destination
41 remove remove the specified files on the next commit
41 remove remove the specified files on the next commit
42 serve start stand-alone webserver
42 serve start stand-alone webserver
43 status show changed files in the working directory
43 status show changed files in the working directory
44 summary summarize working directory state
44 summary summarize working directory state
45 update update working directory (or switch revisions)
45 update update working directory (or switch revisions)
46
46
47 $ hg help
47 $ hg help
48 Mercurial Distributed SCM
48 Mercurial Distributed SCM
49
49
50 list of commands:
50 list of commands:
51
51
52 add add the specified files on the next commit
52 add add the specified files on the next commit
53 addremove add all new files, delete all missing files
53 addremove add all new files, delete all missing files
54 annotate show changeset information by line for each file
54 annotate show changeset information by line for each file
55 archive create an unversioned archive of a repository revision
55 archive create an unversioned archive of a repository revision
56 backout reverse effect of earlier changeset
56 backout reverse effect of earlier changeset
57 bisect subdivision search of changesets
57 bisect subdivision search of changesets
58 bookmarks create a new bookmark or list existing bookmarks
58 bookmarks create a new bookmark or list existing bookmarks
59 branch set or show the current branch name
59 branch set or show the current branch name
60 branches list repository named branches
60 branches list repository named branches
61 bundle create a bundle file
61 bundle create a bundle file
62 cat output the current or given revision of files
62 cat output the current or given revision of files
63 clone make a copy of an existing repository
63 clone make a copy of an existing repository
64 commit commit the specified files or all outstanding changes
64 commit commit the specified files or all outstanding changes
65 config show combined config settings from all hgrc files
65 config show combined config settings from all hgrc files
66 copy mark files as copied for the next commit
66 copy mark files as copied for the next commit
67 diff diff repository (or selected files)
67 diff diff repository (or selected files)
68 export dump the header and diffs for one or more changesets
68 export dump the header and diffs for one or more changesets
69 files list tracked files
69 files list tracked files
70 forget forget the specified files on the next commit
70 forget forget the specified files on the next commit
71 graft copy changes from other branches onto the current branch
71 graft copy changes from other branches onto the current branch
72 grep search revision history for a pattern in specified files
72 grep search revision history for a pattern in specified files
73 heads show branch heads
73 heads show branch heads
74 help show help for a given topic or a help overview
74 help show help for a given topic or a help overview
75 identify identify the working directory or specified revision
75 identify identify the working directory or specified revision
76 import import an ordered set of patches
76 import import an ordered set of patches
77 incoming show new changesets found in source
77 incoming show new changesets found in source
78 init create a new repository in the given directory
78 init create a new repository in the given directory
79 log show revision history of entire repository or files
79 log show revision history of entire repository or files
80 manifest output the current or given revision of the project manifest
80 manifest output the current or given revision of the project manifest
81 merge merge another revision into working directory
81 merge merge another revision into working directory
82 outgoing show changesets not found in the destination
82 outgoing show changesets not found in the destination
83 paths show aliases for remote repositories
83 paths show aliases for remote repositories
84 phase set or show the current phase name
84 phase set or show the current phase name
85 pull pull changes from the specified source
85 pull pull changes from the specified source
86 push push changes to the specified destination
86 push push changes to the specified destination
87 recover roll back an interrupted transaction
87 recover roll back an interrupted transaction
88 remove remove the specified files on the next commit
88 remove remove the specified files on the next commit
89 rename rename files; equivalent of copy + remove
89 rename rename files; equivalent of copy + remove
90 resolve redo merges or set/view the merge status of files
90 resolve redo merges or set/view the merge status of files
91 revert restore files to their checkout state
91 revert restore files to their checkout state
92 root print the root (top) of the current working directory
92 root print the root (top) of the current working directory
93 serve start stand-alone webserver
93 serve start stand-alone webserver
94 status show changed files in the working directory
94 status show changed files in the working directory
95 summary summarize working directory state
95 summary summarize working directory state
96 tag add one or more tags for the current or given revision
96 tag add one or more tags for the current or given revision
97 tags list repository tags
97 tags list repository tags
98 unbundle apply one or more bundle files
98 unbundle apply one or more bundle files
99 update update working directory (or switch revisions)
99 update update working directory (or switch revisions)
100 verify verify the integrity of the repository
100 verify verify the integrity of the repository
101 version output version and copyright information
101 version output version and copyright information
102
102
103 additional help topics:
103 additional help topics:
104
104
105 bundlespec Bundle File Formats
105 bundlespec Bundle File Formats
106 color Colorizing Outputs
106 color Colorizing Outputs
107 config Configuration Files
107 config Configuration Files
108 dates Date Formats
108 dates Date Formats
109 diffs Diff Formats
109 diffs Diff Formats
110 environment Environment Variables
110 environment Environment Variables
111 extensions Using Additional Features
111 extensions Using Additional Features
112 filesets Specifying File Sets
112 filesets Specifying File Sets
113 glossary Glossary
113 glossary Glossary
114 hgignore Syntax for Mercurial Ignore Files
114 hgignore Syntax for Mercurial Ignore Files
115 hgweb Configuring hgweb
115 hgweb Configuring hgweb
116 internals Technical implementation topics
116 internals Technical implementation topics
117 merge-tools Merge Tools
117 merge-tools Merge Tools
118 pager Pager Support
118 pager Pager Support
119 patterns File Name Patterns
119 patterns File Name Patterns
120 phases Working with Phases
120 phases Working with Phases
121 revisions Specifying Revisions
121 revisions Specifying Revisions
122 scripting Using Mercurial from scripts and automation
122 scripting Using Mercurial from scripts and automation
123 subrepos Subrepositories
123 subrepos Subrepositories
124 templating Template Usage
124 templating Template Usage
125 urls URL Paths
125 urls URL Paths
126
126
127 (use 'hg help -v' to show built-in aliases and global options)
127 (use 'hg help -v' to show built-in aliases and global options)
128
128
129 $ hg -q help
129 $ hg -q help
130 add add the specified files on the next commit
130 add add the specified files on the next commit
131 addremove add all new files, delete all missing files
131 addremove add all new files, delete all missing files
132 annotate show changeset information by line for each file
132 annotate show changeset information by line for each file
133 archive create an unversioned archive of a repository revision
133 archive create an unversioned archive of a repository revision
134 backout reverse effect of earlier changeset
134 backout reverse effect of earlier changeset
135 bisect subdivision search of changesets
135 bisect subdivision search of changesets
136 bookmarks create a new bookmark or list existing bookmarks
136 bookmarks create a new bookmark or list existing bookmarks
137 branch set or show the current branch name
137 branch set or show the current branch name
138 branches list repository named branches
138 branches list repository named branches
139 bundle create a bundle file
139 bundle create a bundle file
140 cat output the current or given revision of files
140 cat output the current or given revision of files
141 clone make a copy of an existing repository
141 clone make a copy of an existing repository
142 commit commit the specified files or all outstanding changes
142 commit commit the specified files or all outstanding changes
143 config show combined config settings from all hgrc files
143 config show combined config settings from all hgrc files
144 copy mark files as copied for the next commit
144 copy mark files as copied for the next commit
145 diff diff repository (or selected files)
145 diff diff repository (or selected files)
146 export dump the header and diffs for one or more changesets
146 export dump the header and diffs for one or more changesets
147 files list tracked files
147 files list tracked files
148 forget forget the specified files on the next commit
148 forget forget the specified files on the next commit
149 graft copy changes from other branches onto the current branch
149 graft copy changes from other branches onto the current branch
150 grep search revision history for a pattern in specified files
150 grep search revision history for a pattern in specified files
151 heads show branch heads
151 heads show branch heads
152 help show help for a given topic or a help overview
152 help show help for a given topic or a help overview
153 identify identify the working directory or specified revision
153 identify identify the working directory or specified revision
154 import import an ordered set of patches
154 import import an ordered set of patches
155 incoming show new changesets found in source
155 incoming show new changesets found in source
156 init create a new repository in the given directory
156 init create a new repository in the given directory
157 log show revision history of entire repository or files
157 log show revision history of entire repository or files
158 manifest output the current or given revision of the project manifest
158 manifest output the current or given revision of the project manifest
159 merge merge another revision into working directory
159 merge merge another revision into working directory
160 outgoing show changesets not found in the destination
160 outgoing show changesets not found in the destination
161 paths show aliases for remote repositories
161 paths show aliases for remote repositories
162 phase set or show the current phase name
162 phase set or show the current phase name
163 pull pull changes from the specified source
163 pull pull changes from the specified source
164 push push changes to the specified destination
164 push push changes to the specified destination
165 recover roll back an interrupted transaction
165 recover roll back an interrupted transaction
166 remove remove the specified files on the next commit
166 remove remove the specified files on the next commit
167 rename rename files; equivalent of copy + remove
167 rename rename files; equivalent of copy + remove
168 resolve redo merges or set/view the merge status of files
168 resolve redo merges or set/view the merge status of files
169 revert restore files to their checkout state
169 revert restore files to their checkout state
170 root print the root (top) of the current working directory
170 root print the root (top) of the current working directory
171 serve start stand-alone webserver
171 serve start stand-alone webserver
172 status show changed files in the working directory
172 status show changed files in the working directory
173 summary summarize working directory state
173 summary summarize working directory state
174 tag add one or more tags for the current or given revision
174 tag add one or more tags for the current or given revision
175 tags list repository tags
175 tags list repository tags
176 unbundle apply one or more bundle files
176 unbundle apply one or more bundle files
177 update update working directory (or switch revisions)
177 update update working directory (or switch revisions)
178 verify verify the integrity of the repository
178 verify verify the integrity of the repository
179 version output version and copyright information
179 version output version and copyright information
180
180
181 additional help topics:
181 additional help topics:
182
182
183 bundlespec Bundle File Formats
183 bundlespec Bundle File Formats
184 color Colorizing Outputs
184 color Colorizing Outputs
185 config Configuration Files
185 config Configuration Files
186 dates Date Formats
186 dates Date Formats
187 diffs Diff Formats
187 diffs Diff Formats
188 environment Environment Variables
188 environment Environment Variables
189 extensions Using Additional Features
189 extensions Using Additional Features
190 filesets Specifying File Sets
190 filesets Specifying File Sets
191 glossary Glossary
191 glossary Glossary
192 hgignore Syntax for Mercurial Ignore Files
192 hgignore Syntax for Mercurial Ignore Files
193 hgweb Configuring hgweb
193 hgweb Configuring hgweb
194 internals Technical implementation topics
194 internals Technical implementation topics
195 merge-tools Merge Tools
195 merge-tools Merge Tools
196 pager Pager Support
196 pager Pager Support
197 patterns File Name Patterns
197 patterns File Name Patterns
198 phases Working with Phases
198 phases Working with Phases
199 revisions Specifying Revisions
199 revisions Specifying Revisions
200 scripting Using Mercurial from scripts and automation
200 scripting Using Mercurial from scripts and automation
201 subrepos Subrepositories
201 subrepos Subrepositories
202 templating Template Usage
202 templating Template Usage
203 urls URL Paths
203 urls URL Paths
204
204
205 Test extension help:
205 Test extension help:
206 $ hg help extensions --config extensions.rebase= --config extensions.children=
206 $ hg help extensions --config extensions.rebase= --config extensions.children=
207 Using Additional Features
207 Using Additional Features
208 """""""""""""""""""""""""
208 """""""""""""""""""""""""
209
209
210 Mercurial has the ability to add new features through the use of
210 Mercurial has the ability to add new features through the use of
211 extensions. Extensions may add new commands, add options to existing
211 extensions. Extensions may add new commands, add options to existing
212 commands, change the default behavior of commands, or implement hooks.
212 commands, change the default behavior of commands, or implement hooks.
213
213
214 To enable the "foo" extension, either shipped with Mercurial or in the
214 To enable the "foo" extension, either shipped with Mercurial or in the
215 Python search path, create an entry for it in your configuration file,
215 Python search path, create an entry for it in your configuration file,
216 like this:
216 like this:
217
217
218 [extensions]
218 [extensions]
219 foo =
219 foo =
220
220
221 You may also specify the full path to an extension:
221 You may also specify the full path to an extension:
222
222
223 [extensions]
223 [extensions]
224 myfeature = ~/.hgext/myfeature.py
224 myfeature = ~/.hgext/myfeature.py
225
225
226 See 'hg help config' for more information on configuration files.
226 See 'hg help config' for more information on configuration files.
227
227
228 Extensions are not loaded by default for a variety of reasons: they can
228 Extensions are not loaded by default for a variety of reasons: they can
229 increase startup overhead; they may be meant for advanced usage only; they
229 increase startup overhead; they may be meant for advanced usage only; they
230 may provide potentially dangerous abilities (such as letting you destroy
230 may provide potentially dangerous abilities (such as letting you destroy
231 or modify history); they might not be ready for prime time; or they may
231 or modify history); they might not be ready for prime time; or they may
232 alter some usual behaviors of stock Mercurial. It is thus up to the user
232 alter some usual behaviors of stock Mercurial. It is thus up to the user
233 to activate extensions as needed.
233 to activate extensions as needed.
234
234
235 To explicitly disable an extension enabled in a configuration file of
235 To explicitly disable an extension enabled in a configuration file of
236 broader scope, prepend its path with !:
236 broader scope, prepend its path with !:
237
237
238 [extensions]
238 [extensions]
239 # disabling extension bar residing in /path/to/extension/bar.py
239 # disabling extension bar residing in /path/to/extension/bar.py
240 bar = !/path/to/extension/bar.py
240 bar = !/path/to/extension/bar.py
241 # ditto, but no path was supplied for extension baz
241 # ditto, but no path was supplied for extension baz
242 baz = !
242 baz = !
243
243
244 enabled extensions:
244 enabled extensions:
245
245
246 children command to display child changesets (DEPRECATED)
246 children command to display child changesets (DEPRECATED)
247 rebase command to move sets of revisions to a different ancestor
247 rebase command to move sets of revisions to a different ancestor
248
248
249 disabled extensions:
249 disabled extensions:
250
250
251 acl hooks for controlling repository access
251 acl hooks for controlling repository access
252 blackbox log repository events to a blackbox for debugging
252 blackbox log repository events to a blackbox for debugging
253 bugzilla hooks for integrating with the Bugzilla bug tracker
253 bugzilla hooks for integrating with the Bugzilla bug tracker
254 censor erase file content at a given revision
254 censor erase file content at a given revision
255 churn command to display statistics about repository history
255 churn command to display statistics about repository history
256 clonebundles advertise pre-generated bundles to seed clones
256 clonebundles advertise pre-generated bundles to seed clones
257 convert import revisions from foreign VCS repositories into
257 convert import revisions from foreign VCS repositories into
258 Mercurial
258 Mercurial
259 eol automatically manage newlines in repository files
259 eol automatically manage newlines in repository files
260 extdiff command to allow external programs to compare revisions
260 extdiff command to allow external programs to compare revisions
261 factotum http authentication with factotum
261 factotum http authentication with factotum
262 gpg commands to sign and verify changesets
262 gpg commands to sign and verify changesets
263 hgk browse the repository in a graphical way
263 hgk browse the repository in a graphical way
264 highlight syntax highlighting for hgweb (requires Pygments)
264 highlight syntax highlighting for hgweb (requires Pygments)
265 histedit interactive history editing
265 histedit interactive history editing
266 keyword expand keywords in tracked files
266 keyword expand keywords in tracked files
267 largefiles track large binary files
267 largefiles track large binary files
268 mq manage a stack of patches
268 mq manage a stack of patches
269 notify hooks for sending email push notifications
269 notify hooks for sending email push notifications
270 patchbomb command to send changesets as (a series of) patch emails
270 patchbomb command to send changesets as (a series of) patch emails
271 purge command to delete untracked files from the working
271 purge command to delete untracked files from the working
272 directory
272 directory
273 relink recreates hardlinks between repository clones
273 relink recreates hardlinks between repository clones
274 schemes extend schemes with shortcuts to repository swarms
274 schemes extend schemes with shortcuts to repository swarms
275 share share a common history between several working directories
275 share share a common history between several working directories
276 shelve save and restore changes to the working directory
276 shelve save and restore changes to the working directory
277 strip strip changesets and their descendants from history
277 strip strip changesets and their descendants from history
278 transplant command to transplant changesets from another branch
278 transplant command to transplant changesets from another branch
279 win32mbcs allow the use of MBCS paths with problematic encodings
279 win32mbcs allow the use of MBCS paths with problematic encodings
280 zeroconf discover and advertise repositories on the local network
280 zeroconf discover and advertise repositories on the local network
281
281
282 Verify that extension keywords appear in help templates
282 Verify that extension keywords appear in help templates
283
283
284 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
284 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
285
285
286 Test short command list with verbose option
286 Test short command list with verbose option
287
287
288 $ hg -v help shortlist
288 $ hg -v help shortlist
289 Mercurial Distributed SCM
289 Mercurial Distributed SCM
290
290
291 basic commands:
291 basic commands:
292
292
293 add add the specified files on the next commit
293 add add the specified files on the next commit
294 annotate, blame
294 annotate, blame
295 show changeset information by line for each file
295 show changeset information by line for each file
296 clone make a copy of an existing repository
296 clone make a copy of an existing repository
297 commit, ci commit the specified files or all outstanding changes
297 commit, ci commit the specified files or all outstanding changes
298 diff diff repository (or selected files)
298 diff diff repository (or selected files)
299 export dump the header and diffs for one or more changesets
299 export dump the header and diffs for one or more changesets
300 forget forget the specified files on the next commit
300 forget forget the specified files on the next commit
301 init create a new repository in the given directory
301 init create a new repository in the given directory
302 log, history show revision history of entire repository or files
302 log, history show revision history of entire repository or files
303 merge merge another revision into working directory
303 merge merge another revision into working directory
304 pull pull changes from the specified source
304 pull pull changes from the specified source
305 push push changes to the specified destination
305 push push changes to the specified destination
306 remove, rm remove the specified files on the next commit
306 remove, rm remove the specified files on the next commit
307 serve start stand-alone webserver
307 serve start stand-alone webserver
308 status, st show changed files in the working directory
308 status, st show changed files in the working directory
309 summary, sum summarize working directory state
309 summary, sum summarize working directory state
310 update, up, checkout, co
310 update, up, checkout, co
311 update working directory (or switch revisions)
311 update working directory (or switch revisions)
312
312
313 global options ([+] can be repeated):
313 global options ([+] can be repeated):
314
314
315 -R --repository REPO repository root directory or name of overlay bundle
315 -R --repository REPO repository root directory or name of overlay bundle
316 file
316 file
317 --cwd DIR change working directory
317 --cwd DIR change working directory
318 -y --noninteractive do not prompt, automatically pick the first choice for
318 -y --noninteractive do not prompt, automatically pick the first choice for
319 all prompts
319 all prompts
320 -q --quiet suppress output
320 -q --quiet suppress output
321 -v --verbose enable additional output
321 -v --verbose enable additional output
322 --color TYPE when to colorize (boolean, always, auto, never, or
322 --color TYPE when to colorize (boolean, always, auto, never, or
323 debug)
323 debug)
324 --config CONFIG [+] set/override config option (use 'section.name=value')
324 --config CONFIG [+] set/override config option (use 'section.name=value')
325 --debug enable debugging output
325 --debug enable debugging output
326 --debugger start debugger
326 --debugger start debugger
327 --encoding ENCODE set the charset encoding (default: ascii)
327 --encoding ENCODE set the charset encoding (default: ascii)
328 --encodingmode MODE set the charset encoding mode (default: strict)
328 --encodingmode MODE set the charset encoding mode (default: strict)
329 --traceback always print a traceback on exception
329 --traceback always print a traceback on exception
330 --time time how long the command takes
330 --time time how long the command takes
331 --profile print command execution profile
331 --profile print command execution profile
332 --version output version information and exit
332 --version output version information and exit
333 -h --help display help and exit
333 -h --help display help and exit
334 --hidden consider hidden changesets
334 --hidden consider hidden changesets
335 --pager TYPE when to paginate (boolean, always, auto, or never)
335 --pager TYPE when to paginate (boolean, always, auto, or never)
336 (default: auto)
336 (default: auto)
337
337
338 (use 'hg help' for the full list of commands)
338 (use 'hg help' for the full list of commands)
339
339
340 $ hg add -h
340 $ hg add -h
341 hg add [OPTION]... [FILE]...
341 hg add [OPTION]... [FILE]...
342
342
343 add the specified files on the next commit
343 add the specified files on the next commit
344
344
345 Schedule files to be version controlled and added to the repository.
345 Schedule files to be version controlled and added to the repository.
346
346
347 The files will be added to the repository at the next commit. To undo an
347 The files will be added to the repository at the next commit. To undo an
348 add before that, see 'hg forget'.
348 add before that, see 'hg forget'.
349
349
350 If no names are given, add all files to the repository (except files
350 If no names are given, add all files to the repository (except files
351 matching ".hgignore").
351 matching ".hgignore").
352
352
353 Returns 0 if all files are successfully added.
353 Returns 0 if all files are successfully added.
354
354
355 options ([+] can be repeated):
355 options ([+] can be repeated):
356
356
357 -I --include PATTERN [+] include names matching the given patterns
357 -I --include PATTERN [+] include names matching the given patterns
358 -X --exclude PATTERN [+] exclude names matching the given patterns
358 -X --exclude PATTERN [+] exclude names matching the given patterns
359 -S --subrepos recurse into subrepositories
359 -S --subrepos recurse into subrepositories
360 -n --dry-run do not perform actions, just print output
360 -n --dry-run do not perform actions, just print output
361
361
362 (some details hidden, use --verbose to show complete help)
362 (some details hidden, use --verbose to show complete help)
363
363
364 Verbose help for add
364 Verbose help for add
365
365
366 $ hg add -hv
366 $ hg add -hv
367 hg add [OPTION]... [FILE]...
367 hg add [OPTION]... [FILE]...
368
368
369 add the specified files on the next commit
369 add the specified files on the next commit
370
370
371 Schedule files to be version controlled and added to the repository.
371 Schedule files to be version controlled and added to the repository.
372
372
373 The files will be added to the repository at the next commit. To undo an
373 The files will be added to the repository at the next commit. To undo an
374 add before that, see 'hg forget'.
374 add before that, see 'hg forget'.
375
375
376 If no names are given, add all files to the repository (except files
376 If no names are given, add all files to the repository (except files
377 matching ".hgignore").
377 matching ".hgignore").
378
378
379 Examples:
379 Examples:
380
380
381 - New (unknown) files are added automatically by 'hg add':
381 - New (unknown) files are added automatically by 'hg add':
382
382
383 $ ls
383 $ ls
384 foo.c
384 foo.c
385 $ hg status
385 $ hg status
386 ? foo.c
386 ? foo.c
387 $ hg add
387 $ hg add
388 adding foo.c
388 adding foo.c
389 $ hg status
389 $ hg status
390 A foo.c
390 A foo.c
391
391
392 - Specific files to be added can be specified:
392 - Specific files to be added can be specified:
393
393
394 $ ls
394 $ ls
395 bar.c foo.c
395 bar.c foo.c
396 $ hg status
396 $ hg status
397 ? bar.c
397 ? bar.c
398 ? foo.c
398 ? foo.c
399 $ hg add bar.c
399 $ hg add bar.c
400 $ hg status
400 $ hg status
401 A bar.c
401 A bar.c
402 ? foo.c
402 ? foo.c
403
403
404 Returns 0 if all files are successfully added.
404 Returns 0 if all files are successfully added.
405
405
406 options ([+] can be repeated):
406 options ([+] can be repeated):
407
407
408 -I --include PATTERN [+] include names matching the given patterns
408 -I --include PATTERN [+] include names matching the given patterns
409 -X --exclude PATTERN [+] exclude names matching the given patterns
409 -X --exclude PATTERN [+] exclude names matching the given patterns
410 -S --subrepos recurse into subrepositories
410 -S --subrepos recurse into subrepositories
411 -n --dry-run do not perform actions, just print output
411 -n --dry-run do not perform actions, just print output
412
412
413 global options ([+] can be repeated):
413 global options ([+] can be repeated):
414
414
415 -R --repository REPO repository root directory or name of overlay bundle
415 -R --repository REPO repository root directory or name of overlay bundle
416 file
416 file
417 --cwd DIR change working directory
417 --cwd DIR change working directory
418 -y --noninteractive do not prompt, automatically pick the first choice for
418 -y --noninteractive do not prompt, automatically pick the first choice for
419 all prompts
419 all prompts
420 -q --quiet suppress output
420 -q --quiet suppress output
421 -v --verbose enable additional output
421 -v --verbose enable additional output
422 --color TYPE when to colorize (boolean, always, auto, never, or
422 --color TYPE when to colorize (boolean, always, auto, never, or
423 debug)
423 debug)
424 --config CONFIG [+] set/override config option (use 'section.name=value')
424 --config CONFIG [+] set/override config option (use 'section.name=value')
425 --debug enable debugging output
425 --debug enable debugging output
426 --debugger start debugger
426 --debugger start debugger
427 --encoding ENCODE set the charset encoding (default: ascii)
427 --encoding ENCODE set the charset encoding (default: ascii)
428 --encodingmode MODE set the charset encoding mode (default: strict)
428 --encodingmode MODE set the charset encoding mode (default: strict)
429 --traceback always print a traceback on exception
429 --traceback always print a traceback on exception
430 --time time how long the command takes
430 --time time how long the command takes
431 --profile print command execution profile
431 --profile print command execution profile
432 --version output version information and exit
432 --version output version information and exit
433 -h --help display help and exit
433 -h --help display help and exit
434 --hidden consider hidden changesets
434 --hidden consider hidden changesets
435 --pager TYPE when to paginate (boolean, always, auto, or never)
435 --pager TYPE when to paginate (boolean, always, auto, or never)
436 (default: auto)
436 (default: auto)
437
437
438 Test the textwidth config option
438 Test the textwidth config option
439
439
440 $ hg root -h --config ui.textwidth=50
440 $ hg root -h --config ui.textwidth=50
441 hg root
441 hg root
442
442
443 print the root (top) of the current working
443 print the root (top) of the current working
444 directory
444 directory
445
445
446 Print the root directory of the current
446 Print the root directory of the current
447 repository.
447 repository.
448
448
449 Returns 0 on success.
449 Returns 0 on success.
450
450
451 (some details hidden, use --verbose to show
451 (some details hidden, use --verbose to show
452 complete help)
452 complete help)
453
453
454 Test help option with version option
454 Test help option with version option
455
455
456 $ hg add -h --version
456 $ hg add -h --version
457 Mercurial Distributed SCM (version *) (glob)
457 Mercurial Distributed SCM (version *) (glob)
458 (see https://mercurial-scm.org for more information)
458 (see https://mercurial-scm.org for more information)
459
459
460 Copyright (C) 2005-* Matt Mackall and others (glob)
460 Copyright (C) 2005-* Matt Mackall and others (glob)
461 This is free software; see the source for copying conditions. There is NO
461 This is free software; see the source for copying conditions. There is NO
462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
463
463
464 $ hg add --skjdfks
464 $ hg add --skjdfks
465 hg add: option --skjdfks not recognized
465 hg add: option --skjdfks not recognized
466 hg add [OPTION]... [FILE]...
466 hg add [OPTION]... [FILE]...
467
467
468 add the specified files on the next commit
468 add the specified files on the next commit
469
469
470 options ([+] can be repeated):
470 options ([+] can be repeated):
471
471
472 -I --include PATTERN [+] include names matching the given patterns
472 -I --include PATTERN [+] include names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
473 -X --exclude PATTERN [+] exclude names matching the given patterns
474 -S --subrepos recurse into subrepositories
474 -S --subrepos recurse into subrepositories
475 -n --dry-run do not perform actions, just print output
475 -n --dry-run do not perform actions, just print output
476
476
477 (use 'hg add -h' to show more help)
477 (use 'hg add -h' to show more help)
478 [255]
478 [255]
479
479
480 Test ambiguous command help
480 Test ambiguous command help
481
481
482 $ hg help ad
482 $ hg help ad
483 list of commands:
483 list of commands:
484
484
485 add add the specified files on the next commit
485 add add the specified files on the next commit
486 addremove add all new files, delete all missing files
486 addremove add all new files, delete all missing files
487
487
488 (use 'hg help -v ad' to show built-in aliases and global options)
488 (use 'hg help -v ad' to show built-in aliases and global options)
489
489
490 Test command without options
490 Test command without options
491
491
492 $ hg help verify
492 $ hg help verify
493 hg verify
493 hg verify
494
494
495 verify the integrity of the repository
495 verify the integrity of the repository
496
496
497 Verify the integrity of the current repository.
497 Verify the integrity of the current repository.
498
498
499 This will perform an extensive check of the repository's integrity,
499 This will perform an extensive check of the repository's integrity,
500 validating the hashes and checksums of each entry in the changelog,
500 validating the hashes and checksums of each entry in the changelog,
501 manifest, and tracked files, as well as the integrity of their crosslinks
501 manifest, and tracked files, as well as the integrity of their crosslinks
502 and indices.
502 and indices.
503
503
504 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
504 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
505 information about recovery from corruption of the repository.
505 information about recovery from corruption of the repository.
506
506
507 Returns 0 on success, 1 if errors are encountered.
507 Returns 0 on success, 1 if errors are encountered.
508
508
509 (some details hidden, use --verbose to show complete help)
509 (some details hidden, use --verbose to show complete help)
510
510
511 $ hg help diff
511 $ hg help diff
512 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
512 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
513
513
514 diff repository (or selected files)
514 diff repository (or selected files)
515
515
516 Show differences between revisions for the specified files.
516 Show differences between revisions for the specified files.
517
517
518 Differences between files are shown using the unified diff format.
518 Differences between files are shown using the unified diff format.
519
519
520 Note:
520 Note:
521 'hg diff' may generate unexpected results for merges, as it will
521 'hg diff' may generate unexpected results for merges, as it will
522 default to comparing against the working directory's first parent
522 default to comparing against the working directory's first parent
523 changeset if no revisions are specified.
523 changeset if no revisions are specified.
524
524
525 When two revision arguments are given, then changes are shown between
525 When two revision arguments are given, then changes are shown between
526 those revisions. If only one revision is specified then that revision is
526 those revisions. If only one revision is specified then that revision is
527 compared to the working directory, and, when no revisions are specified,
527 compared to the working directory, and, when no revisions are specified,
528 the working directory files are compared to its first parent.
528 the working directory files are compared to its first parent.
529
529
530 Alternatively you can specify -c/--change with a revision to see the
530 Alternatively you can specify -c/--change with a revision to see the
531 changes in that changeset relative to its first parent.
531 changes in that changeset relative to its first parent.
532
532
533 Without the -a/--text option, diff will avoid generating diffs of files it
533 Without the -a/--text option, diff will avoid generating diffs of files it
534 detects as binary. With -a, diff will generate a diff anyway, probably
534 detects as binary. With -a, diff will generate a diff anyway, probably
535 with undesirable results.
535 with undesirable results.
536
536
537 Use the -g/--git option to generate diffs in the git extended diff format.
537 Use the -g/--git option to generate diffs in the git extended diff format.
538 For more information, read 'hg help diffs'.
538 For more information, read 'hg help diffs'.
539
539
540 Returns 0 on success.
540 Returns 0 on success.
541
541
542 options ([+] can be repeated):
542 options ([+] can be repeated):
543
543
544 -r --rev REV [+] revision
544 -r --rev REV [+] revision
545 -c --change REV change made by revision
545 -c --change REV change made by revision
546 -a --text treat all files as text
546 -a --text treat all files as text
547 -g --git use git extended diff format
547 -g --git use git extended diff format
548 --binary generate binary diffs in git mode (default)
548 --binary generate binary diffs in git mode (default)
549 --nodates omit dates from diff headers
549 --nodates omit dates from diff headers
550 --noprefix omit a/ and b/ prefixes from filenames
550 --noprefix omit a/ and b/ prefixes from filenames
551 -p --show-function show which function each change is in
551 -p --show-function show which function each change is in
552 --reverse produce a diff that undoes the changes
552 --reverse produce a diff that undoes the changes
553 -w --ignore-all-space ignore white space when comparing lines
553 -w --ignore-all-space ignore white space when comparing lines
554 -b --ignore-space-change ignore changes in the amount of white space
554 -b --ignore-space-change ignore changes in the amount of white space
555 -B --ignore-blank-lines ignore changes whose lines are all blank
555 -B --ignore-blank-lines ignore changes whose lines are all blank
556 -U --unified NUM number of lines of context to show
556 -U --unified NUM number of lines of context to show
557 --stat output diffstat-style summary of changes
557 --stat output diffstat-style summary of changes
558 --root DIR produce diffs relative to subdirectory
558 --root DIR produce diffs relative to subdirectory
559 -I --include PATTERN [+] include names matching the given patterns
559 -I --include PATTERN [+] include names matching the given patterns
560 -X --exclude PATTERN [+] exclude names matching the given patterns
560 -X --exclude PATTERN [+] exclude names matching the given patterns
561 -S --subrepos recurse into subrepositories
561 -S --subrepos recurse into subrepositories
562
562
563 (some details hidden, use --verbose to show complete help)
563 (some details hidden, use --verbose to show complete help)
564
564
565 $ hg help status
565 $ hg help status
566 hg status [OPTION]... [FILE]...
566 hg status [OPTION]... [FILE]...
567
567
568 aliases: st
568 aliases: st
569
569
570 show changed files in the working directory
570 show changed files in the working directory
571
571
572 Show status of files in the repository. If names are given, only files
572 Show status of files in the repository. If names are given, only files
573 that match are shown. Files that are clean or ignored or the source of a
573 that match are shown. Files that are clean or ignored or the source of a
574 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
574 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
575 -C/--copies or -A/--all are given. Unless options described with "show
575 -C/--copies or -A/--all are given. Unless options described with "show
576 only ..." are given, the options -mardu are used.
576 only ..." are given, the options -mardu are used.
577
577
578 Option -q/--quiet hides untracked (unknown and ignored) files unless
578 Option -q/--quiet hides untracked (unknown and ignored) files unless
579 explicitly requested with -u/--unknown or -i/--ignored.
579 explicitly requested with -u/--unknown or -i/--ignored.
580
580
581 Note:
581 Note:
582 'hg status' may appear to disagree with diff if permissions have
582 'hg status' may appear to disagree with diff if permissions have
583 changed or a merge has occurred. The standard diff format does not
583 changed or a merge has occurred. The standard diff format does not
584 report permission changes and diff only reports changes relative to one
584 report permission changes and diff only reports changes relative to one
585 merge parent.
585 merge parent.
586
586
587 If one revision is given, it is used as the base revision. If two
587 If one revision is given, it is used as the base revision. If two
588 revisions are given, the differences between them are shown. The --change
588 revisions are given, the differences between them are shown. The --change
589 option can also be used as a shortcut to list the changed files of a
589 option can also be used as a shortcut to list the changed files of a
590 revision from its first parent.
590 revision from its first parent.
591
591
592 The codes used to show the status of files are:
592 The codes used to show the status of files are:
593
593
594 M = modified
594 M = modified
595 A = added
595 A = added
596 R = removed
596 R = removed
597 C = clean
597 C = clean
598 ! = missing (deleted by non-hg command, but still tracked)
598 ! = missing (deleted by non-hg command, but still tracked)
599 ? = not tracked
599 ? = not tracked
600 I = ignored
600 I = ignored
601 = origin of the previous file (with --copies)
601 = origin of the previous file (with --copies)
602
602
603 Returns 0 on success.
603 Returns 0 on success.
604
604
605 options ([+] can be repeated):
605 options ([+] can be repeated):
606
606
607 -A --all show status of all files
607 -A --all show status of all files
608 -m --modified show only modified files
608 -m --modified show only modified files
609 -a --added show only added files
609 -a --added show only added files
610 -r --removed show only removed files
610 -r --removed show only removed files
611 -d --deleted show only deleted (but tracked) files
611 -d --deleted show only deleted (but tracked) files
612 -c --clean show only files without changes
612 -c --clean show only files without changes
613 -u --unknown show only unknown (not tracked) files
613 -u --unknown show only unknown (not tracked) files
614 -i --ignored show only ignored files
614 -i --ignored show only ignored files
615 -n --no-status hide status prefix
615 -n --no-status hide status prefix
616 -C --copies show source of copied files
616 -C --copies show source of copied files
617 -0 --print0 end filenames with NUL, for use with xargs
617 -0 --print0 end filenames with NUL, for use with xargs
618 --rev REV [+] show difference from revision
618 --rev REV [+] show difference from revision
619 --change REV list the changed files of a revision
619 --change REV list the changed files of a revision
620 -I --include PATTERN [+] include names matching the given patterns
620 -I --include PATTERN [+] include names matching the given patterns
621 -X --exclude PATTERN [+] exclude names matching the given patterns
621 -X --exclude PATTERN [+] exclude names matching the given patterns
622 -S --subrepos recurse into subrepositories
622 -S --subrepos recurse into subrepositories
623
623
624 (some details hidden, use --verbose to show complete help)
624 (some details hidden, use --verbose to show complete help)
625
625
626 $ hg -q help status
626 $ hg -q help status
627 hg status [OPTION]... [FILE]...
627 hg status [OPTION]... [FILE]...
628
628
629 show changed files in the working directory
629 show changed files in the working directory
630
630
631 $ hg help foo
631 $ hg help foo
632 abort: no such help topic: foo
632 abort: no such help topic: foo
633 (try 'hg help --keyword foo')
633 (try 'hg help --keyword foo')
634 [255]
634 [255]
635
635
636 $ hg skjdfks
636 $ hg skjdfks
637 hg: unknown command 'skjdfks'
637 hg: unknown command 'skjdfks'
638 Mercurial Distributed SCM
638 Mercurial Distributed SCM
639
639
640 basic commands:
640 basic commands:
641
641
642 add add the specified files on the next commit
642 add add the specified files on the next commit
643 annotate show changeset information by line for each file
643 annotate show changeset information by line for each file
644 clone make a copy of an existing repository
644 clone make a copy of an existing repository
645 commit commit the specified files or all outstanding changes
645 commit commit the specified files or all outstanding changes
646 diff diff repository (or selected files)
646 diff diff repository (or selected files)
647 export dump the header and diffs for one or more changesets
647 export dump the header and diffs for one or more changesets
648 forget forget the specified files on the next commit
648 forget forget the specified files on the next commit
649 init create a new repository in the given directory
649 init create a new repository in the given directory
650 log show revision history of entire repository or files
650 log show revision history of entire repository or files
651 merge merge another revision into working directory
651 merge merge another revision into working directory
652 pull pull changes from the specified source
652 pull pull changes from the specified source
653 push push changes to the specified destination
653 push push changes to the specified destination
654 remove remove the specified files on the next commit
654 remove remove the specified files on the next commit
655 serve start stand-alone webserver
655 serve start stand-alone webserver
656 status show changed files in the working directory
656 status show changed files in the working directory
657 summary summarize working directory state
657 summary summarize working directory state
658 update update working directory (or switch revisions)
658 update update working directory (or switch revisions)
659
659
660 (use 'hg help' for the full list of commands or 'hg -v' for details)
660 (use 'hg help' for the full list of commands or 'hg -v' for details)
661 [255]
661 [255]
662
662
663
663
664 Make sure that we don't run afoul of the help system thinking that
664 Make sure that we don't run afoul of the help system thinking that
665 this is a section and erroring out weirdly.
665 this is a section and erroring out weirdly.
666
666
667 $ hg .log
667 $ hg .log
668 hg: unknown command '.log'
668 hg: unknown command '.log'
669 (did you mean log?)
669 (did you mean log?)
670 [255]
670 [255]
671
671
672 $ hg log.
672 $ hg log.
673 hg: unknown command 'log.'
673 hg: unknown command 'log.'
674 (did you mean log?)
674 (did you mean log?)
675 [255]
675 [255]
676 $ hg pu.lh
676 $ hg pu.lh
677 hg: unknown command 'pu.lh'
677 hg: unknown command 'pu.lh'
678 (did you mean one of pull, push?)
678 (did you mean one of pull, push?)
679 [255]
679 [255]
680
680
681 $ cat > helpext.py <<EOF
681 $ cat > helpext.py <<EOF
682 > import os
682 > import os
683 > from mercurial import cmdutil, commands
683 > from mercurial import cmdutil, commands
684 >
684 >
685 > cmdtable = {}
685 > cmdtable = {}
686 > command = cmdutil.command(cmdtable)
686 > command = cmdutil.command(cmdtable)
687 >
687 >
688 > @command('nohelp',
688 > @command('nohelp',
689 > [('', 'longdesc', 3, 'x'*90),
689 > [('', 'longdesc', 3, 'x'*90),
690 > ('n', '', None, 'normal desc'),
690 > ('n', '', None, 'normal desc'),
691 > ('', 'newline', '', 'line1\nline2')],
691 > ('', 'newline', '', 'line1\nline2')],
692 > 'hg nohelp',
692 > 'hg nohelp',
693 > norepo=True)
693 > norepo=True)
694 > @command('debugoptADV', [('', 'aopt', None, 'option is (ADVANCED)')])
694 > @command('debugoptADV', [('', 'aopt', None, 'option is (ADVANCED)')])
695 > @command('debugoptDEP', [('', 'dopt', None, 'option is (DEPRECATED)')])
695 > @command('debugoptDEP', [('', 'dopt', None, 'option is (DEPRECATED)')])
696 > @command('debugoptEXP', [('', 'eopt', None, 'option is (EXPERIMENTAL)')])
696 > @command('debugoptEXP', [('', 'eopt', None, 'option is (EXPERIMENTAL)')])
697 > def nohelp(ui, *args, **kwargs):
697 > def nohelp(ui, *args, **kwargs):
698 > pass
698 > pass
699 >
699 >
700 > def uisetup(ui):
700 > def uisetup(ui):
701 > ui.setconfig('alias', 'shellalias', '!echo hi', 'helpext')
701 > ui.setconfig('alias', 'shellalias', '!echo hi', 'helpext')
702 > ui.setconfig('alias', 'hgalias', 'summary', 'helpext')
702 > ui.setconfig('alias', 'hgalias', 'summary', 'helpext')
703 >
703 >
704 > EOF
704 > EOF
705 $ echo '[extensions]' >> $HGRCPATH
705 $ echo '[extensions]' >> $HGRCPATH
706 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
706 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
707
707
708 Test for aliases
708 Test for aliases
709
709
710 $ hg help hgalias
710 $ hg help hgalias
711 hg hgalias [--remote]
711 hg hgalias [--remote]
712
712
713 alias for: hg summary
713 alias for: hg summary
714
714
715 summarize working directory state
715 summarize working directory state
716
716
717 This generates a brief summary of the working directory state, including
717 This generates a brief summary of the working directory state, including
718 parents, branch, commit status, phase and available updates.
718 parents, branch, commit status, phase and available updates.
719
719
720 With the --remote option, this will check the default paths for incoming
720 With the --remote option, this will check the default paths for incoming
721 and outgoing changes. This can be time-consuming.
721 and outgoing changes. This can be time-consuming.
722
722
723 Returns 0 on success.
723 Returns 0 on success.
724
724
725 defined by: helpext
725 defined by: helpext
726
726
727 options:
727 options:
728
728
729 --remote check for push and pull
729 --remote check for push and pull
730
730
731 (some details hidden, use --verbose to show complete help)
731 (some details hidden, use --verbose to show complete help)
732
732
733 $ hg help shellalias
733 $ hg help shellalias
734 hg shellalias
734 hg shellalias
735
735
736 shell alias for:
736 shell alias for:
737
737
738 echo hi
738 echo hi
739
739
740 defined by: helpext
740 defined by: helpext
741
741
742 (some details hidden, use --verbose to show complete help)
742 (some details hidden, use --verbose to show complete help)
743
743
744 Test command with no help text
744 Test command with no help text
745
745
746 $ hg help nohelp
746 $ hg help nohelp
747 hg nohelp
747 hg nohelp
748
748
749 (no help text available)
749 (no help text available)
750
750
751 options:
751 options:
752
752
753 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
753 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
754 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
754 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
755 -n -- normal desc
755 -n -- normal desc
756 --newline VALUE line1 line2
756 --newline VALUE line1 line2
757
757
758 (some details hidden, use --verbose to show complete help)
758 (some details hidden, use --verbose to show complete help)
759
759
760 $ hg help -k nohelp
760 $ hg help -k nohelp
761 Commands:
761 Commands:
762
762
763 nohelp hg nohelp
763 nohelp hg nohelp
764
764
765 Extension Commands:
765 Extension Commands:
766
766
767 nohelp (no help text available)
767 nohelp (no help text available)
768
768
769 Test that default list of commands omits extension commands
769 Test that default list of commands omits extension commands
770
770
771 $ hg help
771 $ hg help
772 Mercurial Distributed SCM
772 Mercurial Distributed SCM
773
773
774 list of commands:
774 list of commands:
775
775
776 add add the specified files on the next commit
776 add add the specified files on the next commit
777 addremove add all new files, delete all missing files
777 addremove add all new files, delete all missing files
778 annotate show changeset information by line for each file
778 annotate show changeset information by line for each file
779 archive create an unversioned archive of a repository revision
779 archive create an unversioned archive of a repository revision
780 backout reverse effect of earlier changeset
780 backout reverse effect of earlier changeset
781 bisect subdivision search of changesets
781 bisect subdivision search of changesets
782 bookmarks create a new bookmark or list existing bookmarks
782 bookmarks create a new bookmark or list existing bookmarks
783 branch set or show the current branch name
783 branch set or show the current branch name
784 branches list repository named branches
784 branches list repository named branches
785 bundle create a bundle file
785 bundle create a bundle file
786 cat output the current or given revision of files
786 cat output the current or given revision of files
787 clone make a copy of an existing repository
787 clone make a copy of an existing repository
788 commit commit the specified files or all outstanding changes
788 commit commit the specified files or all outstanding changes
789 config show combined config settings from all hgrc files
789 config show combined config settings from all hgrc files
790 copy mark files as copied for the next commit
790 copy mark files as copied for the next commit
791 diff diff repository (or selected files)
791 diff diff repository (or selected files)
792 export dump the header and diffs for one or more changesets
792 export dump the header and diffs for one or more changesets
793 files list tracked files
793 files list tracked files
794 forget forget the specified files on the next commit
794 forget forget the specified files on the next commit
795 graft copy changes from other branches onto the current branch
795 graft copy changes from other branches onto the current branch
796 grep search revision history for a pattern in specified files
796 grep search revision history for a pattern in specified files
797 heads show branch heads
797 heads show branch heads
798 help show help for a given topic or a help overview
798 help show help for a given topic or a help overview
799 identify identify the working directory or specified revision
799 identify identify the working directory or specified revision
800 import import an ordered set of patches
800 import import an ordered set of patches
801 incoming show new changesets found in source
801 incoming show new changesets found in source
802 init create a new repository in the given directory
802 init create a new repository in the given directory
803 log show revision history of entire repository or files
803 log show revision history of entire repository or files
804 manifest output the current or given revision of the project manifest
804 manifest output the current or given revision of the project manifest
805 merge merge another revision into working directory
805 merge merge another revision into working directory
806 outgoing show changesets not found in the destination
806 outgoing show changesets not found in the destination
807 paths show aliases for remote repositories
807 paths show aliases for remote repositories
808 phase set or show the current phase name
808 phase set or show the current phase name
809 pull pull changes from the specified source
809 pull pull changes from the specified source
810 push push changes to the specified destination
810 push push changes to the specified destination
811 recover roll back an interrupted transaction
811 recover roll back an interrupted transaction
812 remove remove the specified files on the next commit
812 remove remove the specified files on the next commit
813 rename rename files; equivalent of copy + remove
813 rename rename files; equivalent of copy + remove
814 resolve redo merges or set/view the merge status of files
814 resolve redo merges or set/view the merge status of files
815 revert restore files to their checkout state
815 revert restore files to their checkout state
816 root print the root (top) of the current working directory
816 root print the root (top) of the current working directory
817 serve start stand-alone webserver
817 serve start stand-alone webserver
818 status show changed files in the working directory
818 status show changed files in the working directory
819 summary summarize working directory state
819 summary summarize working directory state
820 tag add one or more tags for the current or given revision
820 tag add one or more tags for the current or given revision
821 tags list repository tags
821 tags list repository tags
822 unbundle apply one or more bundle files
822 unbundle apply one or more bundle files
823 update update working directory (or switch revisions)
823 update update working directory (or switch revisions)
824 verify verify the integrity of the repository
824 verify verify the integrity of the repository
825 version output version and copyright information
825 version output version and copyright information
826
826
827 enabled extensions:
827 enabled extensions:
828
828
829 helpext (no help text available)
829 helpext (no help text available)
830
830
831 additional help topics:
831 additional help topics:
832
832
833 bundlespec Bundle File Formats
833 bundlespec Bundle File Formats
834 color Colorizing Outputs
834 color Colorizing Outputs
835 config Configuration Files
835 config Configuration Files
836 dates Date Formats
836 dates Date Formats
837 diffs Diff Formats
837 diffs Diff Formats
838 environment Environment Variables
838 environment Environment Variables
839 extensions Using Additional Features
839 extensions Using Additional Features
840 filesets Specifying File Sets
840 filesets Specifying File Sets
841 glossary Glossary
841 glossary Glossary
842 hgignore Syntax for Mercurial Ignore Files
842 hgignore Syntax for Mercurial Ignore Files
843 hgweb Configuring hgweb
843 hgweb Configuring hgweb
844 internals Technical implementation topics
844 internals Technical implementation topics
845 merge-tools Merge Tools
845 merge-tools Merge Tools
846 pager Pager Support
846 pager Pager Support
847 patterns File Name Patterns
847 patterns File Name Patterns
848 phases Working with Phases
848 phases Working with Phases
849 revisions Specifying Revisions
849 revisions Specifying Revisions
850 scripting Using Mercurial from scripts and automation
850 scripting Using Mercurial from scripts and automation
851 subrepos Subrepositories
851 subrepos Subrepositories
852 templating Template Usage
852 templating Template Usage
853 urls URL Paths
853 urls URL Paths
854
854
855 (use 'hg help -v' to show built-in aliases and global options)
855 (use 'hg help -v' to show built-in aliases and global options)
856
856
857
857
858 Test list of internal help commands
858 Test list of internal help commands
859
859
860 $ hg help debug
860 $ hg help debug
861 debug commands (internal and unsupported):
861 debug commands (internal and unsupported):
862
862
863 debugancestor
863 debugancestor
864 find the ancestor revision of two revisions in a given index
864 find the ancestor revision of two revisions in a given index
865 debugapplystreamclonebundle
865 debugapplystreamclonebundle
866 apply a stream clone bundle file
866 apply a stream clone bundle file
867 debugbuilddag
867 debugbuilddag
868 builds a repo with a given DAG from scratch in the current
868 builds a repo with a given DAG from scratch in the current
869 empty repo
869 empty repo
870 debugbundle lists the contents of a bundle
870 debugbundle lists the contents of a bundle
871 debugcheckstate
871 debugcheckstate
872 validate the correctness of the current dirstate
872 validate the correctness of the current dirstate
873 debugcolor show available color, effects or style
873 debugcolor show available color, effects or style
874 debugcommands
874 debugcommands
875 list all available commands and options
875 list all available commands and options
876 debugcomplete
876 debugcomplete
877 returns the completion list associated with the given command
877 returns the completion list associated with the given command
878 debugcreatestreamclonebundle
878 debugcreatestreamclonebundle
879 create a stream clone bundle file
879 create a stream clone bundle file
880 debugdag format the changelog or an index DAG as a concise textual
880 debugdag format the changelog or an index DAG as a concise textual
881 description
881 description
882 debugdata dump the contents of a data file revision
882 debugdata dump the contents of a data file revision
883 debugdate parse and display a date
883 debugdate parse and display a date
884 debugdeltachain
884 debugdeltachain
885 dump information about delta chains in a revlog
885 dump information about delta chains in a revlog
886 debugdirstate
886 debugdirstate
887 show the contents of the current dirstate
887 show the contents of the current dirstate
888 debugdiscovery
888 debugdiscovery
889 runs the changeset discovery protocol in isolation
889 runs the changeset discovery protocol in isolation
890 debugextensions
890 debugextensions
891 show information about active extensions
891 show information about active extensions
892 debugfileset parse and apply a fileset specification
892 debugfileset parse and apply a fileset specification
893 debugfsinfo show information detected about current filesystem
893 debugfsinfo show information detected about current filesystem
894 debuggetbundle
894 debuggetbundle
895 retrieves a bundle from a repo
895 retrieves a bundle from a repo
896 debugignore display the combined ignore pattern and information about
896 debugignore display the combined ignore pattern and information about
897 ignored files
897 ignored files
898 debugindex dump the contents of an index file
898 debugindex dump the contents of an index file
899 debugindexdot
899 debugindexdot
900 dump an index DAG as a graphviz dot file
900 dump an index DAG as a graphviz dot file
901 debuginstall test Mercurial installation
901 debuginstall test Mercurial installation
902 debugknown test whether node ids are known to a repo
902 debugknown test whether node ids are known to a repo
903 debuglocks show or modify state of locks
903 debuglocks show or modify state of locks
904 debugmergestate
904 debugmergestate
905 print merge state
905 print merge state
906 debugnamecomplete
906 debugnamecomplete
907 complete "names" - tags, open branch names, bookmark names
907 complete "names" - tags, open branch names, bookmark names
908 debugobsolete
908 debugobsolete
909 create arbitrary obsolete marker
909 create arbitrary obsolete marker
910 debugoptADV (no help text available)
910 debugoptADV (no help text available)
911 debugoptDEP (no help text available)
911 debugoptDEP (no help text available)
912 debugoptEXP (no help text available)
912 debugoptEXP (no help text available)
913 debugpathcomplete
913 debugpathcomplete
914 complete part or all of a tracked path
914 complete part or all of a tracked path
915 debugpushkey access the pushkey key/value protocol
915 debugpushkey access the pushkey key/value protocol
916 debugpvec (no help text available)
916 debugpvec (no help text available)
917 debugrebuilddirstate
917 debugrebuilddirstate
918 rebuild the dirstate as it would look like for the given
918 rebuild the dirstate as it would look like for the given
919 revision
919 revision
920 debugrebuildfncache
920 debugrebuildfncache
921 rebuild the fncache file
921 rebuild the fncache file
922 debugrename dump rename information
922 debugrename dump rename information
923 debugrevlog show data and statistics about a revlog
923 debugrevlog show data and statistics about a revlog
924 debugrevspec parse and apply a revision specification
924 debugrevspec parse and apply a revision specification
925 debugsetparents
925 debugsetparents
926 manually set the parents of the current working directory
926 manually set the parents of the current working directory
927 debugsub (no help text available)
927 debugsub (no help text available)
928 debugsuccessorssets
928 debugsuccessorssets
929 show set of successors for revision
929 show set of successors for revision
930 debugtemplate
930 debugtemplate
931 parse and apply a template
931 parse and apply a template
932 debugupgraderepo
932 debugupgraderepo
933 upgrade a repository to use different features
933 upgrade a repository to use different features
934 debugwalk show how files match on given patterns
934 debugwalk show how files match on given patterns
935 debugwireargs
935 debugwireargs
936 (no help text available)
936 (no help text available)
937
937
938 (use 'hg help -v debug' to show built-in aliases and global options)
938 (use 'hg help -v debug' to show built-in aliases and global options)
939
939
940 internals topic renders index of available sub-topics
940 internals topic renders index of available sub-topics
941
941
942 $ hg help internals
942 $ hg help internals
943 Technical implementation topics
943 Technical implementation topics
944 """""""""""""""""""""""""""""""
944 """""""""""""""""""""""""""""""
945
945
946 To access a subtopic, use "hg help internals.{subtopic-name}"
946 To access a subtopic, use "hg help internals.{subtopic-name}"
947
947
948 bundles Bundles
948 bundles Bundles
949 censor Censor
949 censor Censor
950 changegroups Changegroups
950 changegroups Changegroups
951 requirements Repository Requirements
951 requirements Repository Requirements
952 revlogs Revision Logs
952 revlogs Revision Logs
953 wireprotocol Wire Protocol
953 wireprotocol Wire Protocol
954
954
955 sub-topics can be accessed
955 sub-topics can be accessed
956
956
957 $ hg help internals.changegroups
957 $ hg help internals.changegroups
958 Changegroups
958 Changegroups
959 """"""""""""
959 """"""""""""
960
960
961 Changegroups are representations of repository revlog data, specifically
961 Changegroups are representations of repository revlog data, specifically
962 the changelog data, root/flat manifest data, treemanifest data, and
962 the changelog data, root/flat manifest data, treemanifest data, and
963 filelogs.
963 filelogs.
964
964
965 There are 3 versions of changegroups: "1", "2", and "3". From a high-
965 There are 3 versions of changegroups: "1", "2", and "3". From a high-
966 level, versions "1" and "2" are almost exactly the same, with the only
966 level, versions "1" and "2" are almost exactly the same, with the only
967 difference being an additional item in the *delta header*. Version "3"
967 difference being an additional item in the *delta header*. Version "3"
968 adds support for revlog flags in the *delta header* and optionally
968 adds support for revlog flags in the *delta header* and optionally
969 exchanging treemanifests (enabled by setting an option on the
969 exchanging treemanifests (enabled by setting an option on the
970 "changegroup" part in the bundle2).
970 "changegroup" part in the bundle2).
971
971
972 Changegroups when not exchanging treemanifests consist of 3 logical
972 Changegroups when not exchanging treemanifests consist of 3 logical
973 segments:
973 segments:
974
974
975 +---------------------------------+
975 +---------------------------------+
976 | | | |
976 | | | |
977 | changeset | manifest | filelogs |
977 | changeset | manifest | filelogs |
978 | | | |
978 | | | |
979 | | | |
979 | | | |
980 +---------------------------------+
980 +---------------------------------+
981
981
982 When exchanging treemanifests, there are 4 logical segments:
982 When exchanging treemanifests, there are 4 logical segments:
983
983
984 +-------------------------------------------------+
984 +-------------------------------------------------+
985 | | | | |
985 | | | | |
986 | changeset | root | treemanifests | filelogs |
986 | changeset | root | treemanifests | filelogs |
987 | | manifest | | |
987 | | manifest | | |
988 | | | | |
988 | | | | |
989 +-------------------------------------------------+
989 +-------------------------------------------------+
990
990
991 The principle building block of each segment is a *chunk*. A *chunk* is a
991 The principle building block of each segment is a *chunk*. A *chunk* is a
992 framed piece of data:
992 framed piece of data:
993
993
994 +---------------------------------------+
994 +---------------------------------------+
995 | | |
995 | | |
996 | length | data |
996 | length | data |
997 | (4 bytes) | (<length - 4> bytes) |
997 | (4 bytes) | (<length - 4> bytes) |
998 | | |
998 | | |
999 +---------------------------------------+
999 +---------------------------------------+
1000
1000
1001 All integers are big-endian signed integers. Each chunk starts with a
1001 All integers are big-endian signed integers. Each chunk starts with a
1002 32-bit integer indicating the length of the entire chunk (including the
1002 32-bit integer indicating the length of the entire chunk (including the
1003 length field itself).
1003 length field itself).
1004
1004
1005 There is a special case chunk that has a value of 0 for the length
1005 There is a special case chunk that has a value of 0 for the length
1006 ("0x00000000"). We call this an *empty chunk*.
1006 ("0x00000000"). We call this an *empty chunk*.
1007
1007
1008 Delta Groups
1008 Delta Groups
1009 ============
1009 ============
1010
1010
1011 A *delta group* expresses the content of a revlog as a series of deltas,
1011 A *delta group* expresses the content of a revlog as a series of deltas,
1012 or patches against previous revisions.
1012 or patches against previous revisions.
1013
1013
1014 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1014 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1015 to signal the end of the delta group:
1015 to signal the end of the delta group:
1016
1016
1017 +------------------------------------------------------------------------+
1017 +------------------------------------------------------------------------+
1018 | | | | | |
1018 | | | | | |
1019 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1019 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1020 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1020 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1021 | | | | | |
1021 | | | | | |
1022 +------------------------------------------------------------------------+
1022 +------------------------------------------------------------------------+
1023
1023
1024 Each *chunk*'s data consists of the following:
1024 Each *chunk*'s data consists of the following:
1025
1025
1026 +---------------------------------------+
1026 +---------------------------------------+
1027 | | |
1027 | | |
1028 | delta header | delta data |
1028 | delta header | delta data |
1029 | (various by version) | (various) |
1029 | (various by version) | (various) |
1030 | | |
1030 | | |
1031 +---------------------------------------+
1031 +---------------------------------------+
1032
1032
1033 The *delta data* is a series of *delta*s that describe a diff from an
1033 The *delta data* is a series of *delta*s that describe a diff from an
1034 existing entry (either that the recipient already has, or previously
1034 existing entry (either that the recipient already has, or previously
1035 specified in the bundle/changegroup).
1035 specified in the bundle/changegroup).
1036
1036
1037 The *delta header* is different between versions "1", "2", and "3" of the
1037 The *delta header* is different between versions "1", "2", and "3" of the
1038 changegroup format.
1038 changegroup format.
1039
1039
1040 Version 1 (headerlen=80):
1040 Version 1 (headerlen=80):
1041
1041
1042 +------------------------------------------------------+
1042 +------------------------------------------------------+
1043 | | | | |
1043 | | | | |
1044 | node | p1 node | p2 node | link node |
1044 | node | p1 node | p2 node | link node |
1045 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1045 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1046 | | | | |
1046 | | | | |
1047 +------------------------------------------------------+
1047 +------------------------------------------------------+
1048
1048
1049 Version 2 (headerlen=100):
1049 Version 2 (headerlen=100):
1050
1050
1051 +------------------------------------------------------------------+
1051 +------------------------------------------------------------------+
1052 | | | | | |
1052 | | | | | |
1053 | node | p1 node | p2 node | base node | link node |
1053 | node | p1 node | p2 node | base node | link node |
1054 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1054 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1055 | | | | | |
1055 | | | | | |
1056 +------------------------------------------------------------------+
1056 +------------------------------------------------------------------+
1057
1057
1058 Version 3 (headerlen=102):
1058 Version 3 (headerlen=102):
1059
1059
1060 +------------------------------------------------------------------------------+
1060 +------------------------------------------------------------------------------+
1061 | | | | | | |
1061 | | | | | | |
1062 | node | p1 node | p2 node | base node | link node | flags |
1062 | node | p1 node | p2 node | base node | link node | flags |
1063 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1063 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1064 | | | | | | |
1064 | | | | | | |
1065 +------------------------------------------------------------------------------+
1065 +------------------------------------------------------------------------------+
1066
1066
1067 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1067 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1068 contain a series of *delta*s, densely packed (no separators). These deltas
1068 contain a series of *delta*s, densely packed (no separators). These deltas
1069 describe a diff from an existing entry (either that the recipient already
1069 describe a diff from an existing entry (either that the recipient already
1070 has, or previously specified in the bundle/changegroup). The format is
1070 has, or previously specified in the bundle/changegroup). The format is
1071 described more fully in "hg help internals.bdiff", but briefly:
1071 described more fully in "hg help internals.bdiff", but briefly:
1072
1072
1073 +---------------------------------------------------------------+
1073 +---------------------------------------------------------------+
1074 | | | | |
1074 | | | | |
1075 | start offset | end offset | new length | content |
1075 | start offset | end offset | new length | content |
1076 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1076 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1077 | | | | |
1077 | | | | |
1078 +---------------------------------------------------------------+
1078 +---------------------------------------------------------------+
1079
1079
1080 Please note that the length field in the delta data does *not* include
1080 Please note that the length field in the delta data does *not* include
1081 itself.
1081 itself.
1082
1082
1083 In version 1, the delta is always applied against the previous node from
1083 In version 1, the delta is always applied against the previous node from
1084 the changegroup or the first parent if this is the first entry in the
1084 the changegroup or the first parent if this is the first entry in the
1085 changegroup.
1085 changegroup.
1086
1086
1087 In version 2 and up, the delta base node is encoded in the entry in the
1087 In version 2 and up, the delta base node is encoded in the entry in the
1088 changegroup. This allows the delta to be expressed against any parent,
1088 changegroup. This allows the delta to be expressed against any parent,
1089 which can result in smaller deltas and more efficient encoding of data.
1089 which can result in smaller deltas and more efficient encoding of data.
1090
1090
1091 Changeset Segment
1091 Changeset Segment
1092 =================
1092 =================
1093
1093
1094 The *changeset segment* consists of a single *delta group* holding
1094 The *changeset segment* consists of a single *delta group* holding
1095 changelog data. The *empty chunk* at the end of the *delta group* denotes
1095 changelog data. The *empty chunk* at the end of the *delta group* denotes
1096 the boundary to the *manifest segment*.
1096 the boundary to the *manifest segment*.
1097
1097
1098 Manifest Segment
1098 Manifest Segment
1099 ================
1099 ================
1100
1100
1101 The *manifest segment* consists of a single *delta group* holding manifest
1101 The *manifest segment* consists of a single *delta group* holding manifest
1102 data. If treemanifests are in use, it contains only the manifest for the
1102 data. If treemanifests are in use, it contains only the manifest for the
1103 root directory of the repository. Otherwise, it contains the entire
1103 root directory of the repository. Otherwise, it contains the entire
1104 manifest data. The *empty chunk* at the end of the *delta group* denotes
1104 manifest data. The *empty chunk* at the end of the *delta group* denotes
1105 the boundary to the next segment (either the *treemanifests segment* or
1105 the boundary to the next segment (either the *treemanifests segment* or
1106 the *filelogs segment*, depending on version and the request options).
1106 the *filelogs segment*, depending on version and the request options).
1107
1107
1108 Treemanifests Segment
1108 Treemanifests Segment
1109 ---------------------
1109 ---------------------
1110
1110
1111 The *treemanifests segment* only exists in changegroup version "3", and
1111 The *treemanifests segment* only exists in changegroup version "3", and
1112 only if the 'treemanifest' param is part of the bundle2 changegroup part
1112 only if the 'treemanifest' param is part of the bundle2 changegroup part
1113 (it is not possible to use changegroup version 3 outside of bundle2).
1113 (it is not possible to use changegroup version 3 outside of bundle2).
1114 Aside from the filenames in the *treemanifests segment* containing a
1114 Aside from the filenames in the *treemanifests segment* containing a
1115 trailing "/" character, it behaves identically to the *filelogs segment*
1115 trailing "/" character, it behaves identically to the *filelogs segment*
1116 (see below). The final sub-segment is followed by an *empty chunk*
1116 (see below). The final sub-segment is followed by an *empty chunk*
1117 (logically, a sub-segment with filename size 0). This denotes the boundary
1117 (logically, a sub-segment with filename size 0). This denotes the boundary
1118 to the *filelogs segment*.
1118 to the *filelogs segment*.
1119
1119
1120 Filelogs Segment
1120 Filelogs Segment
1121 ================
1121 ================
1122
1122
1123 The *filelogs segment* consists of multiple sub-segments, each
1123 The *filelogs segment* consists of multiple sub-segments, each
1124 corresponding to an individual file whose data is being described:
1124 corresponding to an individual file whose data is being described:
1125
1125
1126 +--------------------------------------------------+
1126 +--------------------------------------------------+
1127 | | | | | |
1127 | | | | | |
1128 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1128 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1129 | | | | | (4 bytes) |
1129 | | | | | (4 bytes) |
1130 | | | | | |
1130 | | | | | |
1131 +--------------------------------------------------+
1131 +--------------------------------------------------+
1132
1132
1133 The final filelog sub-segment is followed by an *empty chunk* (logically,
1133 The final filelog sub-segment is followed by an *empty chunk* (logically,
1134 a sub-segment with filename size 0). This denotes the end of the segment
1134 a sub-segment with filename size 0). This denotes the end of the segment
1135 and of the overall changegroup.
1135 and of the overall changegroup.
1136
1136
1137 Each filelog sub-segment consists of the following:
1137 Each filelog sub-segment consists of the following:
1138
1138
1139 +------------------------------------------------------+
1139 +------------------------------------------------------+
1140 | | | |
1140 | | | |
1141 | filename length | filename | delta group |
1141 | filename length | filename | delta group |
1142 | (4 bytes) | (<length - 4> bytes) | (various) |
1142 | (4 bytes) | (<length - 4> bytes) | (various) |
1143 | | | |
1143 | | | |
1144 +------------------------------------------------------+
1144 +------------------------------------------------------+
1145
1145
1146 That is, a *chunk* consisting of the filename (not terminated or padded)
1146 That is, a *chunk* consisting of the filename (not terminated or padded)
1147 followed by N chunks constituting the *delta group* for this file. The
1147 followed by N chunks constituting the *delta group* for this file. The
1148 *empty chunk* at the end of each *delta group* denotes the boundary to the
1148 *empty chunk* at the end of each *delta group* denotes the boundary to the
1149 next filelog sub-segment.
1149 next filelog sub-segment.
1150
1150
1151 Test list of commands with command with no help text
1151 Test list of commands with command with no help text
1152
1152
1153 $ hg help helpext
1153 $ hg help helpext
1154 helpext extension - no help text available
1154 helpext extension - no help text available
1155
1155
1156 list of commands:
1156 list of commands:
1157
1157
1158 nohelp (no help text available)
1158 nohelp (no help text available)
1159
1159
1160 (use 'hg help -v helpext' to show built-in aliases and global options)
1160 (use 'hg help -v helpext' to show built-in aliases and global options)
1161
1161
1162
1162
1163 test advanced, deprecated and experimental options are hidden in command help
1163 test advanced, deprecated and experimental options are hidden in command help
1164 $ hg help debugoptADV
1164 $ hg help debugoptADV
1165 hg debugoptADV
1165 hg debugoptADV
1166
1166
1167 (no help text available)
1167 (no help text available)
1168
1168
1169 options:
1169 options:
1170
1170
1171 (some details hidden, use --verbose to show complete help)
1171 (some details hidden, use --verbose to show complete help)
1172 $ hg help debugoptDEP
1172 $ hg help debugoptDEP
1173 hg debugoptDEP
1173 hg debugoptDEP
1174
1174
1175 (no help text available)
1175 (no help text available)
1176
1176
1177 options:
1177 options:
1178
1178
1179 (some details hidden, use --verbose to show complete help)
1179 (some details hidden, use --verbose to show complete help)
1180
1180
1181 $ hg help debugoptEXP
1181 $ hg help debugoptEXP
1182 hg debugoptEXP
1182 hg debugoptEXP
1183
1183
1184 (no help text available)
1184 (no help text available)
1185
1185
1186 options:
1186 options:
1187
1187
1188 (some details hidden, use --verbose to show complete help)
1188 (some details hidden, use --verbose to show complete help)
1189
1189
1190 test advanced, deprecated and experimental options are shown with -v
1190 test advanced, deprecated and experimental options are shown with -v
1191 $ hg help -v debugoptADV | grep aopt
1191 $ hg help -v debugoptADV | grep aopt
1192 --aopt option is (ADVANCED)
1192 --aopt option is (ADVANCED)
1193 $ hg help -v debugoptDEP | grep dopt
1193 $ hg help -v debugoptDEP | grep dopt
1194 --dopt option is (DEPRECATED)
1194 --dopt option is (DEPRECATED)
1195 $ hg help -v debugoptEXP | grep eopt
1195 $ hg help -v debugoptEXP | grep eopt
1196 --eopt option is (EXPERIMENTAL)
1196 --eopt option is (EXPERIMENTAL)
1197
1197
1198 #if gettext
1198 #if gettext
1199 test deprecated option is hidden with translation with untranslated description
1199 test deprecated option is hidden with translation with untranslated description
1200 (use many globy for not failing on changed transaction)
1200 (use many globy for not failing on changed transaction)
1201 $ LANGUAGE=sv hg help debugoptDEP
1201 $ LANGUAGE=sv hg help debugoptDEP
1202 hg debugoptDEP
1202 hg debugoptDEP
1203
1203
1204 (*) (glob)
1204 (*) (glob)
1205
1205
1206 options:
1206 options:
1207
1207
1208 (some details hidden, use --verbose to show complete help)
1208 (some details hidden, use --verbose to show complete help)
1209 #endif
1209 #endif
1210
1210
1211 Test commands that collide with topics (issue4240)
1211 Test commands that collide with topics (issue4240)
1212
1212
1213 $ hg config -hq
1213 $ hg config -hq
1214 hg config [-u] [NAME]...
1214 hg config [-u] [NAME]...
1215
1215
1216 show combined config settings from all hgrc files
1216 show combined config settings from all hgrc files
1217 $ hg showconfig -hq
1217 $ hg showconfig -hq
1218 hg config [-u] [NAME]...
1218 hg config [-u] [NAME]...
1219
1219
1220 show combined config settings from all hgrc files
1220 show combined config settings from all hgrc files
1221
1221
1222 Test a help topic
1222 Test a help topic
1223
1223
1224 $ hg help dates
1224 $ hg help dates
1225 Date Formats
1225 Date Formats
1226 """"""""""""
1226 """"""""""""
1227
1227
1228 Some commands allow the user to specify a date, e.g.:
1228 Some commands allow the user to specify a date, e.g.:
1229
1229
1230 - backout, commit, import, tag: Specify the commit date.
1230 - backout, commit, import, tag: Specify the commit date.
1231 - log, revert, update: Select revision(s) by date.
1231 - log, revert, update: Select revision(s) by date.
1232
1232
1233 Many date formats are valid. Here are some examples:
1233 Many date formats are valid. Here are some examples:
1234
1234
1235 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1235 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1236 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1236 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1237 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1237 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1238 - "Dec 6" (midnight)
1238 - "Dec 6" (midnight)
1239 - "13:18" (today assumed)
1239 - "13:18" (today assumed)
1240 - "3:39" (3:39AM assumed)
1240 - "3:39" (3:39AM assumed)
1241 - "3:39pm" (15:39)
1241 - "3:39pm" (15:39)
1242 - "2006-12-06 13:18:29" (ISO 8601 format)
1242 - "2006-12-06 13:18:29" (ISO 8601 format)
1243 - "2006-12-6 13:18"
1243 - "2006-12-6 13:18"
1244 - "2006-12-6"
1244 - "2006-12-6"
1245 - "12-6"
1245 - "12-6"
1246 - "12/6"
1246 - "12/6"
1247 - "12/6/6" (Dec 6 2006)
1247 - "12/6/6" (Dec 6 2006)
1248 - "today" (midnight)
1248 - "today" (midnight)
1249 - "yesterday" (midnight)
1249 - "yesterday" (midnight)
1250 - "now" - right now
1250 - "now" - right now
1251
1251
1252 Lastly, there is Mercurial's internal format:
1252 Lastly, there is Mercurial's internal format:
1253
1253
1254 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1254 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1255
1255
1256 This is the internal representation format for dates. The first number is
1256 This is the internal representation format for dates. The first number is
1257 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1257 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1258 is the offset of the local timezone, in seconds west of UTC (negative if
1258 is the offset of the local timezone, in seconds west of UTC (negative if
1259 the timezone is east of UTC).
1259 the timezone is east of UTC).
1260
1260
1261 The log command also accepts date ranges:
1261 The log command also accepts date ranges:
1262
1262
1263 - "<DATE" - at or before a given date/time
1263 - "<DATE" - at or before a given date/time
1264 - ">DATE" - on or after a given date/time
1264 - ">DATE" - on or after a given date/time
1265 - "DATE to DATE" - a date range, inclusive
1265 - "DATE to DATE" - a date range, inclusive
1266 - "-DAYS" - within a given number of days of today
1266 - "-DAYS" - within a given number of days of today
1267
1267
1268 Test repeated config section name
1268 Test repeated config section name
1269
1269
1270 $ hg help config.host
1270 $ hg help config.host
1271 "http_proxy.host"
1271 "http_proxy.host"
1272 Host name and (optional) port of the proxy server, for example
1272 Host name and (optional) port of the proxy server, for example
1273 "myproxy:8000".
1273 "myproxy:8000".
1274
1274
1275 "smtp.host"
1275 "smtp.host"
1276 Host name of mail server, e.g. "mail.example.com".
1276 Host name of mail server, e.g. "mail.example.com".
1277
1277
1278 Unrelated trailing paragraphs shouldn't be included
1278 Unrelated trailing paragraphs shouldn't be included
1279
1279
1280 $ hg help config.extramsg | grep '^$'
1280 $ hg help config.extramsg | grep '^$'
1281
1281
1282
1282
1283 Test capitalized section name
1283 Test capitalized section name
1284
1284
1285 $ hg help scripting.HGPLAIN > /dev/null
1285 $ hg help scripting.HGPLAIN > /dev/null
1286
1286
1287 Help subsection:
1287 Help subsection:
1288
1288
1289 $ hg help config.charsets |grep "Email example:" > /dev/null
1289 $ hg help config.charsets |grep "Email example:" > /dev/null
1290 [1]
1290 [1]
1291
1291
1292 Show nested definitions
1292 Show nested definitions
1293 ("profiling.type"[break]"ls"[break]"stat"[break])
1293 ("profiling.type"[break]"ls"[break]"stat"[break])
1294
1294
1295 $ hg help config.type | egrep '^$'|wc -l
1295 $ hg help config.type | egrep '^$'|wc -l
1296 \s*3 (re)
1296 \s*3 (re)
1297
1297
1298 Separate sections from subsections
1298 Separate sections from subsections
1299
1299
1300 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1300 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1301 "format"
1301 "format"
1302 --------
1302 --------
1303
1303
1304 "usegeneraldelta"
1304 "usegeneraldelta"
1305
1305
1306 "dotencode"
1306 "dotencode"
1307
1307
1308 "usefncache"
1308 "usefncache"
1309
1309
1310 "usestore"
1310 "usestore"
1311
1311
1312 "profiling"
1312 "profiling"
1313 -----------
1313 -----------
1314
1314
1315 "format"
1315 "format"
1316
1316
1317 "progress"
1317 "progress"
1318 ----------
1318 ----------
1319
1319
1320 "format"
1320 "format"
1321
1321
1322
1322
1323 Last item in help config.*:
1323 Last item in help config.*:
1324
1324
1325 $ hg help config.`hg help config|grep '^ "'| \
1325 $ hg help config.`hg help config|grep '^ "'| \
1326 > tail -1|sed 's![ "]*!!g'`| \
1326 > tail -1|sed 's![ "]*!!g'`| \
1327 > grep 'hg help -c config' > /dev/null
1327 > grep 'hg help -c config' > /dev/null
1328 [1]
1328 [1]
1329
1329
1330 note to use help -c for general hg help config:
1330 note to use help -c for general hg help config:
1331
1331
1332 $ hg help config |grep 'hg help -c config' > /dev/null
1332 $ hg help config |grep 'hg help -c config' > /dev/null
1333
1333
1334 Test templating help
1334 Test templating help
1335
1335
1336 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1336 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1337 desc String. The text of the changeset description.
1337 desc String. The text of the changeset description.
1338 diffstat String. Statistics of changes with the following format:
1338 diffstat String. Statistics of changes with the following format:
1339 firstline Any text. Returns the first line of text.
1339 firstline Any text. Returns the first line of text.
1340 nonempty Any text. Returns '(none)' if the string is empty.
1340 nonempty Any text. Returns '(none)' if the string is empty.
1341
1341
1342 Test deprecated items
1342 Test deprecated items
1343
1343
1344 $ hg help -v templating | grep currentbookmark
1344 $ hg help -v templating | grep currentbookmark
1345 currentbookmark
1345 currentbookmark
1346 $ hg help templating | (grep currentbookmark || true)
1346 $ hg help templating | (grep currentbookmark || true)
1347
1347
1348 Test help hooks
1348 Test help hooks
1349
1349
1350 $ cat > helphook1.py <<EOF
1350 $ cat > helphook1.py <<EOF
1351 > from mercurial import help
1351 > from mercurial import help
1352 >
1352 >
1353 > def rewrite(ui, topic, doc):
1353 > def rewrite(ui, topic, doc):
1354 > return doc + '\nhelphook1\n'
1354 > return doc + '\nhelphook1\n'
1355 >
1355 >
1356 > def extsetup(ui):
1356 > def extsetup(ui):
1357 > help.addtopichook('revisions', rewrite)
1357 > help.addtopichook('revisions', rewrite)
1358 > EOF
1358 > EOF
1359 $ cat > helphook2.py <<EOF
1359 $ cat > helphook2.py <<EOF
1360 > from mercurial import help
1360 > from mercurial import help
1361 >
1361 >
1362 > def rewrite(ui, topic, doc):
1362 > def rewrite(ui, topic, doc):
1363 > return doc + '\nhelphook2\n'
1363 > return doc + '\nhelphook2\n'
1364 >
1364 >
1365 > def extsetup(ui):
1365 > def extsetup(ui):
1366 > help.addtopichook('revisions', rewrite)
1366 > help.addtopichook('revisions', rewrite)
1367 > EOF
1367 > EOF
1368 $ echo '[extensions]' >> $HGRCPATH
1368 $ echo '[extensions]' >> $HGRCPATH
1369 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1369 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1370 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1370 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1371 $ hg help revsets | grep helphook
1371 $ hg help revsets | grep helphook
1372 helphook1
1372 helphook1
1373 helphook2
1373 helphook2
1374
1374
1375 help -c should only show debug --debug
1375 help -c should only show debug --debug
1376
1376
1377 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1377 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1378 [1]
1378 [1]
1379
1379
1380 help -c should only show deprecated for -v
1380 help -c should only show deprecated for -v
1381
1381
1382 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1382 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1383 [1]
1383 [1]
1384
1384
1385 Test -s / --system
1385 Test -s / --system
1386
1386
1387 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1387 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1388 > wc -l | sed -e 's/ //g'
1388 > wc -l | sed -e 's/ //g'
1389 0
1389 0
1390 $ hg help config.files --system unix | grep 'USER' | \
1390 $ hg help config.files --system unix | grep 'USER' | \
1391 > wc -l | sed -e 's/ //g'
1391 > wc -l | sed -e 's/ //g'
1392 0
1392 0
1393
1393
1394 Test -e / -c / -k combinations
1394 Test -e / -c / -k combinations
1395
1395
1396 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1396 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1397 Commands:
1397 Commands:
1398 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1398 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1399 Extensions:
1399 Extensions:
1400 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1400 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1401 Topics:
1401 Topics:
1402 Commands:
1402 Commands:
1403 Extensions:
1403 Extensions:
1404 Extension Commands:
1404 Extension Commands:
1405 $ hg help -c schemes
1405 $ hg help -c schemes
1406 abort: no such help topic: schemes
1406 abort: no such help topic: schemes
1407 (try 'hg help --keyword schemes')
1407 (try 'hg help --keyword schemes')
1408 [255]
1408 [255]
1409 $ hg help -e schemes |head -1
1409 $ hg help -e schemes |head -1
1410 schemes extension - extend schemes with shortcuts to repository swarms
1410 schemes extension - extend schemes with shortcuts to repository swarms
1411 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1411 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1412 Commands:
1412 Commands:
1413 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1413 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1414 Extensions:
1414 Extensions:
1415 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1415 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1416 Extensions:
1416 Extensions:
1417 Commands:
1417 Commands:
1418 $ hg help -c commit > /dev/null
1418 $ hg help -c commit > /dev/null
1419 $ hg help -e -c commit > /dev/null
1419 $ hg help -e -c commit > /dev/null
1420 $ hg help -e commit > /dev/null
1420 $ hg help -e commit > /dev/null
1421 abort: no such help topic: commit
1421 abort: no such help topic: commit
1422 (try 'hg help --keyword commit')
1422 (try 'hg help --keyword commit')
1423 [255]
1423 [255]
1424
1424
1425 Test keyword search help
1425 Test keyword search help
1426
1426
1427 $ cat > prefixedname.py <<EOF
1427 $ cat > prefixedname.py <<EOF
1428 > '''matched against word "clone"
1428 > '''matched against word "clone"
1429 > '''
1429 > '''
1430 > EOF
1430 > EOF
1431 $ echo '[extensions]' >> $HGRCPATH
1431 $ echo '[extensions]' >> $HGRCPATH
1432 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1432 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1433 $ hg help -k clone
1433 $ hg help -k clone
1434 Topics:
1434 Topics:
1435
1435
1436 config Configuration Files
1436 config Configuration Files
1437 extensions Using Additional Features
1437 extensions Using Additional Features
1438 glossary Glossary
1438 glossary Glossary
1439 phases Working with Phases
1439 phases Working with Phases
1440 subrepos Subrepositories
1440 subrepos Subrepositories
1441 urls URL Paths
1441 urls URL Paths
1442
1442
1443 Commands:
1443 Commands:
1444
1444
1445 bookmarks create a new bookmark or list existing bookmarks
1445 bookmarks create a new bookmark or list existing bookmarks
1446 clone make a copy of an existing repository
1446 clone make a copy of an existing repository
1447 paths show aliases for remote repositories
1447 paths show aliases for remote repositories
1448 update update working directory (or switch revisions)
1448 update update working directory (or switch revisions)
1449
1449
1450 Extensions:
1450 Extensions:
1451
1451
1452 clonebundles advertise pre-generated bundles to seed clones
1452 clonebundles advertise pre-generated bundles to seed clones
1453 prefixedname matched against word "clone"
1453 prefixedname matched against word "clone"
1454 relink recreates hardlinks between repository clones
1454 relink recreates hardlinks between repository clones
1455
1455
1456 Extension Commands:
1456 Extension Commands:
1457
1457
1458 qclone clone main and patch repository at same time
1458 qclone clone main and patch repository at same time
1459
1459
1460 Test unfound topic
1460 Test unfound topic
1461
1461
1462 $ hg help nonexistingtopicthatwillneverexisteverever
1462 $ hg help nonexistingtopicthatwillneverexisteverever
1463 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1463 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1464 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1464 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1465 [255]
1465 [255]
1466
1466
1467 Test unfound keyword
1467 Test unfound keyword
1468
1468
1469 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1469 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1470 abort: no matches
1470 abort: no matches
1471 (try 'hg help' for a list of topics)
1471 (try 'hg help' for a list of topics)
1472 [255]
1472 [255]
1473
1473
1474 Test omit indicating for help
1474 Test omit indicating for help
1475
1475
1476 $ cat > addverboseitems.py <<EOF
1476 $ cat > addverboseitems.py <<EOF
1477 > '''extension to test omit indicating.
1477 > '''extension to test omit indicating.
1478 >
1478 >
1479 > This paragraph is never omitted (for extension)
1479 > This paragraph is never omitted (for extension)
1480 >
1480 >
1481 > .. container:: verbose
1481 > .. container:: verbose
1482 >
1482 >
1483 > This paragraph is omitted,
1483 > This paragraph is omitted,
1484 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1484 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1485 >
1485 >
1486 > This paragraph is never omitted, too (for extension)
1486 > This paragraph is never omitted, too (for extension)
1487 > '''
1487 > '''
1488 >
1488 >
1489 > from mercurial import help, commands
1489 > from mercurial import help, commands
1490 > testtopic = """This paragraph is never omitted (for topic).
1490 > testtopic = """This paragraph is never omitted (for topic).
1491 >
1491 >
1492 > .. container:: verbose
1492 > .. container:: verbose
1493 >
1493 >
1494 > This paragraph is omitted,
1494 > This paragraph is omitted,
1495 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1495 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1496 >
1496 >
1497 > This paragraph is never omitted, too (for topic)
1497 > This paragraph is never omitted, too (for topic)
1498 > """
1498 > """
1499 > def extsetup(ui):
1499 > def extsetup(ui):
1500 > help.helptable.append((["topic-containing-verbose"],
1500 > help.helptable.append((["topic-containing-verbose"],
1501 > "This is the topic to test omit indicating.",
1501 > "This is the topic to test omit indicating.",
1502 > lambda ui: testtopic))
1502 > lambda ui: testtopic))
1503 > EOF
1503 > EOF
1504 $ echo '[extensions]' >> $HGRCPATH
1504 $ echo '[extensions]' >> $HGRCPATH
1505 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1505 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1506 $ hg help addverboseitems
1506 $ hg help addverboseitems
1507 addverboseitems extension - extension to test omit indicating.
1507 addverboseitems extension - extension to test omit indicating.
1508
1508
1509 This paragraph is never omitted (for extension)
1509 This paragraph is never omitted (for extension)
1510
1510
1511 This paragraph is never omitted, too (for extension)
1511 This paragraph is never omitted, too (for extension)
1512
1512
1513 (some details hidden, use --verbose to show complete help)
1513 (some details hidden, use --verbose to show complete help)
1514
1514
1515 no commands defined
1515 no commands defined
1516 $ hg help -v addverboseitems
1516 $ hg help -v addverboseitems
1517 addverboseitems extension - extension to test omit indicating.
1517 addverboseitems extension - extension to test omit indicating.
1518
1518
1519 This paragraph is never omitted (for extension)
1519 This paragraph is never omitted (for extension)
1520
1520
1521 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1521 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1522 extension)
1522 extension)
1523
1523
1524 This paragraph is never omitted, too (for extension)
1524 This paragraph is never omitted, too (for extension)
1525
1525
1526 no commands defined
1526 no commands defined
1527 $ hg help topic-containing-verbose
1527 $ hg help topic-containing-verbose
1528 This is the topic to test omit indicating.
1528 This is the topic to test omit indicating.
1529 """"""""""""""""""""""""""""""""""""""""""
1529 """"""""""""""""""""""""""""""""""""""""""
1530
1530
1531 This paragraph is never omitted (for topic).
1531 This paragraph is never omitted (for topic).
1532
1532
1533 This paragraph is never omitted, too (for topic)
1533 This paragraph is never omitted, too (for topic)
1534
1534
1535 (some details hidden, use --verbose to show complete help)
1535 (some details hidden, use --verbose to show complete help)
1536 $ hg help -v topic-containing-verbose
1536 $ hg help -v topic-containing-verbose
1537 This is the topic to test omit indicating.
1537 This is the topic to test omit indicating.
1538 """"""""""""""""""""""""""""""""""""""""""
1538 """"""""""""""""""""""""""""""""""""""""""
1539
1539
1540 This paragraph is never omitted (for topic).
1540 This paragraph is never omitted (for topic).
1541
1541
1542 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1542 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1543 topic)
1543 topic)
1544
1544
1545 This paragraph is never omitted, too (for topic)
1545 This paragraph is never omitted, too (for topic)
1546
1546
1547 Test section lookup
1547 Test section lookup
1548
1548
1549 $ hg help revset.merge
1549 $ hg help revset.merge
1550 "merge()"
1550 "merge()"
1551 Changeset is a merge changeset.
1551 Changeset is a merge changeset.
1552
1552
1553 $ hg help glossary.dag
1553 $ hg help glossary.dag
1554 DAG
1554 DAG
1555 The repository of changesets of a distributed version control system
1555 The repository of changesets of a distributed version control system
1556 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1556 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1557 of nodes and edges, where nodes correspond to changesets and edges
1557 of nodes and edges, where nodes correspond to changesets and edges
1558 imply a parent -> child relation. This graph can be visualized by
1558 imply a parent -> child relation. This graph can be visualized by
1559 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1559 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1560 limited by the requirement for children to have at most two parents.
1560 limited by the requirement for children to have at most two parents.
1561
1561
1562
1562
1563 $ hg help hgrc.paths
1563 $ hg help hgrc.paths
1564 "paths"
1564 "paths"
1565 -------
1565 -------
1566
1566
1567 Assigns symbolic names and behavior to repositories.
1567 Assigns symbolic names and behavior to repositories.
1568
1568
1569 Options are symbolic names defining the URL or directory that is the
1569 Options are symbolic names defining the URL or directory that is the
1570 location of the repository. Example:
1570 location of the repository. Example:
1571
1571
1572 [paths]
1572 [paths]
1573 my_server = https://example.com/my_repo
1573 my_server = https://example.com/my_repo
1574 local_path = /home/me/repo
1574 local_path = /home/me/repo
1575
1575
1576 These symbolic names can be used from the command line. To pull from
1576 These symbolic names can be used from the command line. To pull from
1577 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1577 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1578 local_path'.
1578 local_path'.
1579
1579
1580 Options containing colons (":") denote sub-options that can influence
1580 Options containing colons (":") denote sub-options that can influence
1581 behavior for that specific path. Example:
1581 behavior for that specific path. Example:
1582
1582
1583 [paths]
1583 [paths]
1584 my_server = https://example.com/my_path
1584 my_server = https://example.com/my_path
1585 my_server:pushurl = ssh://example.com/my_path
1585 my_server:pushurl = ssh://example.com/my_path
1586
1586
1587 The following sub-options can be defined:
1587 The following sub-options can be defined:
1588
1588
1589 "pushurl"
1589 "pushurl"
1590 The URL to use for push operations. If not defined, the location
1590 The URL to use for push operations. If not defined, the location
1591 defined by the path's main entry is used.
1591 defined by the path's main entry is used.
1592
1592
1593 "pushrev"
1593 "pushrev"
1594 A revset defining which revisions to push by default.
1594 A revset defining which revisions to push by default.
1595
1595
1596 When 'hg push' is executed without a "-r" argument, the revset defined
1596 When 'hg push' is executed without a "-r" argument, the revset defined
1597 by this sub-option is evaluated to determine what to push.
1597 by this sub-option is evaluated to determine what to push.
1598
1598
1599 For example, a value of "." will push the working directory's revision
1599 For example, a value of "." will push the working directory's revision
1600 by default.
1600 by default.
1601
1601
1602 Revsets specifying bookmarks will not result in the bookmark being
1602 Revsets specifying bookmarks will not result in the bookmark being
1603 pushed.
1603 pushed.
1604
1604
1605 The following special named paths exist:
1605 The following special named paths exist:
1606
1606
1607 "default"
1607 "default"
1608 The URL or directory to use when no source or remote is specified.
1608 The URL or directory to use when no source or remote is specified.
1609
1609
1610 'hg clone' will automatically define this path to the location the
1610 'hg clone' will automatically define this path to the location the
1611 repository was cloned from.
1611 repository was cloned from.
1612
1612
1613 "default-push"
1613 "default-push"
1614 (deprecated) The URL or directory for the default 'hg push' location.
1614 (deprecated) The URL or directory for the default 'hg push' location.
1615 "default:pushurl" should be used instead.
1615 "default:pushurl" should be used instead.
1616
1616
1617 $ hg help glossary.mcguffin
1617 $ hg help glossary.mcguffin
1618 abort: help section not found: glossary.mcguffin
1618 abort: help section not found: glossary.mcguffin
1619 [255]
1619 [255]
1620
1620
1621 $ hg help glossary.mc.guffin
1621 $ hg help glossary.mc.guffin
1622 abort: help section not found: glossary.mc.guffin
1622 abort: help section not found: glossary.mc.guffin
1623 [255]
1623 [255]
1624
1624
1625 $ hg help template.files
1625 $ hg help template.files
1626 files List of strings. All files modified, added, or removed by
1626 files List of strings. All files modified, added, or removed by
1627 this changeset.
1627 this changeset.
1628 files(pattern)
1628 files(pattern)
1629 All files of the current changeset matching the pattern. See
1629 All files of the current changeset matching the pattern. See
1630 'hg help patterns'.
1630 'hg help patterns'.
1631
1631
1632 Test section lookup by translated message
1632 Test section lookup by translated message
1633
1633
1634 str.lower() instead of encoding.lower(str) on translated message might
1634 str.lower() instead of encoding.lower(str) on translated message might
1635 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1635 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1636 as the second or later byte of multi-byte character.
1636 as the second or later byte of multi-byte character.
1637
1637
1638 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1638 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1639 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1639 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1640 replacement makes message meaningless.
1640 replacement makes message meaningless.
1641
1641
1642 This tests that section lookup by translated string isn't broken by
1642 This tests that section lookup by translated string isn't broken by
1643 such str.lower().
1643 such str.lower().
1644
1644
1645 $ python <<EOF
1645 $ python <<EOF
1646 > def escape(s):
1646 > def escape(s):
1647 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1647 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1648 > # translation of "record" in ja_JP.cp932
1648 > # translation of "record" in ja_JP.cp932
1649 > upper = "\x8bL\x98^"
1649 > upper = "\x8bL\x98^"
1650 > # str.lower()-ed section name should be treated as different one
1650 > # str.lower()-ed section name should be treated as different one
1651 > lower = "\x8bl\x98^"
1651 > lower = "\x8bl\x98^"
1652 > with open('ambiguous.py', 'w') as fp:
1652 > with open('ambiguous.py', 'w') as fp:
1653 > fp.write("""# ambiguous section names in ja_JP.cp932
1653 > fp.write("""# ambiguous section names in ja_JP.cp932
1654 > u'''summary of extension
1654 > u'''summary of extension
1655 >
1655 >
1656 > %s
1656 > %s
1657 > ----
1657 > ----
1658 >
1658 >
1659 > Upper name should show only this message
1659 > Upper name should show only this message
1660 >
1660 >
1661 > %s
1661 > %s
1662 > ----
1662 > ----
1663 >
1663 >
1664 > Lower name should show only this message
1664 > Lower name should show only this message
1665 >
1665 >
1666 > subsequent section
1666 > subsequent section
1667 > ------------------
1667 > ------------------
1668 >
1668 >
1669 > This should be hidden at 'hg help ambiguous' with section name.
1669 > This should be hidden at 'hg help ambiguous' with section name.
1670 > '''
1670 > '''
1671 > """ % (escape(upper), escape(lower)))
1671 > """ % (escape(upper), escape(lower)))
1672 > EOF
1672 > EOF
1673
1673
1674 $ cat >> $HGRCPATH <<EOF
1674 $ cat >> $HGRCPATH <<EOF
1675 > [extensions]
1675 > [extensions]
1676 > ambiguous = ./ambiguous.py
1676 > ambiguous = ./ambiguous.py
1677 > EOF
1677 > EOF
1678
1678
1679 $ python <<EOF | sh
1679 $ python <<EOF | sh
1680 > upper = "\x8bL\x98^"
1680 > upper = "\x8bL\x98^"
1681 > print "hg --encoding cp932 help -e ambiguous.%s" % upper
1681 > print "hg --encoding cp932 help -e ambiguous.%s" % upper
1682 > EOF
1682 > EOF
1683 \x8bL\x98^ (esc)
1683 \x8bL\x98^ (esc)
1684 ----
1684 ----
1685
1685
1686 Upper name should show only this message
1686 Upper name should show only this message
1687
1687
1688
1688
1689 $ python <<EOF | sh
1689 $ python <<EOF | sh
1690 > lower = "\x8bl\x98^"
1690 > lower = "\x8bl\x98^"
1691 > print "hg --encoding cp932 help -e ambiguous.%s" % lower
1691 > print "hg --encoding cp932 help -e ambiguous.%s" % lower
1692 > EOF
1692 > EOF
1693 \x8bl\x98^ (esc)
1693 \x8bl\x98^ (esc)
1694 ----
1694 ----
1695
1695
1696 Lower name should show only this message
1696 Lower name should show only this message
1697
1697
1698
1698
1699 $ cat >> $HGRCPATH <<EOF
1699 $ cat >> $HGRCPATH <<EOF
1700 > [extensions]
1700 > [extensions]
1701 > ambiguous = !
1701 > ambiguous = !
1702 > EOF
1702 > EOF
1703
1703
1704 Show help content of disabled extensions
1704 Show help content of disabled extensions
1705
1705
1706 $ cat >> $HGRCPATH <<EOF
1706 $ cat >> $HGRCPATH <<EOF
1707 > [extensions]
1707 > [extensions]
1708 > ambiguous = !./ambiguous.py
1708 > ambiguous = !./ambiguous.py
1709 > EOF
1709 > EOF
1710 $ hg help -e ambiguous
1710 $ hg help -e ambiguous
1711 ambiguous extension - (no help text available)
1711 ambiguous extension - (no help text available)
1712
1712
1713 (use 'hg help extensions' for information on enabling extensions)
1713 (use 'hg help extensions' for information on enabling extensions)
1714
1714
1715 Test dynamic list of merge tools only shows up once
1715 Test dynamic list of merge tools only shows up once
1716 $ hg help merge-tools
1716 $ hg help merge-tools
1717 Merge Tools
1717 Merge Tools
1718 """""""""""
1718 """""""""""
1719
1719
1720 To merge files Mercurial uses merge tools.
1720 To merge files Mercurial uses merge tools.
1721
1721
1722 A merge tool combines two different versions of a file into a merged file.
1722 A merge tool combines two different versions of a file into a merged file.
1723 Merge tools are given the two files and the greatest common ancestor of
1723 Merge tools are given the two files and the greatest common ancestor of
1724 the two file versions, so they can determine the changes made on both
1724 the two file versions, so they can determine the changes made on both
1725 branches.
1725 branches.
1726
1726
1727 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1727 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1728 backout' and in several extensions.
1728 backout' and in several extensions.
1729
1729
1730 Usually, the merge tool tries to automatically reconcile the files by
1730 Usually, the merge tool tries to automatically reconcile the files by
1731 combining all non-overlapping changes that occurred separately in the two
1731 combining all non-overlapping changes that occurred separately in the two
1732 different evolutions of the same initial base file. Furthermore, some
1732 different evolutions of the same initial base file. Furthermore, some
1733 interactive merge programs make it easier to manually resolve conflicting
1733 interactive merge programs make it easier to manually resolve conflicting
1734 merges, either in a graphical way, or by inserting some conflict markers.
1734 merges, either in a graphical way, or by inserting some conflict markers.
1735 Mercurial does not include any interactive merge programs but relies on
1735 Mercurial does not include any interactive merge programs but relies on
1736 external tools for that.
1736 external tools for that.
1737
1737
1738 Available merge tools
1738 Available merge tools
1739 =====================
1739 =====================
1740
1740
1741 External merge tools and their properties are configured in the merge-
1741 External merge tools and their properties are configured in the merge-
1742 tools configuration section - see hgrc(5) - but they can often just be
1742 tools configuration section - see hgrc(5) - but they can often just be
1743 named by their executable.
1743 named by their executable.
1744
1744
1745 A merge tool is generally usable if its executable can be found on the
1745 A merge tool is generally usable if its executable can be found on the
1746 system and if it can handle the merge. The executable is found if it is an
1746 system and if it can handle the merge. The executable is found if it is an
1747 absolute or relative executable path or the name of an application in the
1747 absolute or relative executable path or the name of an application in the
1748 executable search path. The tool is assumed to be able to handle the merge
1748 executable search path. The tool is assumed to be able to handle the merge
1749 if it can handle symlinks if the file is a symlink, if it can handle
1749 if it can handle symlinks if the file is a symlink, if it can handle
1750 binary files if the file is binary, and if a GUI is available if the tool
1750 binary files if the file is binary, and if a GUI is available if the tool
1751 requires a GUI.
1751 requires a GUI.
1752
1752
1753 There are some internal merge tools which can be used. The internal merge
1753 There are some internal merge tools which can be used. The internal merge
1754 tools are:
1754 tools are:
1755
1755
1756 ":dump"
1756 ":dump"
1757 Creates three versions of the files to merge, containing the contents of
1757 Creates three versions of the files to merge, containing the contents of
1758 local, other and base. These files can then be used to perform a merge
1758 local, other and base. These files can then be used to perform a merge
1759 manually. If the file to be merged is named "a.txt", these files will
1759 manually. If the file to be merged is named "a.txt", these files will
1760 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1760 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1761 they will be placed in the same directory as "a.txt".
1761 they will be placed in the same directory as "a.txt".
1762
1762
1763 ":fail"
1763 ":fail"
1764 Rather than attempting to merge files that were modified on both
1764 Rather than attempting to merge files that were modified on both
1765 branches, it marks them as unresolved. The resolve command must be used
1765 branches, it marks them as unresolved. The resolve command must be used
1766 to resolve these conflicts.
1766 to resolve these conflicts.
1767
1767
1768 ":local"
1768 ":local"
1769 Uses the local 'p1()' version of files as the merged version.
1769 Uses the local 'p1()' version of files as the merged version.
1770
1770
1771 ":merge"
1771 ":merge"
1772 Uses the internal non-interactive simple merge algorithm for merging
1772 Uses the internal non-interactive simple merge algorithm for merging
1773 files. It will fail if there are any conflicts and leave markers in the
1773 files. It will fail if there are any conflicts and leave markers in the
1774 partially merged file. Markers will have two sections, one for each side
1774 partially merged file. Markers will have two sections, one for each side
1775 of merge.
1775 of merge.
1776
1776
1777 ":merge-local"
1777 ":merge-local"
1778 Like :merge, but resolve all conflicts non-interactively in favor of the
1778 Like :merge, but resolve all conflicts non-interactively in favor of the
1779 local 'p1()' changes.
1779 local 'p1()' changes.
1780
1780
1781 ":merge-other"
1781 ":merge-other"
1782 Like :merge, but resolve all conflicts non-interactively in favor of the
1782 Like :merge, but resolve all conflicts non-interactively in favor of the
1783 other 'p2()' changes.
1783 other 'p2()' changes.
1784
1784
1785 ":merge3"
1785 ":merge3"
1786 Uses the internal non-interactive simple merge algorithm for merging
1786 Uses the internal non-interactive simple merge algorithm for merging
1787 files. It will fail if there are any conflicts and leave markers in the
1787 files. It will fail if there are any conflicts and leave markers in the
1788 partially merged file. Marker will have three sections, one from each
1788 partially merged file. Marker will have three sections, one from each
1789 side of the merge and one for the base content.
1789 side of the merge and one for the base content.
1790
1790
1791 ":other"
1791 ":other"
1792 Uses the other 'p2()' version of files as the merged version.
1792 Uses the other 'p2()' version of files as the merged version.
1793
1793
1794 ":prompt"
1794 ":prompt"
1795 Asks the user which of the local 'p1()' or the other 'p2()' version to
1795 Asks the user which of the local 'p1()' or the other 'p2()' version to
1796 keep as the merged version.
1796 keep as the merged version.
1797
1797
1798 ":tagmerge"
1798 ":tagmerge"
1799 Uses the internal tag merge algorithm (experimental).
1799 Uses the internal tag merge algorithm (experimental).
1800
1800
1801 ":union"
1801 ":union"
1802 Uses the internal non-interactive simple merge algorithm for merging
1802 Uses the internal non-interactive simple merge algorithm for merging
1803 files. It will use both left and right sides for conflict regions. No
1803 files. It will use both left and right sides for conflict regions. No
1804 markers are inserted.
1804 markers are inserted.
1805
1805
1806 Internal tools are always available and do not require a GUI but will by
1806 Internal tools are always available and do not require a GUI but will by
1807 default not handle symlinks or binary files.
1807 default not handle symlinks or binary files.
1808
1808
1809 Choosing a merge tool
1809 Choosing a merge tool
1810 =====================
1810 =====================
1811
1811
1812 Mercurial uses these rules when deciding which merge tool to use:
1812 Mercurial uses these rules when deciding which merge tool to use:
1813
1813
1814 1. If a tool has been specified with the --tool option to merge or
1814 1. If a tool has been specified with the --tool option to merge or
1815 resolve, it is used. If it is the name of a tool in the merge-tools
1815 resolve, it is used. If it is the name of a tool in the merge-tools
1816 configuration, its configuration is used. Otherwise the specified tool
1816 configuration, its configuration is used. Otherwise the specified tool
1817 must be executable by the shell.
1817 must be executable by the shell.
1818 2. If the "HGMERGE" environment variable is present, its value is used and
1818 2. If the "HGMERGE" environment variable is present, its value is used and
1819 must be executable by the shell.
1819 must be executable by the shell.
1820 3. If the filename of the file to be merged matches any of the patterns in
1820 3. If the filename of the file to be merged matches any of the patterns in
1821 the merge-patterns configuration section, the first usable merge tool
1821 the merge-patterns configuration section, the first usable merge tool
1822 corresponding to a matching pattern is used. Here, binary capabilities
1822 corresponding to a matching pattern is used. Here, binary capabilities
1823 of the merge tool are not considered.
1823 of the merge tool are not considered.
1824 4. If ui.merge is set it will be considered next. If the value is not the
1824 4. If ui.merge is set it will be considered next. If the value is not the
1825 name of a configured tool, the specified value is used and must be
1825 name of a configured tool, the specified value is used and must be
1826 executable by the shell. Otherwise the named tool is used if it is
1826 executable by the shell. Otherwise the named tool is used if it is
1827 usable.
1827 usable.
1828 5. If any usable merge tools are present in the merge-tools configuration
1828 5. If any usable merge tools are present in the merge-tools configuration
1829 section, the one with the highest priority is used.
1829 section, the one with the highest priority is used.
1830 6. If a program named "hgmerge" can be found on the system, it is used -
1830 6. If a program named "hgmerge" can be found on the system, it is used -
1831 but it will by default not be used for symlinks and binary files.
1831 but it will by default not be used for symlinks and binary files.
1832 7. If the file to be merged is not binary and is not a symlink, then
1832 7. If the file to be merged is not binary and is not a symlink, then
1833 internal ":merge" is used.
1833 internal ":merge" is used.
1834 8. The merge of the file fails and must be resolved before commit.
1834 8. Otherwise, ":prompt" is used.
1835
1835
1836 Note:
1836 Note:
1837 After selecting a merge program, Mercurial will by default attempt to
1837 After selecting a merge program, Mercurial will by default attempt to
1838 merge the files using a simple merge algorithm first. Only if it
1838 merge the files using a simple merge algorithm first. Only if it
1839 doesn't succeed because of conflicting changes Mercurial will actually
1839 doesn't succeed because of conflicting changes Mercurial will actually
1840 execute the merge program. Whether to use the simple merge algorithm
1840 execute the merge program. Whether to use the simple merge algorithm
1841 first can be controlled by the premerge setting of the merge tool.
1841 first can be controlled by the premerge setting of the merge tool.
1842 Premerge is enabled by default unless the file is binary or a symlink.
1842 Premerge is enabled by default unless the file is binary or a symlink.
1843
1843
1844 See the merge-tools and ui sections of hgrc(5) for details on the
1844 See the merge-tools and ui sections of hgrc(5) for details on the
1845 configuration of merge tools.
1845 configuration of merge tools.
1846
1846
1847 Compression engines listed in `hg help bundlespec`
1847 Compression engines listed in `hg help bundlespec`
1848
1848
1849 $ hg help bundlespec | grep gzip
1849 $ hg help bundlespec | grep gzip
1850 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1850 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1851 An algorithm that produces smaller bundles than "gzip".
1851 An algorithm that produces smaller bundles than "gzip".
1852 This engine will likely produce smaller bundles than "gzip" but will be
1852 This engine will likely produce smaller bundles than "gzip" but will be
1853 "gzip"
1853 "gzip"
1854 better compression than "gzip". It also frequently yields better (?)
1854 better compression than "gzip". It also frequently yields better (?)
1855
1855
1856 Test usage of section marks in help documents
1856 Test usage of section marks in help documents
1857
1857
1858 $ cd "$TESTDIR"/../doc
1858 $ cd "$TESTDIR"/../doc
1859 $ python check-seclevel.py
1859 $ python check-seclevel.py
1860 $ cd $TESTTMP
1860 $ cd $TESTTMP
1861
1861
1862 #if serve
1862 #if serve
1863
1863
1864 Test the help pages in hgweb.
1864 Test the help pages in hgweb.
1865
1865
1866 Dish up an empty repo; serve it cold.
1866 Dish up an empty repo; serve it cold.
1867
1867
1868 $ hg init "$TESTTMP/test"
1868 $ hg init "$TESTTMP/test"
1869 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1869 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1870 $ cat hg.pid >> $DAEMON_PIDS
1870 $ cat hg.pid >> $DAEMON_PIDS
1871
1871
1872 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1872 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1873 200 Script output follows
1873 200 Script output follows
1874
1874
1875 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1875 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1876 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1876 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1877 <head>
1877 <head>
1878 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1878 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1879 <meta name="robots" content="index, nofollow" />
1879 <meta name="robots" content="index, nofollow" />
1880 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1880 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1881 <script type="text/javascript" src="/static/mercurial.js"></script>
1881 <script type="text/javascript" src="/static/mercurial.js"></script>
1882
1882
1883 <title>Help: Index</title>
1883 <title>Help: Index</title>
1884 </head>
1884 </head>
1885 <body>
1885 <body>
1886
1886
1887 <div class="container">
1887 <div class="container">
1888 <div class="menu">
1888 <div class="menu">
1889 <div class="logo">
1889 <div class="logo">
1890 <a href="https://mercurial-scm.org/">
1890 <a href="https://mercurial-scm.org/">
1891 <img src="/static/hglogo.png" alt="mercurial" /></a>
1891 <img src="/static/hglogo.png" alt="mercurial" /></a>
1892 </div>
1892 </div>
1893 <ul>
1893 <ul>
1894 <li><a href="/shortlog">log</a></li>
1894 <li><a href="/shortlog">log</a></li>
1895 <li><a href="/graph">graph</a></li>
1895 <li><a href="/graph">graph</a></li>
1896 <li><a href="/tags">tags</a></li>
1896 <li><a href="/tags">tags</a></li>
1897 <li><a href="/bookmarks">bookmarks</a></li>
1897 <li><a href="/bookmarks">bookmarks</a></li>
1898 <li><a href="/branches">branches</a></li>
1898 <li><a href="/branches">branches</a></li>
1899 </ul>
1899 </ul>
1900 <ul>
1900 <ul>
1901 <li class="active">help</li>
1901 <li class="active">help</li>
1902 </ul>
1902 </ul>
1903 </div>
1903 </div>
1904
1904
1905 <div class="main">
1905 <div class="main">
1906 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1906 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1907 <form class="search" action="/log">
1907 <form class="search" action="/log">
1908
1908
1909 <p><input name="rev" id="search1" type="text" size="30" /></p>
1909 <p><input name="rev" id="search1" type="text" size="30" /></p>
1910 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1910 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1911 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1911 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1912 </form>
1912 </form>
1913 <table class="bigtable">
1913 <table class="bigtable">
1914 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1914 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1915
1915
1916 <tr><td>
1916 <tr><td>
1917 <a href="/help/bundlespec">
1917 <a href="/help/bundlespec">
1918 bundlespec
1918 bundlespec
1919 </a>
1919 </a>
1920 </td><td>
1920 </td><td>
1921 Bundle File Formats
1921 Bundle File Formats
1922 </td></tr>
1922 </td></tr>
1923 <tr><td>
1923 <tr><td>
1924 <a href="/help/color">
1924 <a href="/help/color">
1925 color
1925 color
1926 </a>
1926 </a>
1927 </td><td>
1927 </td><td>
1928 Colorizing Outputs
1928 Colorizing Outputs
1929 </td></tr>
1929 </td></tr>
1930 <tr><td>
1930 <tr><td>
1931 <a href="/help/config">
1931 <a href="/help/config">
1932 config
1932 config
1933 </a>
1933 </a>
1934 </td><td>
1934 </td><td>
1935 Configuration Files
1935 Configuration Files
1936 </td></tr>
1936 </td></tr>
1937 <tr><td>
1937 <tr><td>
1938 <a href="/help/dates">
1938 <a href="/help/dates">
1939 dates
1939 dates
1940 </a>
1940 </a>
1941 </td><td>
1941 </td><td>
1942 Date Formats
1942 Date Formats
1943 </td></tr>
1943 </td></tr>
1944 <tr><td>
1944 <tr><td>
1945 <a href="/help/diffs">
1945 <a href="/help/diffs">
1946 diffs
1946 diffs
1947 </a>
1947 </a>
1948 </td><td>
1948 </td><td>
1949 Diff Formats
1949 Diff Formats
1950 </td></tr>
1950 </td></tr>
1951 <tr><td>
1951 <tr><td>
1952 <a href="/help/environment">
1952 <a href="/help/environment">
1953 environment
1953 environment
1954 </a>
1954 </a>
1955 </td><td>
1955 </td><td>
1956 Environment Variables
1956 Environment Variables
1957 </td></tr>
1957 </td></tr>
1958 <tr><td>
1958 <tr><td>
1959 <a href="/help/extensions">
1959 <a href="/help/extensions">
1960 extensions
1960 extensions
1961 </a>
1961 </a>
1962 </td><td>
1962 </td><td>
1963 Using Additional Features
1963 Using Additional Features
1964 </td></tr>
1964 </td></tr>
1965 <tr><td>
1965 <tr><td>
1966 <a href="/help/filesets">
1966 <a href="/help/filesets">
1967 filesets
1967 filesets
1968 </a>
1968 </a>
1969 </td><td>
1969 </td><td>
1970 Specifying File Sets
1970 Specifying File Sets
1971 </td></tr>
1971 </td></tr>
1972 <tr><td>
1972 <tr><td>
1973 <a href="/help/glossary">
1973 <a href="/help/glossary">
1974 glossary
1974 glossary
1975 </a>
1975 </a>
1976 </td><td>
1976 </td><td>
1977 Glossary
1977 Glossary
1978 </td></tr>
1978 </td></tr>
1979 <tr><td>
1979 <tr><td>
1980 <a href="/help/hgignore">
1980 <a href="/help/hgignore">
1981 hgignore
1981 hgignore
1982 </a>
1982 </a>
1983 </td><td>
1983 </td><td>
1984 Syntax for Mercurial Ignore Files
1984 Syntax for Mercurial Ignore Files
1985 </td></tr>
1985 </td></tr>
1986 <tr><td>
1986 <tr><td>
1987 <a href="/help/hgweb">
1987 <a href="/help/hgweb">
1988 hgweb
1988 hgweb
1989 </a>
1989 </a>
1990 </td><td>
1990 </td><td>
1991 Configuring hgweb
1991 Configuring hgweb
1992 </td></tr>
1992 </td></tr>
1993 <tr><td>
1993 <tr><td>
1994 <a href="/help/internals">
1994 <a href="/help/internals">
1995 internals
1995 internals
1996 </a>
1996 </a>
1997 </td><td>
1997 </td><td>
1998 Technical implementation topics
1998 Technical implementation topics
1999 </td></tr>
1999 </td></tr>
2000 <tr><td>
2000 <tr><td>
2001 <a href="/help/merge-tools">
2001 <a href="/help/merge-tools">
2002 merge-tools
2002 merge-tools
2003 </a>
2003 </a>
2004 </td><td>
2004 </td><td>
2005 Merge Tools
2005 Merge Tools
2006 </td></tr>
2006 </td></tr>
2007 <tr><td>
2007 <tr><td>
2008 <a href="/help/pager">
2008 <a href="/help/pager">
2009 pager
2009 pager
2010 </a>
2010 </a>
2011 </td><td>
2011 </td><td>
2012 Pager Support
2012 Pager Support
2013 </td></tr>
2013 </td></tr>
2014 <tr><td>
2014 <tr><td>
2015 <a href="/help/patterns">
2015 <a href="/help/patterns">
2016 patterns
2016 patterns
2017 </a>
2017 </a>
2018 </td><td>
2018 </td><td>
2019 File Name Patterns
2019 File Name Patterns
2020 </td></tr>
2020 </td></tr>
2021 <tr><td>
2021 <tr><td>
2022 <a href="/help/phases">
2022 <a href="/help/phases">
2023 phases
2023 phases
2024 </a>
2024 </a>
2025 </td><td>
2025 </td><td>
2026 Working with Phases
2026 Working with Phases
2027 </td></tr>
2027 </td></tr>
2028 <tr><td>
2028 <tr><td>
2029 <a href="/help/revisions">
2029 <a href="/help/revisions">
2030 revisions
2030 revisions
2031 </a>
2031 </a>
2032 </td><td>
2032 </td><td>
2033 Specifying Revisions
2033 Specifying Revisions
2034 </td></tr>
2034 </td></tr>
2035 <tr><td>
2035 <tr><td>
2036 <a href="/help/scripting">
2036 <a href="/help/scripting">
2037 scripting
2037 scripting
2038 </a>
2038 </a>
2039 </td><td>
2039 </td><td>
2040 Using Mercurial from scripts and automation
2040 Using Mercurial from scripts and automation
2041 </td></tr>
2041 </td></tr>
2042 <tr><td>
2042 <tr><td>
2043 <a href="/help/subrepos">
2043 <a href="/help/subrepos">
2044 subrepos
2044 subrepos
2045 </a>
2045 </a>
2046 </td><td>
2046 </td><td>
2047 Subrepositories
2047 Subrepositories
2048 </td></tr>
2048 </td></tr>
2049 <tr><td>
2049 <tr><td>
2050 <a href="/help/templating">
2050 <a href="/help/templating">
2051 templating
2051 templating
2052 </a>
2052 </a>
2053 </td><td>
2053 </td><td>
2054 Template Usage
2054 Template Usage
2055 </td></tr>
2055 </td></tr>
2056 <tr><td>
2056 <tr><td>
2057 <a href="/help/urls">
2057 <a href="/help/urls">
2058 urls
2058 urls
2059 </a>
2059 </a>
2060 </td><td>
2060 </td><td>
2061 URL Paths
2061 URL Paths
2062 </td></tr>
2062 </td></tr>
2063 <tr><td>
2063 <tr><td>
2064 <a href="/help/topic-containing-verbose">
2064 <a href="/help/topic-containing-verbose">
2065 topic-containing-verbose
2065 topic-containing-verbose
2066 </a>
2066 </a>
2067 </td><td>
2067 </td><td>
2068 This is the topic to test omit indicating.
2068 This is the topic to test omit indicating.
2069 </td></tr>
2069 </td></tr>
2070
2070
2071
2071
2072 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2072 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2073
2073
2074 <tr><td>
2074 <tr><td>
2075 <a href="/help/add">
2075 <a href="/help/add">
2076 add
2076 add
2077 </a>
2077 </a>
2078 </td><td>
2078 </td><td>
2079 add the specified files on the next commit
2079 add the specified files on the next commit
2080 </td></tr>
2080 </td></tr>
2081 <tr><td>
2081 <tr><td>
2082 <a href="/help/annotate">
2082 <a href="/help/annotate">
2083 annotate
2083 annotate
2084 </a>
2084 </a>
2085 </td><td>
2085 </td><td>
2086 show changeset information by line for each file
2086 show changeset information by line for each file
2087 </td></tr>
2087 </td></tr>
2088 <tr><td>
2088 <tr><td>
2089 <a href="/help/clone">
2089 <a href="/help/clone">
2090 clone
2090 clone
2091 </a>
2091 </a>
2092 </td><td>
2092 </td><td>
2093 make a copy of an existing repository
2093 make a copy of an existing repository
2094 </td></tr>
2094 </td></tr>
2095 <tr><td>
2095 <tr><td>
2096 <a href="/help/commit">
2096 <a href="/help/commit">
2097 commit
2097 commit
2098 </a>
2098 </a>
2099 </td><td>
2099 </td><td>
2100 commit the specified files or all outstanding changes
2100 commit the specified files or all outstanding changes
2101 </td></tr>
2101 </td></tr>
2102 <tr><td>
2102 <tr><td>
2103 <a href="/help/diff">
2103 <a href="/help/diff">
2104 diff
2104 diff
2105 </a>
2105 </a>
2106 </td><td>
2106 </td><td>
2107 diff repository (or selected files)
2107 diff repository (or selected files)
2108 </td></tr>
2108 </td></tr>
2109 <tr><td>
2109 <tr><td>
2110 <a href="/help/export">
2110 <a href="/help/export">
2111 export
2111 export
2112 </a>
2112 </a>
2113 </td><td>
2113 </td><td>
2114 dump the header and diffs for one or more changesets
2114 dump the header and diffs for one or more changesets
2115 </td></tr>
2115 </td></tr>
2116 <tr><td>
2116 <tr><td>
2117 <a href="/help/forget">
2117 <a href="/help/forget">
2118 forget
2118 forget
2119 </a>
2119 </a>
2120 </td><td>
2120 </td><td>
2121 forget the specified files on the next commit
2121 forget the specified files on the next commit
2122 </td></tr>
2122 </td></tr>
2123 <tr><td>
2123 <tr><td>
2124 <a href="/help/init">
2124 <a href="/help/init">
2125 init
2125 init
2126 </a>
2126 </a>
2127 </td><td>
2127 </td><td>
2128 create a new repository in the given directory
2128 create a new repository in the given directory
2129 </td></tr>
2129 </td></tr>
2130 <tr><td>
2130 <tr><td>
2131 <a href="/help/log">
2131 <a href="/help/log">
2132 log
2132 log
2133 </a>
2133 </a>
2134 </td><td>
2134 </td><td>
2135 show revision history of entire repository or files
2135 show revision history of entire repository or files
2136 </td></tr>
2136 </td></tr>
2137 <tr><td>
2137 <tr><td>
2138 <a href="/help/merge">
2138 <a href="/help/merge">
2139 merge
2139 merge
2140 </a>
2140 </a>
2141 </td><td>
2141 </td><td>
2142 merge another revision into working directory
2142 merge another revision into working directory
2143 </td></tr>
2143 </td></tr>
2144 <tr><td>
2144 <tr><td>
2145 <a href="/help/pull">
2145 <a href="/help/pull">
2146 pull
2146 pull
2147 </a>
2147 </a>
2148 </td><td>
2148 </td><td>
2149 pull changes from the specified source
2149 pull changes from the specified source
2150 </td></tr>
2150 </td></tr>
2151 <tr><td>
2151 <tr><td>
2152 <a href="/help/push">
2152 <a href="/help/push">
2153 push
2153 push
2154 </a>
2154 </a>
2155 </td><td>
2155 </td><td>
2156 push changes to the specified destination
2156 push changes to the specified destination
2157 </td></tr>
2157 </td></tr>
2158 <tr><td>
2158 <tr><td>
2159 <a href="/help/remove">
2159 <a href="/help/remove">
2160 remove
2160 remove
2161 </a>
2161 </a>
2162 </td><td>
2162 </td><td>
2163 remove the specified files on the next commit
2163 remove the specified files on the next commit
2164 </td></tr>
2164 </td></tr>
2165 <tr><td>
2165 <tr><td>
2166 <a href="/help/serve">
2166 <a href="/help/serve">
2167 serve
2167 serve
2168 </a>
2168 </a>
2169 </td><td>
2169 </td><td>
2170 start stand-alone webserver
2170 start stand-alone webserver
2171 </td></tr>
2171 </td></tr>
2172 <tr><td>
2172 <tr><td>
2173 <a href="/help/status">
2173 <a href="/help/status">
2174 status
2174 status
2175 </a>
2175 </a>
2176 </td><td>
2176 </td><td>
2177 show changed files in the working directory
2177 show changed files in the working directory
2178 </td></tr>
2178 </td></tr>
2179 <tr><td>
2179 <tr><td>
2180 <a href="/help/summary">
2180 <a href="/help/summary">
2181 summary
2181 summary
2182 </a>
2182 </a>
2183 </td><td>
2183 </td><td>
2184 summarize working directory state
2184 summarize working directory state
2185 </td></tr>
2185 </td></tr>
2186 <tr><td>
2186 <tr><td>
2187 <a href="/help/update">
2187 <a href="/help/update">
2188 update
2188 update
2189 </a>
2189 </a>
2190 </td><td>
2190 </td><td>
2191 update working directory (or switch revisions)
2191 update working directory (or switch revisions)
2192 </td></tr>
2192 </td></tr>
2193
2193
2194
2194
2195
2195
2196 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2196 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2197
2197
2198 <tr><td>
2198 <tr><td>
2199 <a href="/help/addremove">
2199 <a href="/help/addremove">
2200 addremove
2200 addremove
2201 </a>
2201 </a>
2202 </td><td>
2202 </td><td>
2203 add all new files, delete all missing files
2203 add all new files, delete all missing files
2204 </td></tr>
2204 </td></tr>
2205 <tr><td>
2205 <tr><td>
2206 <a href="/help/archive">
2206 <a href="/help/archive">
2207 archive
2207 archive
2208 </a>
2208 </a>
2209 </td><td>
2209 </td><td>
2210 create an unversioned archive of a repository revision
2210 create an unversioned archive of a repository revision
2211 </td></tr>
2211 </td></tr>
2212 <tr><td>
2212 <tr><td>
2213 <a href="/help/backout">
2213 <a href="/help/backout">
2214 backout
2214 backout
2215 </a>
2215 </a>
2216 </td><td>
2216 </td><td>
2217 reverse effect of earlier changeset
2217 reverse effect of earlier changeset
2218 </td></tr>
2218 </td></tr>
2219 <tr><td>
2219 <tr><td>
2220 <a href="/help/bisect">
2220 <a href="/help/bisect">
2221 bisect
2221 bisect
2222 </a>
2222 </a>
2223 </td><td>
2223 </td><td>
2224 subdivision search of changesets
2224 subdivision search of changesets
2225 </td></tr>
2225 </td></tr>
2226 <tr><td>
2226 <tr><td>
2227 <a href="/help/bookmarks">
2227 <a href="/help/bookmarks">
2228 bookmarks
2228 bookmarks
2229 </a>
2229 </a>
2230 </td><td>
2230 </td><td>
2231 create a new bookmark or list existing bookmarks
2231 create a new bookmark or list existing bookmarks
2232 </td></tr>
2232 </td></tr>
2233 <tr><td>
2233 <tr><td>
2234 <a href="/help/branch">
2234 <a href="/help/branch">
2235 branch
2235 branch
2236 </a>
2236 </a>
2237 </td><td>
2237 </td><td>
2238 set or show the current branch name
2238 set or show the current branch name
2239 </td></tr>
2239 </td></tr>
2240 <tr><td>
2240 <tr><td>
2241 <a href="/help/branches">
2241 <a href="/help/branches">
2242 branches
2242 branches
2243 </a>
2243 </a>
2244 </td><td>
2244 </td><td>
2245 list repository named branches
2245 list repository named branches
2246 </td></tr>
2246 </td></tr>
2247 <tr><td>
2247 <tr><td>
2248 <a href="/help/bundle">
2248 <a href="/help/bundle">
2249 bundle
2249 bundle
2250 </a>
2250 </a>
2251 </td><td>
2251 </td><td>
2252 create a bundle file
2252 create a bundle file
2253 </td></tr>
2253 </td></tr>
2254 <tr><td>
2254 <tr><td>
2255 <a href="/help/cat">
2255 <a href="/help/cat">
2256 cat
2256 cat
2257 </a>
2257 </a>
2258 </td><td>
2258 </td><td>
2259 output the current or given revision of files
2259 output the current or given revision of files
2260 </td></tr>
2260 </td></tr>
2261 <tr><td>
2261 <tr><td>
2262 <a href="/help/config">
2262 <a href="/help/config">
2263 config
2263 config
2264 </a>
2264 </a>
2265 </td><td>
2265 </td><td>
2266 show combined config settings from all hgrc files
2266 show combined config settings from all hgrc files
2267 </td></tr>
2267 </td></tr>
2268 <tr><td>
2268 <tr><td>
2269 <a href="/help/copy">
2269 <a href="/help/copy">
2270 copy
2270 copy
2271 </a>
2271 </a>
2272 </td><td>
2272 </td><td>
2273 mark files as copied for the next commit
2273 mark files as copied for the next commit
2274 </td></tr>
2274 </td></tr>
2275 <tr><td>
2275 <tr><td>
2276 <a href="/help/files">
2276 <a href="/help/files">
2277 files
2277 files
2278 </a>
2278 </a>
2279 </td><td>
2279 </td><td>
2280 list tracked files
2280 list tracked files
2281 </td></tr>
2281 </td></tr>
2282 <tr><td>
2282 <tr><td>
2283 <a href="/help/graft">
2283 <a href="/help/graft">
2284 graft
2284 graft
2285 </a>
2285 </a>
2286 </td><td>
2286 </td><td>
2287 copy changes from other branches onto the current branch
2287 copy changes from other branches onto the current branch
2288 </td></tr>
2288 </td></tr>
2289 <tr><td>
2289 <tr><td>
2290 <a href="/help/grep">
2290 <a href="/help/grep">
2291 grep
2291 grep
2292 </a>
2292 </a>
2293 </td><td>
2293 </td><td>
2294 search revision history for a pattern in specified files
2294 search revision history for a pattern in specified files
2295 </td></tr>
2295 </td></tr>
2296 <tr><td>
2296 <tr><td>
2297 <a href="/help/heads">
2297 <a href="/help/heads">
2298 heads
2298 heads
2299 </a>
2299 </a>
2300 </td><td>
2300 </td><td>
2301 show branch heads
2301 show branch heads
2302 </td></tr>
2302 </td></tr>
2303 <tr><td>
2303 <tr><td>
2304 <a href="/help/help">
2304 <a href="/help/help">
2305 help
2305 help
2306 </a>
2306 </a>
2307 </td><td>
2307 </td><td>
2308 show help for a given topic or a help overview
2308 show help for a given topic or a help overview
2309 </td></tr>
2309 </td></tr>
2310 <tr><td>
2310 <tr><td>
2311 <a href="/help/hgalias">
2311 <a href="/help/hgalias">
2312 hgalias
2312 hgalias
2313 </a>
2313 </a>
2314 </td><td>
2314 </td><td>
2315 summarize working directory state
2315 summarize working directory state
2316 </td></tr>
2316 </td></tr>
2317 <tr><td>
2317 <tr><td>
2318 <a href="/help/identify">
2318 <a href="/help/identify">
2319 identify
2319 identify
2320 </a>
2320 </a>
2321 </td><td>
2321 </td><td>
2322 identify the working directory or specified revision
2322 identify the working directory or specified revision
2323 </td></tr>
2323 </td></tr>
2324 <tr><td>
2324 <tr><td>
2325 <a href="/help/import">
2325 <a href="/help/import">
2326 import
2326 import
2327 </a>
2327 </a>
2328 </td><td>
2328 </td><td>
2329 import an ordered set of patches
2329 import an ordered set of patches
2330 </td></tr>
2330 </td></tr>
2331 <tr><td>
2331 <tr><td>
2332 <a href="/help/incoming">
2332 <a href="/help/incoming">
2333 incoming
2333 incoming
2334 </a>
2334 </a>
2335 </td><td>
2335 </td><td>
2336 show new changesets found in source
2336 show new changesets found in source
2337 </td></tr>
2337 </td></tr>
2338 <tr><td>
2338 <tr><td>
2339 <a href="/help/manifest">
2339 <a href="/help/manifest">
2340 manifest
2340 manifest
2341 </a>
2341 </a>
2342 </td><td>
2342 </td><td>
2343 output the current or given revision of the project manifest
2343 output the current or given revision of the project manifest
2344 </td></tr>
2344 </td></tr>
2345 <tr><td>
2345 <tr><td>
2346 <a href="/help/nohelp">
2346 <a href="/help/nohelp">
2347 nohelp
2347 nohelp
2348 </a>
2348 </a>
2349 </td><td>
2349 </td><td>
2350 (no help text available)
2350 (no help text available)
2351 </td></tr>
2351 </td></tr>
2352 <tr><td>
2352 <tr><td>
2353 <a href="/help/outgoing">
2353 <a href="/help/outgoing">
2354 outgoing
2354 outgoing
2355 </a>
2355 </a>
2356 </td><td>
2356 </td><td>
2357 show changesets not found in the destination
2357 show changesets not found in the destination
2358 </td></tr>
2358 </td></tr>
2359 <tr><td>
2359 <tr><td>
2360 <a href="/help/paths">
2360 <a href="/help/paths">
2361 paths
2361 paths
2362 </a>
2362 </a>
2363 </td><td>
2363 </td><td>
2364 show aliases for remote repositories
2364 show aliases for remote repositories
2365 </td></tr>
2365 </td></tr>
2366 <tr><td>
2366 <tr><td>
2367 <a href="/help/phase">
2367 <a href="/help/phase">
2368 phase
2368 phase
2369 </a>
2369 </a>
2370 </td><td>
2370 </td><td>
2371 set or show the current phase name
2371 set or show the current phase name
2372 </td></tr>
2372 </td></tr>
2373 <tr><td>
2373 <tr><td>
2374 <a href="/help/recover">
2374 <a href="/help/recover">
2375 recover
2375 recover
2376 </a>
2376 </a>
2377 </td><td>
2377 </td><td>
2378 roll back an interrupted transaction
2378 roll back an interrupted transaction
2379 </td></tr>
2379 </td></tr>
2380 <tr><td>
2380 <tr><td>
2381 <a href="/help/rename">
2381 <a href="/help/rename">
2382 rename
2382 rename
2383 </a>
2383 </a>
2384 </td><td>
2384 </td><td>
2385 rename files; equivalent of copy + remove
2385 rename files; equivalent of copy + remove
2386 </td></tr>
2386 </td></tr>
2387 <tr><td>
2387 <tr><td>
2388 <a href="/help/resolve">
2388 <a href="/help/resolve">
2389 resolve
2389 resolve
2390 </a>
2390 </a>
2391 </td><td>
2391 </td><td>
2392 redo merges or set/view the merge status of files
2392 redo merges or set/view the merge status of files
2393 </td></tr>
2393 </td></tr>
2394 <tr><td>
2394 <tr><td>
2395 <a href="/help/revert">
2395 <a href="/help/revert">
2396 revert
2396 revert
2397 </a>
2397 </a>
2398 </td><td>
2398 </td><td>
2399 restore files to their checkout state
2399 restore files to their checkout state
2400 </td></tr>
2400 </td></tr>
2401 <tr><td>
2401 <tr><td>
2402 <a href="/help/root">
2402 <a href="/help/root">
2403 root
2403 root
2404 </a>
2404 </a>
2405 </td><td>
2405 </td><td>
2406 print the root (top) of the current working directory
2406 print the root (top) of the current working directory
2407 </td></tr>
2407 </td></tr>
2408 <tr><td>
2408 <tr><td>
2409 <a href="/help/shellalias">
2409 <a href="/help/shellalias">
2410 shellalias
2410 shellalias
2411 </a>
2411 </a>
2412 </td><td>
2412 </td><td>
2413 (no help text available)
2413 (no help text available)
2414 </td></tr>
2414 </td></tr>
2415 <tr><td>
2415 <tr><td>
2416 <a href="/help/tag">
2416 <a href="/help/tag">
2417 tag
2417 tag
2418 </a>
2418 </a>
2419 </td><td>
2419 </td><td>
2420 add one or more tags for the current or given revision
2420 add one or more tags for the current or given revision
2421 </td></tr>
2421 </td></tr>
2422 <tr><td>
2422 <tr><td>
2423 <a href="/help/tags">
2423 <a href="/help/tags">
2424 tags
2424 tags
2425 </a>
2425 </a>
2426 </td><td>
2426 </td><td>
2427 list repository tags
2427 list repository tags
2428 </td></tr>
2428 </td></tr>
2429 <tr><td>
2429 <tr><td>
2430 <a href="/help/unbundle">
2430 <a href="/help/unbundle">
2431 unbundle
2431 unbundle
2432 </a>
2432 </a>
2433 </td><td>
2433 </td><td>
2434 apply one or more bundle files
2434 apply one or more bundle files
2435 </td></tr>
2435 </td></tr>
2436 <tr><td>
2436 <tr><td>
2437 <a href="/help/verify">
2437 <a href="/help/verify">
2438 verify
2438 verify
2439 </a>
2439 </a>
2440 </td><td>
2440 </td><td>
2441 verify the integrity of the repository
2441 verify the integrity of the repository
2442 </td></tr>
2442 </td></tr>
2443 <tr><td>
2443 <tr><td>
2444 <a href="/help/version">
2444 <a href="/help/version">
2445 version
2445 version
2446 </a>
2446 </a>
2447 </td><td>
2447 </td><td>
2448 output version and copyright information
2448 output version and copyright information
2449 </td></tr>
2449 </td></tr>
2450
2450
2451
2451
2452 </table>
2452 </table>
2453 </div>
2453 </div>
2454 </div>
2454 </div>
2455
2455
2456
2456
2457
2457
2458 </body>
2458 </body>
2459 </html>
2459 </html>
2460
2460
2461
2461
2462 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2462 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2463 200 Script output follows
2463 200 Script output follows
2464
2464
2465 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2465 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2466 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2466 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2467 <head>
2467 <head>
2468 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2468 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2469 <meta name="robots" content="index, nofollow" />
2469 <meta name="robots" content="index, nofollow" />
2470 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2470 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2471 <script type="text/javascript" src="/static/mercurial.js"></script>
2471 <script type="text/javascript" src="/static/mercurial.js"></script>
2472
2472
2473 <title>Help: add</title>
2473 <title>Help: add</title>
2474 </head>
2474 </head>
2475 <body>
2475 <body>
2476
2476
2477 <div class="container">
2477 <div class="container">
2478 <div class="menu">
2478 <div class="menu">
2479 <div class="logo">
2479 <div class="logo">
2480 <a href="https://mercurial-scm.org/">
2480 <a href="https://mercurial-scm.org/">
2481 <img src="/static/hglogo.png" alt="mercurial" /></a>
2481 <img src="/static/hglogo.png" alt="mercurial" /></a>
2482 </div>
2482 </div>
2483 <ul>
2483 <ul>
2484 <li><a href="/shortlog">log</a></li>
2484 <li><a href="/shortlog">log</a></li>
2485 <li><a href="/graph">graph</a></li>
2485 <li><a href="/graph">graph</a></li>
2486 <li><a href="/tags">tags</a></li>
2486 <li><a href="/tags">tags</a></li>
2487 <li><a href="/bookmarks">bookmarks</a></li>
2487 <li><a href="/bookmarks">bookmarks</a></li>
2488 <li><a href="/branches">branches</a></li>
2488 <li><a href="/branches">branches</a></li>
2489 </ul>
2489 </ul>
2490 <ul>
2490 <ul>
2491 <li class="active"><a href="/help">help</a></li>
2491 <li class="active"><a href="/help">help</a></li>
2492 </ul>
2492 </ul>
2493 </div>
2493 </div>
2494
2494
2495 <div class="main">
2495 <div class="main">
2496 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2496 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2497 <h3>Help: add</h3>
2497 <h3>Help: add</h3>
2498
2498
2499 <form class="search" action="/log">
2499 <form class="search" action="/log">
2500
2500
2501 <p><input name="rev" id="search1" type="text" size="30" /></p>
2501 <p><input name="rev" id="search1" type="text" size="30" /></p>
2502 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2502 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2503 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2503 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2504 </form>
2504 </form>
2505 <div id="doc">
2505 <div id="doc">
2506 <p>
2506 <p>
2507 hg add [OPTION]... [FILE]...
2507 hg add [OPTION]... [FILE]...
2508 </p>
2508 </p>
2509 <p>
2509 <p>
2510 add the specified files on the next commit
2510 add the specified files on the next commit
2511 </p>
2511 </p>
2512 <p>
2512 <p>
2513 Schedule files to be version controlled and added to the
2513 Schedule files to be version controlled and added to the
2514 repository.
2514 repository.
2515 </p>
2515 </p>
2516 <p>
2516 <p>
2517 The files will be added to the repository at the next commit. To
2517 The files will be added to the repository at the next commit. To
2518 undo an add before that, see 'hg forget'.
2518 undo an add before that, see 'hg forget'.
2519 </p>
2519 </p>
2520 <p>
2520 <p>
2521 If no names are given, add all files to the repository (except
2521 If no names are given, add all files to the repository (except
2522 files matching &quot;.hgignore&quot;).
2522 files matching &quot;.hgignore&quot;).
2523 </p>
2523 </p>
2524 <p>
2524 <p>
2525 Examples:
2525 Examples:
2526 </p>
2526 </p>
2527 <ul>
2527 <ul>
2528 <li> New (unknown) files are added automatically by 'hg add':
2528 <li> New (unknown) files are added automatically by 'hg add':
2529 <pre>
2529 <pre>
2530 \$ ls (re)
2530 \$ ls (re)
2531 foo.c
2531 foo.c
2532 \$ hg status (re)
2532 \$ hg status (re)
2533 ? foo.c
2533 ? foo.c
2534 \$ hg add (re)
2534 \$ hg add (re)
2535 adding foo.c
2535 adding foo.c
2536 \$ hg status (re)
2536 \$ hg status (re)
2537 A foo.c
2537 A foo.c
2538 </pre>
2538 </pre>
2539 <li> Specific files to be added can be specified:
2539 <li> Specific files to be added can be specified:
2540 <pre>
2540 <pre>
2541 \$ ls (re)
2541 \$ ls (re)
2542 bar.c foo.c
2542 bar.c foo.c
2543 \$ hg status (re)
2543 \$ hg status (re)
2544 ? bar.c
2544 ? bar.c
2545 ? foo.c
2545 ? foo.c
2546 \$ hg add bar.c (re)
2546 \$ hg add bar.c (re)
2547 \$ hg status (re)
2547 \$ hg status (re)
2548 A bar.c
2548 A bar.c
2549 ? foo.c
2549 ? foo.c
2550 </pre>
2550 </pre>
2551 </ul>
2551 </ul>
2552 <p>
2552 <p>
2553 Returns 0 if all files are successfully added.
2553 Returns 0 if all files are successfully added.
2554 </p>
2554 </p>
2555 <p>
2555 <p>
2556 options ([+] can be repeated):
2556 options ([+] can be repeated):
2557 </p>
2557 </p>
2558 <table>
2558 <table>
2559 <tr><td>-I</td>
2559 <tr><td>-I</td>
2560 <td>--include PATTERN [+]</td>
2560 <td>--include PATTERN [+]</td>
2561 <td>include names matching the given patterns</td></tr>
2561 <td>include names matching the given patterns</td></tr>
2562 <tr><td>-X</td>
2562 <tr><td>-X</td>
2563 <td>--exclude PATTERN [+]</td>
2563 <td>--exclude PATTERN [+]</td>
2564 <td>exclude names matching the given patterns</td></tr>
2564 <td>exclude names matching the given patterns</td></tr>
2565 <tr><td>-S</td>
2565 <tr><td>-S</td>
2566 <td>--subrepos</td>
2566 <td>--subrepos</td>
2567 <td>recurse into subrepositories</td></tr>
2567 <td>recurse into subrepositories</td></tr>
2568 <tr><td>-n</td>
2568 <tr><td>-n</td>
2569 <td>--dry-run</td>
2569 <td>--dry-run</td>
2570 <td>do not perform actions, just print output</td></tr>
2570 <td>do not perform actions, just print output</td></tr>
2571 </table>
2571 </table>
2572 <p>
2572 <p>
2573 global options ([+] can be repeated):
2573 global options ([+] can be repeated):
2574 </p>
2574 </p>
2575 <table>
2575 <table>
2576 <tr><td>-R</td>
2576 <tr><td>-R</td>
2577 <td>--repository REPO</td>
2577 <td>--repository REPO</td>
2578 <td>repository root directory or name of overlay bundle file</td></tr>
2578 <td>repository root directory or name of overlay bundle file</td></tr>
2579 <tr><td></td>
2579 <tr><td></td>
2580 <td>--cwd DIR</td>
2580 <td>--cwd DIR</td>
2581 <td>change working directory</td></tr>
2581 <td>change working directory</td></tr>
2582 <tr><td>-y</td>
2582 <tr><td>-y</td>
2583 <td>--noninteractive</td>
2583 <td>--noninteractive</td>
2584 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2584 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2585 <tr><td>-q</td>
2585 <tr><td>-q</td>
2586 <td>--quiet</td>
2586 <td>--quiet</td>
2587 <td>suppress output</td></tr>
2587 <td>suppress output</td></tr>
2588 <tr><td>-v</td>
2588 <tr><td>-v</td>
2589 <td>--verbose</td>
2589 <td>--verbose</td>
2590 <td>enable additional output</td></tr>
2590 <td>enable additional output</td></tr>
2591 <tr><td></td>
2591 <tr><td></td>
2592 <td>--color TYPE</td>
2592 <td>--color TYPE</td>
2593 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2593 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2594 <tr><td></td>
2594 <tr><td></td>
2595 <td>--config CONFIG [+]</td>
2595 <td>--config CONFIG [+]</td>
2596 <td>set/override config option (use 'section.name=value')</td></tr>
2596 <td>set/override config option (use 'section.name=value')</td></tr>
2597 <tr><td></td>
2597 <tr><td></td>
2598 <td>--debug</td>
2598 <td>--debug</td>
2599 <td>enable debugging output</td></tr>
2599 <td>enable debugging output</td></tr>
2600 <tr><td></td>
2600 <tr><td></td>
2601 <td>--debugger</td>
2601 <td>--debugger</td>
2602 <td>start debugger</td></tr>
2602 <td>start debugger</td></tr>
2603 <tr><td></td>
2603 <tr><td></td>
2604 <td>--encoding ENCODE</td>
2604 <td>--encoding ENCODE</td>
2605 <td>set the charset encoding (default: ascii)</td></tr>
2605 <td>set the charset encoding (default: ascii)</td></tr>
2606 <tr><td></td>
2606 <tr><td></td>
2607 <td>--encodingmode MODE</td>
2607 <td>--encodingmode MODE</td>
2608 <td>set the charset encoding mode (default: strict)</td></tr>
2608 <td>set the charset encoding mode (default: strict)</td></tr>
2609 <tr><td></td>
2609 <tr><td></td>
2610 <td>--traceback</td>
2610 <td>--traceback</td>
2611 <td>always print a traceback on exception</td></tr>
2611 <td>always print a traceback on exception</td></tr>
2612 <tr><td></td>
2612 <tr><td></td>
2613 <td>--time</td>
2613 <td>--time</td>
2614 <td>time how long the command takes</td></tr>
2614 <td>time how long the command takes</td></tr>
2615 <tr><td></td>
2615 <tr><td></td>
2616 <td>--profile</td>
2616 <td>--profile</td>
2617 <td>print command execution profile</td></tr>
2617 <td>print command execution profile</td></tr>
2618 <tr><td></td>
2618 <tr><td></td>
2619 <td>--version</td>
2619 <td>--version</td>
2620 <td>output version information and exit</td></tr>
2620 <td>output version information and exit</td></tr>
2621 <tr><td>-h</td>
2621 <tr><td>-h</td>
2622 <td>--help</td>
2622 <td>--help</td>
2623 <td>display help and exit</td></tr>
2623 <td>display help and exit</td></tr>
2624 <tr><td></td>
2624 <tr><td></td>
2625 <td>--hidden</td>
2625 <td>--hidden</td>
2626 <td>consider hidden changesets</td></tr>
2626 <td>consider hidden changesets</td></tr>
2627 <tr><td></td>
2627 <tr><td></td>
2628 <td>--pager TYPE</td>
2628 <td>--pager TYPE</td>
2629 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2629 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2630 </table>
2630 </table>
2631
2631
2632 </div>
2632 </div>
2633 </div>
2633 </div>
2634 </div>
2634 </div>
2635
2635
2636
2636
2637
2637
2638 </body>
2638 </body>
2639 </html>
2639 </html>
2640
2640
2641
2641
2642 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2642 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2643 200 Script output follows
2643 200 Script output follows
2644
2644
2645 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2645 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2646 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2646 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2647 <head>
2647 <head>
2648 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2648 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2649 <meta name="robots" content="index, nofollow" />
2649 <meta name="robots" content="index, nofollow" />
2650 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2650 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2651 <script type="text/javascript" src="/static/mercurial.js"></script>
2651 <script type="text/javascript" src="/static/mercurial.js"></script>
2652
2652
2653 <title>Help: remove</title>
2653 <title>Help: remove</title>
2654 </head>
2654 </head>
2655 <body>
2655 <body>
2656
2656
2657 <div class="container">
2657 <div class="container">
2658 <div class="menu">
2658 <div class="menu">
2659 <div class="logo">
2659 <div class="logo">
2660 <a href="https://mercurial-scm.org/">
2660 <a href="https://mercurial-scm.org/">
2661 <img src="/static/hglogo.png" alt="mercurial" /></a>
2661 <img src="/static/hglogo.png" alt="mercurial" /></a>
2662 </div>
2662 </div>
2663 <ul>
2663 <ul>
2664 <li><a href="/shortlog">log</a></li>
2664 <li><a href="/shortlog">log</a></li>
2665 <li><a href="/graph">graph</a></li>
2665 <li><a href="/graph">graph</a></li>
2666 <li><a href="/tags">tags</a></li>
2666 <li><a href="/tags">tags</a></li>
2667 <li><a href="/bookmarks">bookmarks</a></li>
2667 <li><a href="/bookmarks">bookmarks</a></li>
2668 <li><a href="/branches">branches</a></li>
2668 <li><a href="/branches">branches</a></li>
2669 </ul>
2669 </ul>
2670 <ul>
2670 <ul>
2671 <li class="active"><a href="/help">help</a></li>
2671 <li class="active"><a href="/help">help</a></li>
2672 </ul>
2672 </ul>
2673 </div>
2673 </div>
2674
2674
2675 <div class="main">
2675 <div class="main">
2676 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2676 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2677 <h3>Help: remove</h3>
2677 <h3>Help: remove</h3>
2678
2678
2679 <form class="search" action="/log">
2679 <form class="search" action="/log">
2680
2680
2681 <p><input name="rev" id="search1" type="text" size="30" /></p>
2681 <p><input name="rev" id="search1" type="text" size="30" /></p>
2682 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2682 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2683 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2683 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2684 </form>
2684 </form>
2685 <div id="doc">
2685 <div id="doc">
2686 <p>
2686 <p>
2687 hg remove [OPTION]... FILE...
2687 hg remove [OPTION]... FILE...
2688 </p>
2688 </p>
2689 <p>
2689 <p>
2690 aliases: rm
2690 aliases: rm
2691 </p>
2691 </p>
2692 <p>
2692 <p>
2693 remove the specified files on the next commit
2693 remove the specified files on the next commit
2694 </p>
2694 </p>
2695 <p>
2695 <p>
2696 Schedule the indicated files for removal from the current branch.
2696 Schedule the indicated files for removal from the current branch.
2697 </p>
2697 </p>
2698 <p>
2698 <p>
2699 This command schedules the files to be removed at the next commit.
2699 This command schedules the files to be removed at the next commit.
2700 To undo a remove before that, see 'hg revert'. To undo added
2700 To undo a remove before that, see 'hg revert'. To undo added
2701 files, see 'hg forget'.
2701 files, see 'hg forget'.
2702 </p>
2702 </p>
2703 <p>
2703 <p>
2704 -A/--after can be used to remove only files that have already
2704 -A/--after can be used to remove only files that have already
2705 been deleted, -f/--force can be used to force deletion, and -Af
2705 been deleted, -f/--force can be used to force deletion, and -Af
2706 can be used to remove files from the next revision without
2706 can be used to remove files from the next revision without
2707 deleting them from the working directory.
2707 deleting them from the working directory.
2708 </p>
2708 </p>
2709 <p>
2709 <p>
2710 The following table details the behavior of remove for different
2710 The following table details the behavior of remove for different
2711 file states (columns) and option combinations (rows). The file
2711 file states (columns) and option combinations (rows). The file
2712 states are Added [A], Clean [C], Modified [M] and Missing [!]
2712 states are Added [A], Clean [C], Modified [M] and Missing [!]
2713 (as reported by 'hg status'). The actions are Warn, Remove
2713 (as reported by 'hg status'). The actions are Warn, Remove
2714 (from branch) and Delete (from disk):
2714 (from branch) and Delete (from disk):
2715 </p>
2715 </p>
2716 <table>
2716 <table>
2717 <tr><td>opt/state</td>
2717 <tr><td>opt/state</td>
2718 <td>A</td>
2718 <td>A</td>
2719 <td>C</td>
2719 <td>C</td>
2720 <td>M</td>
2720 <td>M</td>
2721 <td>!</td></tr>
2721 <td>!</td></tr>
2722 <tr><td>none</td>
2722 <tr><td>none</td>
2723 <td>W</td>
2723 <td>W</td>
2724 <td>RD</td>
2724 <td>RD</td>
2725 <td>W</td>
2725 <td>W</td>
2726 <td>R</td></tr>
2726 <td>R</td></tr>
2727 <tr><td>-f</td>
2727 <tr><td>-f</td>
2728 <td>R</td>
2728 <td>R</td>
2729 <td>RD</td>
2729 <td>RD</td>
2730 <td>RD</td>
2730 <td>RD</td>
2731 <td>R</td></tr>
2731 <td>R</td></tr>
2732 <tr><td>-A</td>
2732 <tr><td>-A</td>
2733 <td>W</td>
2733 <td>W</td>
2734 <td>W</td>
2734 <td>W</td>
2735 <td>W</td>
2735 <td>W</td>
2736 <td>R</td></tr>
2736 <td>R</td></tr>
2737 <tr><td>-Af</td>
2737 <tr><td>-Af</td>
2738 <td>R</td>
2738 <td>R</td>
2739 <td>R</td>
2739 <td>R</td>
2740 <td>R</td>
2740 <td>R</td>
2741 <td>R</td></tr>
2741 <td>R</td></tr>
2742 </table>
2742 </table>
2743 <p>
2743 <p>
2744 <b>Note:</b>
2744 <b>Note:</b>
2745 </p>
2745 </p>
2746 <p>
2746 <p>
2747 'hg remove' never deletes files in Added [A] state from the
2747 'hg remove' never deletes files in Added [A] state from the
2748 working directory, not even if &quot;--force&quot; is specified.
2748 working directory, not even if &quot;--force&quot; is specified.
2749 </p>
2749 </p>
2750 <p>
2750 <p>
2751 Returns 0 on success, 1 if any warnings encountered.
2751 Returns 0 on success, 1 if any warnings encountered.
2752 </p>
2752 </p>
2753 <p>
2753 <p>
2754 options ([+] can be repeated):
2754 options ([+] can be repeated):
2755 </p>
2755 </p>
2756 <table>
2756 <table>
2757 <tr><td>-A</td>
2757 <tr><td>-A</td>
2758 <td>--after</td>
2758 <td>--after</td>
2759 <td>record delete for missing files</td></tr>
2759 <td>record delete for missing files</td></tr>
2760 <tr><td>-f</td>
2760 <tr><td>-f</td>
2761 <td>--force</td>
2761 <td>--force</td>
2762 <td>forget added files, delete modified files</td></tr>
2762 <td>forget added files, delete modified files</td></tr>
2763 <tr><td>-S</td>
2763 <tr><td>-S</td>
2764 <td>--subrepos</td>
2764 <td>--subrepos</td>
2765 <td>recurse into subrepositories</td></tr>
2765 <td>recurse into subrepositories</td></tr>
2766 <tr><td>-I</td>
2766 <tr><td>-I</td>
2767 <td>--include PATTERN [+]</td>
2767 <td>--include PATTERN [+]</td>
2768 <td>include names matching the given patterns</td></tr>
2768 <td>include names matching the given patterns</td></tr>
2769 <tr><td>-X</td>
2769 <tr><td>-X</td>
2770 <td>--exclude PATTERN [+]</td>
2770 <td>--exclude PATTERN [+]</td>
2771 <td>exclude names matching the given patterns</td></tr>
2771 <td>exclude names matching the given patterns</td></tr>
2772 </table>
2772 </table>
2773 <p>
2773 <p>
2774 global options ([+] can be repeated):
2774 global options ([+] can be repeated):
2775 </p>
2775 </p>
2776 <table>
2776 <table>
2777 <tr><td>-R</td>
2777 <tr><td>-R</td>
2778 <td>--repository REPO</td>
2778 <td>--repository REPO</td>
2779 <td>repository root directory or name of overlay bundle file</td></tr>
2779 <td>repository root directory or name of overlay bundle file</td></tr>
2780 <tr><td></td>
2780 <tr><td></td>
2781 <td>--cwd DIR</td>
2781 <td>--cwd DIR</td>
2782 <td>change working directory</td></tr>
2782 <td>change working directory</td></tr>
2783 <tr><td>-y</td>
2783 <tr><td>-y</td>
2784 <td>--noninteractive</td>
2784 <td>--noninteractive</td>
2785 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2785 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2786 <tr><td>-q</td>
2786 <tr><td>-q</td>
2787 <td>--quiet</td>
2787 <td>--quiet</td>
2788 <td>suppress output</td></tr>
2788 <td>suppress output</td></tr>
2789 <tr><td>-v</td>
2789 <tr><td>-v</td>
2790 <td>--verbose</td>
2790 <td>--verbose</td>
2791 <td>enable additional output</td></tr>
2791 <td>enable additional output</td></tr>
2792 <tr><td></td>
2792 <tr><td></td>
2793 <td>--color TYPE</td>
2793 <td>--color TYPE</td>
2794 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2794 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2795 <tr><td></td>
2795 <tr><td></td>
2796 <td>--config CONFIG [+]</td>
2796 <td>--config CONFIG [+]</td>
2797 <td>set/override config option (use 'section.name=value')</td></tr>
2797 <td>set/override config option (use 'section.name=value')</td></tr>
2798 <tr><td></td>
2798 <tr><td></td>
2799 <td>--debug</td>
2799 <td>--debug</td>
2800 <td>enable debugging output</td></tr>
2800 <td>enable debugging output</td></tr>
2801 <tr><td></td>
2801 <tr><td></td>
2802 <td>--debugger</td>
2802 <td>--debugger</td>
2803 <td>start debugger</td></tr>
2803 <td>start debugger</td></tr>
2804 <tr><td></td>
2804 <tr><td></td>
2805 <td>--encoding ENCODE</td>
2805 <td>--encoding ENCODE</td>
2806 <td>set the charset encoding (default: ascii)</td></tr>
2806 <td>set the charset encoding (default: ascii)</td></tr>
2807 <tr><td></td>
2807 <tr><td></td>
2808 <td>--encodingmode MODE</td>
2808 <td>--encodingmode MODE</td>
2809 <td>set the charset encoding mode (default: strict)</td></tr>
2809 <td>set the charset encoding mode (default: strict)</td></tr>
2810 <tr><td></td>
2810 <tr><td></td>
2811 <td>--traceback</td>
2811 <td>--traceback</td>
2812 <td>always print a traceback on exception</td></tr>
2812 <td>always print a traceback on exception</td></tr>
2813 <tr><td></td>
2813 <tr><td></td>
2814 <td>--time</td>
2814 <td>--time</td>
2815 <td>time how long the command takes</td></tr>
2815 <td>time how long the command takes</td></tr>
2816 <tr><td></td>
2816 <tr><td></td>
2817 <td>--profile</td>
2817 <td>--profile</td>
2818 <td>print command execution profile</td></tr>
2818 <td>print command execution profile</td></tr>
2819 <tr><td></td>
2819 <tr><td></td>
2820 <td>--version</td>
2820 <td>--version</td>
2821 <td>output version information and exit</td></tr>
2821 <td>output version information and exit</td></tr>
2822 <tr><td>-h</td>
2822 <tr><td>-h</td>
2823 <td>--help</td>
2823 <td>--help</td>
2824 <td>display help and exit</td></tr>
2824 <td>display help and exit</td></tr>
2825 <tr><td></td>
2825 <tr><td></td>
2826 <td>--hidden</td>
2826 <td>--hidden</td>
2827 <td>consider hidden changesets</td></tr>
2827 <td>consider hidden changesets</td></tr>
2828 <tr><td></td>
2828 <tr><td></td>
2829 <td>--pager TYPE</td>
2829 <td>--pager TYPE</td>
2830 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2830 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2831 </table>
2831 </table>
2832
2832
2833 </div>
2833 </div>
2834 </div>
2834 </div>
2835 </div>
2835 </div>
2836
2836
2837
2837
2838
2838
2839 </body>
2839 </body>
2840 </html>
2840 </html>
2841
2841
2842
2842
2843 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2843 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2844 200 Script output follows
2844 200 Script output follows
2845
2845
2846 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2846 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2847 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2847 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2848 <head>
2848 <head>
2849 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2849 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2850 <meta name="robots" content="index, nofollow" />
2850 <meta name="robots" content="index, nofollow" />
2851 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2851 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2852 <script type="text/javascript" src="/static/mercurial.js"></script>
2852 <script type="text/javascript" src="/static/mercurial.js"></script>
2853
2853
2854 <title>Help: dates</title>
2854 <title>Help: dates</title>
2855 </head>
2855 </head>
2856 <body>
2856 <body>
2857
2857
2858 <div class="container">
2858 <div class="container">
2859 <div class="menu">
2859 <div class="menu">
2860 <div class="logo">
2860 <div class="logo">
2861 <a href="https://mercurial-scm.org/">
2861 <a href="https://mercurial-scm.org/">
2862 <img src="/static/hglogo.png" alt="mercurial" /></a>
2862 <img src="/static/hglogo.png" alt="mercurial" /></a>
2863 </div>
2863 </div>
2864 <ul>
2864 <ul>
2865 <li><a href="/shortlog">log</a></li>
2865 <li><a href="/shortlog">log</a></li>
2866 <li><a href="/graph">graph</a></li>
2866 <li><a href="/graph">graph</a></li>
2867 <li><a href="/tags">tags</a></li>
2867 <li><a href="/tags">tags</a></li>
2868 <li><a href="/bookmarks">bookmarks</a></li>
2868 <li><a href="/bookmarks">bookmarks</a></li>
2869 <li><a href="/branches">branches</a></li>
2869 <li><a href="/branches">branches</a></li>
2870 </ul>
2870 </ul>
2871 <ul>
2871 <ul>
2872 <li class="active"><a href="/help">help</a></li>
2872 <li class="active"><a href="/help">help</a></li>
2873 </ul>
2873 </ul>
2874 </div>
2874 </div>
2875
2875
2876 <div class="main">
2876 <div class="main">
2877 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2877 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2878 <h3>Help: dates</h3>
2878 <h3>Help: dates</h3>
2879
2879
2880 <form class="search" action="/log">
2880 <form class="search" action="/log">
2881
2881
2882 <p><input name="rev" id="search1" type="text" size="30" /></p>
2882 <p><input name="rev" id="search1" type="text" size="30" /></p>
2883 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2883 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2884 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2884 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2885 </form>
2885 </form>
2886 <div id="doc">
2886 <div id="doc">
2887 <h1>Date Formats</h1>
2887 <h1>Date Formats</h1>
2888 <p>
2888 <p>
2889 Some commands allow the user to specify a date, e.g.:
2889 Some commands allow the user to specify a date, e.g.:
2890 </p>
2890 </p>
2891 <ul>
2891 <ul>
2892 <li> backout, commit, import, tag: Specify the commit date.
2892 <li> backout, commit, import, tag: Specify the commit date.
2893 <li> log, revert, update: Select revision(s) by date.
2893 <li> log, revert, update: Select revision(s) by date.
2894 </ul>
2894 </ul>
2895 <p>
2895 <p>
2896 Many date formats are valid. Here are some examples:
2896 Many date formats are valid. Here are some examples:
2897 </p>
2897 </p>
2898 <ul>
2898 <ul>
2899 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2899 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2900 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2900 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2901 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2901 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2902 <li> &quot;Dec 6&quot; (midnight)
2902 <li> &quot;Dec 6&quot; (midnight)
2903 <li> &quot;13:18&quot; (today assumed)
2903 <li> &quot;13:18&quot; (today assumed)
2904 <li> &quot;3:39&quot; (3:39AM assumed)
2904 <li> &quot;3:39&quot; (3:39AM assumed)
2905 <li> &quot;3:39pm&quot; (15:39)
2905 <li> &quot;3:39pm&quot; (15:39)
2906 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2906 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2907 <li> &quot;2006-12-6 13:18&quot;
2907 <li> &quot;2006-12-6 13:18&quot;
2908 <li> &quot;2006-12-6&quot;
2908 <li> &quot;2006-12-6&quot;
2909 <li> &quot;12-6&quot;
2909 <li> &quot;12-6&quot;
2910 <li> &quot;12/6&quot;
2910 <li> &quot;12/6&quot;
2911 <li> &quot;12/6/6&quot; (Dec 6 2006)
2911 <li> &quot;12/6/6&quot; (Dec 6 2006)
2912 <li> &quot;today&quot; (midnight)
2912 <li> &quot;today&quot; (midnight)
2913 <li> &quot;yesterday&quot; (midnight)
2913 <li> &quot;yesterday&quot; (midnight)
2914 <li> &quot;now&quot; - right now
2914 <li> &quot;now&quot; - right now
2915 </ul>
2915 </ul>
2916 <p>
2916 <p>
2917 Lastly, there is Mercurial's internal format:
2917 Lastly, there is Mercurial's internal format:
2918 </p>
2918 </p>
2919 <ul>
2919 <ul>
2920 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2920 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2921 </ul>
2921 </ul>
2922 <p>
2922 <p>
2923 This is the internal representation format for dates. The first number
2923 This is the internal representation format for dates. The first number
2924 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2924 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2925 second is the offset of the local timezone, in seconds west of UTC
2925 second is the offset of the local timezone, in seconds west of UTC
2926 (negative if the timezone is east of UTC).
2926 (negative if the timezone is east of UTC).
2927 </p>
2927 </p>
2928 <p>
2928 <p>
2929 The log command also accepts date ranges:
2929 The log command also accepts date ranges:
2930 </p>
2930 </p>
2931 <ul>
2931 <ul>
2932 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2932 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2933 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2933 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2934 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2934 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2935 <li> &quot;-DAYS&quot; - within a given number of days of today
2935 <li> &quot;-DAYS&quot; - within a given number of days of today
2936 </ul>
2936 </ul>
2937
2937
2938 </div>
2938 </div>
2939 </div>
2939 </div>
2940 </div>
2940 </div>
2941
2941
2942
2942
2943
2943
2944 </body>
2944 </body>
2945 </html>
2945 </html>
2946
2946
2947
2947
2948 Sub-topic indexes rendered properly
2948 Sub-topic indexes rendered properly
2949
2949
2950 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
2950 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
2951 200 Script output follows
2951 200 Script output follows
2952
2952
2953 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2953 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2954 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2954 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2955 <head>
2955 <head>
2956 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2956 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2957 <meta name="robots" content="index, nofollow" />
2957 <meta name="robots" content="index, nofollow" />
2958 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2958 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2959 <script type="text/javascript" src="/static/mercurial.js"></script>
2959 <script type="text/javascript" src="/static/mercurial.js"></script>
2960
2960
2961 <title>Help: internals</title>
2961 <title>Help: internals</title>
2962 </head>
2962 </head>
2963 <body>
2963 <body>
2964
2964
2965 <div class="container">
2965 <div class="container">
2966 <div class="menu">
2966 <div class="menu">
2967 <div class="logo">
2967 <div class="logo">
2968 <a href="https://mercurial-scm.org/">
2968 <a href="https://mercurial-scm.org/">
2969 <img src="/static/hglogo.png" alt="mercurial" /></a>
2969 <img src="/static/hglogo.png" alt="mercurial" /></a>
2970 </div>
2970 </div>
2971 <ul>
2971 <ul>
2972 <li><a href="/shortlog">log</a></li>
2972 <li><a href="/shortlog">log</a></li>
2973 <li><a href="/graph">graph</a></li>
2973 <li><a href="/graph">graph</a></li>
2974 <li><a href="/tags">tags</a></li>
2974 <li><a href="/tags">tags</a></li>
2975 <li><a href="/bookmarks">bookmarks</a></li>
2975 <li><a href="/bookmarks">bookmarks</a></li>
2976 <li><a href="/branches">branches</a></li>
2976 <li><a href="/branches">branches</a></li>
2977 </ul>
2977 </ul>
2978 <ul>
2978 <ul>
2979 <li><a href="/help">help</a></li>
2979 <li><a href="/help">help</a></li>
2980 </ul>
2980 </ul>
2981 </div>
2981 </div>
2982
2982
2983 <div class="main">
2983 <div class="main">
2984 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2984 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2985 <form class="search" action="/log">
2985 <form class="search" action="/log">
2986
2986
2987 <p><input name="rev" id="search1" type="text" size="30" /></p>
2987 <p><input name="rev" id="search1" type="text" size="30" /></p>
2988 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2988 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2989 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2989 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2990 </form>
2990 </form>
2991 <table class="bigtable">
2991 <table class="bigtable">
2992 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
2992 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
2993
2993
2994 <tr><td>
2994 <tr><td>
2995 <a href="/help/internals.bundles">
2995 <a href="/help/internals.bundles">
2996 bundles
2996 bundles
2997 </a>
2997 </a>
2998 </td><td>
2998 </td><td>
2999 Bundles
2999 Bundles
3000 </td></tr>
3000 </td></tr>
3001 <tr><td>
3001 <tr><td>
3002 <a href="/help/internals.censor">
3002 <a href="/help/internals.censor">
3003 censor
3003 censor
3004 </a>
3004 </a>
3005 </td><td>
3005 </td><td>
3006 Censor
3006 Censor
3007 </td></tr>
3007 </td></tr>
3008 <tr><td>
3008 <tr><td>
3009 <a href="/help/internals.changegroups">
3009 <a href="/help/internals.changegroups">
3010 changegroups
3010 changegroups
3011 </a>
3011 </a>
3012 </td><td>
3012 </td><td>
3013 Changegroups
3013 Changegroups
3014 </td></tr>
3014 </td></tr>
3015 <tr><td>
3015 <tr><td>
3016 <a href="/help/internals.requirements">
3016 <a href="/help/internals.requirements">
3017 requirements
3017 requirements
3018 </a>
3018 </a>
3019 </td><td>
3019 </td><td>
3020 Repository Requirements
3020 Repository Requirements
3021 </td></tr>
3021 </td></tr>
3022 <tr><td>
3022 <tr><td>
3023 <a href="/help/internals.revlogs">
3023 <a href="/help/internals.revlogs">
3024 revlogs
3024 revlogs
3025 </a>
3025 </a>
3026 </td><td>
3026 </td><td>
3027 Revision Logs
3027 Revision Logs
3028 </td></tr>
3028 </td></tr>
3029 <tr><td>
3029 <tr><td>
3030 <a href="/help/internals.wireprotocol">
3030 <a href="/help/internals.wireprotocol">
3031 wireprotocol
3031 wireprotocol
3032 </a>
3032 </a>
3033 </td><td>
3033 </td><td>
3034 Wire Protocol
3034 Wire Protocol
3035 </td></tr>
3035 </td></tr>
3036
3036
3037
3037
3038
3038
3039
3039
3040
3040
3041 </table>
3041 </table>
3042 </div>
3042 </div>
3043 </div>
3043 </div>
3044
3044
3045
3045
3046
3046
3047 </body>
3047 </body>
3048 </html>
3048 </html>
3049
3049
3050
3050
3051 Sub-topic topics rendered properly
3051 Sub-topic topics rendered properly
3052
3052
3053 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3053 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3054 200 Script output follows
3054 200 Script output follows
3055
3055
3056 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3056 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3057 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3057 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3058 <head>
3058 <head>
3059 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3059 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3060 <meta name="robots" content="index, nofollow" />
3060 <meta name="robots" content="index, nofollow" />
3061 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3061 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3062 <script type="text/javascript" src="/static/mercurial.js"></script>
3062 <script type="text/javascript" src="/static/mercurial.js"></script>
3063
3063
3064 <title>Help: internals.changegroups</title>
3064 <title>Help: internals.changegroups</title>
3065 </head>
3065 </head>
3066 <body>
3066 <body>
3067
3067
3068 <div class="container">
3068 <div class="container">
3069 <div class="menu">
3069 <div class="menu">
3070 <div class="logo">
3070 <div class="logo">
3071 <a href="https://mercurial-scm.org/">
3071 <a href="https://mercurial-scm.org/">
3072 <img src="/static/hglogo.png" alt="mercurial" /></a>
3072 <img src="/static/hglogo.png" alt="mercurial" /></a>
3073 </div>
3073 </div>
3074 <ul>
3074 <ul>
3075 <li><a href="/shortlog">log</a></li>
3075 <li><a href="/shortlog">log</a></li>
3076 <li><a href="/graph">graph</a></li>
3076 <li><a href="/graph">graph</a></li>
3077 <li><a href="/tags">tags</a></li>
3077 <li><a href="/tags">tags</a></li>
3078 <li><a href="/bookmarks">bookmarks</a></li>
3078 <li><a href="/bookmarks">bookmarks</a></li>
3079 <li><a href="/branches">branches</a></li>
3079 <li><a href="/branches">branches</a></li>
3080 </ul>
3080 </ul>
3081 <ul>
3081 <ul>
3082 <li class="active"><a href="/help">help</a></li>
3082 <li class="active"><a href="/help">help</a></li>
3083 </ul>
3083 </ul>
3084 </div>
3084 </div>
3085
3085
3086 <div class="main">
3086 <div class="main">
3087 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3087 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3088 <h3>Help: internals.changegroups</h3>
3088 <h3>Help: internals.changegroups</h3>
3089
3089
3090 <form class="search" action="/log">
3090 <form class="search" action="/log">
3091
3091
3092 <p><input name="rev" id="search1" type="text" size="30" /></p>
3092 <p><input name="rev" id="search1" type="text" size="30" /></p>
3093 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3093 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3094 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3094 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3095 </form>
3095 </form>
3096 <div id="doc">
3096 <div id="doc">
3097 <h1>Changegroups</h1>
3097 <h1>Changegroups</h1>
3098 <p>
3098 <p>
3099 Changegroups are representations of repository revlog data, specifically
3099 Changegroups are representations of repository revlog data, specifically
3100 the changelog data, root/flat manifest data, treemanifest data, and
3100 the changelog data, root/flat manifest data, treemanifest data, and
3101 filelogs.
3101 filelogs.
3102 </p>
3102 </p>
3103 <p>
3103 <p>
3104 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3104 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3105 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3105 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3106 only difference being an additional item in the *delta header*. Version
3106 only difference being an additional item in the *delta header*. Version
3107 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3107 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3108 exchanging treemanifests (enabled by setting an option on the
3108 exchanging treemanifests (enabled by setting an option on the
3109 &quot;changegroup&quot; part in the bundle2).
3109 &quot;changegroup&quot; part in the bundle2).
3110 </p>
3110 </p>
3111 <p>
3111 <p>
3112 Changegroups when not exchanging treemanifests consist of 3 logical
3112 Changegroups when not exchanging treemanifests consist of 3 logical
3113 segments:
3113 segments:
3114 </p>
3114 </p>
3115 <pre>
3115 <pre>
3116 +---------------------------------+
3116 +---------------------------------+
3117 | | | |
3117 | | | |
3118 | changeset | manifest | filelogs |
3118 | changeset | manifest | filelogs |
3119 | | | |
3119 | | | |
3120 | | | |
3120 | | | |
3121 +---------------------------------+
3121 +---------------------------------+
3122 </pre>
3122 </pre>
3123 <p>
3123 <p>
3124 When exchanging treemanifests, there are 4 logical segments:
3124 When exchanging treemanifests, there are 4 logical segments:
3125 </p>
3125 </p>
3126 <pre>
3126 <pre>
3127 +-------------------------------------------------+
3127 +-------------------------------------------------+
3128 | | | | |
3128 | | | | |
3129 | changeset | root | treemanifests | filelogs |
3129 | changeset | root | treemanifests | filelogs |
3130 | | manifest | | |
3130 | | manifest | | |
3131 | | | | |
3131 | | | | |
3132 +-------------------------------------------------+
3132 +-------------------------------------------------+
3133 </pre>
3133 </pre>
3134 <p>
3134 <p>
3135 The principle building block of each segment is a *chunk*. A *chunk*
3135 The principle building block of each segment is a *chunk*. A *chunk*
3136 is a framed piece of data:
3136 is a framed piece of data:
3137 </p>
3137 </p>
3138 <pre>
3138 <pre>
3139 +---------------------------------------+
3139 +---------------------------------------+
3140 | | |
3140 | | |
3141 | length | data |
3141 | length | data |
3142 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3142 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3143 | | |
3143 | | |
3144 +---------------------------------------+
3144 +---------------------------------------+
3145 </pre>
3145 </pre>
3146 <p>
3146 <p>
3147 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3147 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3148 integer indicating the length of the entire chunk (including the length field
3148 integer indicating the length of the entire chunk (including the length field
3149 itself).
3149 itself).
3150 </p>
3150 </p>
3151 <p>
3151 <p>
3152 There is a special case chunk that has a value of 0 for the length
3152 There is a special case chunk that has a value of 0 for the length
3153 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3153 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3154 </p>
3154 </p>
3155 <h2>Delta Groups</h2>
3155 <h2>Delta Groups</h2>
3156 <p>
3156 <p>
3157 A *delta group* expresses the content of a revlog as a series of deltas,
3157 A *delta group* expresses the content of a revlog as a series of deltas,
3158 or patches against previous revisions.
3158 or patches against previous revisions.
3159 </p>
3159 </p>
3160 <p>
3160 <p>
3161 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3161 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3162 to signal the end of the delta group:
3162 to signal the end of the delta group:
3163 </p>
3163 </p>
3164 <pre>
3164 <pre>
3165 +------------------------------------------------------------------------+
3165 +------------------------------------------------------------------------+
3166 | | | | | |
3166 | | | | | |
3167 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3167 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3168 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3168 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3169 | | | | | |
3169 | | | | | |
3170 +------------------------------------------------------------------------+
3170 +------------------------------------------------------------------------+
3171 </pre>
3171 </pre>
3172 <p>
3172 <p>
3173 Each *chunk*'s data consists of the following:
3173 Each *chunk*'s data consists of the following:
3174 </p>
3174 </p>
3175 <pre>
3175 <pre>
3176 +---------------------------------------+
3176 +---------------------------------------+
3177 | | |
3177 | | |
3178 | delta header | delta data |
3178 | delta header | delta data |
3179 | (various by version) | (various) |
3179 | (various by version) | (various) |
3180 | | |
3180 | | |
3181 +---------------------------------------+
3181 +---------------------------------------+
3182 </pre>
3182 </pre>
3183 <p>
3183 <p>
3184 The *delta data* is a series of *delta*s that describe a diff from an existing
3184 The *delta data* is a series of *delta*s that describe a diff from an existing
3185 entry (either that the recipient already has, or previously specified in the
3185 entry (either that the recipient already has, or previously specified in the
3186 bundle/changegroup).
3186 bundle/changegroup).
3187 </p>
3187 </p>
3188 <p>
3188 <p>
3189 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3189 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3190 &quot;3&quot; of the changegroup format.
3190 &quot;3&quot; of the changegroup format.
3191 </p>
3191 </p>
3192 <p>
3192 <p>
3193 Version 1 (headerlen=80):
3193 Version 1 (headerlen=80):
3194 </p>
3194 </p>
3195 <pre>
3195 <pre>
3196 +------------------------------------------------------+
3196 +------------------------------------------------------+
3197 | | | | |
3197 | | | | |
3198 | node | p1 node | p2 node | link node |
3198 | node | p1 node | p2 node | link node |
3199 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3199 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3200 | | | | |
3200 | | | | |
3201 +------------------------------------------------------+
3201 +------------------------------------------------------+
3202 </pre>
3202 </pre>
3203 <p>
3203 <p>
3204 Version 2 (headerlen=100):
3204 Version 2 (headerlen=100):
3205 </p>
3205 </p>
3206 <pre>
3206 <pre>
3207 +------------------------------------------------------------------+
3207 +------------------------------------------------------------------+
3208 | | | | | |
3208 | | | | | |
3209 | node | p1 node | p2 node | base node | link node |
3209 | node | p1 node | p2 node | base node | link node |
3210 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3210 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3211 | | | | | |
3211 | | | | | |
3212 +------------------------------------------------------------------+
3212 +------------------------------------------------------------------+
3213 </pre>
3213 </pre>
3214 <p>
3214 <p>
3215 Version 3 (headerlen=102):
3215 Version 3 (headerlen=102):
3216 </p>
3216 </p>
3217 <pre>
3217 <pre>
3218 +------------------------------------------------------------------------------+
3218 +------------------------------------------------------------------------------+
3219 | | | | | | |
3219 | | | | | | |
3220 | node | p1 node | p2 node | base node | link node | flags |
3220 | node | p1 node | p2 node | base node | link node | flags |
3221 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3221 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3222 | | | | | | |
3222 | | | | | | |
3223 +------------------------------------------------------------------------------+
3223 +------------------------------------------------------------------------------+
3224 </pre>
3224 </pre>
3225 <p>
3225 <p>
3226 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3226 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3227 series of *delta*s, densely packed (no separators). These deltas describe a diff
3227 series of *delta*s, densely packed (no separators). These deltas describe a diff
3228 from an existing entry (either that the recipient already has, or previously
3228 from an existing entry (either that the recipient already has, or previously
3229 specified in the bundle/changegroup). The format is described more fully in
3229 specified in the bundle/changegroup). The format is described more fully in
3230 &quot;hg help internals.bdiff&quot;, but briefly:
3230 &quot;hg help internals.bdiff&quot;, but briefly:
3231 </p>
3231 </p>
3232 <pre>
3232 <pre>
3233 +---------------------------------------------------------------+
3233 +---------------------------------------------------------------+
3234 | | | | |
3234 | | | | |
3235 | start offset | end offset | new length | content |
3235 | start offset | end offset | new length | content |
3236 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3236 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3237 | | | | |
3237 | | | | |
3238 +---------------------------------------------------------------+
3238 +---------------------------------------------------------------+
3239 </pre>
3239 </pre>
3240 <p>
3240 <p>
3241 Please note that the length field in the delta data does *not* include itself.
3241 Please note that the length field in the delta data does *not* include itself.
3242 </p>
3242 </p>
3243 <p>
3243 <p>
3244 In version 1, the delta is always applied against the previous node from
3244 In version 1, the delta is always applied against the previous node from
3245 the changegroup or the first parent if this is the first entry in the
3245 the changegroup or the first parent if this is the first entry in the
3246 changegroup.
3246 changegroup.
3247 </p>
3247 </p>
3248 <p>
3248 <p>
3249 In version 2 and up, the delta base node is encoded in the entry in the
3249 In version 2 and up, the delta base node is encoded in the entry in the
3250 changegroup. This allows the delta to be expressed against any parent,
3250 changegroup. This allows the delta to be expressed against any parent,
3251 which can result in smaller deltas and more efficient encoding of data.
3251 which can result in smaller deltas and more efficient encoding of data.
3252 </p>
3252 </p>
3253 <h2>Changeset Segment</h2>
3253 <h2>Changeset Segment</h2>
3254 <p>
3254 <p>
3255 The *changeset segment* consists of a single *delta group* holding
3255 The *changeset segment* consists of a single *delta group* holding
3256 changelog data. The *empty chunk* at the end of the *delta group* denotes
3256 changelog data. The *empty chunk* at the end of the *delta group* denotes
3257 the boundary to the *manifest segment*.
3257 the boundary to the *manifest segment*.
3258 </p>
3258 </p>
3259 <h2>Manifest Segment</h2>
3259 <h2>Manifest Segment</h2>
3260 <p>
3260 <p>
3261 The *manifest segment* consists of a single *delta group* holding manifest
3261 The *manifest segment* consists of a single *delta group* holding manifest
3262 data. If treemanifests are in use, it contains only the manifest for the
3262 data. If treemanifests are in use, it contains only the manifest for the
3263 root directory of the repository. Otherwise, it contains the entire
3263 root directory of the repository. Otherwise, it contains the entire
3264 manifest data. The *empty chunk* at the end of the *delta group* denotes
3264 manifest data. The *empty chunk* at the end of the *delta group* denotes
3265 the boundary to the next segment (either the *treemanifests segment* or the
3265 the boundary to the next segment (either the *treemanifests segment* or the
3266 *filelogs segment*, depending on version and the request options).
3266 *filelogs segment*, depending on version and the request options).
3267 </p>
3267 </p>
3268 <h3>Treemanifests Segment</h3>
3268 <h3>Treemanifests Segment</h3>
3269 <p>
3269 <p>
3270 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3270 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3271 only if the 'treemanifest' param is part of the bundle2 changegroup part
3271 only if the 'treemanifest' param is part of the bundle2 changegroup part
3272 (it is not possible to use changegroup version 3 outside of bundle2).
3272 (it is not possible to use changegroup version 3 outside of bundle2).
3273 Aside from the filenames in the *treemanifests segment* containing a
3273 Aside from the filenames in the *treemanifests segment* containing a
3274 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3274 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3275 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3275 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3276 a sub-segment with filename size 0). This denotes the boundary to the
3276 a sub-segment with filename size 0). This denotes the boundary to the
3277 *filelogs segment*.
3277 *filelogs segment*.
3278 </p>
3278 </p>
3279 <h2>Filelogs Segment</h2>
3279 <h2>Filelogs Segment</h2>
3280 <p>
3280 <p>
3281 The *filelogs segment* consists of multiple sub-segments, each
3281 The *filelogs segment* consists of multiple sub-segments, each
3282 corresponding to an individual file whose data is being described:
3282 corresponding to an individual file whose data is being described:
3283 </p>
3283 </p>
3284 <pre>
3284 <pre>
3285 +--------------------------------------------------+
3285 +--------------------------------------------------+
3286 | | | | | |
3286 | | | | | |
3287 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3287 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3288 | | | | | (4 bytes) |
3288 | | | | | (4 bytes) |
3289 | | | | | |
3289 | | | | | |
3290 +--------------------------------------------------+
3290 +--------------------------------------------------+
3291 </pre>
3291 </pre>
3292 <p>
3292 <p>
3293 The final filelog sub-segment is followed by an *empty chunk* (logically,
3293 The final filelog sub-segment is followed by an *empty chunk* (logically,
3294 a sub-segment with filename size 0). This denotes the end of the segment
3294 a sub-segment with filename size 0). This denotes the end of the segment
3295 and of the overall changegroup.
3295 and of the overall changegroup.
3296 </p>
3296 </p>
3297 <p>
3297 <p>
3298 Each filelog sub-segment consists of the following:
3298 Each filelog sub-segment consists of the following:
3299 </p>
3299 </p>
3300 <pre>
3300 <pre>
3301 +------------------------------------------------------+
3301 +------------------------------------------------------+
3302 | | | |
3302 | | | |
3303 | filename length | filename | delta group |
3303 | filename length | filename | delta group |
3304 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3304 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3305 | | | |
3305 | | | |
3306 +------------------------------------------------------+
3306 +------------------------------------------------------+
3307 </pre>
3307 </pre>
3308 <p>
3308 <p>
3309 That is, a *chunk* consisting of the filename (not terminated or padded)
3309 That is, a *chunk* consisting of the filename (not terminated or padded)
3310 followed by N chunks constituting the *delta group* for this file. The
3310 followed by N chunks constituting the *delta group* for this file. The
3311 *empty chunk* at the end of each *delta group* denotes the boundary to the
3311 *empty chunk* at the end of each *delta group* denotes the boundary to the
3312 next filelog sub-segment.
3312 next filelog sub-segment.
3313 </p>
3313 </p>
3314
3314
3315 </div>
3315 </div>
3316 </div>
3316 </div>
3317 </div>
3317 </div>
3318
3318
3319
3319
3320
3320
3321 </body>
3321 </body>
3322 </html>
3322 </html>
3323
3323
3324
3324
3325 $ killdaemons.py
3325 $ killdaemons.py
3326
3326
3327 #endif
3327 #endif
@@ -1,634 +1,634 b''
1 #require serve ssl
1 #require serve ssl
2
2
3 Proper https client requires the built-in ssl from Python 2.6.
3 Proper https client requires the built-in ssl from Python 2.6.
4
4
5 Make server certificates:
5 Make server certificates:
6
6
7 $ CERTSDIR="$TESTDIR/sslcerts"
7 $ CERTSDIR="$TESTDIR/sslcerts"
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
9 $ PRIV=`pwd`/server.pem
9 $ PRIV=`pwd`/server.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
12
12
13 $ hg init test
13 $ hg init test
14 $ cd test
14 $ cd test
15 $ echo foo>foo
15 $ echo foo>foo
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
17 $ echo foo>foo.d/foo
17 $ echo foo>foo.d/foo
18 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/bAr.hg.d/BaR
19 $ echo bar>foo.d/baR.d.hg/bAR
19 $ echo bar>foo.d/baR.d.hg/bAR
20 $ hg commit -A -m 1
20 $ hg commit -A -m 1
21 adding foo
21 adding foo
22 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/bAr.hg.d/BaR
23 adding foo.d/baR.d.hg/bAR
23 adding foo.d/baR.d.hg/bAR
24 adding foo.d/foo
24 adding foo.d/foo
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
26 $ cat ../hg0.pid >> $DAEMON_PIDS
26 $ cat ../hg0.pid >> $DAEMON_PIDS
27
27
28 cacert not found
28 cacert not found
29
29
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
31 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
31 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
32 abort: could not find web.cacerts: no-such.pem
32 abort: could not find web.cacerts: no-such.pem
33 [255]
33 [255]
34
34
35 Test server address cannot be reused
35 Test server address cannot be reused
36
36
37 #if windows
37 #if windows
38 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
38 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
39 abort: cannot start server at 'localhost:$HGPORT': * (glob)
39 abort: cannot start server at 'localhost:$HGPORT': * (glob)
40 [255]
40 [255]
41 #else
41 #else
42 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
42 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
43 abort: cannot start server at 'localhost:$HGPORT': Address already in use
43 abort: cannot start server at 'localhost:$HGPORT': Address already in use
44 [255]
44 [255]
45 #endif
45 #endif
46 $ cd ..
46 $ cd ..
47
47
48 Our test cert is not signed by a trusted CA. It should fail to verify if
48 Our test cert is not signed by a trusted CA. It should fail to verify if
49 we are able to load CA certs.
49 we are able to load CA certs.
50
50
51 #if sslcontext defaultcacerts no-defaultcacertsloaded
51 #if sslcontext defaultcacerts no-defaultcacertsloaded
52 $ hg clone https://localhost:$HGPORT/ copy-pull
52 $ hg clone https://localhost:$HGPORT/ copy-pull
53 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
53 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
54 abort: error: *certificate verify failed* (glob)
54 abort: error: *certificate verify failed* (glob)
55 [255]
55 [255]
56 #endif
56 #endif
57
57
58 #if no-sslcontext defaultcacerts
58 #if no-sslcontext defaultcacerts
59 $ hg clone https://localhost:$HGPORT/ copy-pull
59 $ hg clone https://localhost:$HGPORT/ copy-pull
60 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
60 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
61 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
61 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
62 abort: error: *certificate verify failed* (glob)
62 abort: error: *certificate verify failed* (glob)
63 [255]
63 [255]
64 #endif
64 #endif
65
65
66 #if no-sslcontext windows
66 #if no-sslcontext windows
67 $ hg clone https://localhost:$HGPORT/ copy-pull
67 $ hg clone https://localhost:$HGPORT/ copy-pull
68 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
68 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
69 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
69 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
70 abort: error: *certificate verify failed* (glob)
70 abort: error: *certificate verify failed* (glob)
71 [255]
71 [255]
72 #endif
72 #endif
73
73
74 #if no-sslcontext osx
74 #if no-sslcontext osx
75 $ hg clone https://localhost:$HGPORT/ copy-pull
75 $ hg clone https://localhost:$HGPORT/ copy-pull
76 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
76 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
77 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
77 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
78 abort: localhost certificate error: no certificate received
78 abort: localhost certificate error: no certificate received
79 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
79 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
80 [255]
80 [255]
81 #endif
81 #endif
82
82
83 #if defaultcacertsloaded
83 #if defaultcacertsloaded
84 $ hg clone https://localhost:$HGPORT/ copy-pull
84 $ hg clone https://localhost:$HGPORT/ copy-pull
85 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
85 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
86 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
86 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
87 abort: error: *certificate verify failed* (glob)
87 abort: error: *certificate verify failed* (glob)
88 [255]
88 [255]
89 #endif
89 #endif
90
90
91 #if no-defaultcacerts
91 #if no-defaultcacerts
92 $ hg clone https://localhost:$HGPORT/ copy-pull
92 $ hg clone https://localhost:$HGPORT/ copy-pull
93 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
93 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
94 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
94 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
95 abort: localhost certificate error: no certificate received
95 abort: localhost certificate error: no certificate received
96 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
96 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
97 [255]
97 [255]
98 #endif
98 #endif
99
99
100 Specifying a per-host certificate file that doesn't exist will abort. The full
100 Specifying a per-host certificate file that doesn't exist will abort. The full
101 C:/path/to/msysroot will print on Windows.
101 C:/path/to/msysroot will print on Windows.
102
102
103 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
103 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
104 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
104 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
105 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: */does/not/exist (glob)
105 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: */does/not/exist (glob)
106 [255]
106 [255]
107
107
108 A malformed per-host certificate file will raise an error
108 A malformed per-host certificate file will raise an error
109
109
110 $ echo baddata > badca.pem
110 $ echo baddata > badca.pem
111 #if sslcontext
111 #if sslcontext
112 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
112 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
113 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
113 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
114 abort: error loading CA file badca.pem: * (glob)
114 abort: error loading CA file badca.pem: * (glob)
115 (file is empty or malformed?)
115 (file is empty or malformed?)
116 [255]
116 [255]
117 #else
117 #else
118 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
118 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
119 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
119 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
120 abort: error: * (glob)
120 abort: error: * (glob)
121 [255]
121 [255]
122 #endif
122 #endif
123
123
124 A per-host certificate mismatching the server will fail verification
124 A per-host certificate mismatching the server will fail verification
125
125
126 (modern ssl is able to discern whether the loaded cert is a CA cert)
126 (modern ssl is able to discern whether the loaded cert is a CA cert)
127 #if sslcontext
127 #if sslcontext
128 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
128 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
129 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
129 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
130 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
130 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
131 abort: error: *certificate verify failed* (glob)
131 abort: error: *certificate verify failed* (glob)
132 [255]
132 [255]
133 #else
133 #else
134 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
134 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
135 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
135 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
136 abort: error: *certificate verify failed* (glob)
136 abort: error: *certificate verify failed* (glob)
137 [255]
137 [255]
138 #endif
138 #endif
139
139
140 A per-host certificate matching the server's cert will be accepted
140 A per-host certificate matching the server's cert will be accepted
141
141
142 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
142 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
143 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
143 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
144 requesting all changes
144 requesting all changes
145 adding changesets
145 adding changesets
146 adding manifests
146 adding manifests
147 adding file changes
147 adding file changes
148 added 1 changesets with 4 changes to 4 files
148 added 1 changesets with 4 changes to 4 files
149
149
150 A per-host certificate with multiple certs and one matching will be accepted
150 A per-host certificate with multiple certs and one matching will be accepted
151
151
152 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
152 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
153 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
153 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
154 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
154 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
155 requesting all changes
155 requesting all changes
156 adding changesets
156 adding changesets
157 adding manifests
157 adding manifests
158 adding file changes
158 adding file changes
159 added 1 changesets with 4 changes to 4 files
159 added 1 changesets with 4 changes to 4 files
160
160
161 Defining both per-host certificate and a fingerprint will print a warning
161 Defining both per-host certificate and a fingerprint will print a warning
162
162
163 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
163 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
164 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
164 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
165 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
165 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
166 requesting all changes
166 requesting all changes
167 adding changesets
167 adding changesets
168 adding manifests
168 adding manifests
169 adding file changes
169 adding file changes
170 added 1 changesets with 4 changes to 4 files
170 added 1 changesets with 4 changes to 4 files
171
171
172 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
172 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
173
173
174 Inability to verify peer certificate will result in abort
174 Inability to verify peer certificate will result in abort
175
175
176 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
176 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
177 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
177 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
178 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
178 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
179 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
179 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
180 [255]
180 [255]
181
181
182 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
182 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
183 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
183 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
184 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
184 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
185 requesting all changes
185 requesting all changes
186 adding changesets
186 adding changesets
187 adding manifests
187 adding manifests
188 adding file changes
188 adding file changes
189 added 1 changesets with 4 changes to 4 files
189 added 1 changesets with 4 changes to 4 files
190 updating to branch default
190 updating to branch default
191 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 $ hg verify -R copy-pull
192 $ hg verify -R copy-pull
193 checking changesets
193 checking changesets
194 checking manifests
194 checking manifests
195 crosschecking files in changesets and manifests
195 crosschecking files in changesets and manifests
196 checking files
196 checking files
197 4 files, 1 changesets, 4 total revisions
197 4 files, 1 changesets, 4 total revisions
198 $ cd test
198 $ cd test
199 $ echo bar > bar
199 $ echo bar > bar
200 $ hg commit -A -d '1 0' -m 2
200 $ hg commit -A -d '1 0' -m 2
201 adding bar
201 adding bar
202 $ cd ..
202 $ cd ..
203
203
204 pull without cacert
204 pull without cacert
205
205
206 $ cd copy-pull
206 $ cd copy-pull
207 $ cat >> .hg/hgrc <<EOF
207 $ cat >> .hg/hgrc <<EOF
208 > [hooks]
208 > [hooks]
209 > changegroup = sh -c "printenv.py changegroup"
209 > changegroup = sh -c "printenv.py changegroup"
210 > EOF
210 > EOF
211 $ hg pull $DISABLECACERTS
211 $ hg pull $DISABLECACERTS
212 pulling from https://localhost:$HGPORT/
212 pulling from https://localhost:$HGPORT/
213 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
213 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
214 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
214 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
215 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
215 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
216 [255]
216 [255]
217
217
218 $ hg pull --insecure
218 $ hg pull --insecure
219 pulling from https://localhost:$HGPORT/
219 pulling from https://localhost:$HGPORT/
220 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
220 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
221 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
221 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
222 searching for changes
222 searching for changes
223 adding changesets
223 adding changesets
224 adding manifests
224 adding manifests
225 adding file changes
225 adding file changes
226 added 1 changesets with 1 changes to 1 files
226 added 1 changesets with 1 changes to 1 files
227 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=https://localhost:$HGPORT/
227 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=https://localhost:$HGPORT/
228 (run 'hg update' to get a working copy)
228 (run 'hg update' to get a working copy)
229 $ cd ..
229 $ cd ..
230
230
231 cacert configured in local repo
231 cacert configured in local repo
232
232
233 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
233 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
234 $ echo "[web]" >> copy-pull/.hg/hgrc
234 $ echo "[web]" >> copy-pull/.hg/hgrc
235 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
235 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
236 $ hg -R copy-pull pull
236 $ hg -R copy-pull pull
237 pulling from https://localhost:$HGPORT/
237 pulling from https://localhost:$HGPORT/
238 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
238 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
239 searching for changes
239 searching for changes
240 no changes found
240 no changes found
241 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
241 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
242
242
243 cacert configured globally, also testing expansion of environment
243 cacert configured globally, also testing expansion of environment
244 variables in the filename
244 variables in the filename
245
245
246 $ echo "[web]" >> $HGRCPATH
246 $ echo "[web]" >> $HGRCPATH
247 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
247 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
248 $ P="$CERTSDIR" hg -R copy-pull pull
248 $ P="$CERTSDIR" hg -R copy-pull pull
249 pulling from https://localhost:$HGPORT/
249 pulling from https://localhost:$HGPORT/
250 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
250 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
251 searching for changes
251 searching for changes
252 no changes found
252 no changes found
253 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
253 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
254 pulling from https://localhost:$HGPORT/
254 pulling from https://localhost:$HGPORT/
255 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
255 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
256 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
256 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
257 searching for changes
257 searching for changes
258 no changes found
258 no changes found
259
259
260 empty cacert file
260 empty cacert file
261
261
262 $ touch emptycafile
262 $ touch emptycafile
263
263
264 #if sslcontext
264 #if sslcontext
265 $ hg --config web.cacerts=emptycafile -R copy-pull pull
265 $ hg --config web.cacerts=emptycafile -R copy-pull pull
266 pulling from https://localhost:$HGPORT/
266 pulling from https://localhost:$HGPORT/
267 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
267 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
268 abort: error loading CA file emptycafile: * (glob)
268 abort: error loading CA file emptycafile: * (glob)
269 (file is empty or malformed?)
269 (file is empty or malformed?)
270 [255]
270 [255]
271 #else
271 #else
272 $ hg --config web.cacerts=emptycafile -R copy-pull pull
272 $ hg --config web.cacerts=emptycafile -R copy-pull pull
273 pulling from https://localhost:$HGPORT/
273 pulling from https://localhost:$HGPORT/
274 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
274 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
275 abort: error: * (glob)
275 abort: error: * (glob)
276 [255]
276 [255]
277 #endif
277 #endif
278
278
279 cacert mismatch
279 cacert mismatch
280
280
281 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
281 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
282 > https://$LOCALIP:$HGPORT/
282 > https://$LOCALIP:$HGPORT/
283 pulling from https://*:$HGPORT/ (glob)
283 pulling from https://*:$HGPORT/ (glob)
284 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
284 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
285 abort: $LOCALIP certificate error: certificate is for localhost (glob)
285 abort: $LOCALIP certificate error: certificate is for localhost (glob)
286 (set hostsecurity.$LOCALIP:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
286 (set hostsecurity.$LOCALIP:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
287 [255]
287 [255]
288 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
288 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
289 > https://$LOCALIP:$HGPORT/ --insecure
289 > https://$LOCALIP:$HGPORT/ --insecure
290 pulling from https://*:$HGPORT/ (glob)
290 pulling from https://*:$HGPORT/ (glob)
291 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
291 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
292 warning: connection security to $LOCALIP is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
292 warning: connection security to $LOCALIP is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
293 searching for changes
293 searching for changes
294 no changes found
294 no changes found
295 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
295 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
296 pulling from https://localhost:$HGPORT/
296 pulling from https://localhost:$HGPORT/
297 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
297 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
298 abort: error: *certificate verify failed* (glob)
298 abort: error: *certificate verify failed* (glob)
299 [255]
299 [255]
300 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
300 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
301 > --insecure
301 > --insecure
302 pulling from https://localhost:$HGPORT/
302 pulling from https://localhost:$HGPORT/
303 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
303 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
304 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
304 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
305 searching for changes
305 searching for changes
306 no changes found
306 no changes found
307
307
308 Test server cert which isn't valid yet
308 Test server cert which isn't valid yet
309
309
310 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
310 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
311 $ cat hg1.pid >> $DAEMON_PIDS
311 $ cat hg1.pid >> $DAEMON_PIDS
312 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
312 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
313 > https://localhost:$HGPORT1/
313 > https://localhost:$HGPORT1/
314 pulling from https://localhost:$HGPORT1/
314 pulling from https://localhost:$HGPORT1/
315 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
315 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
316 abort: error: *certificate verify failed* (glob)
316 abort: error: *certificate verify failed* (glob)
317 [255]
317 [255]
318
318
319 Test server cert which no longer is valid
319 Test server cert which no longer is valid
320
320
321 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
321 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
322 $ cat hg2.pid >> $DAEMON_PIDS
322 $ cat hg2.pid >> $DAEMON_PIDS
323 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
323 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
324 > https://localhost:$HGPORT2/
324 > https://localhost:$HGPORT2/
325 pulling from https://localhost:$HGPORT2/
325 pulling from https://localhost:$HGPORT2/
326 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
326 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
327 abort: error: *certificate verify failed* (glob)
327 abort: error: *certificate verify failed* (glob)
328 [255]
328 [255]
329
329
330 Disabling the TLS 1.0 warning works
330 Disabling the TLS 1.0 warning works
331 $ hg -R copy-pull id https://localhost:$HGPORT/ \
331 $ hg -R copy-pull id https://localhost:$HGPORT/ \
332 > --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 \
332 > --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 \
333 > --config hostsecurity.disabletls10warning=true
333 > --config hostsecurity.disabletls10warning=true
334 5fed3813f7f5
334 5fed3813f7f5
335
335
336 Error message for setting ciphers is different depending on SSLContext support
336 Error message for setting ciphers is different depending on SSLContext support
337
337
338 #if no-sslcontext
338 #if no-sslcontext
339 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
339 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
340 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
340 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
341 abort: *No cipher can be selected. (glob)
341 abort: *No cipher can be selected. (glob)
342 [255]
342 [255]
343
343
344 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
344 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
345 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
345 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
346 5fed3813f7f5
346 5fed3813f7f5
347 #endif
347 #endif
348
348
349 #if sslcontext
349 #if sslcontext
350 Setting ciphers to an invalid value aborts
350 Setting ciphers to an invalid value aborts
351 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
351 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
352 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
352 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
353 abort: could not set ciphers: No cipher can be selected.
353 abort: could not set ciphers: No cipher can be selected.
354 (change cipher string (invalid) in config)
354 (change cipher string (invalid) in config)
355 [255]
355 [255]
356
356
357 $ P="$CERTSDIR" hg --config hostsecurity.localhost:ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
357 $ P="$CERTSDIR" hg --config hostsecurity.localhost:ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
358 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
358 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
359 abort: could not set ciphers: No cipher can be selected.
359 abort: could not set ciphers: No cipher can be selected.
360 (change cipher string (invalid) in config)
360 (change cipher string (invalid) in config)
361 [255]
361 [255]
362
362
363 Changing the cipher string works
363 Changing the cipher string works
364
364
365 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
365 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
366 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
366 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
367 5fed3813f7f5
367 5fed3813f7f5
368 #endif
368 #endif
369
369
370 Fingerprints
370 Fingerprints
371
371
372 - works without cacerts (hostfingerprints)
372 - works without cacerts (hostfingerprints)
373 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
373 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
374 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
374 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
375 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost.fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
375 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
376 5fed3813f7f5
376 5fed3813f7f5
377
377
378 - works without cacerts (hostsecurity)
378 - works without cacerts (hostsecurity)
379 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
379 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
380 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
380 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
381 5fed3813f7f5
381 5fed3813f7f5
382
382
383 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
383 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
384 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
384 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
385 5fed3813f7f5
385 5fed3813f7f5
386
386
387 - multiple fingerprints specified and first matches
387 - multiple fingerprints specified and first matches
388 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
388 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
389 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
389 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
390 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost.fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
390 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
391 5fed3813f7f5
391 5fed3813f7f5
392
392
393 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
393 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
394 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
394 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
395 5fed3813f7f5
395 5fed3813f7f5
396
396
397 - multiple fingerprints specified and last matches
397 - multiple fingerprints specified and last matches
398 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
398 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
399 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
399 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
400 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost.fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
400 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
401 5fed3813f7f5
401 5fed3813f7f5
402
402
403 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
403 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
404 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
404 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
405 5fed3813f7f5
405 5fed3813f7f5
406
406
407 - multiple fingerprints specified and none match
407 - multiple fingerprints specified and none match
408
408
409 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
409 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
410 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
410 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
411 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
411 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
412 (check hostfingerprint configuration)
412 (check hostfingerprint configuration)
413 [255]
413 [255]
414
414
415 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
415 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
416 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
416 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
417 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
417 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
418 (check hostsecurity configuration)
418 (check hostsecurity configuration)
419 [255]
419 [255]
420
420
421 - fails when cert doesn't match hostname (port is ignored)
421 - fails when cert doesn't match hostname (port is ignored)
422 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
422 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
423 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
423 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
424 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
424 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
425 (check hostfingerprint configuration)
425 (check hostfingerprint configuration)
426 [255]
426 [255]
427
427
428
428
429 - ignores that certificate doesn't match hostname
429 - ignores that certificate doesn't match hostname
430 $ hg -R copy-pull id https://$LOCALIP:$HGPORT/ --config hostfingerprints.$LOCALIP=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
430 $ hg -R copy-pull id https://$LOCALIP:$HGPORT/ --config hostfingerprints.$LOCALIP=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
431 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
431 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
432 (SHA-1 fingerprint for $LOCALIP found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: $LOCALIP.fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
432 (SHA-1 fingerprint for $LOCALIP found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: $LOCALIP:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
433 5fed3813f7f5
433 5fed3813f7f5
434
434
435 Ports used by next test. Kill servers.
435 Ports used by next test. Kill servers.
436
436
437 $ killdaemons.py hg0.pid
437 $ killdaemons.py hg0.pid
438 $ killdaemons.py hg1.pid
438 $ killdaemons.py hg1.pid
439 $ killdaemons.py hg2.pid
439 $ killdaemons.py hg2.pid
440
440
441 #if sslcontext tls1.2
441 #if sslcontext tls1.2
442 Start servers running supported TLS versions
442 Start servers running supported TLS versions
443
443
444 $ cd test
444 $ cd test
445 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
445 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
446 > --config devel.serverexactprotocol=tls1.0
446 > --config devel.serverexactprotocol=tls1.0
447 $ cat ../hg0.pid >> $DAEMON_PIDS
447 $ cat ../hg0.pid >> $DAEMON_PIDS
448 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
448 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
449 > --config devel.serverexactprotocol=tls1.1
449 > --config devel.serverexactprotocol=tls1.1
450 $ cat ../hg1.pid >> $DAEMON_PIDS
450 $ cat ../hg1.pid >> $DAEMON_PIDS
451 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
451 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
452 > --config devel.serverexactprotocol=tls1.2
452 > --config devel.serverexactprotocol=tls1.2
453 $ cat ../hg2.pid >> $DAEMON_PIDS
453 $ cat ../hg2.pid >> $DAEMON_PIDS
454 $ cd ..
454 $ cd ..
455
455
456 Clients talking same TLS versions work
456 Clients talking same TLS versions work
457
457
458 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
458 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
459 5fed3813f7f5
459 5fed3813f7f5
460 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
460 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
461 5fed3813f7f5
461 5fed3813f7f5
462 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
462 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
463 5fed3813f7f5
463 5fed3813f7f5
464
464
465 Clients requiring newer TLS version than what server supports fail
465 Clients requiring newer TLS version than what server supports fail
466
466
467 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
467 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
468 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
468 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
469 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
469 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
470 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
470 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
471 abort: error: *unsupported protocol* (glob)
471 abort: error: *unsupported protocol* (glob)
472 [255]
472 [255]
473
473
474 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
474 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
475 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
475 (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
476 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
476 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
477 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
477 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
478 abort: error: *unsupported protocol* (glob)
478 abort: error: *unsupported protocol* (glob)
479 [255]
479 [255]
480 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
480 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
481 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
481 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
482 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
482 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
483 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
483 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
484 abort: error: *unsupported protocol* (glob)
484 abort: error: *unsupported protocol* (glob)
485 [255]
485 [255]
486 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
486 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
487 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
487 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
488 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
488 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
489 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
489 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
490 abort: error: *unsupported protocol* (glob)
490 abort: error: *unsupported protocol* (glob)
491 [255]
491 [255]
492
492
493 --insecure will allow TLS 1.0 connections and override configs
493 --insecure will allow TLS 1.0 connections and override configs
494
494
495 $ hg --config hostsecurity.minimumprotocol=tls1.2 id --insecure https://localhost:$HGPORT1/
495 $ hg --config hostsecurity.minimumprotocol=tls1.2 id --insecure https://localhost:$HGPORT1/
496 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
496 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
497 5fed3813f7f5
497 5fed3813f7f5
498
498
499 The per-host config option overrides the default
499 The per-host config option overrides the default
500
500
501 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
501 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
502 > --config hostsecurity.minimumprotocol=tls1.2 \
502 > --config hostsecurity.minimumprotocol=tls1.2 \
503 > --config hostsecurity.localhost:minimumprotocol=tls1.0
503 > --config hostsecurity.localhost:minimumprotocol=tls1.0
504 5fed3813f7f5
504 5fed3813f7f5
505
505
506 The per-host config option by itself works
506 The per-host config option by itself works
507
507
508 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
508 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
509 > --config hostsecurity.localhost:minimumprotocol=tls1.2
509 > --config hostsecurity.localhost:minimumprotocol=tls1.2
510 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
510 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
511 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
511 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
512 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
512 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
513 abort: error: *unsupported protocol* (glob)
513 abort: error: *unsupported protocol* (glob)
514 [255]
514 [255]
515
515
516 .hg/hgrc file [hostsecurity] settings are applied to remote ui instances (issue5305)
516 .hg/hgrc file [hostsecurity] settings are applied to remote ui instances (issue5305)
517
517
518 $ cat >> copy-pull/.hg/hgrc << EOF
518 $ cat >> copy-pull/.hg/hgrc << EOF
519 > [hostsecurity]
519 > [hostsecurity]
520 > localhost:minimumprotocol=tls1.2
520 > localhost:minimumprotocol=tls1.2
521 > EOF
521 > EOF
522 $ P="$CERTSDIR" hg -R copy-pull id https://localhost:$HGPORT/
522 $ P="$CERTSDIR" hg -R copy-pull id https://localhost:$HGPORT/
523 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
523 (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
524 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
524 (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
525 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
525 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
526 abort: error: *unsupported protocol* (glob)
526 abort: error: *unsupported protocol* (glob)
527 [255]
527 [255]
528
528
529 $ killdaemons.py hg0.pid
529 $ killdaemons.py hg0.pid
530 $ killdaemons.py hg1.pid
530 $ killdaemons.py hg1.pid
531 $ killdaemons.py hg2.pid
531 $ killdaemons.py hg2.pid
532 #endif
532 #endif
533
533
534 Prepare for connecting through proxy
534 Prepare for connecting through proxy
535
535
536 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
536 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
537 $ cat hg0.pid >> $DAEMON_PIDS
537 $ cat hg0.pid >> $DAEMON_PIDS
538 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
538 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
539 $ cat hg2.pid >> $DAEMON_PIDS
539 $ cat hg2.pid >> $DAEMON_PIDS
540 tinyproxy.py doesn't fully detach, so killing it may result in extra output
540 tinyproxy.py doesn't fully detach, so killing it may result in extra output
541 from the shell. So don't kill it.
541 from the shell. So don't kill it.
542 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
542 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
543 $ while [ ! -f proxy.pid ]; do sleep 0; done
543 $ while [ ! -f proxy.pid ]; do sleep 0; done
544 $ cat proxy.pid >> $DAEMON_PIDS
544 $ cat proxy.pid >> $DAEMON_PIDS
545
545
546 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
546 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
547 $ echo "always=True" >> copy-pull/.hg/hgrc
547 $ echo "always=True" >> copy-pull/.hg/hgrc
548 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
548 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
549 $ echo "localhost =" >> copy-pull/.hg/hgrc
549 $ echo "localhost =" >> copy-pull/.hg/hgrc
550
550
551 Test unvalidated https through proxy
551 Test unvalidated https through proxy
552
552
553 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure
553 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure
554 pulling from https://localhost:$HGPORT/
554 pulling from https://localhost:$HGPORT/
555 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
555 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
556 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
556 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
557 searching for changes
557 searching for changes
558 no changes found
558 no changes found
559
559
560 Test https with cacert and fingerprint through proxy
560 Test https with cacert and fingerprint through proxy
561
561
562 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
562 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
563 > --config web.cacerts="$CERTSDIR/pub.pem"
563 > --config web.cacerts="$CERTSDIR/pub.pem"
564 pulling from https://localhost:$HGPORT/
564 pulling from https://localhost:$HGPORT/
565 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
565 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
566 searching for changes
566 searching for changes
567 no changes found
567 no changes found
568 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://localhost:$HGPORT/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 --trace
568 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://localhost:$HGPORT/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 --trace
569 pulling from https://*:$HGPORT/ (glob)
569 pulling from https://*:$HGPORT/ (glob)
570 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
570 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
571 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost.fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
571 (SHA-1 fingerprint for localhost found in legacy [hostfingerprints] section; if you trust this fingerprint, set the following config value in [hostsecurity] and remove the old one from [hostfingerprints] to upgrade to a more secure SHA-256 fingerprint: localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e)
572 searching for changes
572 searching for changes
573 no changes found
573 no changes found
574
574
575 Test https with cert problems through proxy
575 Test https with cert problems through proxy
576
576
577 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
577 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
578 > --config web.cacerts="$CERTSDIR/pub-other.pem"
578 > --config web.cacerts="$CERTSDIR/pub-other.pem"
579 pulling from https://localhost:$HGPORT/
579 pulling from https://localhost:$HGPORT/
580 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
580 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
581 abort: error: *certificate verify failed* (glob)
581 abort: error: *certificate verify failed* (glob)
582 [255]
582 [255]
583 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
583 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
584 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
584 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
585 pulling from https://localhost:$HGPORT2/
585 pulling from https://localhost:$HGPORT2/
586 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
586 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
587 abort: error: *certificate verify failed* (glob)
587 abort: error: *certificate verify failed* (glob)
588 [255]
588 [255]
589
589
590
590
591 $ killdaemons.py hg0.pid
591 $ killdaemons.py hg0.pid
592
592
593 #if sslcontext
593 #if sslcontext
594
594
595 Start hgweb that requires client certificates:
595 Start hgweb that requires client certificates:
596
596
597 $ cd test
597 $ cd test
598 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
598 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
599 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
599 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
600 $ cat ../hg0.pid >> $DAEMON_PIDS
600 $ cat ../hg0.pid >> $DAEMON_PIDS
601 $ cd ..
601 $ cd ..
602
602
603 without client certificate:
603 without client certificate:
604
604
605 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
605 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
606 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
606 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
607 abort: error: *handshake failure* (glob)
607 abort: error: *handshake failure* (glob)
608 [255]
608 [255]
609
609
610 with client certificate:
610 with client certificate:
611
611
612 $ cat << EOT >> $HGRCPATH
612 $ cat << EOT >> $HGRCPATH
613 > [auth]
613 > [auth]
614 > l.prefix = localhost
614 > l.prefix = localhost
615 > l.cert = $CERTSDIR/client-cert.pem
615 > l.cert = $CERTSDIR/client-cert.pem
616 > l.key = $CERTSDIR/client-key.pem
616 > l.key = $CERTSDIR/client-key.pem
617 > EOT
617 > EOT
618
618
619 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
619 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
620 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
620 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
621 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
621 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
622 5fed3813f7f5
622 5fed3813f7f5
623
623
624 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
624 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
625 > --config ui.interactive=True --config ui.nontty=True
625 > --config ui.interactive=True --config ui.nontty=True
626 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
626 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
627 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
627 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
628
628
629 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
629 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
630 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
630 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
631 abort: error: * (glob)
631 abort: error: * (glob)
632 [255]
632 [255]
633
633
634 #endif
634 #endif
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now