##// END OF EJS Templates
merge with stable
Martin von Zweigbergk -
r32245:3a755652 merge default
parent child Browse files
Show More
@@ -1,55 +1,55 b''
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
3 3
4 4 <?include guids.wxi ?>
5 5 <?include defines.wxi ?>
6 6
7 7 <Fragment>
8 8 <ComponentGroup Id='helpFolder'>
9 9 <ComponentRef Id='help.root' />
10 10 <ComponentRef Id='help.internals' />
11 11 </ComponentGroup>
12 12 </Fragment>
13 13
14 14 <Fragment>
15 15 <DirectoryRef Id="INSTALLDIR">
16 16 <Directory Id="helpdir" Name="help" FileSource="$(var.SourceDir)">
17 17 <Component Id="help.root" Guid="$(var.help.root.guid)" Win64='$(var.IsX64)'>
18 18 <File Name="bundlespec.txt" />
19 19 <File Name="color.txt" />
20 20 <File Name="config.txt" KeyPath="yes" />
21 21 <File Name="dates.txt" />
22 22 <File Name="diffs.txt" />
23 23 <File Name="environment.txt" />
24 24 <File Name="extensions.txt" />
25 25 <File Name="filesets.txt" />
26 26 <File Name="glossary.txt" />
27 27 <File Name="hgignore.txt" />
28 28 <File Name="hgweb.txt" />
29 29 <File Name="merge-tools.txt" />
30 30 <File Name="pager.txt" />
31 31 <File Name="patterns.txt" />
32 32 <File Name="phases.txt" />
33 33 <File Name="revisions.txt" />
34 34 <File Name="scripting.txt" />
35 35 <File Name="subrepos.txt" />
36 36 <File Name="templates.txt" />
37 37 <File Name="urls.txt" />
38 38 </Component>
39 39
40 40 <Directory Id="help.internaldir" Name="internals">
41 41 <Component Id="help.internals" Guid="$(var.help.internals.guid)" Win64='$(var.IsX64)'>
42 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 44 <File Id="internals.changegroups.txt" Name="changegroups.txt" />
45 45 <File Id="internals.requirements.txt" Name="requirements.txt" />
46 46 <File Id="internals.revlogs.txt" Name="revlogs.txt" />
47 47 <File Id="internals.wireprotocol.txt" Name="wireprotocol.txt" />
48 48 </Component>
49 49 </Directory>
50 50
51 51 </Directory>
52 52 </DirectoryRef>
53 53 </Fragment>
54 54
55 55 </Wix>
@@ -1,211 +1,211 b''
1 1 # churn.py - create a graph of revisions count grouped by template
2 2 #
3 3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 4 # Copyright 2008 Alexander Solovyov <piranha@piranha.org.ua>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''command to display statistics about repository history'''
10 10
11 11 from __future__ import absolute_import
12 12
13 13 import datetime
14 14 import os
15 15 import time
16 16
17 17 from mercurial.i18n import _
18 18 from mercurial import (
19 19 cmdutil,
20 20 commands,
21 21 encoding,
22 22 patch,
23 23 scmutil,
24 24 util,
25 25 )
26 26
27 27 cmdtable = {}
28 28 command = cmdutil.command(cmdtable)
29 29 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
30 30 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
31 31 # be specifying the version(s) of Mercurial they are tested with, or
32 32 # leave the attribute unspecified.
33 33 testedwith = 'ships-with-hg-core'
34 34
35 35 def maketemplater(ui, repo, tmpl):
36 36 return cmdutil.changeset_templater(ui, repo, False, None, tmpl, None, False)
37 37
38 38 def changedlines(ui, repo, ctx1, ctx2, fns):
39 39 added, removed = 0, 0
40 40 fmatch = scmutil.matchfiles(repo, fns)
41 41 diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
42 42 for l in diff.split('\n'):
43 43 if l.startswith("+") and not l.startswith("+++ "):
44 44 added += 1
45 45 elif l.startswith("-") and not l.startswith("--- "):
46 46 removed += 1
47 47 return (added, removed)
48 48
49 49 def countrate(ui, repo, amap, *pats, **opts):
50 50 """Calculate stats"""
51 51 if opts.get('dateformat'):
52 52 def getkey(ctx):
53 53 t, tz = ctx.date()
54 54 date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
55 55 return date.strftime(opts['dateformat'])
56 56 else:
57 57 tmpl = opts.get('oldtemplate') or opts.get('template')
58 58 tmpl = maketemplater(ui, repo, tmpl)
59 59 def getkey(ctx):
60 60 ui.pushbuffer()
61 61 tmpl.show(ctx)
62 62 return ui.popbuffer()
63 63
64 64 state = {'count': 0}
65 65 rate = {}
66 66 df = False
67 67 if opts.get('date'):
68 68 df = util.matchdate(opts['date'])
69 69
70 70 m = scmutil.match(repo[None], pats, opts)
71 71 def prep(ctx, fns):
72 72 rev = ctx.rev()
73 73 if df and not df(ctx.date()[0]): # doesn't match date format
74 74 return
75 75
76 76 key = getkey(ctx).strip()
77 77 key = amap.get(key, key) # alias remap
78 78 if opts.get('changesets'):
79 79 rate[key] = (rate.get(key, (0,))[0] + 1, 0)
80 80 else:
81 81 parents = ctx.parents()
82 82 if len(parents) > 1:
83 83 ui.note(_('revision %d is a merge, ignoring...\n') % (rev,))
84 84 return
85 85
86 86 ctx1 = parents[0]
87 87 lines = changedlines(ui, repo, ctx1, ctx, fns)
88 88 rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
89 89
90 90 state['count'] += 1
91 91 ui.progress(_('analyzing'), state['count'], total=len(repo),
92 92 unit=_('revisions'))
93 93
94 94 for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
95 95 continue
96 96
97 97 ui.progress(_('analyzing'), None)
98 98
99 99 return rate
100 100
101 101
102 102 @command('churn',
103 103 [('r', 'rev', [],
104 104 _('count rate for the specified revision or revset'), _('REV')),
105 105 ('d', 'date', '',
106 106 _('count rate for revisions matching date spec'), _('DATE')),
107 107 ('t', 'oldtemplate', '',
108 108 _('template to group changesets (DEPRECATED)'), _('TEMPLATE')),
109 109 ('T', 'template', '{author|email}',
110 110 _('template to group changesets'), _('TEMPLATE')),
111 111 ('f', 'dateformat', '',
112 112 _('strftime-compatible format for grouping by date'), _('FORMAT')),
113 113 ('c', 'changesets', False, _('count rate by number of changesets')),
114 114 ('s', 'sort', False, _('sort by key (default: sort by count)')),
115 115 ('', 'diffstat', False, _('display added/removed lines separately')),
116 116 ('', 'aliases', '', _('file with email aliases'), _('FILE')),
117 117 ] + commands.walkopts,
118 118 _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]"),
119 119 inferrepo=True)
120 120 def churn(ui, repo, *pats, **opts):
121 121 '''histogram of changes to the repository
122 122
123 123 This command will display a histogram representing the number
124 124 of changed lines or revisions, grouped according to the given
125 125 template. The default template will group changes by author.
126 126 The --dateformat option may be used to group the results by
127 127 date instead.
128 128
129 129 Statistics are based on the number of changed lines, or
130 130 alternatively the number of matching revisions if the
131 131 --changesets option is specified.
132 132
133 133 Examples::
134 134
135 135 # display count of changed lines for every committer
136 hg churn -t "{author|email}"
136 hg churn -T "{author|email}"
137 137
138 138 # display daily activity graph
139 139 hg churn -f "%H" -s -c
140 140
141 141 # display activity of developers by month
142 142 hg churn -f "%Y-%m" -s -c
143 143
144 144 # display count of lines changed in every year
145 145 hg churn -f "%Y" -s
146 146
147 147 It is possible to map alternate email addresses to a main address
148 148 by providing a file using the following format::
149 149
150 150 <alias email> = <actual email>
151 151
152 152 Such a file may be specified with the --aliases option, otherwise
153 153 a .hgchurn file will be looked for in the working directory root.
154 154 Aliases will be split from the rightmost "=".
155 155 '''
156 156 def pad(s, l):
157 157 return s + " " * (l - encoding.colwidth(s))
158 158
159 159 amap = {}
160 160 aliases = opts.get('aliases')
161 161 if not aliases and os.path.exists(repo.wjoin('.hgchurn')):
162 162 aliases = repo.wjoin('.hgchurn')
163 163 if aliases:
164 164 for l in open(aliases, "r"):
165 165 try:
166 166 alias, actual = l.rsplit('=' in l and '=' or None, 1)
167 167 amap[alias.strip()] = actual.strip()
168 168 except ValueError:
169 169 l = l.strip()
170 170 if l:
171 171 ui.warn(_("skipping malformed alias: %s\n") % l)
172 172 continue
173 173
174 174 rate = countrate(ui, repo, amap, *pats, **opts).items()
175 175 if not rate:
176 176 return
177 177
178 178 if opts.get('sort'):
179 179 rate.sort()
180 180 else:
181 181 rate.sort(key=lambda x: (-sum(x[1]), x))
182 182
183 183 # Be careful not to have a zero maxcount (issue833)
184 184 maxcount = float(max(sum(v) for k, v in rate)) or 1.0
185 185 maxname = max(len(k) for k, v in rate)
186 186
187 187 ttywidth = ui.termwidth()
188 188 ui.debug("assuming %i character terminal\n" % ttywidth)
189 189 width = ttywidth - maxname - 2 - 2 - 2
190 190
191 191 if opts.get('diffstat'):
192 192 width -= 15
193 193 def format(name, diffstat):
194 194 added, removed = diffstat
195 195 return "%s %15s %s%s\n" % (pad(name, maxname),
196 196 '+%d/-%d' % (added, removed),
197 197 ui.label('+' * charnum(added),
198 198 'diffstat.inserted'),
199 199 ui.label('-' * charnum(removed),
200 200 'diffstat.deleted'))
201 201 else:
202 202 width -= 6
203 203 def format(name, count):
204 204 return "%s %6d %s\n" % (pad(name, maxname), sum(count),
205 205 '*' * charnum(sum(count)))
206 206
207 207 def charnum(count):
208 208 return int(round(count * width / maxcount))
209 209
210 210 for name, count in rate:
211 211 ui.write(format(name, count))
@@ -1,204 +1,210 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''setup for largefiles extension: uisetup'''
10 10 from __future__ import absolute_import
11 11
12 12 from mercurial.i18n import _
13 13
14 14 from mercurial.hgweb import (
15 15 hgweb_mod,
16 16 webcommands,
17 17 )
18 18
19 19 from mercurial import (
20 20 archival,
21 21 cmdutil,
22 22 commands,
23 23 copies,
24 debugcommands,
24 25 exchange,
25 26 extensions,
26 27 filemerge,
27 28 hg,
28 29 httppeer,
29 30 merge,
30 31 scmutil,
31 32 sshpeer,
32 33 subrepo,
33 34 wireproto,
34 35 )
35 36
36 37 from . import (
37 38 overrides,
38 39 proto,
39 40 )
40 41
41 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 48 # Disable auto-status for some commands which assume that all
43 49 # files in the result are under Mercurial's control
44 50
45 51 entry = extensions.wrapcommand(commands.table, 'add',
46 52 overrides.overrideadd)
47 53 addopt = [('', 'large', None, _('add as largefile')),
48 54 ('', 'normal', None, _('add as normal file')),
49 55 ('', 'lfsize', '', _('add all files above this size '
50 56 '(in megabytes) as largefiles '
51 57 '(default: 10)'))]
52 58 entry[1].extend(addopt)
53 59
54 60 # The scmutil function is called both by the (trivial) addremove command,
55 61 # and in the process of handling commit -A (issue3542)
56 62 entry = extensions.wrapfunction(scmutil, 'addremove',
57 63 overrides.scmutiladdremove)
58 64 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
59 65 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
60 66 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
61 67
62 68 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
63 69
64 70 # Subrepos call status function
65 71 entry = extensions.wrapcommand(commands.table, 'status',
66 72 overrides.overridestatus)
67 73 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
68 74 overrides.overridestatusfn)
69 75
70 76 entry = extensions.wrapcommand(commands.table, 'log',
71 77 overrides.overridelog)
72 78 entry = extensions.wrapcommand(commands.table, 'rollback',
73 79 overrides.overriderollback)
74 80 entry = extensions.wrapcommand(commands.table, 'verify',
75 81 overrides.overrideverify)
76 82
77 83 verifyopt = [('', 'large', None,
78 84 _('verify that all largefiles in current revision exists')),
79 85 ('', 'lfa', None,
80 86 _('verify largefiles in all revisions, not just current')),
81 87 ('', 'lfc', None,
82 88 _('verify local largefile contents, not just existence'))]
83 89 entry[1].extend(verifyopt)
84 90
85 91 entry = extensions.wrapcommand(commands.table, 'debugstate',
86 92 overrides.overridedebugstate)
87 93 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
88 94 entry[1].extend(debugstateopt)
89 95
90 96 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
91 97 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
92 98 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
93 99 entry[1].extend(outgoingopt)
94 100 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
95 101 entry = extensions.wrapcommand(commands.table, 'summary',
96 102 overrides.overridesummary)
97 103 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
98 104 entry[1].extend(summaryopt)
99 105 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
100 106
101 107 entry = extensions.wrapcommand(commands.table, 'pull',
102 108 overrides.overridepull)
103 109 pullopt = [('', 'all-largefiles', None,
104 110 _('download all pulled versions of largefiles (DEPRECATED)')),
105 111 ('', 'lfrev', [],
106 112 _('download largefiles for these revisions'), _('REV'))]
107 113 entry[1].extend(pullopt)
108 114
109 115 entry = extensions.wrapcommand(commands.table, 'push',
110 116 overrides.overridepush)
111 117 pushopt = [('', 'lfrev', [],
112 118 _('upload largefiles for these revisions'), _('REV'))]
113 119 entry[1].extend(pushopt)
114 120 entry = extensions.wrapfunction(exchange, 'pushoperation',
115 121 overrides.exchangepushoperation)
116 122
117 123 entry = extensions.wrapcommand(commands.table, 'clone',
118 124 overrides.overrideclone)
119 125 cloneopt = [('', 'all-largefiles', None,
120 126 _('download all versions of all largefiles'))]
121 127 entry[1].extend(cloneopt)
122 128 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
123 129 entry = extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
124 130
125 131 entry = extensions.wrapcommand(commands.table, 'cat',
126 132 overrides.overridecat)
127 133 entry = extensions.wrapfunction(merge, '_checkunknownfile',
128 134 overrides.overridecheckunknownfile)
129 135 entry = extensions.wrapfunction(merge, 'calculateupdates',
130 136 overrides.overridecalculateupdates)
131 137 entry = extensions.wrapfunction(merge, 'recordupdates',
132 138 overrides.mergerecordupdates)
133 139 entry = extensions.wrapfunction(merge, 'update',
134 140 overrides.mergeupdate)
135 141 entry = extensions.wrapfunction(filemerge, '_filemerge',
136 142 overrides.overridefilemerge)
137 143 entry = extensions.wrapfunction(cmdutil, 'copy',
138 144 overrides.overridecopy)
139 145
140 146 # Summary calls dirty on the subrepos
141 147 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
142 148 overrides.overridedirty)
143 149
144 150 entry = extensions.wrapfunction(cmdutil, 'revert',
145 151 overrides.overriderevert)
146 152
147 153 extensions.wrapcommand(commands.table, 'archive',
148 154 overrides.overridearchivecmd)
149 155 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
150 156 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
151 157 overrides.hgsubrepoarchive)
152 158 extensions.wrapfunction(webcommands, 'archive',
153 159 overrides.hgwebarchive)
154 160 extensions.wrapfunction(cmdutil, 'bailifchanged',
155 161 overrides.overridebailifchanged)
156 162
157 163 extensions.wrapfunction(cmdutil, 'postcommitstatus',
158 164 overrides.postcommitstatus)
159 165 extensions.wrapfunction(scmutil, 'marktouched',
160 166 overrides.scmutilmarktouched)
161 167
162 168 # create the new wireproto commands ...
163 169 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
164 170 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
165 171 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
166 172
167 173 # ... and wrap some existing ones
168 174 wireproto.commands['capabilities'] = (proto.capabilities, '')
169 175 wireproto.commands['heads'] = (proto.heads, '')
170 176 wireproto.commands['lheads'] = (wireproto.heads, '')
171 177
172 178 # make putlfile behave the same as push and {get,stat}lfile behave
173 179 # the same as pull w.r.t. permissions checks
174 180 hgweb_mod.perms['putlfile'] = 'push'
175 181 hgweb_mod.perms['getlfile'] = 'pull'
176 182 hgweb_mod.perms['statlfile'] = 'pull'
177 183
178 184 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
179 185
180 186 # the hello wireproto command uses wireproto.capabilities, so it won't see
181 187 # our largefiles capability unless we replace the actual function as well.
182 188 proto.capabilitiesorig = wireproto.capabilities
183 189 wireproto.capabilities = proto.capabilities
184 190
185 191 # can't do this in reposetup because it needs to have happened before
186 192 # wirerepo.__init__ is called
187 193 proto.ssholdcallstream = sshpeer.sshpeer._callstream
188 194 proto.httpoldcallstream = httppeer.httppeer._callstream
189 195 sshpeer.sshpeer._callstream = proto.sshrepocallstream
190 196 httppeer.httppeer._callstream = proto.httprepocallstream
191 197
192 198 # override some extensions' stuff as well
193 199 for name, module in extensions.extensions():
194 200 if name == 'purge':
195 201 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
196 202 overrides.overridepurge)
197 203 if name == 'rebase':
198 204 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
199 205 overrides.overriderebase)
200 206 extensions.wrapfunction(module, 'rebase',
201 207 overrides.overriderebase)
202 208 if name == 'transplant':
203 209 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
204 210 overrides.overridetransplant)
@@ -1,195 +1,196 b''
1 1 # win32mbcs.py -- MBCS filename support for Mercurial
2 2 #
3 3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 4 #
5 5 # Version: 0.3
6 6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 7 #
8 8 # This software may be used and distributed according to the terms of the
9 9 # GNU General Public License version 2 or any later version.
10 10 #
11 11
12 12 '''allow the use of MBCS paths with problematic encodings
13 13
14 14 Some MBCS encodings are not good for some path operations (i.e.
15 15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 17 This extension can be used to fix the issue with those encodings by
18 18 wrapping some functions to convert to Unicode string before path
19 19 operation.
20 20
21 21 This extension is useful for:
22 22
23 23 - Japanese Windows users using shift_jis encoding.
24 24 - Chinese Windows users using big5 encoding.
25 25 - All users who use a repository with one of problematic encodings on
26 26 case-insensitive file system.
27 27
28 28 This extension is not needed for:
29 29
30 30 - Any user who use only ASCII chars in path.
31 31 - Any user who do not use any of problematic encodings.
32 32
33 33 Note that there are some limitations on using this extension:
34 34
35 35 - You should use single encoding in one repository.
36 36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
37 37 - win32mbcs is not compatible with fixutf8 extension.
38 38
39 39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
40 40 You can specify the encoding by config option::
41 41
42 42 [win32mbcs]
43 43 encoding = sjis
44 44
45 45 It is useful for the users who want to commit with UTF-8 log message.
46 46 '''
47 47 from __future__ import absolute_import
48 48
49 49 import os
50 50 import sys
51 51
52 52 from mercurial.i18n import _
53 53 from mercurial import (
54 54 encoding,
55 55 error,
56 56 pycompat,
57 57 )
58 58
59 59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
60 60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
61 61 # be specifying the version(s) of Mercurial they are tested with, or
62 62 # leave the attribute unspecified.
63 63 testedwith = 'ships-with-hg-core'
64 64
65 65 _encoding = None # see extsetup
66 66
67 67 def decode(arg):
68 68 if isinstance(arg, str):
69 69 uarg = arg.decode(_encoding)
70 70 if arg == uarg.encode(_encoding):
71 71 return uarg
72 72 raise UnicodeError("Not local encoding")
73 73 elif isinstance(arg, tuple):
74 74 return tuple(map(decode, arg))
75 75 elif isinstance(arg, list):
76 76 return map(decode, arg)
77 77 elif isinstance(arg, dict):
78 78 for k, v in arg.items():
79 79 arg[k] = decode(v)
80 80 return arg
81 81
82 82 def encode(arg):
83 83 if isinstance(arg, unicode):
84 84 return arg.encode(_encoding)
85 85 elif isinstance(arg, tuple):
86 86 return tuple(map(encode, arg))
87 87 elif isinstance(arg, list):
88 88 return map(encode, arg)
89 89 elif isinstance(arg, dict):
90 90 for k, v in arg.items():
91 91 arg[k] = encode(v)
92 92 return arg
93 93
94 94 def appendsep(s):
95 95 # ensure the path ends with os.sep, appending it if necessary.
96 96 try:
97 97 us = decode(s)
98 98 except UnicodeError:
99 99 us = s
100 100 if us and us[-1] not in ':/\\':
101 101 s += pycompat.ossep
102 102 return s
103 103
104 104
105 105 def basewrapper(func, argtype, enc, dec, args, kwds):
106 106 # check check already converted, then call original
107 107 for arg in args:
108 108 if isinstance(arg, argtype):
109 109 return func(*args, **kwds)
110 110
111 111 try:
112 112 # convert string arguments, call func, then convert back the
113 113 # return value.
114 114 return enc(func(*dec(args), **dec(kwds)))
115 115 except UnicodeError:
116 116 raise error.Abort(_("[win32mbcs] filename conversion failed with"
117 117 " %s encoding\n") % (_encoding))
118 118
119 119 def wrapper(func, args, kwds):
120 120 return basewrapper(func, unicode, encode, decode, args, kwds)
121 121
122 122
123 123 def reversewrapper(func, args, kwds):
124 124 return basewrapper(func, str, decode, encode, args, kwds)
125 125
126 126 def wrapperforlistdir(func, args, kwds):
127 127 # Ensure 'path' argument ends with os.sep to avoids
128 128 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
129 129 if args:
130 130 args = list(args)
131 131 args[0] = appendsep(args[0])
132 132 if 'path' in kwds:
133 133 kwds['path'] = appendsep(kwds['path'])
134 134 return func(*args, **kwds)
135 135
136 136 def wrapname(name, wrapper):
137 137 module, name = name.rsplit('.', 1)
138 138 module = sys.modules[module]
139 139 func = getattr(module, name)
140 140 def f(*args, **kwds):
141 141 return wrapper(func, args, kwds)
142 142 f.__name__ = func.__name__
143 143 setattr(module, name, f)
144 144
145 145 # List of functions to be wrapped.
146 146 # NOTE: os.path.dirname() and os.path.basename() are safe because
147 147 # they use result of os.path.split()
148 148 funcs = '''os.path.join os.path.split os.path.splitext
149 149 os.path.normpath os.makedirs mercurial.util.endswithsep
150 150 mercurial.util.splitpath mercurial.util.fscasesensitive
151 151 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
152 152 mercurial.util.checkwinfilename mercurial.util.checkosfilename
153 153 mercurial.util.split'''
154 154
155 155 # These functions are required to be called with local encoded string
156 156 # because they expects argument is local encoded string and cause
157 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 161 # List of Windows specific functions to be wrapped.
161 162 winfuncs = '''os.path.splitunc'''
162 163
163 164 # codec and alias names of sjis and big5 to be faked.
164 165 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
165 166 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
166 167 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
167 168 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
168 169
169 170 def extsetup(ui):
170 171 # TODO: decide use of config section for this extension
171 172 if ((not os.path.supports_unicode_filenames) and
172 173 (pycompat.sysplatform != 'cygwin')):
173 174 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
174 175 return
175 176 # determine encoding for filename
176 177 global _encoding
177 178 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
178 179 # fake is only for relevant environment.
179 180 if _encoding.lower() in problematic_encodings.split():
180 181 for f in funcs.split():
181 182 wrapname(f, wrapper)
182 183 if pycompat.osname == 'nt':
183 184 for f in winfuncs.split():
184 185 wrapname(f, wrapper)
185 186 wrapname("mercurial.util.listdir", wrapperforlistdir)
186 187 wrapname("mercurial.windows.listdir", wrapperforlistdir)
187 188 # wrap functions to be called with local byte string arguments
188 189 for f in rfuncs.split():
189 190 wrapname(f, reversewrapper)
190 191 # Check sys.args manually instead of using ui.debug() because
191 192 # command line options is not yet applied when
192 193 # extensions.loadall() is called.
193 194 if '--debug' in sys.argv:
194 195 ui.write(("[win32mbcs] activated with encoding: %s\n")
195 196 % _encoding)
@@ -1,5518 +1,5520 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import os
13 13 import re
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 hex,
18 18 nullid,
19 19 nullrev,
20 20 short,
21 21 )
22 22 from . import (
23 23 archival,
24 24 bookmarks,
25 25 bundle2,
26 26 changegroup,
27 27 cmdutil,
28 28 copies,
29 29 destutil,
30 30 dirstateguard,
31 31 discovery,
32 32 encoding,
33 33 error,
34 34 exchange,
35 35 extensions,
36 36 graphmod,
37 37 hbisect,
38 38 help,
39 39 hg,
40 40 lock as lockmod,
41 41 merge as mergemod,
42 42 obsolete,
43 43 patch,
44 44 phases,
45 45 pycompat,
46 46 rcutil,
47 47 revsetlang,
48 48 scmutil,
49 49 server,
50 50 sshserver,
51 51 streamclone,
52 52 tags as tagsmod,
53 53 templatekw,
54 54 ui as uimod,
55 55 util,
56 56 )
57 57
58 58 release = lockmod.release
59 59
60 60 table = {}
61 61
62 62 command = cmdutil.command(table)
63 63
64 64 # label constants
65 65 # until 3.5, bookmarks.current was the advertised name, not
66 66 # bookmarks.active, so we must use both to avoid breaking old
67 67 # custom styles
68 68 activebookmarklabel = 'bookmarks.active bookmarks.current'
69 69
70 70 # common command options
71 71
72 72 globalopts = [
73 73 ('R', 'repository', '',
74 74 _('repository root directory or name of overlay bundle file'),
75 75 _('REPO')),
76 76 ('', 'cwd', '',
77 77 _('change working directory'), _('DIR')),
78 78 ('y', 'noninteractive', None,
79 79 _('do not prompt, automatically pick the first choice for all prompts')),
80 80 ('q', 'quiet', None, _('suppress output')),
81 81 ('v', 'verbose', None, _('enable additional output')),
82 82 ('', 'color', '',
83 83 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
84 84 # and should not be translated
85 85 _("when to colorize (boolean, always, auto, never, or debug)"),
86 86 _('TYPE')),
87 87 ('', 'config', [],
88 88 _('set/override config option (use \'section.name=value\')'),
89 89 _('CONFIG')),
90 90 ('', 'debug', None, _('enable debugging output')),
91 91 ('', 'debugger', None, _('start debugger')),
92 92 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
93 93 _('ENCODE')),
94 94 ('', 'encodingmode', encoding.encodingmode,
95 95 _('set the charset encoding mode'), _('MODE')),
96 96 ('', 'traceback', None, _('always print a traceback on exception')),
97 97 ('', 'time', None, _('time how long the command takes')),
98 98 ('', 'profile', None, _('print command execution profile')),
99 99 ('', 'version', None, _('output version information and exit')),
100 100 ('h', 'help', None, _('display help and exit')),
101 101 ('', 'hidden', False, _('consider hidden changesets')),
102 102 ('', 'pager', 'auto',
103 103 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
104 104 ]
105 105
106 106 dryrunopts = [('n', 'dry-run', None,
107 107 _('do not perform actions, just print output'))]
108 108
109 109 remoteopts = [
110 110 ('e', 'ssh', '',
111 111 _('specify ssh command to use'), _('CMD')),
112 112 ('', 'remotecmd', '',
113 113 _('specify hg command to run on the remote side'), _('CMD')),
114 114 ('', 'insecure', None,
115 115 _('do not verify server certificate (ignoring web.cacerts config)')),
116 116 ]
117 117
118 118 walkopts = [
119 119 ('I', 'include', [],
120 120 _('include names matching the given patterns'), _('PATTERN')),
121 121 ('X', 'exclude', [],
122 122 _('exclude names matching the given patterns'), _('PATTERN')),
123 123 ]
124 124
125 125 commitopts = [
126 126 ('m', 'message', '',
127 127 _('use text as commit message'), _('TEXT')),
128 128 ('l', 'logfile', '',
129 129 _('read commit message from file'), _('FILE')),
130 130 ]
131 131
132 132 commitopts2 = [
133 133 ('d', 'date', '',
134 134 _('record the specified date as commit date'), _('DATE')),
135 135 ('u', 'user', '',
136 136 _('record the specified user as committer'), _('USER')),
137 137 ]
138 138
139 139 # hidden for now
140 140 formatteropts = [
141 141 ('T', 'template', '',
142 142 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
143 143 ]
144 144
145 145 templateopts = [
146 146 ('', 'style', '',
147 147 _('display using template map file (DEPRECATED)'), _('STYLE')),
148 148 ('T', 'template', '',
149 149 _('display with template'), _('TEMPLATE')),
150 150 ]
151 151
152 152 logopts = [
153 153 ('p', 'patch', None, _('show patch')),
154 154 ('g', 'git', None, _('use git extended diff format')),
155 155 ('l', 'limit', '',
156 156 _('limit number of changes displayed'), _('NUM')),
157 157 ('M', 'no-merges', None, _('do not show merges')),
158 158 ('', 'stat', None, _('output diffstat-style summary of changes')),
159 159 ('G', 'graph', None, _("show the revision DAG")),
160 160 ] + templateopts
161 161
162 162 diffopts = [
163 163 ('a', 'text', None, _('treat all files as text')),
164 164 ('g', 'git', None, _('use git extended diff format')),
165 165 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
166 166 ('', 'nodates', None, _('omit dates from diff headers'))
167 167 ]
168 168
169 169 diffwsopts = [
170 170 ('w', 'ignore-all-space', None,
171 171 _('ignore white space when comparing lines')),
172 172 ('b', 'ignore-space-change', None,
173 173 _('ignore changes in the amount of white space')),
174 174 ('B', 'ignore-blank-lines', None,
175 175 _('ignore changes whose lines are all blank')),
176 176 ]
177 177
178 178 diffopts2 = [
179 179 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
180 180 ('p', 'show-function', None, _('show which function each change is in')),
181 181 ('', 'reverse', None, _('produce a diff that undoes the changes')),
182 182 ] + diffwsopts + [
183 183 ('U', 'unified', '',
184 184 _('number of lines of context to show'), _('NUM')),
185 185 ('', 'stat', None, _('output diffstat-style summary of changes')),
186 186 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
187 187 ]
188 188
189 189 mergetoolopts = [
190 190 ('t', 'tool', '', _('specify merge tool')),
191 191 ]
192 192
193 193 similarityopts = [
194 194 ('s', 'similarity', '',
195 195 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
196 196 ]
197 197
198 198 subrepoopts = [
199 199 ('S', 'subrepos', None,
200 200 _('recurse into subrepositories'))
201 201 ]
202 202
203 203 debugrevlogopts = [
204 204 ('c', 'changelog', False, _('open changelog')),
205 205 ('m', 'manifest', False, _('open manifest')),
206 206 ('', 'dir', '', _('open directory manifest')),
207 207 ]
208 208
209 209 # Commands start here, listed alphabetically
210 210
211 211 @command('^add',
212 212 walkopts + subrepoopts + dryrunopts,
213 213 _('[OPTION]... [FILE]...'),
214 214 inferrepo=True)
215 215 def add(ui, repo, *pats, **opts):
216 216 """add the specified files on the next commit
217 217
218 218 Schedule files to be version controlled and added to the
219 219 repository.
220 220
221 221 The files will be added to the repository at the next commit. To
222 222 undo an add before that, see :hg:`forget`.
223 223
224 224 If no names are given, add all files to the repository (except
225 225 files matching ``.hgignore``).
226 226
227 227 .. container:: verbose
228 228
229 229 Examples:
230 230
231 231 - New (unknown) files are added
232 232 automatically by :hg:`add`::
233 233
234 234 $ ls
235 235 foo.c
236 236 $ hg status
237 237 ? foo.c
238 238 $ hg add
239 239 adding foo.c
240 240 $ hg status
241 241 A foo.c
242 242
243 243 - Specific files to be added can be specified::
244 244
245 245 $ ls
246 246 bar.c foo.c
247 247 $ hg status
248 248 ? bar.c
249 249 ? foo.c
250 250 $ hg add bar.c
251 251 $ hg status
252 252 A bar.c
253 253 ? foo.c
254 254
255 255 Returns 0 if all files are successfully added.
256 256 """
257 257
258 258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 259 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
260 260 return rejected and 1 or 0
261 261
262 262 @command('addremove',
263 263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 264 _('[OPTION]... [FILE]...'),
265 265 inferrepo=True)
266 266 def addremove(ui, repo, *pats, **opts):
267 267 """add all new files, delete all missing files
268 268
269 269 Add all new files and remove all missing files from the
270 270 repository.
271 271
272 272 Unless names are given, new files are ignored if they match any of
273 273 the patterns in ``.hgignore``. As with add, these changes take
274 274 effect at the next commit.
275 275
276 276 Use the -s/--similarity option to detect renamed files. This
277 277 option takes a percentage between 0 (disabled) and 100 (files must
278 278 be identical) as its parameter. With a parameter greater than 0,
279 279 this compares every removed file with every added file and records
280 280 those similar enough as renames. Detecting renamed files this way
281 281 can be expensive. After using this option, :hg:`status -C` can be
282 282 used to check which files were identified as moved or renamed. If
283 283 not specified, -s/--similarity defaults to 100 and only renames of
284 284 identical files are detected.
285 285
286 286 .. container:: verbose
287 287
288 288 Examples:
289 289
290 290 - A number of files (bar.c and foo.c) are new,
291 291 while foobar.c has been removed (without using :hg:`remove`)
292 292 from the repository::
293 293
294 294 $ ls
295 295 bar.c foo.c
296 296 $ hg status
297 297 ! foobar.c
298 298 ? bar.c
299 299 ? foo.c
300 300 $ hg addremove
301 301 adding bar.c
302 302 adding foo.c
303 303 removing foobar.c
304 304 $ hg status
305 305 A bar.c
306 306 A foo.c
307 307 R foobar.c
308 308
309 309 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 310 Afterwards, it was edited slightly::
311 311
312 312 $ ls
313 313 foo.c
314 314 $ hg status
315 315 ! foobar.c
316 316 ? foo.c
317 317 $ hg addremove --similarity 90
318 318 removing foobar.c
319 319 adding foo.c
320 320 recording removal of foobar.c as rename to foo.c (94% similar)
321 321 $ hg status -C
322 322 A foo.c
323 323 foobar.c
324 324 R foobar.c
325 325
326 326 Returns 0 if all files are successfully added.
327 327 """
328 328 opts = pycompat.byteskwargs(opts)
329 329 try:
330 330 sim = float(opts.get('similarity') or 100)
331 331 except ValueError:
332 332 raise error.Abort(_('similarity must be a number'))
333 333 if sim < 0 or sim > 100:
334 334 raise error.Abort(_('similarity must be between 0 and 100'))
335 335 matcher = scmutil.match(repo[None], pats, opts)
336 336 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
337 337
338 338 @command('^annotate|blame',
339 339 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
340 340 ('', 'follow', None,
341 341 _('follow copies/renames and list the filename (DEPRECATED)')),
342 342 ('', 'no-follow', None, _("don't follow copies and renames")),
343 343 ('a', 'text', None, _('treat all files as text')),
344 344 ('u', 'user', None, _('list the author (long with -v)')),
345 345 ('f', 'file', None, _('list the filename')),
346 346 ('d', 'date', None, _('list the date (short with -q)')),
347 347 ('n', 'number', None, _('list the revision number (default)')),
348 348 ('c', 'changeset', None, _('list the changeset')),
349 349 ('l', 'line-number', None, _('show line number at the first appearance'))
350 350 ] + diffwsopts + walkopts + formatteropts,
351 351 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
352 352 inferrepo=True)
353 353 def annotate(ui, repo, *pats, **opts):
354 354 """show changeset information by line for each file
355 355
356 356 List changes in files, showing the revision id responsible for
357 357 each line.
358 358
359 359 This command is useful for discovering when a change was made and
360 360 by whom.
361 361
362 362 If you include --file, --user, or --date, the revision number is
363 363 suppressed unless you also include --number.
364 364
365 365 Without the -a/--text option, annotate will avoid processing files
366 366 it detects as binary. With -a, annotate will annotate the file
367 367 anyway, although the results will probably be neither useful
368 368 nor desirable.
369 369
370 370 Returns 0 on success.
371 371 """
372 372 opts = pycompat.byteskwargs(opts)
373 373 if not pats:
374 374 raise error.Abort(_('at least one filename or pattern is required'))
375 375
376 376 if opts.get('follow'):
377 377 # --follow is deprecated and now just an alias for -f/--file
378 378 # to mimic the behavior of Mercurial before version 1.5
379 379 opts['file'] = True
380 380
381 381 ctx = scmutil.revsingle(repo, opts.get('rev'))
382 382
383 383 fm = ui.formatter('annotate', opts)
384 384 if ui.quiet:
385 385 datefunc = util.shortdate
386 386 else:
387 387 datefunc = util.datestr
388 388 if ctx.rev() is None:
389 389 def hexfn(node):
390 390 if node is None:
391 391 return None
392 392 else:
393 393 return fm.hexfunc(node)
394 394 if opts.get('changeset'):
395 395 # omit "+" suffix which is appended to node hex
396 396 def formatrev(rev):
397 397 if rev is None:
398 398 return '%d' % ctx.p1().rev()
399 399 else:
400 400 return '%d' % rev
401 401 else:
402 402 def formatrev(rev):
403 403 if rev is None:
404 404 return '%d+' % ctx.p1().rev()
405 405 else:
406 406 return '%d ' % rev
407 407 def formathex(hex):
408 408 if hex is None:
409 409 return '%s+' % fm.hexfunc(ctx.p1().node())
410 410 else:
411 411 return '%s ' % hex
412 412 else:
413 413 hexfn = fm.hexfunc
414 414 formatrev = formathex = str
415 415
416 416 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
417 417 ('number', ' ', lambda x: x[0].rev(), formatrev),
418 418 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
419 419 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
420 420 ('file', ' ', lambda x: x[0].path(), str),
421 421 ('line_number', ':', lambda x: x[1], str),
422 422 ]
423 423 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
424 424
425 425 if (not opts.get('user') and not opts.get('changeset')
426 426 and not opts.get('date') and not opts.get('file')):
427 427 opts['number'] = True
428 428
429 429 linenumber = opts.get('line_number') is not None
430 430 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
431 431 raise error.Abort(_('at least one of -n/-c is required for -l'))
432 432
433 433 ui.pager('annotate')
434 434
435 435 if fm.isplain():
436 436 def makefunc(get, fmt):
437 437 return lambda x: fmt(get(x))
438 438 else:
439 439 def makefunc(get, fmt):
440 440 return get
441 441 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
442 442 if opts.get(op)]
443 443 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
444 444 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
445 445 if opts.get(op))
446 446
447 447 def bad(x, y):
448 448 raise error.Abort("%s: %s" % (x, y))
449 449
450 450 m = scmutil.match(ctx, pats, opts, badfn=bad)
451 451
452 452 follow = not opts.get('no_follow')
453 453 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
454 454 whitespace=True)
455 455 for abs in ctx.walk(m):
456 456 fctx = ctx[abs]
457 457 if not opts.get('text') and fctx.isbinary():
458 458 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
459 459 continue
460 460
461 461 lines = fctx.annotate(follow=follow, linenumber=linenumber,
462 462 diffopts=diffopts)
463 463 if not lines:
464 464 continue
465 465 formats = []
466 466 pieces = []
467 467
468 468 for f, sep in funcmap:
469 469 l = [f(n) for n, dummy in lines]
470 470 if fm.isplain():
471 471 sizes = [encoding.colwidth(x) for x in l]
472 472 ml = max(sizes)
473 473 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
474 474 else:
475 475 formats.append(['%s' for x in l])
476 476 pieces.append(l)
477 477
478 478 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
479 479 fm.startitem()
480 480 fm.write(fields, "".join(f), *p)
481 481 fm.write('line', ": %s", l[1])
482 482
483 483 if not lines[-1][1].endswith('\n'):
484 484 fm.plain('\n')
485 485
486 486 fm.end()
487 487
488 488 @command('archive',
489 489 [('', 'no-decode', None, _('do not pass files through decoders')),
490 490 ('p', 'prefix', '', _('directory prefix for files in archive'),
491 491 _('PREFIX')),
492 492 ('r', 'rev', '', _('revision to distribute'), _('REV')),
493 493 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
494 494 ] + subrepoopts + walkopts,
495 495 _('[OPTION]... DEST'))
496 496 def archive(ui, repo, dest, **opts):
497 497 '''create an unversioned archive of a repository revision
498 498
499 499 By default, the revision used is the parent of the working
500 500 directory; use -r/--rev to specify a different revision.
501 501
502 502 The archive type is automatically detected based on file
503 503 extension (to override, use -t/--type).
504 504
505 505 .. container:: verbose
506 506
507 507 Examples:
508 508
509 509 - create a zip file containing the 1.0 release::
510 510
511 511 hg archive -r 1.0 project-1.0.zip
512 512
513 513 - create a tarball excluding .hg files::
514 514
515 515 hg archive project.tar.gz -X ".hg*"
516 516
517 517 Valid types are:
518 518
519 519 :``files``: a directory full of files (default)
520 520 :``tar``: tar archive, uncompressed
521 521 :``tbz2``: tar archive, compressed using bzip2
522 522 :``tgz``: tar archive, compressed using gzip
523 523 :``uzip``: zip archive, uncompressed
524 524 :``zip``: zip archive, compressed using deflate
525 525
526 526 The exact name of the destination archive or directory is given
527 527 using a format string; see :hg:`help export` for details.
528 528
529 529 Each member added to an archive file has a directory prefix
530 530 prepended. Use -p/--prefix to specify a format string for the
531 531 prefix. The default is the basename of the archive, with suffixes
532 532 removed.
533 533
534 534 Returns 0 on success.
535 535 '''
536 536
537 537 opts = pycompat.byteskwargs(opts)
538 538 ctx = scmutil.revsingle(repo, opts.get('rev'))
539 539 if not ctx:
540 540 raise error.Abort(_('no working directory: please specify a revision'))
541 541 node = ctx.node()
542 542 dest = cmdutil.makefilename(repo, dest, node)
543 543 if os.path.realpath(dest) == repo.root:
544 544 raise error.Abort(_('repository root cannot be destination'))
545 545
546 546 kind = opts.get('type') or archival.guesskind(dest) or 'files'
547 547 prefix = opts.get('prefix')
548 548
549 549 if dest == '-':
550 550 if kind == 'files':
551 551 raise error.Abort(_('cannot archive plain files to stdout'))
552 552 dest = cmdutil.makefileobj(repo, dest)
553 553 if not prefix:
554 554 prefix = os.path.basename(repo.root) + '-%h'
555 555
556 556 prefix = cmdutil.makefilename(repo, prefix, node)
557 557 matchfn = scmutil.match(ctx, [], opts)
558 558 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
559 559 matchfn, prefix, subrepos=opts.get('subrepos'))
560 560
561 561 @command('backout',
562 562 [('', 'merge', None, _('merge with old dirstate parent after backout')),
563 563 ('', 'commit', None,
564 564 _('commit if no conflicts were encountered (DEPRECATED)')),
565 565 ('', 'no-commit', None, _('do not commit')),
566 566 ('', 'parent', '',
567 567 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
568 568 ('r', 'rev', '', _('revision to backout'), _('REV')),
569 569 ('e', 'edit', False, _('invoke editor on commit messages')),
570 570 ] + mergetoolopts + walkopts + commitopts + commitopts2,
571 571 _('[OPTION]... [-r] REV'))
572 572 def backout(ui, repo, node=None, rev=None, **opts):
573 573 '''reverse effect of earlier changeset
574 574
575 575 Prepare a new changeset with the effect of REV undone in the
576 576 current working directory. If no conflicts were encountered,
577 577 it will be committed immediately.
578 578
579 579 If REV is the parent of the working directory, then this new changeset
580 580 is committed automatically (unless --no-commit is specified).
581 581
582 582 .. note::
583 583
584 584 :hg:`backout` cannot be used to fix either an unwanted or
585 585 incorrect merge.
586 586
587 587 .. container:: verbose
588 588
589 589 Examples:
590 590
591 591 - Reverse the effect of the parent of the working directory.
592 592 This backout will be committed immediately::
593 593
594 594 hg backout -r .
595 595
596 596 - Reverse the effect of previous bad revision 23::
597 597
598 598 hg backout -r 23
599 599
600 600 - Reverse the effect of previous bad revision 23 and
601 601 leave changes uncommitted::
602 602
603 603 hg backout -r 23 --no-commit
604 604 hg commit -m "Backout revision 23"
605 605
606 606 By default, the pending changeset will have one parent,
607 607 maintaining a linear history. With --merge, the pending
608 608 changeset will instead have two parents: the old parent of the
609 609 working directory and a new child of REV that simply undoes REV.
610 610
611 611 Before version 1.7, the behavior without --merge was equivalent
612 612 to specifying --merge followed by :hg:`update --clean .` to
613 613 cancel the merge and leave the child of REV as a head to be
614 614 merged separately.
615 615
616 616 See :hg:`help dates` for a list of formats valid for -d/--date.
617 617
618 618 See :hg:`help revert` for a way to restore files to the state
619 619 of another revision.
620 620
621 621 Returns 0 on success, 1 if nothing to backout or there are unresolved
622 622 files.
623 623 '''
624 624 wlock = lock = None
625 625 try:
626 626 wlock = repo.wlock()
627 627 lock = repo.lock()
628 628 return _dobackout(ui, repo, node, rev, **opts)
629 629 finally:
630 630 release(lock, wlock)
631 631
632 632 def _dobackout(ui, repo, node=None, rev=None, **opts):
633 633 opts = pycompat.byteskwargs(opts)
634 634 if opts.get('commit') and opts.get('no_commit'):
635 635 raise error.Abort(_("cannot use --commit with --no-commit"))
636 636 if opts.get('merge') and opts.get('no_commit'):
637 637 raise error.Abort(_("cannot use --merge with --no-commit"))
638 638
639 639 if rev and node:
640 640 raise error.Abort(_("please specify just one revision"))
641 641
642 642 if not rev:
643 643 rev = node
644 644
645 645 if not rev:
646 646 raise error.Abort(_("please specify a revision to backout"))
647 647
648 648 date = opts.get('date')
649 649 if date:
650 650 opts['date'] = util.parsedate(date)
651 651
652 652 cmdutil.checkunfinished(repo)
653 653 cmdutil.bailifchanged(repo)
654 654 node = scmutil.revsingle(repo, rev).node()
655 655
656 656 op1, op2 = repo.dirstate.parents()
657 657 if not repo.changelog.isancestor(node, op1):
658 658 raise error.Abort(_('cannot backout change that is not an ancestor'))
659 659
660 660 p1, p2 = repo.changelog.parents(node)
661 661 if p1 == nullid:
662 662 raise error.Abort(_('cannot backout a change with no parents'))
663 663 if p2 != nullid:
664 664 if not opts.get('parent'):
665 665 raise error.Abort(_('cannot backout a merge changeset'))
666 666 p = repo.lookup(opts['parent'])
667 667 if p not in (p1, p2):
668 668 raise error.Abort(_('%s is not a parent of %s') %
669 669 (short(p), short(node)))
670 670 parent = p
671 671 else:
672 672 if opts.get('parent'):
673 673 raise error.Abort(_('cannot use --parent on non-merge changeset'))
674 674 parent = p1
675 675
676 676 # the backout should appear on the same branch
677 677 branch = repo.dirstate.branch()
678 678 bheads = repo.branchheads(branch)
679 679 rctx = scmutil.revsingle(repo, hex(parent))
680 680 if not opts.get('merge') and op1 != node:
681 681 dsguard = dirstateguard.dirstateguard(repo, 'backout')
682 682 try:
683 683 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
684 684 'backout')
685 685 stats = mergemod.update(repo, parent, True, True, node, False)
686 686 repo.setparents(op1, op2)
687 687 dsguard.close()
688 688 hg._showstats(repo, stats)
689 689 if stats[3]:
690 690 repo.ui.status(_("use 'hg resolve' to retry unresolved "
691 691 "file merges\n"))
692 692 return 1
693 693 finally:
694 694 ui.setconfig('ui', 'forcemerge', '', '')
695 695 lockmod.release(dsguard)
696 696 else:
697 697 hg.clean(repo, node, show_stats=False)
698 698 repo.dirstate.setbranch(branch)
699 699 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
700 700
701 701 if opts.get('no_commit'):
702 702 msg = _("changeset %s backed out, "
703 703 "don't forget to commit.\n")
704 704 ui.status(msg % short(node))
705 705 return 0
706 706
707 707 def commitfunc(ui, repo, message, match, opts):
708 708 editform = 'backout'
709 709 e = cmdutil.getcommiteditor(editform=editform,
710 710 **pycompat.strkwargs(opts))
711 711 if not message:
712 712 # we don't translate commit messages
713 713 message = "Backed out changeset %s" % short(node)
714 714 e = cmdutil.getcommiteditor(edit=True, editform=editform)
715 715 return repo.commit(message, opts.get('user'), opts.get('date'),
716 716 match, editor=e)
717 717 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
718 718 if not newnode:
719 719 ui.status(_("nothing changed\n"))
720 720 return 1
721 721 cmdutil.commitstatus(repo, newnode, branch, bheads)
722 722
723 723 def nice(node):
724 724 return '%d:%s' % (repo.changelog.rev(node), short(node))
725 725 ui.status(_('changeset %s backs out changeset %s\n') %
726 726 (nice(repo.changelog.tip()), nice(node)))
727 727 if opts.get('merge') and op1 != node:
728 728 hg.clean(repo, op1, show_stats=False)
729 729 ui.status(_('merging with changeset %s\n')
730 730 % nice(repo.changelog.tip()))
731 731 try:
732 732 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
733 733 'backout')
734 734 return hg.merge(repo, hex(repo.changelog.tip()))
735 735 finally:
736 736 ui.setconfig('ui', 'forcemerge', '', '')
737 737 return 0
738 738
739 739 @command('bisect',
740 740 [('r', 'reset', False, _('reset bisect state')),
741 741 ('g', 'good', False, _('mark changeset good')),
742 742 ('b', 'bad', False, _('mark changeset bad')),
743 743 ('s', 'skip', False, _('skip testing changeset')),
744 744 ('e', 'extend', False, _('extend the bisect range')),
745 745 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
746 746 ('U', 'noupdate', False, _('do not update to target'))],
747 747 _("[-gbsr] [-U] [-c CMD] [REV]"))
748 748 def bisect(ui, repo, rev=None, extra=None, command=None,
749 749 reset=None, good=None, bad=None, skip=None, extend=None,
750 750 noupdate=None):
751 751 """subdivision search of changesets
752 752
753 753 This command helps to find changesets which introduce problems. To
754 754 use, mark the earliest changeset you know exhibits the problem as
755 755 bad, then mark the latest changeset which is free from the problem
756 756 as good. Bisect will update your working directory to a revision
757 757 for testing (unless the -U/--noupdate option is specified). Once
758 758 you have performed tests, mark the working directory as good or
759 759 bad, and bisect will either update to another candidate changeset
760 760 or announce that it has found the bad revision.
761 761
762 762 As a shortcut, you can also use the revision argument to mark a
763 763 revision as good or bad without checking it out first.
764 764
765 765 If you supply a command, it will be used for automatic bisection.
766 766 The environment variable HG_NODE will contain the ID of the
767 767 changeset being tested. The exit status of the command will be
768 768 used to mark revisions as good or bad: status 0 means good, 125
769 769 means to skip the revision, 127 (command not found) will abort the
770 770 bisection, and any other non-zero exit status means the revision
771 771 is bad.
772 772
773 773 .. container:: verbose
774 774
775 775 Some examples:
776 776
777 777 - start a bisection with known bad revision 34, and good revision 12::
778 778
779 779 hg bisect --bad 34
780 780 hg bisect --good 12
781 781
782 782 - advance the current bisection by marking current revision as good or
783 783 bad::
784 784
785 785 hg bisect --good
786 786 hg bisect --bad
787 787
788 788 - mark the current revision, or a known revision, to be skipped (e.g. if
789 789 that revision is not usable because of another issue)::
790 790
791 791 hg bisect --skip
792 792 hg bisect --skip 23
793 793
794 794 - skip all revisions that do not touch directories ``foo`` or ``bar``::
795 795
796 796 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
797 797
798 798 - forget the current bisection::
799 799
800 800 hg bisect --reset
801 801
802 802 - use 'make && make tests' to automatically find the first broken
803 803 revision::
804 804
805 805 hg bisect --reset
806 806 hg bisect --bad 34
807 807 hg bisect --good 12
808 808 hg bisect --command "make && make tests"
809 809
810 810 - see all changesets whose states are already known in the current
811 811 bisection::
812 812
813 813 hg log -r "bisect(pruned)"
814 814
815 815 - see the changeset currently being bisected (especially useful
816 816 if running with -U/--noupdate)::
817 817
818 818 hg log -r "bisect(current)"
819 819
820 820 - see all changesets that took part in the current bisection::
821 821
822 822 hg log -r "bisect(range)"
823 823
824 824 - you can even get a nice graph::
825 825
826 826 hg log --graph -r "bisect(range)"
827 827
828 828 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
829 829
830 830 Returns 0 on success.
831 831 """
832 832 # backward compatibility
833 833 if rev in "good bad reset init".split():
834 834 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
835 835 cmd, rev, extra = rev, extra, None
836 836 if cmd == "good":
837 837 good = True
838 838 elif cmd == "bad":
839 839 bad = True
840 840 else:
841 841 reset = True
842 842 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
843 843 raise error.Abort(_('incompatible arguments'))
844 844
845 845 if reset:
846 846 hbisect.resetstate(repo)
847 847 return
848 848
849 849 state = hbisect.load_state(repo)
850 850
851 851 # update state
852 852 if good or bad or skip:
853 853 if rev:
854 854 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
855 855 else:
856 856 nodes = [repo.lookup('.')]
857 857 if good:
858 858 state['good'] += nodes
859 859 elif bad:
860 860 state['bad'] += nodes
861 861 elif skip:
862 862 state['skip'] += nodes
863 863 hbisect.save_state(repo, state)
864 864 if not (state['good'] and state['bad']):
865 865 return
866 866
867 867 def mayupdate(repo, node, show_stats=True):
868 868 """common used update sequence"""
869 869 if noupdate:
870 870 return
871 871 cmdutil.checkunfinished(repo)
872 872 cmdutil.bailifchanged(repo)
873 873 return hg.clean(repo, node, show_stats=show_stats)
874 874
875 875 displayer = cmdutil.show_changeset(ui, repo, {})
876 876
877 877 if command:
878 878 changesets = 1
879 879 if noupdate:
880 880 try:
881 881 node = state['current'][0]
882 882 except LookupError:
883 883 raise error.Abort(_('current bisect revision is unknown - '
884 884 'start a new bisect to fix'))
885 885 else:
886 886 node, p2 = repo.dirstate.parents()
887 887 if p2 != nullid:
888 888 raise error.Abort(_('current bisect revision is a merge'))
889 889 if rev:
890 890 node = repo[scmutil.revsingle(repo, rev, node)].node()
891 891 try:
892 892 while changesets:
893 893 # update state
894 894 state['current'] = [node]
895 895 hbisect.save_state(repo, state)
896 896 status = ui.system(command, environ={'HG_NODE': hex(node)},
897 897 blockedtag='bisect_check')
898 898 if status == 125:
899 899 transition = "skip"
900 900 elif status == 0:
901 901 transition = "good"
902 902 # status < 0 means process was killed
903 903 elif status == 127:
904 904 raise error.Abort(_("failed to execute %s") % command)
905 905 elif status < 0:
906 906 raise error.Abort(_("%s killed") % command)
907 907 else:
908 908 transition = "bad"
909 909 state[transition].append(node)
910 910 ctx = repo[node]
911 911 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
912 912 hbisect.checkstate(state)
913 913 # bisect
914 914 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
915 915 # update to next check
916 916 node = nodes[0]
917 917 mayupdate(repo, node, show_stats=False)
918 918 finally:
919 919 state['current'] = [node]
920 920 hbisect.save_state(repo, state)
921 921 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
922 922 return
923 923
924 924 hbisect.checkstate(state)
925 925
926 926 # actually bisect
927 927 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
928 928 if extend:
929 929 if not changesets:
930 930 extendnode = hbisect.extendrange(repo, state, nodes, good)
931 931 if extendnode is not None:
932 932 ui.write(_("Extending search to changeset %d:%s\n")
933 933 % (extendnode.rev(), extendnode))
934 934 state['current'] = [extendnode.node()]
935 935 hbisect.save_state(repo, state)
936 936 return mayupdate(repo, extendnode.node())
937 937 raise error.Abort(_("nothing to extend"))
938 938
939 939 if changesets == 0:
940 940 hbisect.printresult(ui, repo, state, displayer, nodes, good)
941 941 else:
942 942 assert len(nodes) == 1 # only a single node can be tested next
943 943 node = nodes[0]
944 944 # compute the approximate number of remaining tests
945 945 tests, size = 0, 2
946 946 while size <= changesets:
947 947 tests, size = tests + 1, size * 2
948 948 rev = repo.changelog.rev(node)
949 949 ui.write(_("Testing changeset %d:%s "
950 950 "(%d changesets remaining, ~%d tests)\n")
951 951 % (rev, short(node), changesets, tests))
952 952 state['current'] = [node]
953 953 hbisect.save_state(repo, state)
954 954 return mayupdate(repo, node)
955 955
956 956 @command('bookmarks|bookmark',
957 957 [('f', 'force', False, _('force')),
958 958 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
959 959 ('d', 'delete', False, _('delete a given bookmark')),
960 960 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
961 961 ('i', 'inactive', False, _('mark a bookmark inactive')),
962 962 ] + formatteropts,
963 963 _('hg bookmarks [OPTIONS]... [NAME]...'))
964 964 def bookmark(ui, repo, *names, **opts):
965 965 '''create a new bookmark or list existing bookmarks
966 966
967 967 Bookmarks are labels on changesets to help track lines of development.
968 968 Bookmarks are unversioned and can be moved, renamed and deleted.
969 969 Deleting or moving a bookmark has no effect on the associated changesets.
970 970
971 971 Creating or updating to a bookmark causes it to be marked as 'active'.
972 972 The active bookmark is indicated with a '*'.
973 973 When a commit is made, the active bookmark will advance to the new commit.
974 974 A plain :hg:`update` will also advance an active bookmark, if possible.
975 975 Updating away from a bookmark will cause it to be deactivated.
976 976
977 977 Bookmarks can be pushed and pulled between repositories (see
978 978 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
979 979 diverged, a new 'divergent bookmark' of the form 'name@path' will
980 980 be created. Using :hg:`merge` will resolve the divergence.
981 981
982 982 A bookmark named '@' has the special property that :hg:`clone` will
983 983 check it out by default if it exists.
984 984
985 985 .. container:: verbose
986 986
987 987 Examples:
988 988
989 989 - create an active bookmark for a new line of development::
990 990
991 991 hg book new-feature
992 992
993 993 - create an inactive bookmark as a place marker::
994 994
995 995 hg book -i reviewed
996 996
997 997 - create an inactive bookmark on another changeset::
998 998
999 999 hg book -r .^ tested
1000 1000
1001 1001 - rename bookmark turkey to dinner::
1002 1002
1003 1003 hg book -m turkey dinner
1004 1004
1005 1005 - move the '@' bookmark from another branch::
1006 1006
1007 1007 hg book -f @
1008 1008 '''
1009 1009 opts = pycompat.byteskwargs(opts)
1010 1010 force = opts.get('force')
1011 1011 rev = opts.get('rev')
1012 1012 delete = opts.get('delete')
1013 1013 rename = opts.get('rename')
1014 1014 inactive = opts.get('inactive')
1015 1015
1016 1016 def checkformat(mark):
1017 1017 mark = mark.strip()
1018 1018 if not mark:
1019 1019 raise error.Abort(_("bookmark names cannot consist entirely of "
1020 1020 "whitespace"))
1021 1021 scmutil.checknewlabel(repo, mark, 'bookmark')
1022 1022 return mark
1023 1023
1024 1024 def checkconflict(repo, mark, cur, force=False, target=None):
1025 1025 if mark in marks and not force:
1026 1026 if target:
1027 1027 if marks[mark] == target and target == cur:
1028 1028 # re-activating a bookmark
1029 1029 return
1030 1030 anc = repo.changelog.ancestors([repo[target].rev()])
1031 1031 bmctx = repo[marks[mark]]
1032 1032 divs = [repo[b].node() for b in marks
1033 1033 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1034 1034
1035 1035 # allow resolving a single divergent bookmark even if moving
1036 1036 # the bookmark across branches when a revision is specified
1037 1037 # that contains a divergent bookmark
1038 1038 if bmctx.rev() not in anc and target in divs:
1039 1039 bookmarks.deletedivergent(repo, [target], mark)
1040 1040 return
1041 1041
1042 1042 deletefrom = [b for b in divs
1043 1043 if repo[b].rev() in anc or b == target]
1044 1044 bookmarks.deletedivergent(repo, deletefrom, mark)
1045 1045 if bookmarks.validdest(repo, bmctx, repo[target]):
1046 1046 ui.status(_("moving bookmark '%s' forward from %s\n") %
1047 1047 (mark, short(bmctx.node())))
1048 1048 return
1049 1049 raise error.Abort(_("bookmark '%s' already exists "
1050 1050 "(use -f to force)") % mark)
1051 1051 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1052 1052 and not force):
1053 1053 raise error.Abort(
1054 1054 _("a bookmark cannot have the name of an existing branch"))
1055 1055
1056 1056 if delete and rename:
1057 1057 raise error.Abort(_("--delete and --rename are incompatible"))
1058 1058 if delete and rev:
1059 1059 raise error.Abort(_("--rev is incompatible with --delete"))
1060 1060 if rename and rev:
1061 1061 raise error.Abort(_("--rev is incompatible with --rename"))
1062 1062 if not names and (delete or rev):
1063 1063 raise error.Abort(_("bookmark name required"))
1064 1064
1065 1065 if delete or rename or names or inactive:
1066 1066 wlock = lock = tr = None
1067 1067 try:
1068 1068 wlock = repo.wlock()
1069 1069 lock = repo.lock()
1070 1070 cur = repo.changectx('.').node()
1071 1071 marks = repo._bookmarks
1072 1072 if delete:
1073 1073 tr = repo.transaction('bookmark')
1074 1074 for mark in names:
1075 1075 if mark not in marks:
1076 1076 raise error.Abort(_("bookmark '%s' does not exist") %
1077 1077 mark)
1078 1078 if mark == repo._activebookmark:
1079 1079 bookmarks.deactivate(repo)
1080 1080 del marks[mark]
1081 1081
1082 1082 elif rename:
1083 1083 tr = repo.transaction('bookmark')
1084 1084 if not names:
1085 1085 raise error.Abort(_("new bookmark name required"))
1086 1086 elif len(names) > 1:
1087 1087 raise error.Abort(_("only one new bookmark name allowed"))
1088 1088 mark = checkformat(names[0])
1089 1089 if rename not in marks:
1090 1090 raise error.Abort(_("bookmark '%s' does not exist")
1091 1091 % rename)
1092 1092 checkconflict(repo, mark, cur, force)
1093 1093 marks[mark] = marks[rename]
1094 1094 if repo._activebookmark == rename and not inactive:
1095 1095 bookmarks.activate(repo, mark)
1096 1096 del marks[rename]
1097 1097 elif names:
1098 1098 tr = repo.transaction('bookmark')
1099 1099 newact = None
1100 1100 for mark in names:
1101 1101 mark = checkformat(mark)
1102 1102 if newact is None:
1103 1103 newact = mark
1104 1104 if inactive and mark == repo._activebookmark:
1105 1105 bookmarks.deactivate(repo)
1106 1106 return
1107 1107 tgt = cur
1108 1108 if rev:
1109 1109 tgt = scmutil.revsingle(repo, rev).node()
1110 1110 checkconflict(repo, mark, cur, force, tgt)
1111 1111 marks[mark] = tgt
1112 1112 if not inactive and cur == marks[newact] and not rev:
1113 1113 bookmarks.activate(repo, newact)
1114 1114 elif cur != tgt and newact == repo._activebookmark:
1115 1115 bookmarks.deactivate(repo)
1116 1116 elif inactive:
1117 1117 if len(marks) == 0:
1118 1118 ui.status(_("no bookmarks set\n"))
1119 1119 elif not repo._activebookmark:
1120 1120 ui.status(_("no active bookmark\n"))
1121 1121 else:
1122 1122 bookmarks.deactivate(repo)
1123 1123 if tr is not None:
1124 1124 marks.recordchange(tr)
1125 1125 tr.close()
1126 1126 finally:
1127 1127 lockmod.release(tr, lock, wlock)
1128 1128 else: # show bookmarks
1129 1129 fm = ui.formatter('bookmarks', opts)
1130 1130 hexfn = fm.hexfunc
1131 1131 marks = repo._bookmarks
1132 1132 if len(marks) == 0 and fm.isplain():
1133 1133 ui.status(_("no bookmarks set\n"))
1134 1134 for bmark, n in sorted(marks.iteritems()):
1135 1135 active = repo._activebookmark
1136 1136 if bmark == active:
1137 1137 prefix, label = '*', activebookmarklabel
1138 1138 else:
1139 1139 prefix, label = ' ', ''
1140 1140
1141 1141 fm.startitem()
1142 1142 if not ui.quiet:
1143 1143 fm.plain(' %s ' % prefix, label=label)
1144 1144 fm.write('bookmark', '%s', bmark, label=label)
1145 1145 pad = " " * (25 - encoding.colwidth(bmark))
1146 1146 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1147 1147 repo.changelog.rev(n), hexfn(n), label=label)
1148 1148 fm.data(active=(bmark == active))
1149 1149 fm.plain('\n')
1150 1150 fm.end()
1151 1151
1152 1152 @command('branch',
1153 1153 [('f', 'force', None,
1154 1154 _('set branch name even if it shadows an existing branch')),
1155 1155 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1156 1156 _('[-fC] [NAME]'))
1157 1157 def branch(ui, repo, label=None, **opts):
1158 1158 """set or show the current branch name
1159 1159
1160 1160 .. note::
1161 1161
1162 1162 Branch names are permanent and global. Use :hg:`bookmark` to create a
1163 1163 light-weight bookmark instead. See :hg:`help glossary` for more
1164 1164 information about named branches and bookmarks.
1165 1165
1166 1166 With no argument, show the current branch name. With one argument,
1167 1167 set the working directory branch name (the branch will not exist
1168 1168 in the repository until the next commit). Standard practice
1169 1169 recommends that primary development take place on the 'default'
1170 1170 branch.
1171 1171
1172 1172 Unless -f/--force is specified, branch will not let you set a
1173 1173 branch name that already exists.
1174 1174
1175 1175 Use -C/--clean to reset the working directory branch to that of
1176 1176 the parent of the working directory, negating a previous branch
1177 1177 change.
1178 1178
1179 1179 Use the command :hg:`update` to switch to an existing branch. Use
1180 1180 :hg:`commit --close-branch` to mark this branch head as closed.
1181 1181 When all heads of a branch are closed, the branch will be
1182 1182 considered closed.
1183 1183
1184 1184 Returns 0 on success.
1185 1185 """
1186 1186 opts = pycompat.byteskwargs(opts)
1187 1187 if label:
1188 1188 label = label.strip()
1189 1189
1190 1190 if not opts.get('clean') and not label:
1191 1191 ui.write("%s\n" % repo.dirstate.branch())
1192 1192 return
1193 1193
1194 1194 with repo.wlock():
1195 1195 if opts.get('clean'):
1196 1196 label = repo[None].p1().branch()
1197 1197 repo.dirstate.setbranch(label)
1198 1198 ui.status(_('reset working directory to branch %s\n') % label)
1199 1199 elif label:
1200 1200 if not opts.get('force') and label in repo.branchmap():
1201 1201 if label not in [p.branch() for p in repo[None].parents()]:
1202 1202 raise error.Abort(_('a branch of the same name already'
1203 1203 ' exists'),
1204 1204 # i18n: "it" refers to an existing branch
1205 1205 hint=_("use 'hg update' to switch to it"))
1206 1206 scmutil.checknewlabel(repo, label, 'branch')
1207 1207 repo.dirstate.setbranch(label)
1208 1208 ui.status(_('marked working directory as branch %s\n') % label)
1209 1209
1210 1210 # find any open named branches aside from default
1211 1211 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1212 1212 if n != "default" and not c]
1213 1213 if not others:
1214 1214 ui.status(_('(branches are permanent and global, '
1215 1215 'did you want a bookmark?)\n'))
1216 1216
1217 1217 @command('branches',
1218 1218 [('a', 'active', False,
1219 1219 _('show only branches that have unmerged heads (DEPRECATED)')),
1220 1220 ('c', 'closed', False, _('show normal and closed branches')),
1221 1221 ] + formatteropts,
1222 1222 _('[-c]'))
1223 1223 def branches(ui, repo, active=False, closed=False, **opts):
1224 1224 """list repository named branches
1225 1225
1226 1226 List the repository's named branches, indicating which ones are
1227 1227 inactive. If -c/--closed is specified, also list branches which have
1228 1228 been marked closed (see :hg:`commit --close-branch`).
1229 1229
1230 1230 Use the command :hg:`update` to switch to an existing branch.
1231 1231
1232 1232 Returns 0.
1233 1233 """
1234 1234
1235 1235 opts = pycompat.byteskwargs(opts)
1236 1236 ui.pager('branches')
1237 1237 fm = ui.formatter('branches', opts)
1238 1238 hexfunc = fm.hexfunc
1239 1239
1240 1240 allheads = set(repo.heads())
1241 1241 branches = []
1242 1242 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1243 1243 isactive = not isclosed and bool(set(heads) & allheads)
1244 1244 branches.append((tag, repo[tip], isactive, not isclosed))
1245 1245 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1246 1246 reverse=True)
1247 1247
1248 1248 for tag, ctx, isactive, isopen in branches:
1249 1249 if active and not isactive:
1250 1250 continue
1251 1251 if isactive:
1252 1252 label = 'branches.active'
1253 1253 notice = ''
1254 1254 elif not isopen:
1255 1255 if not closed:
1256 1256 continue
1257 1257 label = 'branches.closed'
1258 1258 notice = _(' (closed)')
1259 1259 else:
1260 1260 label = 'branches.inactive'
1261 1261 notice = _(' (inactive)')
1262 1262 current = (tag == repo.dirstate.branch())
1263 1263 if current:
1264 1264 label = 'branches.current'
1265 1265
1266 1266 fm.startitem()
1267 1267 fm.write('branch', '%s', tag, label=label)
1268 1268 rev = ctx.rev()
1269 1269 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1270 1270 fmt = ' ' * padsize + ' %d:%s'
1271 1271 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1272 1272 label='log.changeset changeset.%s' % ctx.phasestr())
1273 1273 fm.context(ctx=ctx)
1274 1274 fm.data(active=isactive, closed=not isopen, current=current)
1275 1275 if not ui.quiet:
1276 1276 fm.plain(notice)
1277 1277 fm.plain('\n')
1278 1278 fm.end()
1279 1279
1280 1280 @command('bundle',
1281 1281 [('f', 'force', None, _('run even when the destination is unrelated')),
1282 1282 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1283 1283 _('REV')),
1284 1284 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1285 1285 _('BRANCH')),
1286 1286 ('', 'base', [],
1287 1287 _('a base changeset assumed to be available at the destination'),
1288 1288 _('REV')),
1289 1289 ('a', 'all', None, _('bundle all changesets in the repository')),
1290 1290 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1291 1291 ] + remoteopts,
1292 1292 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1293 1293 def bundle(ui, repo, fname, dest=None, **opts):
1294 1294 """create a bundle file
1295 1295
1296 1296 Generate a bundle file containing data to be added to a repository.
1297 1297
1298 1298 To create a bundle containing all changesets, use -a/--all
1299 1299 (or --base null). Otherwise, hg assumes the destination will have
1300 1300 all the nodes you specify with --base parameters. Otherwise, hg
1301 1301 will assume the repository has all the nodes in destination, or
1302 1302 default-push/default if no destination is specified.
1303 1303
1304 1304 You can change bundle format with the -t/--type option. See
1305 1305 :hg:`help bundlespec` for documentation on this format. By default,
1306 1306 the most appropriate format is used and compression defaults to
1307 1307 bzip2.
1308 1308
1309 1309 The bundle file can then be transferred using conventional means
1310 1310 and applied to another repository with the unbundle or pull
1311 1311 command. This is useful when direct push and pull are not
1312 1312 available or when exporting an entire repository is undesirable.
1313 1313
1314 1314 Applying bundles preserves all changeset contents including
1315 1315 permissions, copy/rename information, and revision history.
1316 1316
1317 1317 Returns 0 on success, 1 if no changes found.
1318 1318 """
1319 1319 opts = pycompat.byteskwargs(opts)
1320 1320 revs = None
1321 1321 if 'rev' in opts:
1322 1322 revstrings = opts['rev']
1323 1323 revs = scmutil.revrange(repo, revstrings)
1324 1324 if revstrings and not revs:
1325 1325 raise error.Abort(_('no commits to bundle'))
1326 1326
1327 1327 bundletype = opts.get('type', 'bzip2').lower()
1328 1328 try:
1329 1329 bcompression, cgversion, params = exchange.parsebundlespec(
1330 1330 repo, bundletype, strict=False)
1331 1331 except error.UnsupportedBundleSpecification as e:
1332 1332 raise error.Abort(str(e),
1333 1333 hint=_("see 'hg help bundlespec' for supported "
1334 1334 "values for --type"))
1335 1335
1336 1336 # Packed bundles are a pseudo bundle format for now.
1337 1337 if cgversion == 's1':
1338 1338 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1339 1339 hint=_("use 'hg debugcreatestreamclonebundle'"))
1340 1340
1341 1341 if opts.get('all'):
1342 1342 if dest:
1343 1343 raise error.Abort(_("--all is incompatible with specifying "
1344 1344 "a destination"))
1345 1345 if opts.get('base'):
1346 1346 ui.warn(_("ignoring --base because --all was specified\n"))
1347 1347 base = ['null']
1348 1348 else:
1349 1349 base = scmutil.revrange(repo, opts.get('base'))
1350 1350 if cgversion not in changegroup.supportedoutgoingversions(repo):
1351 1351 raise error.Abort(_("repository does not support bundle version %s") %
1352 1352 cgversion)
1353 1353
1354 1354 if base:
1355 1355 if dest:
1356 1356 raise error.Abort(_("--base is incompatible with specifying "
1357 1357 "a destination"))
1358 1358 common = [repo.lookup(rev) for rev in base]
1359 1359 heads = revs and map(repo.lookup, revs) or None
1360 1360 outgoing = discovery.outgoing(repo, common, heads)
1361 1361 else:
1362 1362 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1363 1363 dest, branches = hg.parseurl(dest, opts.get('branch'))
1364 1364 other = hg.peer(repo, opts, dest)
1365 1365 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1366 1366 heads = revs and map(repo.lookup, revs) or revs
1367 1367 outgoing = discovery.findcommonoutgoing(repo, other,
1368 1368 onlyheads=heads,
1369 1369 force=opts.get('force'),
1370 1370 portable=True)
1371 1371
1372 1372 if not outgoing.missing:
1373 1373 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1374 1374 return 1
1375 1375
1376 1376 if cgversion == '01': #bundle1
1377 1377 if bcompression is None:
1378 1378 bcompression = 'UN'
1379 1379 bversion = 'HG10' + bcompression
1380 1380 bcompression = None
1381 1381 elif cgversion in ('02', '03'):
1382 1382 bversion = 'HG20'
1383 1383 else:
1384 1384 raise error.ProgrammingError(
1385 1385 'bundle: unexpected changegroup version %s' % cgversion)
1386 1386
1387 1387 # TODO compression options should be derived from bundlespec parsing.
1388 1388 # This is a temporary hack to allow adjusting bundle compression
1389 1389 # level without a) formalizing the bundlespec changes to declare it
1390 1390 # b) introducing a command flag.
1391 1391 compopts = {}
1392 1392 complevel = ui.configint('experimental', 'bundlecomplevel')
1393 1393 if complevel is not None:
1394 1394 compopts['level'] = complevel
1395 1395
1396 1396
1397 1397 contentopts = {'cg.version': cgversion}
1398 1398 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1399 1399 contentopts, compression=bcompression,
1400 1400 compopts=compopts)
1401 1401
1402 1402 @command('cat',
1403 1403 [('o', 'output', '',
1404 1404 _('print output to file with formatted name'), _('FORMAT')),
1405 1405 ('r', 'rev', '', _('print the given revision'), _('REV')),
1406 1406 ('', 'decode', None, _('apply any matching decode filter')),
1407 1407 ] + walkopts,
1408 1408 _('[OPTION]... FILE...'),
1409 1409 inferrepo=True)
1410 1410 def cat(ui, repo, file1, *pats, **opts):
1411 1411 """output the current or given revision of files
1412 1412
1413 1413 Print the specified files as they were at the given revision. If
1414 1414 no revision is given, the parent of the working directory is used.
1415 1415
1416 1416 Output may be to a file, in which case the name of the file is
1417 1417 given using a format string. The formatting rules as follows:
1418 1418
1419 1419 :``%%``: literal "%" character
1420 1420 :``%s``: basename of file being printed
1421 1421 :``%d``: dirname of file being printed, or '.' if in repository root
1422 1422 :``%p``: root-relative path name of file being printed
1423 1423 :``%H``: changeset hash (40 hexadecimal digits)
1424 1424 :``%R``: changeset revision number
1425 1425 :``%h``: short-form changeset hash (12 hexadecimal digits)
1426 1426 :``%r``: zero-padded changeset revision number
1427 1427 :``%b``: basename of the exporting repository
1428 1428
1429 1429 Returns 0 on success.
1430 1430 """
1431 1431 ctx = scmutil.revsingle(repo, opts.get('rev'))
1432 1432 m = scmutil.match(ctx, (file1,) + pats, opts)
1433 1433
1434 1434 ui.pager('cat')
1435 1435 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1436 1436
1437 1437 @command('^clone',
1438 1438 [('U', 'noupdate', None, _('the clone will include an empty working '
1439 1439 'directory (only a repository)')),
1440 1440 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1441 1441 _('REV')),
1442 1442 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1443 1443 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1444 1444 ('', 'pull', None, _('use pull protocol to copy metadata')),
1445 1445 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1446 1446 ] + remoteopts,
1447 1447 _('[OPTION]... SOURCE [DEST]'),
1448 1448 norepo=True)
1449 1449 def clone(ui, source, dest=None, **opts):
1450 1450 """make a copy of an existing repository
1451 1451
1452 1452 Create a copy of an existing repository in a new directory.
1453 1453
1454 1454 If no destination directory name is specified, it defaults to the
1455 1455 basename of the source.
1456 1456
1457 1457 The location of the source is added to the new repository's
1458 1458 ``.hg/hgrc`` file, as the default to be used for future pulls.
1459 1459
1460 1460 Only local paths and ``ssh://`` URLs are supported as
1461 1461 destinations. For ``ssh://`` destinations, no working directory or
1462 1462 ``.hg/hgrc`` will be created on the remote side.
1463 1463
1464 1464 If the source repository has a bookmark called '@' set, that
1465 1465 revision will be checked out in the new repository by default.
1466 1466
1467 1467 To check out a particular version, use -u/--update, or
1468 1468 -U/--noupdate to create a clone with no working directory.
1469 1469
1470 1470 To pull only a subset of changesets, specify one or more revisions
1471 1471 identifiers with -r/--rev or branches with -b/--branch. The
1472 1472 resulting clone will contain only the specified changesets and
1473 1473 their ancestors. These options (or 'clone src#rev dest') imply
1474 1474 --pull, even for local source repositories.
1475 1475
1476 1476 .. note::
1477 1477
1478 1478 Specifying a tag will include the tagged changeset but not the
1479 1479 changeset containing the tag.
1480 1480
1481 1481 .. container:: verbose
1482 1482
1483 1483 For efficiency, hardlinks are used for cloning whenever the
1484 1484 source and destination are on the same filesystem (note this
1485 1485 applies only to the repository data, not to the working
1486 1486 directory). Some filesystems, such as AFS, implement hardlinking
1487 1487 incorrectly, but do not report errors. In these cases, use the
1488 1488 --pull option to avoid hardlinking.
1489 1489
1490 1490 In some cases, you can clone repositories and the working
1491 1491 directory using full hardlinks with ::
1492 1492
1493 1493 $ cp -al REPO REPOCLONE
1494 1494
1495 1495 This is the fastest way to clone, but it is not always safe. The
1496 1496 operation is not atomic (making sure REPO is not modified during
1497 1497 the operation is up to you) and you have to make sure your
1498 1498 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1499 1499 so). Also, this is not compatible with certain extensions that
1500 1500 place their metadata under the .hg directory, such as mq.
1501 1501
1502 1502 Mercurial will update the working directory to the first applicable
1503 1503 revision from this list:
1504 1504
1505 1505 a) null if -U or the source repository has no changesets
1506 1506 b) if -u . and the source repository is local, the first parent of
1507 1507 the source repository's working directory
1508 1508 c) the changeset specified with -u (if a branch name, this means the
1509 1509 latest head of that branch)
1510 1510 d) the changeset specified with -r
1511 1511 e) the tipmost head specified with -b
1512 1512 f) the tipmost head specified with the url#branch source syntax
1513 1513 g) the revision marked with the '@' bookmark, if present
1514 1514 h) the tipmost head of the default branch
1515 1515 i) tip
1516 1516
1517 1517 When cloning from servers that support it, Mercurial may fetch
1518 1518 pre-generated data from a server-advertised URL. When this is done,
1519 1519 hooks operating on incoming changesets and changegroups may fire twice,
1520 1520 once for the bundle fetched from the URL and another for any additional
1521 1521 data not fetched from this URL. In addition, if an error occurs, the
1522 1522 repository may be rolled back to a partial clone. This behavior may
1523 1523 change in future releases. See :hg:`help -e clonebundles` for more.
1524 1524
1525 1525 Examples:
1526 1526
1527 1527 - clone a remote repository to a new directory named hg/::
1528 1528
1529 1529 hg clone https://www.mercurial-scm.org/repo/hg/
1530 1530
1531 1531 - create a lightweight local clone::
1532 1532
1533 1533 hg clone project/ project-feature/
1534 1534
1535 1535 - clone from an absolute path on an ssh server (note double-slash)::
1536 1536
1537 1537 hg clone ssh://user@server//home/projects/alpha/
1538 1538
1539 1539 - do a high-speed clone over a LAN while checking out a
1540 1540 specified version::
1541 1541
1542 1542 hg clone --uncompressed http://server/repo -u 1.5
1543 1543
1544 1544 - create a repository without changesets after a particular revision::
1545 1545
1546 1546 hg clone -r 04e544 experimental/ good/
1547 1547
1548 1548 - clone (and track) a particular named branch::
1549 1549
1550 1550 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1551 1551
1552 1552 See :hg:`help urls` for details on specifying URLs.
1553 1553
1554 1554 Returns 0 on success.
1555 1555 """
1556 1556 opts = pycompat.byteskwargs(opts)
1557 1557 if opts.get('noupdate') and opts.get('updaterev'):
1558 1558 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1559 1559
1560 1560 r = hg.clone(ui, opts, source, dest,
1561 1561 pull=opts.get('pull'),
1562 1562 stream=opts.get('uncompressed'),
1563 1563 rev=opts.get('rev'),
1564 1564 update=opts.get('updaterev') or not opts.get('noupdate'),
1565 1565 branch=opts.get('branch'),
1566 1566 shareopts=opts.get('shareopts'))
1567 1567
1568 1568 return r is None
1569 1569
1570 1570 @command('^commit|ci',
1571 1571 [('A', 'addremove', None,
1572 1572 _('mark new/missing files as added/removed before committing')),
1573 1573 ('', 'close-branch', None,
1574 1574 _('mark a branch head as closed')),
1575 1575 ('', 'amend', None, _('amend the parent of the working directory')),
1576 1576 ('s', 'secret', None, _('use the secret phase for committing')),
1577 1577 ('e', 'edit', None, _('invoke editor on commit messages')),
1578 1578 ('i', 'interactive', None, _('use interactive mode')),
1579 1579 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1580 1580 _('[OPTION]... [FILE]...'),
1581 1581 inferrepo=True)
1582 1582 def commit(ui, repo, *pats, **opts):
1583 1583 """commit the specified files or all outstanding changes
1584 1584
1585 1585 Commit changes to the given files into the repository. Unlike a
1586 1586 centralized SCM, this operation is a local operation. See
1587 1587 :hg:`push` for a way to actively distribute your changes.
1588 1588
1589 1589 If a list of files is omitted, all changes reported by :hg:`status`
1590 1590 will be committed.
1591 1591
1592 1592 If you are committing the result of a merge, do not provide any
1593 1593 filenames or -I/-X filters.
1594 1594
1595 1595 If no commit message is specified, Mercurial starts your
1596 1596 configured editor where you can enter a message. In case your
1597 1597 commit fails, you will find a backup of your message in
1598 1598 ``.hg/last-message.txt``.
1599 1599
1600 1600 The --close-branch flag can be used to mark the current branch
1601 1601 head closed. When all heads of a branch are closed, the branch
1602 1602 will be considered closed and no longer listed.
1603 1603
1604 1604 The --amend flag can be used to amend the parent of the
1605 1605 working directory with a new commit that contains the changes
1606 1606 in the parent in addition to those currently reported by :hg:`status`,
1607 1607 if there are any. The old commit is stored in a backup bundle in
1608 1608 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1609 1609 on how to restore it).
1610 1610
1611 1611 Message, user and date are taken from the amended commit unless
1612 1612 specified. When a message isn't specified on the command line,
1613 1613 the editor will open with the message of the amended commit.
1614 1614
1615 1615 It is not possible to amend public changesets (see :hg:`help phases`)
1616 1616 or changesets that have children.
1617 1617
1618 1618 See :hg:`help dates` for a list of formats valid for -d/--date.
1619 1619
1620 1620 Returns 0 on success, 1 if nothing changed.
1621 1621
1622 1622 .. container:: verbose
1623 1623
1624 1624 Examples:
1625 1625
1626 1626 - commit all files ending in .py::
1627 1627
1628 1628 hg commit --include "set:**.py"
1629 1629
1630 1630 - commit all non-binary files::
1631 1631
1632 1632 hg commit --exclude "set:binary()"
1633 1633
1634 1634 - amend the current commit and set the date to now::
1635 1635
1636 1636 hg commit --amend --date now
1637 1637 """
1638 1638 wlock = lock = None
1639 1639 try:
1640 1640 wlock = repo.wlock()
1641 1641 lock = repo.lock()
1642 1642 return _docommit(ui, repo, *pats, **opts)
1643 1643 finally:
1644 1644 release(lock, wlock)
1645 1645
1646 1646 def _docommit(ui, repo, *pats, **opts):
1647 1647 if opts.get(r'interactive'):
1648 1648 opts.pop(r'interactive')
1649 1649 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1650 1650 cmdutil.recordfilter, *pats,
1651 1651 **opts)
1652 1652 # ret can be 0 (no changes to record) or the value returned by
1653 1653 # commit(), 1 if nothing changed or None on success.
1654 1654 return 1 if ret == 0 else ret
1655 1655
1656 1656 opts = pycompat.byteskwargs(opts)
1657 1657 if opts.get('subrepos'):
1658 1658 if opts.get('amend'):
1659 1659 raise error.Abort(_('cannot amend with --subrepos'))
1660 1660 # Let --subrepos on the command line override config setting.
1661 1661 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1662 1662
1663 1663 cmdutil.checkunfinished(repo, commit=True)
1664 1664
1665 1665 branch = repo[None].branch()
1666 1666 bheads = repo.branchheads(branch)
1667 1667
1668 1668 extra = {}
1669 1669 if opts.get('close_branch'):
1670 1670 extra['close'] = 1
1671 1671
1672 1672 if not bheads:
1673 1673 raise error.Abort(_('can only close branch heads'))
1674 1674 elif opts.get('amend'):
1675 1675 if repo[None].parents()[0].p1().branch() != branch and \
1676 1676 repo[None].parents()[0].p2().branch() != branch:
1677 1677 raise error.Abort(_('can only close branch heads'))
1678 1678
1679 1679 if opts.get('amend'):
1680 1680 if ui.configbool('ui', 'commitsubrepos'):
1681 1681 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1682 1682
1683 1683 old = repo['.']
1684 1684 if not old.mutable():
1685 1685 raise error.Abort(_('cannot amend public changesets'))
1686 1686 if len(repo[None].parents()) > 1:
1687 1687 raise error.Abort(_('cannot amend while merging'))
1688 1688 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1689 1689 if not allowunstable and old.children():
1690 1690 raise error.Abort(_('cannot amend changeset with children'))
1691 1691
1692 1692 # Currently histedit gets confused if an amend happens while histedit
1693 1693 # is in progress. Since we have a checkunfinished command, we are
1694 1694 # temporarily honoring it.
1695 1695 #
1696 1696 # Note: eventually this guard will be removed. Please do not expect
1697 1697 # this behavior to remain.
1698 1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1699 1699 cmdutil.checkunfinished(repo)
1700 1700
1701 1701 # commitfunc is used only for temporary amend commit by cmdutil.amend
1702 1702 def commitfunc(ui, repo, message, match, opts):
1703 1703 return repo.commit(message,
1704 1704 opts.get('user') or old.user(),
1705 1705 opts.get('date') or old.date(),
1706 1706 match,
1707 1707 extra=extra)
1708 1708
1709 1709 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1710 1710 if node == old.node():
1711 1711 ui.status(_("nothing changed\n"))
1712 1712 return 1
1713 1713 else:
1714 1714 def commitfunc(ui, repo, message, match, opts):
1715 1715 overrides = {}
1716 1716 if opts.get('secret'):
1717 1717 overrides[('phases', 'new-commit')] = 'secret'
1718 1718
1719 1719 baseui = repo.baseui
1720 1720 with baseui.configoverride(overrides, 'commit'):
1721 1721 with ui.configoverride(overrides, 'commit'):
1722 1722 editform = cmdutil.mergeeditform(repo[None],
1723 1723 'commit.normal')
1724 1724 editor = cmdutil.getcommiteditor(
1725 1725 editform=editform, **pycompat.strkwargs(opts))
1726 1726 return repo.commit(message,
1727 1727 opts.get('user'),
1728 1728 opts.get('date'),
1729 1729 match,
1730 1730 editor=editor,
1731 1731 extra=extra)
1732 1732
1733 1733 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1734 1734
1735 1735 if not node:
1736 1736 stat = cmdutil.postcommitstatus(repo, pats, opts)
1737 1737 if stat[3]:
1738 1738 ui.status(_("nothing changed (%d missing files, see "
1739 1739 "'hg status')\n") % len(stat[3]))
1740 1740 else:
1741 1741 ui.status(_("nothing changed\n"))
1742 1742 return 1
1743 1743
1744 1744 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1745 1745
1746 1746 @command('config|showconfig|debugconfig',
1747 1747 [('u', 'untrusted', None, _('show untrusted configuration options')),
1748 1748 ('e', 'edit', None, _('edit user config')),
1749 1749 ('l', 'local', None, _('edit repository config')),
1750 1750 ('g', 'global', None, _('edit global config'))] + formatteropts,
1751 1751 _('[-u] [NAME]...'),
1752 1752 optionalrepo=True)
1753 1753 def config(ui, repo, *values, **opts):
1754 1754 """show combined config settings from all hgrc files
1755 1755
1756 1756 With no arguments, print names and values of all config items.
1757 1757
1758 1758 With one argument of the form section.name, print just the value
1759 1759 of that config item.
1760 1760
1761 1761 With multiple arguments, print names and values of all config
1762 1762 items with matching section names.
1763 1763
1764 1764 With --edit, start an editor on the user-level config file. With
1765 1765 --global, edit the system-wide config file. With --local, edit the
1766 1766 repository-level config file.
1767 1767
1768 1768 With --debug, the source (filename and line number) is printed
1769 1769 for each config item.
1770 1770
1771 1771 See :hg:`help config` for more information about config files.
1772 1772
1773 1773 Returns 0 on success, 1 if NAME does not exist.
1774 1774
1775 1775 """
1776 1776
1777 1777 opts = pycompat.byteskwargs(opts)
1778 1778 if opts.get('edit') or opts.get('local') or opts.get('global'):
1779 1779 if opts.get('local') and opts.get('global'):
1780 1780 raise error.Abort(_("can't use --local and --global together"))
1781 1781
1782 1782 if opts.get('local'):
1783 1783 if not repo:
1784 1784 raise error.Abort(_("can't use --local outside a repository"))
1785 1785 paths = [repo.vfs.join('hgrc')]
1786 1786 elif opts.get('global'):
1787 1787 paths = rcutil.systemrcpath()
1788 1788 else:
1789 1789 paths = rcutil.userrcpath()
1790 1790
1791 1791 for f in paths:
1792 1792 if os.path.exists(f):
1793 1793 break
1794 1794 else:
1795 1795 if opts.get('global'):
1796 1796 samplehgrc = uimod.samplehgrcs['global']
1797 1797 elif opts.get('local'):
1798 1798 samplehgrc = uimod.samplehgrcs['local']
1799 1799 else:
1800 1800 samplehgrc = uimod.samplehgrcs['user']
1801 1801
1802 1802 f = paths[0]
1803 1803 fp = open(f, "w")
1804 1804 fp.write(samplehgrc)
1805 1805 fp.close()
1806 1806
1807 1807 editor = ui.geteditor()
1808 1808 ui.system("%s \"%s\"" % (editor, f),
1809 1809 onerr=error.Abort, errprefix=_("edit failed"),
1810 1810 blockedtag='config_edit')
1811 1811 return
1812 1812 ui.pager('config')
1813 1813 fm = ui.formatter('config', opts)
1814 1814 for t, f in rcutil.rccomponents():
1815 1815 if t == 'path':
1816 1816 ui.debug('read config from: %s\n' % f)
1817 1817 elif t == 'items':
1818 1818 for section, name, value, source in f:
1819 1819 ui.debug('set config by: %s\n' % source)
1820 1820 else:
1821 1821 raise error.ProgrammingError('unknown rctype: %s' % t)
1822 1822 untrusted = bool(opts.get('untrusted'))
1823 1823 if values:
1824 1824 sections = [v for v in values if '.' not in v]
1825 1825 items = [v for v in values if '.' in v]
1826 1826 if len(items) > 1 or items and sections:
1827 1827 raise error.Abort(_('only one config item permitted'))
1828 1828 matched = False
1829 1829 for section, name, value in ui.walkconfig(untrusted=untrusted):
1830 1830 source = ui.configsource(section, name, untrusted)
1831 1831 value = pycompat.bytestr(value)
1832 1832 if fm.isplain():
1833 1833 source = source or 'none'
1834 1834 value = value.replace('\n', '\\n')
1835 1835 entryname = section + '.' + name
1836 1836 if values:
1837 1837 for v in values:
1838 1838 if v == section:
1839 1839 fm.startitem()
1840 1840 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1841 1841 fm.write('name value', '%s=%s\n', entryname, value)
1842 1842 matched = True
1843 1843 elif v == entryname:
1844 1844 fm.startitem()
1845 1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1846 1846 fm.write('value', '%s\n', value)
1847 1847 fm.data(name=entryname)
1848 1848 matched = True
1849 1849 else:
1850 1850 fm.startitem()
1851 1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1852 1852 fm.write('name value', '%s=%s\n', entryname, value)
1853 1853 matched = True
1854 1854 fm.end()
1855 1855 if matched:
1856 1856 return 0
1857 1857 return 1
1858 1858
1859 1859 @command('copy|cp',
1860 1860 [('A', 'after', None, _('record a copy that has already occurred')),
1861 1861 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1862 1862 ] + walkopts + dryrunopts,
1863 1863 _('[OPTION]... [SOURCE]... DEST'))
1864 1864 def copy(ui, repo, *pats, **opts):
1865 1865 """mark files as copied for the next commit
1866 1866
1867 1867 Mark dest as having copies of source files. If dest is a
1868 1868 directory, copies are put in that directory. If dest is a file,
1869 1869 the source must be a single file.
1870 1870
1871 1871 By default, this command copies the contents of files as they
1872 1872 exist in the working directory. If invoked with -A/--after, the
1873 1873 operation is recorded, but no copying is performed.
1874 1874
1875 1875 This command takes effect with the next commit. To undo a copy
1876 1876 before that, see :hg:`revert`.
1877 1877
1878 1878 Returns 0 on success, 1 if errors are encountered.
1879 1879 """
1880 1880 opts = pycompat.byteskwargs(opts)
1881 1881 with repo.wlock(False):
1882 1882 return cmdutil.copy(ui, repo, pats, opts)
1883 1883
1884 1884 @command('^diff',
1885 1885 [('r', 'rev', [], _('revision'), _('REV')),
1886 1886 ('c', 'change', '', _('change made by revision'), _('REV'))
1887 1887 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1888 1888 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1889 1889 inferrepo=True)
1890 1890 def diff(ui, repo, *pats, **opts):
1891 1891 """diff repository (or selected files)
1892 1892
1893 1893 Show differences between revisions for the specified files.
1894 1894
1895 1895 Differences between files are shown using the unified diff format.
1896 1896
1897 1897 .. note::
1898 1898
1899 1899 :hg:`diff` may generate unexpected results for merges, as it will
1900 1900 default to comparing against the working directory's first
1901 1901 parent changeset if no revisions are specified.
1902 1902
1903 1903 When two revision arguments are given, then changes are shown
1904 1904 between those revisions. If only one revision is specified then
1905 1905 that revision is compared to the working directory, and, when no
1906 1906 revisions are specified, the working directory files are compared
1907 1907 to its first parent.
1908 1908
1909 1909 Alternatively you can specify -c/--change with a revision to see
1910 1910 the changes in that changeset relative to its first parent.
1911 1911
1912 1912 Without the -a/--text option, diff will avoid generating diffs of
1913 1913 files it detects as binary. With -a, diff will generate a diff
1914 1914 anyway, probably with undesirable results.
1915 1915
1916 1916 Use the -g/--git option to generate diffs in the git extended diff
1917 1917 format. For more information, read :hg:`help diffs`.
1918 1918
1919 1919 .. container:: verbose
1920 1920
1921 1921 Examples:
1922 1922
1923 1923 - compare a file in the current working directory to its parent::
1924 1924
1925 1925 hg diff foo.c
1926 1926
1927 1927 - compare two historical versions of a directory, with rename info::
1928 1928
1929 1929 hg diff --git -r 1.0:1.2 lib/
1930 1930
1931 1931 - get change stats relative to the last change on some date::
1932 1932
1933 1933 hg diff --stat -r "date('may 2')"
1934 1934
1935 1935 - diff all newly-added files that contain a keyword::
1936 1936
1937 1937 hg diff "set:added() and grep(GNU)"
1938 1938
1939 1939 - compare a revision and its parents::
1940 1940
1941 1941 hg diff -c 9353 # compare against first parent
1942 1942 hg diff -r 9353^:9353 # same using revset syntax
1943 1943 hg diff -r 9353^2:9353 # compare against the second parent
1944 1944
1945 1945 Returns 0 on success.
1946 1946 """
1947 1947
1948 1948 opts = pycompat.byteskwargs(opts)
1949 1949 revs = opts.get('rev')
1950 1950 change = opts.get('change')
1951 1951 stat = opts.get('stat')
1952 1952 reverse = opts.get('reverse')
1953 1953
1954 1954 if revs and change:
1955 1955 msg = _('cannot specify --rev and --change at the same time')
1956 1956 raise error.Abort(msg)
1957 1957 elif change:
1958 1958 node2 = scmutil.revsingle(repo, change, None).node()
1959 1959 node1 = repo[node2].p1().node()
1960 1960 else:
1961 1961 node1, node2 = scmutil.revpair(repo, revs)
1962 1962
1963 1963 if reverse:
1964 1964 node1, node2 = node2, node1
1965 1965
1966 1966 diffopts = patch.diffallopts(ui, opts)
1967 1967 m = scmutil.match(repo[node2], pats, opts)
1968 1968 ui.pager('diff')
1969 1969 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1970 1970 listsubrepos=opts.get('subrepos'),
1971 1971 root=opts.get('root'))
1972 1972
1973 1973 @command('^export',
1974 1974 [('o', 'output', '',
1975 1975 _('print output to file with formatted name'), _('FORMAT')),
1976 1976 ('', 'switch-parent', None, _('diff against the second parent')),
1977 1977 ('r', 'rev', [], _('revisions to export'), _('REV')),
1978 1978 ] + diffopts,
1979 1979 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1980 1980 def export(ui, repo, *changesets, **opts):
1981 1981 """dump the header and diffs for one or more changesets
1982 1982
1983 1983 Print the changeset header and diffs for one or more revisions.
1984 1984 If no revision is given, the parent of the working directory is used.
1985 1985
1986 1986 The information shown in the changeset header is: author, date,
1987 1987 branch name (if non-default), changeset hash, parent(s) and commit
1988 1988 comment.
1989 1989
1990 1990 .. note::
1991 1991
1992 1992 :hg:`export` may generate unexpected diff output for merge
1993 1993 changesets, as it will compare the merge changeset against its
1994 1994 first parent only.
1995 1995
1996 1996 Output may be to a file, in which case the name of the file is
1997 1997 given using a format string. The formatting rules are as follows:
1998 1998
1999 1999 :``%%``: literal "%" character
2000 2000 :``%H``: changeset hash (40 hexadecimal digits)
2001 2001 :``%N``: number of patches being generated
2002 2002 :``%R``: changeset revision number
2003 2003 :``%b``: basename of the exporting repository
2004 2004 :``%h``: short-form changeset hash (12 hexadecimal digits)
2005 2005 :``%m``: first line of the commit message (only alphanumeric characters)
2006 2006 :``%n``: zero-padded sequence number, starting at 1
2007 2007 :``%r``: zero-padded changeset revision number
2008 2008
2009 2009 Without the -a/--text option, export will avoid generating diffs
2010 2010 of files it detects as binary. With -a, export will generate a
2011 2011 diff anyway, probably with undesirable results.
2012 2012
2013 2013 Use the -g/--git option to generate diffs in the git extended diff
2014 2014 format. See :hg:`help diffs` for more information.
2015 2015
2016 2016 With the --switch-parent option, the diff will be against the
2017 2017 second parent. It can be useful to review a merge.
2018 2018
2019 2019 .. container:: verbose
2020 2020
2021 2021 Examples:
2022 2022
2023 2023 - use export and import to transplant a bugfix to the current
2024 2024 branch::
2025 2025
2026 2026 hg export -r 9353 | hg import -
2027 2027
2028 2028 - export all the changesets between two revisions to a file with
2029 2029 rename information::
2030 2030
2031 2031 hg export --git -r 123:150 > changes.txt
2032 2032
2033 2033 - split outgoing changes into a series of patches with
2034 2034 descriptive names::
2035 2035
2036 2036 hg export -r "outgoing()" -o "%n-%m.patch"
2037 2037
2038 2038 Returns 0 on success.
2039 2039 """
2040 2040 opts = pycompat.byteskwargs(opts)
2041 2041 changesets += tuple(opts.get('rev', []))
2042 2042 if not changesets:
2043 2043 changesets = ['.']
2044 2044 revs = scmutil.revrange(repo, changesets)
2045 2045 if not revs:
2046 2046 raise error.Abort(_("export requires at least one changeset"))
2047 2047 if len(revs) > 1:
2048 2048 ui.note(_('exporting patches:\n'))
2049 2049 else:
2050 2050 ui.note(_('exporting patch:\n'))
2051 2051 ui.pager('export')
2052 2052 cmdutil.export(repo, revs, template=opts.get('output'),
2053 2053 switch_parent=opts.get('switch_parent'),
2054 2054 opts=patch.diffallopts(ui, opts))
2055 2055
2056 2056 @command('files',
2057 2057 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2058 2058 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2059 2059 ] + walkopts + formatteropts + subrepoopts,
2060 2060 _('[OPTION]... [FILE]...'))
2061 2061 def files(ui, repo, *pats, **opts):
2062 2062 """list tracked files
2063 2063
2064 2064 Print files under Mercurial control in the working directory or
2065 2065 specified revision for given files (excluding removed files).
2066 2066 Files can be specified as filenames or filesets.
2067 2067
2068 2068 If no files are given to match, this command prints the names
2069 2069 of all files under Mercurial control.
2070 2070
2071 2071 .. container:: verbose
2072 2072
2073 2073 Examples:
2074 2074
2075 2075 - list all files under the current directory::
2076 2076
2077 2077 hg files .
2078 2078
2079 2079 - shows sizes and flags for current revision::
2080 2080
2081 2081 hg files -vr .
2082 2082
2083 2083 - list all files named README::
2084 2084
2085 2085 hg files -I "**/README"
2086 2086
2087 2087 - list all binary files::
2088 2088
2089 2089 hg files "set:binary()"
2090 2090
2091 2091 - find files containing a regular expression::
2092 2092
2093 2093 hg files "set:grep('bob')"
2094 2094
2095 2095 - search tracked file contents with xargs and grep::
2096 2096
2097 2097 hg files -0 | xargs -0 grep foo
2098 2098
2099 2099 See :hg:`help patterns` and :hg:`help filesets` for more information
2100 2100 on specifying file patterns.
2101 2101
2102 2102 Returns 0 if a match is found, 1 otherwise.
2103 2103
2104 2104 """
2105 2105
2106 2106 opts = pycompat.byteskwargs(opts)
2107 2107 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2108 2108
2109 2109 end = '\n'
2110 2110 if opts.get('print0'):
2111 2111 end = '\0'
2112 2112 fmt = '%s' + end
2113 2113
2114 2114 m = scmutil.match(ctx, pats, opts)
2115 2115 ui.pager('files')
2116 2116 with ui.formatter('files', opts) as fm:
2117 2117 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2118 2118
2119 2119 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2120 2120 def forget(ui, repo, *pats, **opts):
2121 2121 """forget the specified files on the next commit
2122 2122
2123 2123 Mark the specified files so they will no longer be tracked
2124 2124 after the next commit.
2125 2125
2126 2126 This only removes files from the current branch, not from the
2127 2127 entire project history, and it does not delete them from the
2128 2128 working directory.
2129 2129
2130 2130 To delete the file from the working directory, see :hg:`remove`.
2131 2131
2132 2132 To undo a forget before the next commit, see :hg:`add`.
2133 2133
2134 2134 .. container:: verbose
2135 2135
2136 2136 Examples:
2137 2137
2138 2138 - forget newly-added binary files::
2139 2139
2140 2140 hg forget "set:added() and binary()"
2141 2141
2142 2142 - forget files that would be excluded by .hgignore::
2143 2143
2144 2144 hg forget "set:hgignore()"
2145 2145
2146 2146 Returns 0 on success.
2147 2147 """
2148 2148
2149 2149 opts = pycompat.byteskwargs(opts)
2150 2150 if not pats:
2151 2151 raise error.Abort(_('no files specified'))
2152 2152
2153 2153 m = scmutil.match(repo[None], pats, opts)
2154 2154 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2155 2155 return rejected and 1 or 0
2156 2156
2157 2157 @command(
2158 2158 'graft',
2159 2159 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2160 2160 ('c', 'continue', False, _('resume interrupted graft')),
2161 2161 ('e', 'edit', False, _('invoke editor on commit messages')),
2162 2162 ('', 'log', None, _('append graft info to log message')),
2163 2163 ('f', 'force', False, _('force graft')),
2164 2164 ('D', 'currentdate', False,
2165 2165 _('record the current date as commit date')),
2166 2166 ('U', 'currentuser', False,
2167 2167 _('record the current user as committer'), _('DATE'))]
2168 2168 + commitopts2 + mergetoolopts + dryrunopts,
2169 2169 _('[OPTION]... [-r REV]... REV...'))
2170 2170 def graft(ui, repo, *revs, **opts):
2171 2171 '''copy changes from other branches onto the current branch
2172 2172
2173 2173 This command uses Mercurial's merge logic to copy individual
2174 2174 changes from other branches without merging branches in the
2175 2175 history graph. This is sometimes known as 'backporting' or
2176 2176 'cherry-picking'. By default, graft will copy user, date, and
2177 2177 description from the source changesets.
2178 2178
2179 2179 Changesets that are ancestors of the current revision, that have
2180 2180 already been grafted, or that are merges will be skipped.
2181 2181
2182 2182 If --log is specified, log messages will have a comment appended
2183 2183 of the form::
2184 2184
2185 2185 (grafted from CHANGESETHASH)
2186 2186
2187 2187 If --force is specified, revisions will be grafted even if they
2188 2188 are already ancestors of or have been grafted to the destination.
2189 2189 This is useful when the revisions have since been backed out.
2190 2190
2191 2191 If a graft merge results in conflicts, the graft process is
2192 2192 interrupted so that the current merge can be manually resolved.
2193 2193 Once all conflicts are addressed, the graft process can be
2194 2194 continued with the -c/--continue option.
2195 2195
2196 2196 .. note::
2197 2197
2198 2198 The -c/--continue option does not reapply earlier options, except
2199 2199 for --force.
2200 2200
2201 2201 .. container:: verbose
2202 2202
2203 2203 Examples:
2204 2204
2205 2205 - copy a single change to the stable branch and edit its description::
2206 2206
2207 2207 hg update stable
2208 2208 hg graft --edit 9393
2209 2209
2210 2210 - graft a range of changesets with one exception, updating dates::
2211 2211
2212 2212 hg graft -D "2085::2093 and not 2091"
2213 2213
2214 2214 - continue a graft after resolving conflicts::
2215 2215
2216 2216 hg graft -c
2217 2217
2218 2218 - show the source of a grafted changeset::
2219 2219
2220 2220 hg log --debug -r .
2221 2221
2222 2222 - show revisions sorted by date::
2223 2223
2224 2224 hg log -r "sort(all(), date)"
2225 2225
2226 2226 See :hg:`help revisions` for more about specifying revisions.
2227 2227
2228 2228 Returns 0 on successful completion.
2229 2229 '''
2230 2230 with repo.wlock():
2231 2231 return _dograft(ui, repo, *revs, **opts)
2232 2232
2233 2233 def _dograft(ui, repo, *revs, **opts):
2234 2234 opts = pycompat.byteskwargs(opts)
2235 2235 if revs and opts.get('rev'):
2236 2236 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2237 2237 'revision ordering!\n'))
2238 2238
2239 2239 revs = list(revs)
2240 2240 revs.extend(opts.get('rev'))
2241 2241
2242 2242 if not opts.get('user') and opts.get('currentuser'):
2243 2243 opts['user'] = ui.username()
2244 2244 if not opts.get('date') and opts.get('currentdate'):
2245 2245 opts['date'] = "%d %d" % util.makedate()
2246 2246
2247 2247 editor = cmdutil.getcommiteditor(editform='graft',
2248 2248 **pycompat.strkwargs(opts))
2249 2249
2250 2250 cont = False
2251 2251 if opts.get('continue'):
2252 2252 cont = True
2253 2253 if revs:
2254 2254 raise error.Abort(_("can't specify --continue and revisions"))
2255 2255 # read in unfinished revisions
2256 2256 try:
2257 2257 nodes = repo.vfs.read('graftstate').splitlines()
2258 2258 revs = [repo[node].rev() for node in nodes]
2259 2259 except IOError as inst:
2260 2260 if inst.errno != errno.ENOENT:
2261 2261 raise
2262 2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2263 2263 else:
2264 2264 cmdutil.checkunfinished(repo)
2265 2265 cmdutil.bailifchanged(repo)
2266 2266 if not revs:
2267 2267 raise error.Abort(_('no revisions specified'))
2268 2268 revs = scmutil.revrange(repo, revs)
2269 2269
2270 2270 skipped = set()
2271 2271 # check for merges
2272 2272 for rev in repo.revs('%ld and merge()', revs):
2273 2273 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2274 2274 skipped.add(rev)
2275 2275 revs = [r for r in revs if r not in skipped]
2276 2276 if not revs:
2277 2277 return -1
2278 2278
2279 2279 # Don't check in the --continue case, in effect retaining --force across
2280 2280 # --continues. That's because without --force, any revisions we decided to
2281 2281 # skip would have been filtered out here, so they wouldn't have made their
2282 2282 # way to the graftstate. With --force, any revisions we would have otherwise
2283 2283 # skipped would not have been filtered out, and if they hadn't been applied
2284 2284 # already, they'd have been in the graftstate.
2285 2285 if not (cont or opts.get('force')):
2286 2286 # check for ancestors of dest branch
2287 2287 crev = repo['.'].rev()
2288 2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2289 2289 # XXX make this lazy in the future
2290 2290 # don't mutate while iterating, create a copy
2291 2291 for rev in list(revs):
2292 2292 if rev in ancestors:
2293 2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2294 2294 (rev, repo[rev]))
2295 2295 # XXX remove on list is slow
2296 2296 revs.remove(rev)
2297 2297 if not revs:
2298 2298 return -1
2299 2299
2300 2300 # analyze revs for earlier grafts
2301 2301 ids = {}
2302 2302 for ctx in repo.set("%ld", revs):
2303 2303 ids[ctx.hex()] = ctx.rev()
2304 2304 n = ctx.extra().get('source')
2305 2305 if n:
2306 2306 ids[n] = ctx.rev()
2307 2307
2308 2308 # check ancestors for earlier grafts
2309 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 2314 ctx = repo[rev]
2313 2315 n = ctx.extra().get('source')
2314 2316 if n in ids:
2315 2317 try:
2316 2318 r = repo[n].rev()
2317 2319 except error.RepoLookupError:
2318 2320 r = None
2319 2321 if r in revs:
2320 2322 ui.warn(_('skipping revision %d:%s '
2321 2323 '(already grafted to %d:%s)\n')
2322 2324 % (r, repo[r], rev, ctx))
2323 2325 revs.remove(r)
2324 2326 elif ids[n] in revs:
2325 2327 if r is None:
2326 2328 ui.warn(_('skipping already grafted revision %d:%s '
2327 2329 '(%d:%s also has unknown origin %s)\n')
2328 2330 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2329 2331 else:
2330 2332 ui.warn(_('skipping already grafted revision %d:%s '
2331 2333 '(%d:%s also has origin %d:%s)\n')
2332 2334 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2333 2335 revs.remove(ids[n])
2334 2336 elif ctx.hex() in ids:
2335 2337 r = ids[ctx.hex()]
2336 2338 ui.warn(_('skipping already grafted revision %d:%s '
2337 2339 '(was grafted from %d:%s)\n') %
2338 2340 (r, repo[r], rev, ctx))
2339 2341 revs.remove(r)
2340 2342 if not revs:
2341 2343 return -1
2342 2344
2343 2345 for pos, ctx in enumerate(repo.set("%ld", revs)):
2344 2346 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2345 2347 ctx.description().split('\n', 1)[0])
2346 2348 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2347 2349 if names:
2348 2350 desc += ' (%s)' % ' '.join(names)
2349 2351 ui.status(_('grafting %s\n') % desc)
2350 2352 if opts.get('dry_run'):
2351 2353 continue
2352 2354
2353 2355 source = ctx.extra().get('source')
2354 2356 extra = {}
2355 2357 if source:
2356 2358 extra['source'] = source
2357 2359 extra['intermediate-source'] = ctx.hex()
2358 2360 else:
2359 2361 extra['source'] = ctx.hex()
2360 2362 user = ctx.user()
2361 2363 if opts.get('user'):
2362 2364 user = opts['user']
2363 2365 date = ctx.date()
2364 2366 if opts.get('date'):
2365 2367 date = opts['date']
2366 2368 message = ctx.description()
2367 2369 if opts.get('log'):
2368 2370 message += '\n(grafted from %s)' % ctx.hex()
2369 2371
2370 2372 # we don't merge the first commit when continuing
2371 2373 if not cont:
2372 2374 # perform the graft merge with p1(rev) as 'ancestor'
2373 2375 try:
2374 2376 # ui.forcemerge is an internal variable, do not document
2375 2377 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2376 2378 'graft')
2377 2379 stats = mergemod.graft(repo, ctx, ctx.p1(),
2378 2380 ['local', 'graft'])
2379 2381 finally:
2380 2382 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2381 2383 # report any conflicts
2382 2384 if stats and stats[3] > 0:
2383 2385 # write out state for --continue
2384 2386 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2385 2387 repo.vfs.write('graftstate', ''.join(nodelines))
2386 2388 extra = ''
2387 2389 if opts.get('user'):
2388 2390 extra += ' --user %s' % util.shellquote(opts['user'])
2389 2391 if opts.get('date'):
2390 2392 extra += ' --date %s' % util.shellquote(opts['date'])
2391 2393 if opts.get('log'):
2392 2394 extra += ' --log'
2393 2395 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2394 2396 raise error.Abort(
2395 2397 _("unresolved conflicts, can't continue"),
2396 2398 hint=hint)
2397 2399 else:
2398 2400 cont = False
2399 2401
2400 2402 # commit
2401 2403 node = repo.commit(text=message, user=user,
2402 2404 date=date, extra=extra, editor=editor)
2403 2405 if node is None:
2404 2406 ui.warn(
2405 2407 _('note: graft of %d:%s created no changes to commit\n') %
2406 2408 (ctx.rev(), ctx))
2407 2409
2408 2410 # remove state when we complete successfully
2409 2411 if not opts.get('dry_run'):
2410 2412 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2411 2413
2412 2414 return 0
2413 2415
2414 2416 @command('grep',
2415 2417 [('0', 'print0', None, _('end fields with NUL')),
2416 2418 ('', 'all', None, _('print all revisions that match')),
2417 2419 ('a', 'text', None, _('treat all files as text')),
2418 2420 ('f', 'follow', None,
2419 2421 _('follow changeset history,'
2420 2422 ' or file history across copies and renames')),
2421 2423 ('i', 'ignore-case', None, _('ignore case when matching')),
2422 2424 ('l', 'files-with-matches', None,
2423 2425 _('print only filenames and revisions that match')),
2424 2426 ('n', 'line-number', None, _('print matching line numbers')),
2425 2427 ('r', 'rev', [],
2426 2428 _('only search files changed within revision range'), _('REV')),
2427 2429 ('u', 'user', None, _('list the author (long with -v)')),
2428 2430 ('d', 'date', None, _('list the date (short with -q)')),
2429 2431 ] + formatteropts + walkopts,
2430 2432 _('[OPTION]... PATTERN [FILE]...'),
2431 2433 inferrepo=True)
2432 2434 def grep(ui, repo, pattern, *pats, **opts):
2433 2435 """search revision history for a pattern in specified files
2434 2436
2435 2437 Search revision history for a regular expression in the specified
2436 2438 files or the entire project.
2437 2439
2438 2440 By default, grep prints the most recent revision number for each
2439 2441 file in which it finds a match. To get it to print every revision
2440 2442 that contains a change in match status ("-" for a match that becomes
2441 2443 a non-match, or "+" for a non-match that becomes a match), use the
2442 2444 --all flag.
2443 2445
2444 2446 PATTERN can be any Python (roughly Perl-compatible) regular
2445 2447 expression.
2446 2448
2447 2449 If no FILEs are specified (and -f/--follow isn't set), all files in
2448 2450 the repository are searched, including those that don't exist in the
2449 2451 current branch or have been deleted in a prior changeset.
2450 2452
2451 2453 Returns 0 if a match is found, 1 otherwise.
2452 2454 """
2453 2455 opts = pycompat.byteskwargs(opts)
2454 2456 reflags = re.M
2455 2457 if opts.get('ignore_case'):
2456 2458 reflags |= re.I
2457 2459 try:
2458 2460 regexp = util.re.compile(pattern, reflags)
2459 2461 except re.error as inst:
2460 2462 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2461 2463 return 1
2462 2464 sep, eol = ':', '\n'
2463 2465 if opts.get('print0'):
2464 2466 sep = eol = '\0'
2465 2467
2466 2468 getfile = util.lrucachefunc(repo.file)
2467 2469
2468 2470 def matchlines(body):
2469 2471 begin = 0
2470 2472 linenum = 0
2471 2473 while begin < len(body):
2472 2474 match = regexp.search(body, begin)
2473 2475 if not match:
2474 2476 break
2475 2477 mstart, mend = match.span()
2476 2478 linenum += body.count('\n', begin, mstart) + 1
2477 2479 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2478 2480 begin = body.find('\n', mend) + 1 or len(body) + 1
2479 2481 lend = begin - 1
2480 2482 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2481 2483
2482 2484 class linestate(object):
2483 2485 def __init__(self, line, linenum, colstart, colend):
2484 2486 self.line = line
2485 2487 self.linenum = linenum
2486 2488 self.colstart = colstart
2487 2489 self.colend = colend
2488 2490
2489 2491 def __hash__(self):
2490 2492 return hash((self.linenum, self.line))
2491 2493
2492 2494 def __eq__(self, other):
2493 2495 return self.line == other.line
2494 2496
2495 2497 def findpos(self):
2496 2498 """Iterate all (start, end) indices of matches"""
2497 2499 yield self.colstart, self.colend
2498 2500 p = self.colend
2499 2501 while p < len(self.line):
2500 2502 m = regexp.search(self.line, p)
2501 2503 if not m:
2502 2504 break
2503 2505 yield m.span()
2504 2506 p = m.end()
2505 2507
2506 2508 matches = {}
2507 2509 copies = {}
2508 2510 def grepbody(fn, rev, body):
2509 2511 matches[rev].setdefault(fn, [])
2510 2512 m = matches[rev][fn]
2511 2513 for lnum, cstart, cend, line in matchlines(body):
2512 2514 s = linestate(line, lnum, cstart, cend)
2513 2515 m.append(s)
2514 2516
2515 2517 def difflinestates(a, b):
2516 2518 sm = difflib.SequenceMatcher(None, a, b)
2517 2519 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2518 2520 if tag == 'insert':
2519 2521 for i in xrange(blo, bhi):
2520 2522 yield ('+', b[i])
2521 2523 elif tag == 'delete':
2522 2524 for i in xrange(alo, ahi):
2523 2525 yield ('-', a[i])
2524 2526 elif tag == 'replace':
2525 2527 for i in xrange(alo, ahi):
2526 2528 yield ('-', a[i])
2527 2529 for i in xrange(blo, bhi):
2528 2530 yield ('+', b[i])
2529 2531
2530 2532 def display(fm, fn, ctx, pstates, states):
2531 2533 rev = ctx.rev()
2532 2534 if fm.isplain():
2533 2535 formatuser = ui.shortuser
2534 2536 else:
2535 2537 formatuser = str
2536 2538 if ui.quiet:
2537 2539 datefmt = '%Y-%m-%d'
2538 2540 else:
2539 2541 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2540 2542 found = False
2541 2543 @util.cachefunc
2542 2544 def binary():
2543 2545 flog = getfile(fn)
2544 2546 return util.binary(flog.read(ctx.filenode(fn)))
2545 2547
2546 2548 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2547 2549 if opts.get('all'):
2548 2550 iter = difflinestates(pstates, states)
2549 2551 else:
2550 2552 iter = [('', l) for l in states]
2551 2553 for change, l in iter:
2552 2554 fm.startitem()
2553 2555 fm.data(node=fm.hexfunc(ctx.node()))
2554 2556 cols = [
2555 2557 ('filename', fn, True),
2556 2558 ('rev', rev, True),
2557 2559 ('linenumber', l.linenum, opts.get('line_number')),
2558 2560 ]
2559 2561 if opts.get('all'):
2560 2562 cols.append(('change', change, True))
2561 2563 cols.extend([
2562 2564 ('user', formatuser(ctx.user()), opts.get('user')),
2563 2565 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2564 2566 ])
2565 2567 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2566 2568 for name, data, cond in cols:
2567 2569 field = fieldnamemap.get(name, name)
2568 2570 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2569 2571 if cond and name != lastcol:
2570 2572 fm.plain(sep, label='grep.sep')
2571 2573 if not opts.get('files_with_matches'):
2572 2574 fm.plain(sep, label='grep.sep')
2573 2575 if not opts.get('text') and binary():
2574 2576 fm.plain(_(" Binary file matches"))
2575 2577 else:
2576 2578 displaymatches(fm.nested('texts'), l)
2577 2579 fm.plain(eol)
2578 2580 found = True
2579 2581 if opts.get('files_with_matches'):
2580 2582 break
2581 2583 return found
2582 2584
2583 2585 def displaymatches(fm, l):
2584 2586 p = 0
2585 2587 for s, e in l.findpos():
2586 2588 if p < s:
2587 2589 fm.startitem()
2588 2590 fm.write('text', '%s', l.line[p:s])
2589 2591 fm.data(matched=False)
2590 2592 fm.startitem()
2591 2593 fm.write('text', '%s', l.line[s:e], label='grep.match')
2592 2594 fm.data(matched=True)
2593 2595 p = e
2594 2596 if p < len(l.line):
2595 2597 fm.startitem()
2596 2598 fm.write('text', '%s', l.line[p:])
2597 2599 fm.data(matched=False)
2598 2600 fm.end()
2599 2601
2600 2602 skip = {}
2601 2603 revfiles = {}
2602 2604 matchfn = scmutil.match(repo[None], pats, opts)
2603 2605 found = False
2604 2606 follow = opts.get('follow')
2605 2607
2606 2608 def prep(ctx, fns):
2607 2609 rev = ctx.rev()
2608 2610 pctx = ctx.p1()
2609 2611 parent = pctx.rev()
2610 2612 matches.setdefault(rev, {})
2611 2613 matches.setdefault(parent, {})
2612 2614 files = revfiles.setdefault(rev, [])
2613 2615 for fn in fns:
2614 2616 flog = getfile(fn)
2615 2617 try:
2616 2618 fnode = ctx.filenode(fn)
2617 2619 except error.LookupError:
2618 2620 continue
2619 2621
2620 2622 copied = flog.renamed(fnode)
2621 2623 copy = follow and copied and copied[0]
2622 2624 if copy:
2623 2625 copies.setdefault(rev, {})[fn] = copy
2624 2626 if fn in skip:
2625 2627 if copy:
2626 2628 skip[copy] = True
2627 2629 continue
2628 2630 files.append(fn)
2629 2631
2630 2632 if fn not in matches[rev]:
2631 2633 grepbody(fn, rev, flog.read(fnode))
2632 2634
2633 2635 pfn = copy or fn
2634 2636 if pfn not in matches[parent]:
2635 2637 try:
2636 2638 fnode = pctx.filenode(pfn)
2637 2639 grepbody(pfn, parent, flog.read(fnode))
2638 2640 except error.LookupError:
2639 2641 pass
2640 2642
2641 2643 ui.pager('grep')
2642 2644 fm = ui.formatter('grep', opts)
2643 2645 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2644 2646 rev = ctx.rev()
2645 2647 parent = ctx.p1().rev()
2646 2648 for fn in sorted(revfiles.get(rev, [])):
2647 2649 states = matches[rev][fn]
2648 2650 copy = copies.get(rev, {}).get(fn)
2649 2651 if fn in skip:
2650 2652 if copy:
2651 2653 skip[copy] = True
2652 2654 continue
2653 2655 pstates = matches.get(parent, {}).get(copy or fn, [])
2654 2656 if pstates or states:
2655 2657 r = display(fm, fn, ctx, pstates, states)
2656 2658 found = found or r
2657 2659 if r and not opts.get('all'):
2658 2660 skip[fn] = True
2659 2661 if copy:
2660 2662 skip[copy] = True
2661 2663 del matches[rev]
2662 2664 del revfiles[rev]
2663 2665 fm.end()
2664 2666
2665 2667 return not found
2666 2668
2667 2669 @command('heads',
2668 2670 [('r', 'rev', '',
2669 2671 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2670 2672 ('t', 'topo', False, _('show topological heads only')),
2671 2673 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2672 2674 ('c', 'closed', False, _('show normal and closed branch heads')),
2673 2675 ] + templateopts,
2674 2676 _('[-ct] [-r STARTREV] [REV]...'))
2675 2677 def heads(ui, repo, *branchrevs, **opts):
2676 2678 """show branch heads
2677 2679
2678 2680 With no arguments, show all open branch heads in the repository.
2679 2681 Branch heads are changesets that have no descendants on the
2680 2682 same branch. They are where development generally takes place and
2681 2683 are the usual targets for update and merge operations.
2682 2684
2683 2685 If one or more REVs are given, only open branch heads on the
2684 2686 branches associated with the specified changesets are shown. This
2685 2687 means that you can use :hg:`heads .` to see the heads on the
2686 2688 currently checked-out branch.
2687 2689
2688 2690 If -c/--closed is specified, also show branch heads marked closed
2689 2691 (see :hg:`commit --close-branch`).
2690 2692
2691 2693 If STARTREV is specified, only those heads that are descendants of
2692 2694 STARTREV will be displayed.
2693 2695
2694 2696 If -t/--topo is specified, named branch mechanics will be ignored and only
2695 2697 topological heads (changesets with no children) will be shown.
2696 2698
2697 2699 Returns 0 if matching heads are found, 1 if not.
2698 2700 """
2699 2701
2700 2702 opts = pycompat.byteskwargs(opts)
2701 2703 start = None
2702 2704 if 'rev' in opts:
2703 2705 start = scmutil.revsingle(repo, opts['rev'], None).node()
2704 2706
2705 2707 if opts.get('topo'):
2706 2708 heads = [repo[h] for h in repo.heads(start)]
2707 2709 else:
2708 2710 heads = []
2709 2711 for branch in repo.branchmap():
2710 2712 heads += repo.branchheads(branch, start, opts.get('closed'))
2711 2713 heads = [repo[h] for h in heads]
2712 2714
2713 2715 if branchrevs:
2714 2716 branches = set(repo[br].branch() for br in branchrevs)
2715 2717 heads = [h for h in heads if h.branch() in branches]
2716 2718
2717 2719 if opts.get('active') and branchrevs:
2718 2720 dagheads = repo.heads(start)
2719 2721 heads = [h for h in heads if h.node() in dagheads]
2720 2722
2721 2723 if branchrevs:
2722 2724 haveheads = set(h.branch() for h in heads)
2723 2725 if branches - haveheads:
2724 2726 headless = ', '.join(b for b in branches - haveheads)
2725 2727 msg = _('no open branch heads found on branches %s')
2726 2728 if opts.get('rev'):
2727 2729 msg += _(' (started at %s)') % opts['rev']
2728 2730 ui.warn((msg + '\n') % headless)
2729 2731
2730 2732 if not heads:
2731 2733 return 1
2732 2734
2733 2735 ui.pager('heads')
2734 2736 heads = sorted(heads, key=lambda x: -x.rev())
2735 2737 displayer = cmdutil.show_changeset(ui, repo, opts)
2736 2738 for ctx in heads:
2737 2739 displayer.show(ctx)
2738 2740 displayer.close()
2739 2741
2740 2742 @command('help',
2741 2743 [('e', 'extension', None, _('show only help for extensions')),
2742 2744 ('c', 'command', None, _('show only help for commands')),
2743 2745 ('k', 'keyword', None, _('show topics matching keyword')),
2744 2746 ('s', 'system', [], _('show help for specific platform(s)')),
2745 2747 ],
2746 2748 _('[-ecks] [TOPIC]'),
2747 2749 norepo=True)
2748 2750 def help_(ui, name=None, **opts):
2749 2751 """show help for a given topic or a help overview
2750 2752
2751 2753 With no arguments, print a list of commands with short help messages.
2752 2754
2753 2755 Given a topic, extension, or command name, print help for that
2754 2756 topic.
2755 2757
2756 2758 Returns 0 if successful.
2757 2759 """
2758 2760
2759 2761 keep = opts.get(r'system') or []
2760 2762 if len(keep) == 0:
2761 2763 if pycompat.sysplatform.startswith('win'):
2762 2764 keep.append('windows')
2763 2765 elif pycompat.sysplatform == 'OpenVMS':
2764 2766 keep.append('vms')
2765 2767 elif pycompat.sysplatform == 'plan9':
2766 2768 keep.append('plan9')
2767 2769 else:
2768 2770 keep.append('unix')
2769 2771 keep.append(pycompat.sysplatform.lower())
2770 2772 if ui.verbose:
2771 2773 keep.append('verbose')
2772 2774
2773 2775 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2774 2776 ui.pager('help')
2775 2777 ui.write(formatted)
2776 2778
2777 2779
2778 2780 @command('identify|id',
2779 2781 [('r', 'rev', '',
2780 2782 _('identify the specified revision'), _('REV')),
2781 2783 ('n', 'num', None, _('show local revision number')),
2782 2784 ('i', 'id', None, _('show global revision id')),
2783 2785 ('b', 'branch', None, _('show branch')),
2784 2786 ('t', 'tags', None, _('show tags')),
2785 2787 ('B', 'bookmarks', None, _('show bookmarks')),
2786 2788 ] + remoteopts,
2787 2789 _('[-nibtB] [-r REV] [SOURCE]'),
2788 2790 optionalrepo=True)
2789 2791 def identify(ui, repo, source=None, rev=None,
2790 2792 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2791 2793 """identify the working directory or specified revision
2792 2794
2793 2795 Print a summary identifying the repository state at REV using one or
2794 2796 two parent hash identifiers, followed by a "+" if the working
2795 2797 directory has uncommitted changes, the branch name (if not default),
2796 2798 a list of tags, and a list of bookmarks.
2797 2799
2798 2800 When REV is not given, print a summary of the current state of the
2799 2801 repository.
2800 2802
2801 2803 Specifying a path to a repository root or Mercurial bundle will
2802 2804 cause lookup to operate on that repository/bundle.
2803 2805
2804 2806 .. container:: verbose
2805 2807
2806 2808 Examples:
2807 2809
2808 2810 - generate a build identifier for the working directory::
2809 2811
2810 2812 hg id --id > build-id.dat
2811 2813
2812 2814 - find the revision corresponding to a tag::
2813 2815
2814 2816 hg id -n -r 1.3
2815 2817
2816 2818 - check the most recent revision of a remote repository::
2817 2819
2818 2820 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2819 2821
2820 2822 See :hg:`log` for generating more information about specific revisions,
2821 2823 including full hash identifiers.
2822 2824
2823 2825 Returns 0 if successful.
2824 2826 """
2825 2827
2826 2828 opts = pycompat.byteskwargs(opts)
2827 2829 if not repo and not source:
2828 2830 raise error.Abort(_("there is no Mercurial repository here "
2829 2831 "(.hg not found)"))
2830 2832
2831 2833 if ui.debugflag:
2832 2834 hexfunc = hex
2833 2835 else:
2834 2836 hexfunc = short
2835 2837 default = not (num or id or branch or tags or bookmarks)
2836 2838 output = []
2837 2839 revs = []
2838 2840
2839 2841 if source:
2840 2842 source, branches = hg.parseurl(ui.expandpath(source))
2841 2843 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2842 2844 repo = peer.local()
2843 2845 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2844 2846
2845 2847 if not repo:
2846 2848 if num or branch or tags:
2847 2849 raise error.Abort(
2848 2850 _("can't query remote revision number, branch, or tags"))
2849 2851 if not rev and revs:
2850 2852 rev = revs[0]
2851 2853 if not rev:
2852 2854 rev = "tip"
2853 2855
2854 2856 remoterev = peer.lookup(rev)
2855 2857 if default or id:
2856 2858 output = [hexfunc(remoterev)]
2857 2859
2858 2860 def getbms():
2859 2861 bms = []
2860 2862
2861 2863 if 'bookmarks' in peer.listkeys('namespaces'):
2862 2864 hexremoterev = hex(remoterev)
2863 2865 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2864 2866 if bmr == hexremoterev]
2865 2867
2866 2868 return sorted(bms)
2867 2869
2868 2870 if bookmarks:
2869 2871 output.extend(getbms())
2870 2872 elif default and not ui.quiet:
2871 2873 # multiple bookmarks for a single parent separated by '/'
2872 2874 bm = '/'.join(getbms())
2873 2875 if bm:
2874 2876 output.append(bm)
2875 2877 else:
2876 2878 ctx = scmutil.revsingle(repo, rev, None)
2877 2879
2878 2880 if ctx.rev() is None:
2879 2881 ctx = repo[None]
2880 2882 parents = ctx.parents()
2881 2883 taglist = []
2882 2884 for p in parents:
2883 2885 taglist.extend(p.tags())
2884 2886
2885 2887 changed = ""
2886 2888 if default or id or num:
2887 2889 if (any(repo.status())
2888 2890 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2889 2891 changed = '+'
2890 2892 if default or id:
2891 2893 output = ["%s%s" %
2892 2894 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2893 2895 if num:
2894 2896 output.append("%s%s" %
2895 2897 ('+'.join([str(p.rev()) for p in parents]), changed))
2896 2898 else:
2897 2899 if default or id:
2898 2900 output = [hexfunc(ctx.node())]
2899 2901 if num:
2900 2902 output.append(str(ctx.rev()))
2901 2903 taglist = ctx.tags()
2902 2904
2903 2905 if default and not ui.quiet:
2904 2906 b = ctx.branch()
2905 2907 if b != 'default':
2906 2908 output.append("(%s)" % b)
2907 2909
2908 2910 # multiple tags for a single parent separated by '/'
2909 2911 t = '/'.join(taglist)
2910 2912 if t:
2911 2913 output.append(t)
2912 2914
2913 2915 # multiple bookmarks for a single parent separated by '/'
2914 2916 bm = '/'.join(ctx.bookmarks())
2915 2917 if bm:
2916 2918 output.append(bm)
2917 2919 else:
2918 2920 if branch:
2919 2921 output.append(ctx.branch())
2920 2922
2921 2923 if tags:
2922 2924 output.extend(taglist)
2923 2925
2924 2926 if bookmarks:
2925 2927 output.extend(ctx.bookmarks())
2926 2928
2927 2929 ui.write("%s\n" % ' '.join(output))
2928 2930
2929 2931 @command('import|patch',
2930 2932 [('p', 'strip', 1,
2931 2933 _('directory strip option for patch. This has the same '
2932 2934 'meaning as the corresponding patch option'), _('NUM')),
2933 2935 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2934 2936 ('e', 'edit', False, _('invoke editor on commit messages')),
2935 2937 ('f', 'force', None,
2936 2938 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2937 2939 ('', 'no-commit', None,
2938 2940 _("don't commit, just update the working directory")),
2939 2941 ('', 'bypass', None,
2940 2942 _("apply patch without touching the working directory")),
2941 2943 ('', 'partial', None,
2942 2944 _('commit even if some hunks fail')),
2943 2945 ('', 'exact', None,
2944 2946 _('abort if patch would apply lossily')),
2945 2947 ('', 'prefix', '',
2946 2948 _('apply patch to subdirectory'), _('DIR')),
2947 2949 ('', 'import-branch', None,
2948 2950 _('use any branch information in patch (implied by --exact)'))] +
2949 2951 commitopts + commitopts2 + similarityopts,
2950 2952 _('[OPTION]... PATCH...'))
2951 2953 def import_(ui, repo, patch1=None, *patches, **opts):
2952 2954 """import an ordered set of patches
2953 2955
2954 2956 Import a list of patches and commit them individually (unless
2955 2957 --no-commit is specified).
2956 2958
2957 2959 To read a patch from standard input (stdin), use "-" as the patch
2958 2960 name. If a URL is specified, the patch will be downloaded from
2959 2961 there.
2960 2962
2961 2963 Import first applies changes to the working directory (unless
2962 2964 --bypass is specified), import will abort if there are outstanding
2963 2965 changes.
2964 2966
2965 2967 Use --bypass to apply and commit patches directly to the
2966 2968 repository, without affecting the working directory. Without
2967 2969 --exact, patches will be applied on top of the working directory
2968 2970 parent revision.
2969 2971
2970 2972 You can import a patch straight from a mail message. Even patches
2971 2973 as attachments work (to use the body part, it must have type
2972 2974 text/plain or text/x-patch). From and Subject headers of email
2973 2975 message are used as default committer and commit message. All
2974 2976 text/plain body parts before first diff are added to the commit
2975 2977 message.
2976 2978
2977 2979 If the imported patch was generated by :hg:`export`, user and
2978 2980 description from patch override values from message headers and
2979 2981 body. Values given on command line with -m/--message and -u/--user
2980 2982 override these.
2981 2983
2982 2984 If --exact is specified, import will set the working directory to
2983 2985 the parent of each patch before applying it, and will abort if the
2984 2986 resulting changeset has a different ID than the one recorded in
2985 2987 the patch. This will guard against various ways that portable
2986 2988 patch formats and mail systems might fail to transfer Mercurial
2987 2989 data or metadata. See :hg:`bundle` for lossless transmission.
2988 2990
2989 2991 Use --partial to ensure a changeset will be created from the patch
2990 2992 even if some hunks fail to apply. Hunks that fail to apply will be
2991 2993 written to a <target-file>.rej file. Conflicts can then be resolved
2992 2994 by hand before :hg:`commit --amend` is run to update the created
2993 2995 changeset. This flag exists to let people import patches that
2994 2996 partially apply without losing the associated metadata (author,
2995 2997 date, description, ...).
2996 2998
2997 2999 .. note::
2998 3000
2999 3001 When no hunks apply cleanly, :hg:`import --partial` will create
3000 3002 an empty changeset, importing only the patch metadata.
3001 3003
3002 3004 With -s/--similarity, hg will attempt to discover renames and
3003 3005 copies in the patch in the same way as :hg:`addremove`.
3004 3006
3005 3007 It is possible to use external patch programs to perform the patch
3006 3008 by setting the ``ui.patch`` configuration option. For the default
3007 3009 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3008 3010 See :hg:`help config` for more information about configuration
3009 3011 files and how to use these options.
3010 3012
3011 3013 See :hg:`help dates` for a list of formats valid for -d/--date.
3012 3014
3013 3015 .. container:: verbose
3014 3016
3015 3017 Examples:
3016 3018
3017 3019 - import a traditional patch from a website and detect renames::
3018 3020
3019 3021 hg import -s 80 http://example.com/bugfix.patch
3020 3022
3021 3023 - import a changeset from an hgweb server::
3022 3024
3023 3025 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3024 3026
3025 3027 - import all the patches in an Unix-style mbox::
3026 3028
3027 3029 hg import incoming-patches.mbox
3028 3030
3029 3031 - import patches from stdin::
3030 3032
3031 3033 hg import -
3032 3034
3033 3035 - attempt to exactly restore an exported changeset (not always
3034 3036 possible)::
3035 3037
3036 3038 hg import --exact proposed-fix.patch
3037 3039
3038 3040 - use an external tool to apply a patch which is too fuzzy for
3039 3041 the default internal tool.
3040 3042
3041 3043 hg import --config ui.patch="patch --merge" fuzzy.patch
3042 3044
3043 3045 - change the default fuzzing from 2 to a less strict 7
3044 3046
3045 3047 hg import --config ui.fuzz=7 fuzz.patch
3046 3048
3047 3049 Returns 0 on success, 1 on partial success (see --partial).
3048 3050 """
3049 3051
3050 3052 opts = pycompat.byteskwargs(opts)
3051 3053 if not patch1:
3052 3054 raise error.Abort(_('need at least one patch to import'))
3053 3055
3054 3056 patches = (patch1,) + patches
3055 3057
3056 3058 date = opts.get('date')
3057 3059 if date:
3058 3060 opts['date'] = util.parsedate(date)
3059 3061
3060 3062 exact = opts.get('exact')
3061 3063 update = not opts.get('bypass')
3062 3064 if not update and opts.get('no_commit'):
3063 3065 raise error.Abort(_('cannot use --no-commit with --bypass'))
3064 3066 try:
3065 3067 sim = float(opts.get('similarity') or 0)
3066 3068 except ValueError:
3067 3069 raise error.Abort(_('similarity must be a number'))
3068 3070 if sim < 0 or sim > 100:
3069 3071 raise error.Abort(_('similarity must be between 0 and 100'))
3070 3072 if sim and not update:
3071 3073 raise error.Abort(_('cannot use --similarity with --bypass'))
3072 3074 if exact:
3073 3075 if opts.get('edit'):
3074 3076 raise error.Abort(_('cannot use --exact with --edit'))
3075 3077 if opts.get('prefix'):
3076 3078 raise error.Abort(_('cannot use --exact with --prefix'))
3077 3079
3078 3080 base = opts["base"]
3079 3081 wlock = dsguard = lock = tr = None
3080 3082 msgs = []
3081 3083 ret = 0
3082 3084
3083 3085
3084 3086 try:
3085 3087 wlock = repo.wlock()
3086 3088
3087 3089 if update:
3088 3090 cmdutil.checkunfinished(repo)
3089 3091 if (exact or not opts.get('force')):
3090 3092 cmdutil.bailifchanged(repo)
3091 3093
3092 3094 if not opts.get('no_commit'):
3093 3095 lock = repo.lock()
3094 3096 tr = repo.transaction('import')
3095 3097 else:
3096 3098 dsguard = dirstateguard.dirstateguard(repo, 'import')
3097 3099 parents = repo[None].parents()
3098 3100 for patchurl in patches:
3099 3101 if patchurl == '-':
3100 3102 ui.status(_('applying patch from stdin\n'))
3101 3103 patchfile = ui.fin
3102 3104 patchurl = 'stdin' # for error message
3103 3105 else:
3104 3106 patchurl = os.path.join(base, patchurl)
3105 3107 ui.status(_('applying %s\n') % patchurl)
3106 3108 patchfile = hg.openpath(ui, patchurl)
3107 3109
3108 3110 haspatch = False
3109 3111 for hunk in patch.split(patchfile):
3110 3112 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3111 3113 parents, opts,
3112 3114 msgs, hg.clean)
3113 3115 if msg:
3114 3116 haspatch = True
3115 3117 ui.note(msg + '\n')
3116 3118 if update or exact:
3117 3119 parents = repo[None].parents()
3118 3120 else:
3119 3121 parents = [repo[node]]
3120 3122 if rej:
3121 3123 ui.write_err(_("patch applied partially\n"))
3122 3124 ui.write_err(_("(fix the .rej files and run "
3123 3125 "`hg commit --amend`)\n"))
3124 3126 ret = 1
3125 3127 break
3126 3128
3127 3129 if not haspatch:
3128 3130 raise error.Abort(_('%s: no diffs found') % patchurl)
3129 3131
3130 3132 if tr:
3131 3133 tr.close()
3132 3134 if msgs:
3133 3135 repo.savecommitmessage('\n* * *\n'.join(msgs))
3134 3136 if dsguard:
3135 3137 dsguard.close()
3136 3138 return ret
3137 3139 finally:
3138 3140 if tr:
3139 3141 tr.release()
3140 3142 release(lock, dsguard, wlock)
3141 3143
3142 3144 @command('incoming|in',
3143 3145 [('f', 'force', None,
3144 3146 _('run even if remote repository is unrelated')),
3145 3147 ('n', 'newest-first', None, _('show newest record first')),
3146 3148 ('', 'bundle', '',
3147 3149 _('file to store the bundles into'), _('FILE')),
3148 3150 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3149 3151 ('B', 'bookmarks', False, _("compare bookmarks")),
3150 3152 ('b', 'branch', [],
3151 3153 _('a specific branch you would like to pull'), _('BRANCH')),
3152 3154 ] + logopts + remoteopts + subrepoopts,
3153 3155 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3154 3156 def incoming(ui, repo, source="default", **opts):
3155 3157 """show new changesets found in source
3156 3158
3157 3159 Show new changesets found in the specified path/URL or the default
3158 3160 pull location. These are the changesets that would have been pulled
3159 3161 if a pull at the time you issued this command.
3160 3162
3161 3163 See pull for valid source format details.
3162 3164
3163 3165 .. container:: verbose
3164 3166
3165 3167 With -B/--bookmarks, the result of bookmark comparison between
3166 3168 local and remote repositories is displayed. With -v/--verbose,
3167 3169 status is also displayed for each bookmark like below::
3168 3170
3169 3171 BM1 01234567890a added
3170 3172 BM2 1234567890ab advanced
3171 3173 BM3 234567890abc diverged
3172 3174 BM4 34567890abcd changed
3173 3175
3174 3176 The action taken locally when pulling depends on the
3175 3177 status of each bookmark:
3176 3178
3177 3179 :``added``: pull will create it
3178 3180 :``advanced``: pull will update it
3179 3181 :``diverged``: pull will create a divergent bookmark
3180 3182 :``changed``: result depends on remote changesets
3181 3183
3182 3184 From the point of view of pulling behavior, bookmark
3183 3185 existing only in the remote repository are treated as ``added``,
3184 3186 even if it is in fact locally deleted.
3185 3187
3186 3188 .. container:: verbose
3187 3189
3188 3190 For remote repository, using --bundle avoids downloading the
3189 3191 changesets twice if the incoming is followed by a pull.
3190 3192
3191 3193 Examples:
3192 3194
3193 3195 - show incoming changes with patches and full description::
3194 3196
3195 3197 hg incoming -vp
3196 3198
3197 3199 - show incoming changes excluding merges, store a bundle::
3198 3200
3199 3201 hg in -vpM --bundle incoming.hg
3200 3202 hg pull incoming.hg
3201 3203
3202 3204 - briefly list changes inside a bundle::
3203 3205
3204 3206 hg in changes.hg -T "{desc|firstline}\\n"
3205 3207
3206 3208 Returns 0 if there are incoming changes, 1 otherwise.
3207 3209 """
3208 3210 opts = pycompat.byteskwargs(opts)
3209 3211 if opts.get('graph'):
3210 3212 cmdutil.checkunsupportedgraphflags([], opts)
3211 3213 def display(other, chlist, displayer):
3212 3214 revdag = cmdutil.graphrevs(other, chlist, opts)
3213 3215 cmdutil.displaygraph(ui, repo, revdag, displayer,
3214 3216 graphmod.asciiedges)
3215 3217
3216 3218 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3217 3219 return 0
3218 3220
3219 3221 if opts.get('bundle') and opts.get('subrepos'):
3220 3222 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3221 3223
3222 3224 if opts.get('bookmarks'):
3223 3225 source, branches = hg.parseurl(ui.expandpath(source),
3224 3226 opts.get('branch'))
3225 3227 other = hg.peer(repo, opts, source)
3226 3228 if 'bookmarks' not in other.listkeys('namespaces'):
3227 3229 ui.warn(_("remote doesn't support bookmarks\n"))
3228 3230 return 0
3229 3231 ui.pager('incoming')
3230 3232 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3231 3233 return bookmarks.incoming(ui, repo, other)
3232 3234
3233 3235 repo._subtoppath = ui.expandpath(source)
3234 3236 try:
3235 3237 return hg.incoming(ui, repo, source, opts)
3236 3238 finally:
3237 3239 del repo._subtoppath
3238 3240
3239 3241
3240 3242 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3241 3243 norepo=True)
3242 3244 def init(ui, dest=".", **opts):
3243 3245 """create a new repository in the given directory
3244 3246
3245 3247 Initialize a new repository in the given directory. If the given
3246 3248 directory does not exist, it will be created.
3247 3249
3248 3250 If no directory is given, the current directory is used.
3249 3251
3250 3252 It is possible to specify an ``ssh://`` URL as the destination.
3251 3253 See :hg:`help urls` for more information.
3252 3254
3253 3255 Returns 0 on success.
3254 3256 """
3255 3257 opts = pycompat.byteskwargs(opts)
3256 3258 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3257 3259
3258 3260 @command('locate',
3259 3261 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3260 3262 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3261 3263 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3262 3264 ] + walkopts,
3263 3265 _('[OPTION]... [PATTERN]...'))
3264 3266 def locate(ui, repo, *pats, **opts):
3265 3267 """locate files matching specific patterns (DEPRECATED)
3266 3268
3267 3269 Print files under Mercurial control in the working directory whose
3268 3270 names match the given patterns.
3269 3271
3270 3272 By default, this command searches all directories in the working
3271 3273 directory. To search just the current directory and its
3272 3274 subdirectories, use "--include .".
3273 3275
3274 3276 If no patterns are given to match, this command prints the names
3275 3277 of all files under Mercurial control in the working directory.
3276 3278
3277 3279 If you want to feed the output of this command into the "xargs"
3278 3280 command, use the -0 option to both this command and "xargs". This
3279 3281 will avoid the problem of "xargs" treating single filenames that
3280 3282 contain whitespace as multiple filenames.
3281 3283
3282 3284 See :hg:`help files` for a more versatile command.
3283 3285
3284 3286 Returns 0 if a match is found, 1 otherwise.
3285 3287 """
3286 3288 opts = pycompat.byteskwargs(opts)
3287 3289 if opts.get('print0'):
3288 3290 end = '\0'
3289 3291 else:
3290 3292 end = '\n'
3291 3293 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3292 3294
3293 3295 ret = 1
3294 3296 ctx = repo[rev]
3295 3297 m = scmutil.match(ctx, pats, opts, default='relglob',
3296 3298 badfn=lambda x, y: False)
3297 3299
3298 3300 ui.pager('locate')
3299 3301 for abs in ctx.matches(m):
3300 3302 if opts.get('fullpath'):
3301 3303 ui.write(repo.wjoin(abs), end)
3302 3304 else:
3303 3305 ui.write(((pats and m.rel(abs)) or abs), end)
3304 3306 ret = 0
3305 3307
3306 3308 return ret
3307 3309
3308 3310 @command('^log|history',
3309 3311 [('f', 'follow', None,
3310 3312 _('follow changeset history, or file history across copies and renames')),
3311 3313 ('', 'follow-first', None,
3312 3314 _('only follow the first parent of merge changesets (DEPRECATED)')),
3313 3315 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3314 3316 ('C', 'copies', None, _('show copied files')),
3315 3317 ('k', 'keyword', [],
3316 3318 _('do case-insensitive search for a given text'), _('TEXT')),
3317 3319 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3318 3320 ('', 'removed', None, _('include revisions where files were removed')),
3319 3321 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3320 3322 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3321 3323 ('', 'only-branch', [],
3322 3324 _('show only changesets within the given named branch (DEPRECATED)'),
3323 3325 _('BRANCH')),
3324 3326 ('b', 'branch', [],
3325 3327 _('show changesets within the given named branch'), _('BRANCH')),
3326 3328 ('P', 'prune', [],
3327 3329 _('do not display revision or any of its ancestors'), _('REV')),
3328 3330 ] + logopts + walkopts,
3329 3331 _('[OPTION]... [FILE]'),
3330 3332 inferrepo=True)
3331 3333 def log(ui, repo, *pats, **opts):
3332 3334 """show revision history of entire repository or files
3333 3335
3334 3336 Print the revision history of the specified files or the entire
3335 3337 project.
3336 3338
3337 3339 If no revision range is specified, the default is ``tip:0`` unless
3338 3340 --follow is set, in which case the working directory parent is
3339 3341 used as the starting revision.
3340 3342
3341 3343 File history is shown without following rename or copy history of
3342 3344 files. Use -f/--follow with a filename to follow history across
3343 3345 renames and copies. --follow without a filename will only show
3344 3346 ancestors or descendants of the starting revision.
3345 3347
3346 3348 By default this command prints revision number and changeset id,
3347 3349 tags, non-trivial parents, user, date and time, and a summary for
3348 3350 each commit. When the -v/--verbose switch is used, the list of
3349 3351 changed files and full commit message are shown.
3350 3352
3351 3353 With --graph the revisions are shown as an ASCII art DAG with the most
3352 3354 recent changeset at the top.
3353 3355 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3354 3356 and '+' represents a fork where the changeset from the lines below is a
3355 3357 parent of the 'o' merge on the same line.
3356 3358 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3357 3359 of a '|' indicates one or more revisions in a path are omitted.
3358 3360
3359 3361 .. note::
3360 3362
3361 3363 :hg:`log --patch` may generate unexpected diff output for merge
3362 3364 changesets, as it will only compare the merge changeset against
3363 3365 its first parent. Also, only files different from BOTH parents
3364 3366 will appear in files:.
3365 3367
3366 3368 .. note::
3367 3369
3368 3370 For performance reasons, :hg:`log FILE` may omit duplicate changes
3369 3371 made on branches and will not show removals or mode changes. To
3370 3372 see all such changes, use the --removed switch.
3371 3373
3372 3374 .. container:: verbose
3373 3375
3374 3376 Some examples:
3375 3377
3376 3378 - changesets with full descriptions and file lists::
3377 3379
3378 3380 hg log -v
3379 3381
3380 3382 - changesets ancestral to the working directory::
3381 3383
3382 3384 hg log -f
3383 3385
3384 3386 - last 10 commits on the current branch::
3385 3387
3386 3388 hg log -l 10 -b .
3387 3389
3388 3390 - changesets showing all modifications of a file, including removals::
3389 3391
3390 3392 hg log --removed file.c
3391 3393
3392 3394 - all changesets that touch a directory, with diffs, excluding merges::
3393 3395
3394 3396 hg log -Mp lib/
3395 3397
3396 3398 - all revision numbers that match a keyword::
3397 3399
3398 3400 hg log -k bug --template "{rev}\\n"
3399 3401
3400 3402 - the full hash identifier of the working directory parent::
3401 3403
3402 3404 hg log -r . --template "{node}\\n"
3403 3405
3404 3406 - list available log templates::
3405 3407
3406 3408 hg log -T list
3407 3409
3408 3410 - check if a given changeset is included in a tagged release::
3409 3411
3410 3412 hg log -r "a21ccf and ancestor(1.9)"
3411 3413
3412 3414 - find all changesets by some user in a date range::
3413 3415
3414 3416 hg log -k alice -d "may 2008 to jul 2008"
3415 3417
3416 3418 - summary of all changesets after the last tag::
3417 3419
3418 3420 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3419 3421
3420 3422 See :hg:`help dates` for a list of formats valid for -d/--date.
3421 3423
3422 3424 See :hg:`help revisions` for more about specifying and ordering
3423 3425 revisions.
3424 3426
3425 3427 See :hg:`help templates` for more about pre-packaged styles and
3426 3428 specifying custom templates.
3427 3429
3428 3430 Returns 0 on success.
3429 3431
3430 3432 """
3431 3433 opts = pycompat.byteskwargs(opts)
3432 3434 if opts.get('follow') and opts.get('rev'):
3433 3435 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3434 3436 del opts['follow']
3435 3437
3436 3438 if opts.get('graph'):
3437 3439 return cmdutil.graphlog(ui, repo, pats, opts)
3438 3440
3439 3441 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3440 3442 limit = cmdutil.loglimit(opts)
3441 3443 count = 0
3442 3444
3443 3445 getrenamed = None
3444 3446 if opts.get('copies'):
3445 3447 endrev = None
3446 3448 if opts.get('rev'):
3447 3449 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3448 3450 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3449 3451
3450 3452 ui.pager('log')
3451 3453 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3452 3454 for rev in revs:
3453 3455 if count == limit:
3454 3456 break
3455 3457 ctx = repo[rev]
3456 3458 copies = None
3457 3459 if getrenamed is not None and rev:
3458 3460 copies = []
3459 3461 for fn in ctx.files():
3460 3462 rename = getrenamed(fn, rev)
3461 3463 if rename:
3462 3464 copies.append((fn, rename[0]))
3463 3465 if filematcher:
3464 3466 revmatchfn = filematcher(ctx.rev())
3465 3467 else:
3466 3468 revmatchfn = None
3467 3469 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3468 3470 if displayer.flush(ctx):
3469 3471 count += 1
3470 3472
3471 3473 displayer.close()
3472 3474
3473 3475 @command('manifest',
3474 3476 [('r', 'rev', '', _('revision to display'), _('REV')),
3475 3477 ('', 'all', False, _("list files from all revisions"))]
3476 3478 + formatteropts,
3477 3479 _('[-r REV]'))
3478 3480 def manifest(ui, repo, node=None, rev=None, **opts):
3479 3481 """output the current or given revision of the project manifest
3480 3482
3481 3483 Print a list of version controlled files for the given revision.
3482 3484 If no revision is given, the first parent of the working directory
3483 3485 is used, or the null revision if no revision is checked out.
3484 3486
3485 3487 With -v, print file permissions, symlink and executable bits.
3486 3488 With --debug, print file revision hashes.
3487 3489
3488 3490 If option --all is specified, the list of all files from all revisions
3489 3491 is printed. This includes deleted and renamed files.
3490 3492
3491 3493 Returns 0 on success.
3492 3494 """
3493 3495 opts = pycompat.byteskwargs(opts)
3494 3496 fm = ui.formatter('manifest', opts)
3495 3497
3496 3498 if opts.get('all'):
3497 3499 if rev or node:
3498 3500 raise error.Abort(_("can't specify a revision with --all"))
3499 3501
3500 3502 res = []
3501 3503 prefix = "data/"
3502 3504 suffix = ".i"
3503 3505 plen = len(prefix)
3504 3506 slen = len(suffix)
3505 3507 with repo.lock():
3506 3508 for fn, b, size in repo.store.datafiles():
3507 3509 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3508 3510 res.append(fn[plen:-slen])
3509 3511 ui.pager('manifest')
3510 3512 for f in res:
3511 3513 fm.startitem()
3512 3514 fm.write("path", '%s\n', f)
3513 3515 fm.end()
3514 3516 return
3515 3517
3516 3518 if rev and node:
3517 3519 raise error.Abort(_("please specify just one revision"))
3518 3520
3519 3521 if not node:
3520 3522 node = rev
3521 3523
3522 3524 char = {'l': '@', 'x': '*', '': ''}
3523 3525 mode = {'l': '644', 'x': '755', '': '644'}
3524 3526 ctx = scmutil.revsingle(repo, node)
3525 3527 mf = ctx.manifest()
3526 3528 ui.pager('manifest')
3527 3529 for f in ctx:
3528 3530 fm.startitem()
3529 3531 fl = ctx[f].flags()
3530 3532 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3531 3533 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3532 3534 fm.write('path', '%s\n', f)
3533 3535 fm.end()
3534 3536
3535 3537 @command('^merge',
3536 3538 [('f', 'force', None,
3537 3539 _('force a merge including outstanding changes (DEPRECATED)')),
3538 3540 ('r', 'rev', '', _('revision to merge'), _('REV')),
3539 3541 ('P', 'preview', None,
3540 3542 _('review revisions to merge (no merge is performed)'))
3541 3543 ] + mergetoolopts,
3542 3544 _('[-P] [[-r] REV]'))
3543 3545 def merge(ui, repo, node=None, **opts):
3544 3546 """merge another revision into working directory
3545 3547
3546 3548 The current working directory is updated with all changes made in
3547 3549 the requested revision since the last common predecessor revision.
3548 3550
3549 3551 Files that changed between either parent are marked as changed for
3550 3552 the next commit and a commit must be performed before any further
3551 3553 updates to the repository are allowed. The next commit will have
3552 3554 two parents.
3553 3555
3554 3556 ``--tool`` can be used to specify the merge tool used for file
3555 3557 merges. It overrides the HGMERGE environment variable and your
3556 3558 configuration files. See :hg:`help merge-tools` for options.
3557 3559
3558 3560 If no revision is specified, the working directory's parent is a
3559 3561 head revision, and the current branch contains exactly one other
3560 3562 head, the other head is merged with by default. Otherwise, an
3561 3563 explicit revision with which to merge with must be provided.
3562 3564
3563 3565 See :hg:`help resolve` for information on handling file conflicts.
3564 3566
3565 3567 To undo an uncommitted merge, use :hg:`update --clean .` which
3566 3568 will check out a clean copy of the original merge parent, losing
3567 3569 all changes.
3568 3570
3569 3571 Returns 0 on success, 1 if there are unresolved files.
3570 3572 """
3571 3573
3572 3574 opts = pycompat.byteskwargs(opts)
3573 3575 if opts.get('rev') and node:
3574 3576 raise error.Abort(_("please specify just one revision"))
3575 3577 if not node:
3576 3578 node = opts.get('rev')
3577 3579
3578 3580 if node:
3579 3581 node = scmutil.revsingle(repo, node).node()
3580 3582
3581 3583 if not node:
3582 3584 node = repo[destutil.destmerge(repo)].node()
3583 3585
3584 3586 if opts.get('preview'):
3585 3587 # find nodes that are ancestors of p2 but not of p1
3586 3588 p1 = repo.lookup('.')
3587 3589 p2 = repo.lookup(node)
3588 3590 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3589 3591
3590 3592 displayer = cmdutil.show_changeset(ui, repo, opts)
3591 3593 for node in nodes:
3592 3594 displayer.show(repo[node])
3593 3595 displayer.close()
3594 3596 return 0
3595 3597
3596 3598 try:
3597 3599 # ui.forcemerge is an internal variable, do not document
3598 3600 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3599 3601 force = opts.get('force')
3600 3602 labels = ['working copy', 'merge rev']
3601 3603 return hg.merge(repo, node, force=force, mergeforce=force,
3602 3604 labels=labels)
3603 3605 finally:
3604 3606 ui.setconfig('ui', 'forcemerge', '', 'merge')
3605 3607
3606 3608 @command('outgoing|out',
3607 3609 [('f', 'force', None, _('run even when the destination is unrelated')),
3608 3610 ('r', 'rev', [],
3609 3611 _('a changeset intended to be included in the destination'), _('REV')),
3610 3612 ('n', 'newest-first', None, _('show newest record first')),
3611 3613 ('B', 'bookmarks', False, _('compare bookmarks')),
3612 3614 ('b', 'branch', [], _('a specific branch you would like to push'),
3613 3615 _('BRANCH')),
3614 3616 ] + logopts + remoteopts + subrepoopts,
3615 3617 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3616 3618 def outgoing(ui, repo, dest=None, **opts):
3617 3619 """show changesets not found in the destination
3618 3620
3619 3621 Show changesets not found in the specified destination repository
3620 3622 or the default push location. These are the changesets that would
3621 3623 be pushed if a push was requested.
3622 3624
3623 3625 See pull for details of valid destination formats.
3624 3626
3625 3627 .. container:: verbose
3626 3628
3627 3629 With -B/--bookmarks, the result of bookmark comparison between
3628 3630 local and remote repositories is displayed. With -v/--verbose,
3629 3631 status is also displayed for each bookmark like below::
3630 3632
3631 3633 BM1 01234567890a added
3632 3634 BM2 deleted
3633 3635 BM3 234567890abc advanced
3634 3636 BM4 34567890abcd diverged
3635 3637 BM5 4567890abcde changed
3636 3638
3637 3639 The action taken when pushing depends on the
3638 3640 status of each bookmark:
3639 3641
3640 3642 :``added``: push with ``-B`` will create it
3641 3643 :``deleted``: push with ``-B`` will delete it
3642 3644 :``advanced``: push will update it
3643 3645 :``diverged``: push with ``-B`` will update it
3644 3646 :``changed``: push with ``-B`` will update it
3645 3647
3646 3648 From the point of view of pushing behavior, bookmarks
3647 3649 existing only in the remote repository are treated as
3648 3650 ``deleted``, even if it is in fact added remotely.
3649 3651
3650 3652 Returns 0 if there are outgoing changes, 1 otherwise.
3651 3653 """
3652 3654 opts = pycompat.byteskwargs(opts)
3653 3655 if opts.get('graph'):
3654 3656 cmdutil.checkunsupportedgraphflags([], opts)
3655 3657 o, other = hg._outgoing(ui, repo, dest, opts)
3656 3658 if not o:
3657 3659 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3658 3660 return
3659 3661
3660 3662 revdag = cmdutil.graphrevs(repo, o, opts)
3661 3663 ui.pager('outgoing')
3662 3664 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3663 3665 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3664 3666 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3665 3667 return 0
3666 3668
3667 3669 if opts.get('bookmarks'):
3668 3670 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3669 3671 dest, branches = hg.parseurl(dest, opts.get('branch'))
3670 3672 other = hg.peer(repo, opts, dest)
3671 3673 if 'bookmarks' not in other.listkeys('namespaces'):
3672 3674 ui.warn(_("remote doesn't support bookmarks\n"))
3673 3675 return 0
3674 3676 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3675 3677 ui.pager('outgoing')
3676 3678 return bookmarks.outgoing(ui, repo, other)
3677 3679
3678 3680 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3679 3681 try:
3680 3682 return hg.outgoing(ui, repo, dest, opts)
3681 3683 finally:
3682 3684 del repo._subtoppath
3683 3685
3684 3686 @command('parents',
3685 3687 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3686 3688 ] + templateopts,
3687 3689 _('[-r REV] [FILE]'),
3688 3690 inferrepo=True)
3689 3691 def parents(ui, repo, file_=None, **opts):
3690 3692 """show the parents of the working directory or revision (DEPRECATED)
3691 3693
3692 3694 Print the working directory's parent revisions. If a revision is
3693 3695 given via -r/--rev, the parent of that revision will be printed.
3694 3696 If a file argument is given, the revision in which the file was
3695 3697 last changed (before the working directory revision or the
3696 3698 argument to --rev if given) is printed.
3697 3699
3698 3700 This command is equivalent to::
3699 3701
3700 3702 hg log -r "p1()+p2()" or
3701 3703 hg log -r "p1(REV)+p2(REV)" or
3702 3704 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3703 3705 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3704 3706
3705 3707 See :hg:`summary` and :hg:`help revsets` for related information.
3706 3708
3707 3709 Returns 0 on success.
3708 3710 """
3709 3711
3710 3712 opts = pycompat.byteskwargs(opts)
3711 3713 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3712 3714
3713 3715 if file_:
3714 3716 m = scmutil.match(ctx, (file_,), opts)
3715 3717 if m.anypats() or len(m.files()) != 1:
3716 3718 raise error.Abort(_('can only specify an explicit filename'))
3717 3719 file_ = m.files()[0]
3718 3720 filenodes = []
3719 3721 for cp in ctx.parents():
3720 3722 if not cp:
3721 3723 continue
3722 3724 try:
3723 3725 filenodes.append(cp.filenode(file_))
3724 3726 except error.LookupError:
3725 3727 pass
3726 3728 if not filenodes:
3727 3729 raise error.Abort(_("'%s' not found in manifest!") % file_)
3728 3730 p = []
3729 3731 for fn in filenodes:
3730 3732 fctx = repo.filectx(file_, fileid=fn)
3731 3733 p.append(fctx.node())
3732 3734 else:
3733 3735 p = [cp.node() for cp in ctx.parents()]
3734 3736
3735 3737 displayer = cmdutil.show_changeset(ui, repo, opts)
3736 3738 for n in p:
3737 3739 if n != nullid:
3738 3740 displayer.show(repo[n])
3739 3741 displayer.close()
3740 3742
3741 3743 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3742 3744 def paths(ui, repo, search=None, **opts):
3743 3745 """show aliases for remote repositories
3744 3746
3745 3747 Show definition of symbolic path name NAME. If no name is given,
3746 3748 show definition of all available names.
3747 3749
3748 3750 Option -q/--quiet suppresses all output when searching for NAME
3749 3751 and shows only the path names when listing all definitions.
3750 3752
3751 3753 Path names are defined in the [paths] section of your
3752 3754 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3753 3755 repository, ``.hg/hgrc`` is used, too.
3754 3756
3755 3757 The path names ``default`` and ``default-push`` have a special
3756 3758 meaning. When performing a push or pull operation, they are used
3757 3759 as fallbacks if no location is specified on the command-line.
3758 3760 When ``default-push`` is set, it will be used for push and
3759 3761 ``default`` will be used for pull; otherwise ``default`` is used
3760 3762 as the fallback for both. When cloning a repository, the clone
3761 3763 source is written as ``default`` in ``.hg/hgrc``.
3762 3764
3763 3765 .. note::
3764 3766
3765 3767 ``default`` and ``default-push`` apply to all inbound (e.g.
3766 3768 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3767 3769 and :hg:`bundle`) operations.
3768 3770
3769 3771 See :hg:`help urls` for more information.
3770 3772
3771 3773 Returns 0 on success.
3772 3774 """
3773 3775
3774 3776 opts = pycompat.byteskwargs(opts)
3775 3777 ui.pager('paths')
3776 3778 if search:
3777 3779 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3778 3780 if name == search]
3779 3781 else:
3780 3782 pathitems = sorted(ui.paths.iteritems())
3781 3783
3782 3784 fm = ui.formatter('paths', opts)
3783 3785 if fm.isplain():
3784 3786 hidepassword = util.hidepassword
3785 3787 else:
3786 3788 hidepassword = str
3787 3789 if ui.quiet:
3788 3790 namefmt = '%s\n'
3789 3791 else:
3790 3792 namefmt = '%s = '
3791 3793 showsubopts = not search and not ui.quiet
3792 3794
3793 3795 for name, path in pathitems:
3794 3796 fm.startitem()
3795 3797 fm.condwrite(not search, 'name', namefmt, name)
3796 3798 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3797 3799 for subopt, value in sorted(path.suboptions.items()):
3798 3800 assert subopt not in ('name', 'url')
3799 3801 if showsubopts:
3800 3802 fm.plain('%s:%s = ' % (name, subopt))
3801 3803 fm.condwrite(showsubopts, subopt, '%s\n', value)
3802 3804
3803 3805 fm.end()
3804 3806
3805 3807 if search and not pathitems:
3806 3808 if not ui.quiet:
3807 3809 ui.warn(_("not found!\n"))
3808 3810 return 1
3809 3811 else:
3810 3812 return 0
3811 3813
3812 3814 @command('phase',
3813 3815 [('p', 'public', False, _('set changeset phase to public')),
3814 3816 ('d', 'draft', False, _('set changeset phase to draft')),
3815 3817 ('s', 'secret', False, _('set changeset phase to secret')),
3816 3818 ('f', 'force', False, _('allow to move boundary backward')),
3817 3819 ('r', 'rev', [], _('target revision'), _('REV')),
3818 3820 ],
3819 3821 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3820 3822 def phase(ui, repo, *revs, **opts):
3821 3823 """set or show the current phase name
3822 3824
3823 3825 With no argument, show the phase name of the current revision(s).
3824 3826
3825 3827 With one of -p/--public, -d/--draft or -s/--secret, change the
3826 3828 phase value of the specified revisions.
3827 3829
3828 3830 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3829 3831 lower phase to an higher phase. Phases are ordered as follows::
3830 3832
3831 3833 public < draft < secret
3832 3834
3833 3835 Returns 0 on success, 1 if some phases could not be changed.
3834 3836
3835 3837 (For more information about the phases concept, see :hg:`help phases`.)
3836 3838 """
3837 3839 opts = pycompat.byteskwargs(opts)
3838 3840 # search for a unique phase argument
3839 3841 targetphase = None
3840 3842 for idx, name in enumerate(phases.phasenames):
3841 3843 if opts[name]:
3842 3844 if targetphase is not None:
3843 3845 raise error.Abort(_('only one phase can be specified'))
3844 3846 targetphase = idx
3845 3847
3846 3848 # look for specified revision
3847 3849 revs = list(revs)
3848 3850 revs.extend(opts['rev'])
3849 3851 if not revs:
3850 3852 # display both parents as the second parent phase can influence
3851 3853 # the phase of a merge commit
3852 3854 revs = [c.rev() for c in repo[None].parents()]
3853 3855
3854 3856 revs = scmutil.revrange(repo, revs)
3855 3857
3856 3858 lock = None
3857 3859 ret = 0
3858 3860 if targetphase is None:
3859 3861 # display
3860 3862 for r in revs:
3861 3863 ctx = repo[r]
3862 3864 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3863 3865 else:
3864 3866 tr = None
3865 3867 lock = repo.lock()
3866 3868 try:
3867 3869 tr = repo.transaction("phase")
3868 3870 # set phase
3869 3871 if not revs:
3870 3872 raise error.Abort(_('empty revision set'))
3871 3873 nodes = [repo[r].node() for r in revs]
3872 3874 # moving revision from public to draft may hide them
3873 3875 # We have to check result on an unfiltered repository
3874 3876 unfi = repo.unfiltered()
3875 3877 getphase = unfi._phasecache.phase
3876 3878 olddata = [getphase(unfi, r) for r in unfi]
3877 3879 phases.advanceboundary(repo, tr, targetphase, nodes)
3878 3880 if opts['force']:
3879 3881 phases.retractboundary(repo, tr, targetphase, nodes)
3880 3882 tr.close()
3881 3883 finally:
3882 3884 if tr is not None:
3883 3885 tr.release()
3884 3886 lock.release()
3885 3887 getphase = unfi._phasecache.phase
3886 3888 newdata = [getphase(unfi, r) for r in unfi]
3887 3889 changes = sum(newdata[r] != olddata[r] for r in unfi)
3888 3890 cl = unfi.changelog
3889 3891 rejected = [n for n in nodes
3890 3892 if newdata[cl.rev(n)] < targetphase]
3891 3893 if rejected:
3892 3894 ui.warn(_('cannot move %i changesets to a higher '
3893 3895 'phase, use --force\n') % len(rejected))
3894 3896 ret = 1
3895 3897 if changes:
3896 3898 msg = _('phase changed for %i changesets\n') % changes
3897 3899 if ret:
3898 3900 ui.status(msg)
3899 3901 else:
3900 3902 ui.note(msg)
3901 3903 else:
3902 3904 ui.warn(_('no phases changed\n'))
3903 3905 return ret
3904 3906
3905 3907 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3906 3908 """Run after a changegroup has been added via pull/unbundle
3907 3909
3908 3910 This takes arguments below:
3909 3911
3910 3912 :modheads: change of heads by pull/unbundle
3911 3913 :optupdate: updating working directory is needed or not
3912 3914 :checkout: update destination revision (or None to default destination)
3913 3915 :brev: a name, which might be a bookmark to be activated after updating
3914 3916 """
3915 3917 if modheads == 0:
3916 3918 return
3917 3919 if optupdate:
3918 3920 try:
3919 3921 return hg.updatetotally(ui, repo, checkout, brev)
3920 3922 except error.UpdateAbort as inst:
3921 3923 msg = _("not updating: %s") % str(inst)
3922 3924 hint = inst.hint
3923 3925 raise error.UpdateAbort(msg, hint=hint)
3924 3926 if modheads > 1:
3925 3927 currentbranchheads = len(repo.branchheads())
3926 3928 if currentbranchheads == modheads:
3927 3929 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3928 3930 elif currentbranchheads > 1:
3929 3931 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3930 3932 "merge)\n"))
3931 3933 else:
3932 3934 ui.status(_("(run 'hg heads' to see heads)\n"))
3933 3935 else:
3934 3936 ui.status(_("(run 'hg update' to get a working copy)\n"))
3935 3937
3936 3938 @command('^pull',
3937 3939 [('u', 'update', None,
3938 3940 _('update to new branch head if changesets were pulled')),
3939 3941 ('f', 'force', None, _('run even when remote repository is unrelated')),
3940 3942 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3941 3943 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3942 3944 ('b', 'branch', [], _('a specific branch you would like to pull'),
3943 3945 _('BRANCH')),
3944 3946 ] + remoteopts,
3945 3947 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3946 3948 def pull(ui, repo, source="default", **opts):
3947 3949 """pull changes from the specified source
3948 3950
3949 3951 Pull changes from a remote repository to a local one.
3950 3952
3951 3953 This finds all changes from the repository at the specified path
3952 3954 or URL and adds them to a local repository (the current one unless
3953 3955 -R is specified). By default, this does not update the copy of the
3954 3956 project in the working directory.
3955 3957
3956 3958 Use :hg:`incoming` if you want to see what would have been added
3957 3959 by a pull at the time you issued this command. If you then decide
3958 3960 to add those changes to the repository, you should use :hg:`pull
3959 3961 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3960 3962
3961 3963 If SOURCE is omitted, the 'default' path will be used.
3962 3964 See :hg:`help urls` for more information.
3963 3965
3964 3966 Specifying bookmark as ``.`` is equivalent to specifying the active
3965 3967 bookmark's name.
3966 3968
3967 3969 Returns 0 on success, 1 if an update had unresolved files.
3968 3970 """
3969 3971
3970 3972 opts = pycompat.byteskwargs(opts)
3971 3973 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3972 3974 msg = _('update destination required by configuration')
3973 3975 hint = _('use hg pull followed by hg update DEST')
3974 3976 raise error.Abort(msg, hint=hint)
3975 3977
3976 3978 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3977 3979 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3978 3980 other = hg.peer(repo, opts, source)
3979 3981 try:
3980 3982 revs, checkout = hg.addbranchrevs(repo, other, branches,
3981 3983 opts.get('rev'))
3982 3984
3983 3985
3984 3986 pullopargs = {}
3985 3987 if opts.get('bookmark'):
3986 3988 if not revs:
3987 3989 revs = []
3988 3990 # The list of bookmark used here is not the one used to actually
3989 3991 # update the bookmark name. This can result in the revision pulled
3990 3992 # not ending up with the name of the bookmark because of a race
3991 3993 # condition on the server. (See issue 4689 for details)
3992 3994 remotebookmarks = other.listkeys('bookmarks')
3993 3995 pullopargs['remotebookmarks'] = remotebookmarks
3994 3996 for b in opts['bookmark']:
3995 3997 b = repo._bookmarks.expandname(b)
3996 3998 if b not in remotebookmarks:
3997 3999 raise error.Abort(_('remote bookmark %s not found!') % b)
3998 4000 revs.append(remotebookmarks[b])
3999 4001
4000 4002 if revs:
4001 4003 try:
4002 4004 # When 'rev' is a bookmark name, we cannot guarantee that it
4003 4005 # will be updated with that name because of a race condition
4004 4006 # server side. (See issue 4689 for details)
4005 4007 oldrevs = revs
4006 4008 revs = [] # actually, nodes
4007 4009 for r in oldrevs:
4008 4010 node = other.lookup(r)
4009 4011 revs.append(node)
4010 4012 if r == checkout:
4011 4013 checkout = node
4012 4014 except error.CapabilityError:
4013 4015 err = _("other repository doesn't support revision lookup, "
4014 4016 "so a rev cannot be specified.")
4015 4017 raise error.Abort(err)
4016 4018
4017 4019 pullopargs.update(opts.get('opargs', {}))
4018 4020 modheads = exchange.pull(repo, other, heads=revs,
4019 4021 force=opts.get('force'),
4020 4022 bookmarks=opts.get('bookmark', ()),
4021 4023 opargs=pullopargs).cgresult
4022 4024
4023 4025 # brev is a name, which might be a bookmark to be activated at
4024 4026 # the end of the update. In other words, it is an explicit
4025 4027 # destination of the update
4026 4028 brev = None
4027 4029
4028 4030 if checkout:
4029 4031 checkout = str(repo.changelog.rev(checkout))
4030 4032
4031 4033 # order below depends on implementation of
4032 4034 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4033 4035 # because 'checkout' is determined without it.
4034 4036 if opts.get('rev'):
4035 4037 brev = opts['rev'][0]
4036 4038 elif opts.get('branch'):
4037 4039 brev = opts['branch'][0]
4038 4040 else:
4039 4041 brev = branches[0]
4040 4042 repo._subtoppath = source
4041 4043 try:
4042 4044 ret = postincoming(ui, repo, modheads, opts.get('update'),
4043 4045 checkout, brev)
4044 4046
4045 4047 finally:
4046 4048 del repo._subtoppath
4047 4049
4048 4050 finally:
4049 4051 other.close()
4050 4052 return ret
4051 4053
4052 4054 @command('^push',
4053 4055 [('f', 'force', None, _('force push')),
4054 4056 ('r', 'rev', [],
4055 4057 _('a changeset intended to be included in the destination'),
4056 4058 _('REV')),
4057 4059 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4058 4060 ('b', 'branch', [],
4059 4061 _('a specific branch you would like to push'), _('BRANCH')),
4060 4062 ('', 'new-branch', False, _('allow pushing a new branch')),
4061 4063 ] + remoteopts,
4062 4064 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4063 4065 def push(ui, repo, dest=None, **opts):
4064 4066 """push changes to the specified destination
4065 4067
4066 4068 Push changesets from the local repository to the specified
4067 4069 destination.
4068 4070
4069 4071 This operation is symmetrical to pull: it is identical to a pull
4070 4072 in the destination repository from the current one.
4071 4073
4072 4074 By default, push will not allow creation of new heads at the
4073 4075 destination, since multiple heads would make it unclear which head
4074 4076 to use. In this situation, it is recommended to pull and merge
4075 4077 before pushing.
4076 4078
4077 4079 Use --new-branch if you want to allow push to create a new named
4078 4080 branch that is not present at the destination. This allows you to
4079 4081 only create a new branch without forcing other changes.
4080 4082
4081 4083 .. note::
4082 4084
4083 4085 Extra care should be taken with the -f/--force option,
4084 4086 which will push all new heads on all branches, an action which will
4085 4087 almost always cause confusion for collaborators.
4086 4088
4087 4089 If -r/--rev is used, the specified revision and all its ancestors
4088 4090 will be pushed to the remote repository.
4089 4091
4090 4092 If -B/--bookmark is used, the specified bookmarked revision, its
4091 4093 ancestors, and the bookmark will be pushed to the remote
4092 4094 repository. Specifying ``.`` is equivalent to specifying the active
4093 4095 bookmark's name.
4094 4096
4095 4097 Please see :hg:`help urls` for important details about ``ssh://``
4096 4098 URLs. If DESTINATION is omitted, a default path will be used.
4097 4099
4098 4100 Returns 0 if push was successful, 1 if nothing to push.
4099 4101 """
4100 4102
4101 4103 opts = pycompat.byteskwargs(opts)
4102 4104 if opts.get('bookmark'):
4103 4105 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4104 4106 for b in opts['bookmark']:
4105 4107 # translate -B options to -r so changesets get pushed
4106 4108 b = repo._bookmarks.expandname(b)
4107 4109 if b in repo._bookmarks:
4108 4110 opts.setdefault('rev', []).append(b)
4109 4111 else:
4110 4112 # if we try to push a deleted bookmark, translate it to null
4111 4113 # this lets simultaneous -r, -b options continue working
4112 4114 opts.setdefault('rev', []).append("null")
4113 4115
4114 4116 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4115 4117 if not path:
4116 4118 raise error.Abort(_('default repository not configured!'),
4117 4119 hint=_("see 'hg help config.paths'"))
4118 4120 dest = path.pushloc or path.loc
4119 4121 branches = (path.branch, opts.get('branch') or [])
4120 4122 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4121 4123 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4122 4124 other = hg.peer(repo, opts, dest)
4123 4125
4124 4126 if revs:
4125 4127 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4126 4128 if not revs:
4127 4129 raise error.Abort(_("specified revisions evaluate to an empty set"),
4128 4130 hint=_("use different revision arguments"))
4129 4131 elif path.pushrev:
4130 4132 # It doesn't make any sense to specify ancestor revisions. So limit
4131 4133 # to DAG heads to make discovery simpler.
4132 4134 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4133 4135 revs = scmutil.revrange(repo, [expr])
4134 4136 revs = [repo[rev].node() for rev in revs]
4135 4137 if not revs:
4136 4138 raise error.Abort(_('default push revset for path evaluates to an '
4137 4139 'empty set'))
4138 4140
4139 4141 repo._subtoppath = dest
4140 4142 try:
4141 4143 # push subrepos depth-first for coherent ordering
4142 4144 c = repo['']
4143 4145 subs = c.substate # only repos that are committed
4144 4146 for s in sorted(subs):
4145 4147 result = c.sub(s).push(opts)
4146 4148 if result == 0:
4147 4149 return not result
4148 4150 finally:
4149 4151 del repo._subtoppath
4150 4152 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4151 4153 newbranch=opts.get('new_branch'),
4152 4154 bookmarks=opts.get('bookmark', ()),
4153 4155 opargs=opts.get('opargs'))
4154 4156
4155 4157 result = not pushop.cgresult
4156 4158
4157 4159 if pushop.bkresult is not None:
4158 4160 if pushop.bkresult == 2:
4159 4161 result = 2
4160 4162 elif not result and pushop.bkresult:
4161 4163 result = 2
4162 4164
4163 4165 return result
4164 4166
4165 4167 @command('recover', [])
4166 4168 def recover(ui, repo):
4167 4169 """roll back an interrupted transaction
4168 4170
4169 4171 Recover from an interrupted commit or pull.
4170 4172
4171 4173 This command tries to fix the repository status after an
4172 4174 interrupted operation. It should only be necessary when Mercurial
4173 4175 suggests it.
4174 4176
4175 4177 Returns 0 if successful, 1 if nothing to recover or verify fails.
4176 4178 """
4177 4179 if repo.recover():
4178 4180 return hg.verify(repo)
4179 4181 return 1
4180 4182
4181 4183 @command('^remove|rm',
4182 4184 [('A', 'after', None, _('record delete for missing files')),
4183 4185 ('f', 'force', None,
4184 4186 _('forget added files, delete modified files')),
4185 4187 ] + subrepoopts + walkopts,
4186 4188 _('[OPTION]... FILE...'),
4187 4189 inferrepo=True)
4188 4190 def remove(ui, repo, *pats, **opts):
4189 4191 """remove the specified files on the next commit
4190 4192
4191 4193 Schedule the indicated files for removal from the current branch.
4192 4194
4193 4195 This command schedules the files to be removed at the next commit.
4194 4196 To undo a remove before that, see :hg:`revert`. To undo added
4195 4197 files, see :hg:`forget`.
4196 4198
4197 4199 .. container:: verbose
4198 4200
4199 4201 -A/--after can be used to remove only files that have already
4200 4202 been deleted, -f/--force can be used to force deletion, and -Af
4201 4203 can be used to remove files from the next revision without
4202 4204 deleting them from the working directory.
4203 4205
4204 4206 The following table details the behavior of remove for different
4205 4207 file states (columns) and option combinations (rows). The file
4206 4208 states are Added [A], Clean [C], Modified [M] and Missing [!]
4207 4209 (as reported by :hg:`status`). The actions are Warn, Remove
4208 4210 (from branch) and Delete (from disk):
4209 4211
4210 4212 ========= == == == ==
4211 4213 opt/state A C M !
4212 4214 ========= == == == ==
4213 4215 none W RD W R
4214 4216 -f R RD RD R
4215 4217 -A W W W R
4216 4218 -Af R R R R
4217 4219 ========= == == == ==
4218 4220
4219 4221 .. note::
4220 4222
4221 4223 :hg:`remove` never deletes files in Added [A] state from the
4222 4224 working directory, not even if ``--force`` is specified.
4223 4225
4224 4226 Returns 0 on success, 1 if any warnings encountered.
4225 4227 """
4226 4228
4227 4229 opts = pycompat.byteskwargs(opts)
4228 4230 after, force = opts.get('after'), opts.get('force')
4229 4231 if not pats and not after:
4230 4232 raise error.Abort(_('no files specified'))
4231 4233
4232 4234 m = scmutil.match(repo[None], pats, opts)
4233 4235 subrepos = opts.get('subrepos')
4234 4236 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4235 4237
4236 4238 @command('rename|move|mv',
4237 4239 [('A', 'after', None, _('record a rename that has already occurred')),
4238 4240 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4239 4241 ] + walkopts + dryrunopts,
4240 4242 _('[OPTION]... SOURCE... DEST'))
4241 4243 def rename(ui, repo, *pats, **opts):
4242 4244 """rename files; equivalent of copy + remove
4243 4245
4244 4246 Mark dest as copies of sources; mark sources for deletion. If dest
4245 4247 is a directory, copies are put in that directory. If dest is a
4246 4248 file, there can only be one source.
4247 4249
4248 4250 By default, this command copies the contents of files as they
4249 4251 exist in the working directory. If invoked with -A/--after, the
4250 4252 operation is recorded, but no copying is performed.
4251 4253
4252 4254 This command takes effect at the next commit. To undo a rename
4253 4255 before that, see :hg:`revert`.
4254 4256
4255 4257 Returns 0 on success, 1 if errors are encountered.
4256 4258 """
4257 4259 opts = pycompat.byteskwargs(opts)
4258 4260 with repo.wlock(False):
4259 4261 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4260 4262
4261 4263 @command('resolve',
4262 4264 [('a', 'all', None, _('select all unresolved files')),
4263 4265 ('l', 'list', None, _('list state of files needing merge')),
4264 4266 ('m', 'mark', None, _('mark files as resolved')),
4265 4267 ('u', 'unmark', None, _('mark files as unresolved')),
4266 4268 ('n', 'no-status', None, _('hide status prefix'))]
4267 4269 + mergetoolopts + walkopts + formatteropts,
4268 4270 _('[OPTION]... [FILE]...'),
4269 4271 inferrepo=True)
4270 4272 def resolve(ui, repo, *pats, **opts):
4271 4273 """redo merges or set/view the merge status of files
4272 4274
4273 4275 Merges with unresolved conflicts are often the result of
4274 4276 non-interactive merging using the ``internal:merge`` configuration
4275 4277 setting, or a command-line merge tool like ``diff3``. The resolve
4276 4278 command is used to manage the files involved in a merge, after
4277 4279 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4278 4280 working directory must have two parents). See :hg:`help
4279 4281 merge-tools` for information on configuring merge tools.
4280 4282
4281 4283 The resolve command can be used in the following ways:
4282 4284
4283 4285 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4284 4286 files, discarding any previous merge attempts. Re-merging is not
4285 4287 performed for files already marked as resolved. Use ``--all/-a``
4286 4288 to select all unresolved files. ``--tool`` can be used to specify
4287 4289 the merge tool used for the given files. It overrides the HGMERGE
4288 4290 environment variable and your configuration files. Previous file
4289 4291 contents are saved with a ``.orig`` suffix.
4290 4292
4291 4293 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4292 4294 (e.g. after having manually fixed-up the files). The default is
4293 4295 to mark all unresolved files.
4294 4296
4295 4297 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4296 4298 default is to mark all resolved files.
4297 4299
4298 4300 - :hg:`resolve -l`: list files which had or still have conflicts.
4299 4301 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4300 4302 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4301 4303 the list. See :hg:`help filesets` for details.
4302 4304
4303 4305 .. note::
4304 4306
4305 4307 Mercurial will not let you commit files with unresolved merge
4306 4308 conflicts. You must use :hg:`resolve -m ...` before you can
4307 4309 commit after a conflicting merge.
4308 4310
4309 4311 Returns 0 on success, 1 if any files fail a resolve attempt.
4310 4312 """
4311 4313
4312 4314 opts = pycompat.byteskwargs(opts)
4313 4315 flaglist = 'all mark unmark list no_status'.split()
4314 4316 all, mark, unmark, show, nostatus = \
4315 4317 [opts.get(o) for o in flaglist]
4316 4318
4317 4319 if (show and (mark or unmark)) or (mark and unmark):
4318 4320 raise error.Abort(_("too many options specified"))
4319 4321 if pats and all:
4320 4322 raise error.Abort(_("can't specify --all and patterns"))
4321 4323 if not (all or pats or show or mark or unmark):
4322 4324 raise error.Abort(_('no files or directories specified'),
4323 4325 hint=('use --all to re-merge all unresolved files'))
4324 4326
4325 4327 if show:
4326 4328 ui.pager('resolve')
4327 4329 fm = ui.formatter('resolve', opts)
4328 4330 ms = mergemod.mergestate.read(repo)
4329 4331 m = scmutil.match(repo[None], pats, opts)
4330 4332 for f in ms:
4331 4333 if not m(f):
4332 4334 continue
4333 4335 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4334 4336 'd': 'driverresolved'}[ms[f]]
4335 4337 fm.startitem()
4336 4338 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4337 4339 fm.write('path', '%s\n', f, label=l)
4338 4340 fm.end()
4339 4341 return 0
4340 4342
4341 4343 with repo.wlock():
4342 4344 ms = mergemod.mergestate.read(repo)
4343 4345
4344 4346 if not (ms.active() or repo.dirstate.p2() != nullid):
4345 4347 raise error.Abort(
4346 4348 _('resolve command not applicable when not merging'))
4347 4349
4348 4350 wctx = repo[None]
4349 4351
4350 4352 if ms.mergedriver and ms.mdstate() == 'u':
4351 4353 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4352 4354 ms.commit()
4353 4355 # allow mark and unmark to go through
4354 4356 if not mark and not unmark and not proceed:
4355 4357 return 1
4356 4358
4357 4359 m = scmutil.match(wctx, pats, opts)
4358 4360 ret = 0
4359 4361 didwork = False
4360 4362 runconclude = False
4361 4363
4362 4364 tocomplete = []
4363 4365 for f in ms:
4364 4366 if not m(f):
4365 4367 continue
4366 4368
4367 4369 didwork = True
4368 4370
4369 4371 # don't let driver-resolved files be marked, and run the conclude
4370 4372 # step if asked to resolve
4371 4373 if ms[f] == "d":
4372 4374 exact = m.exact(f)
4373 4375 if mark:
4374 4376 if exact:
4375 4377 ui.warn(_('not marking %s as it is driver-resolved\n')
4376 4378 % f)
4377 4379 elif unmark:
4378 4380 if exact:
4379 4381 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4380 4382 % f)
4381 4383 else:
4382 4384 runconclude = True
4383 4385 continue
4384 4386
4385 4387 if mark:
4386 4388 ms.mark(f, "r")
4387 4389 elif unmark:
4388 4390 ms.mark(f, "u")
4389 4391 else:
4390 4392 # backup pre-resolve (merge uses .orig for its own purposes)
4391 4393 a = repo.wjoin(f)
4392 4394 try:
4393 4395 util.copyfile(a, a + ".resolve")
4394 4396 except (IOError, OSError) as inst:
4395 4397 if inst.errno != errno.ENOENT:
4396 4398 raise
4397 4399
4398 4400 try:
4399 4401 # preresolve file
4400 4402 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4401 4403 'resolve')
4402 4404 complete, r = ms.preresolve(f, wctx)
4403 4405 if not complete:
4404 4406 tocomplete.append(f)
4405 4407 elif r:
4406 4408 ret = 1
4407 4409 finally:
4408 4410 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4409 4411 ms.commit()
4410 4412
4411 4413 # replace filemerge's .orig file with our resolve file, but only
4412 4414 # for merges that are complete
4413 4415 if complete:
4414 4416 try:
4415 4417 util.rename(a + ".resolve",
4416 4418 scmutil.origpath(ui, repo, a))
4417 4419 except OSError as inst:
4418 4420 if inst.errno != errno.ENOENT:
4419 4421 raise
4420 4422
4421 4423 for f in tocomplete:
4422 4424 try:
4423 4425 # resolve file
4424 4426 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4425 4427 'resolve')
4426 4428 r = ms.resolve(f, wctx)
4427 4429 if r:
4428 4430 ret = 1
4429 4431 finally:
4430 4432 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4431 4433 ms.commit()
4432 4434
4433 4435 # replace filemerge's .orig file with our resolve file
4434 4436 a = repo.wjoin(f)
4435 4437 try:
4436 4438 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4437 4439 except OSError as inst:
4438 4440 if inst.errno != errno.ENOENT:
4439 4441 raise
4440 4442
4441 4443 ms.commit()
4442 4444 ms.recordactions()
4443 4445
4444 4446 if not didwork and pats:
4445 4447 hint = None
4446 4448 if not any([p for p in pats if p.find(':') >= 0]):
4447 4449 pats = ['path:%s' % p for p in pats]
4448 4450 m = scmutil.match(wctx, pats, opts)
4449 4451 for f in ms:
4450 4452 if not m(f):
4451 4453 continue
4452 4454 flags = ''.join(['-%s ' % o[0] for o in flaglist
4453 4455 if opts.get(o)])
4454 4456 hint = _("(try: hg resolve %s%s)\n") % (
4455 4457 flags,
4456 4458 ' '.join(pats))
4457 4459 break
4458 4460 ui.warn(_("arguments do not match paths that need resolving\n"))
4459 4461 if hint:
4460 4462 ui.warn(hint)
4461 4463 elif ms.mergedriver and ms.mdstate() != 's':
4462 4464 # run conclude step when either a driver-resolved file is requested
4463 4465 # or there are no driver-resolved files
4464 4466 # we can't use 'ret' to determine whether any files are unresolved
4465 4467 # because we might not have tried to resolve some
4466 4468 if ((runconclude or not list(ms.driverresolved()))
4467 4469 and not list(ms.unresolved())):
4468 4470 proceed = mergemod.driverconclude(repo, ms, wctx)
4469 4471 ms.commit()
4470 4472 if not proceed:
4471 4473 return 1
4472 4474
4473 4475 # Nudge users into finishing an unfinished operation
4474 4476 unresolvedf = list(ms.unresolved())
4475 4477 driverresolvedf = list(ms.driverresolved())
4476 4478 if not unresolvedf and not driverresolvedf:
4477 4479 ui.status(_('(no more unresolved files)\n'))
4478 4480 cmdutil.checkafterresolved(repo)
4479 4481 elif not unresolvedf:
4480 4482 ui.status(_('(no more unresolved files -- '
4481 4483 'run "hg resolve --all" to conclude)\n'))
4482 4484
4483 4485 return ret
4484 4486
4485 4487 @command('revert',
4486 4488 [('a', 'all', None, _('revert all changes when no arguments given')),
4487 4489 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4488 4490 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4489 4491 ('C', 'no-backup', None, _('do not save backup copies of files')),
4490 4492 ('i', 'interactive', None,
4491 4493 _('interactively select the changes (EXPERIMENTAL)')),
4492 4494 ] + walkopts + dryrunopts,
4493 4495 _('[OPTION]... [-r REV] [NAME]...'))
4494 4496 def revert(ui, repo, *pats, **opts):
4495 4497 """restore files to their checkout state
4496 4498
4497 4499 .. note::
4498 4500
4499 4501 To check out earlier revisions, you should use :hg:`update REV`.
4500 4502 To cancel an uncommitted merge (and lose your changes),
4501 4503 use :hg:`update --clean .`.
4502 4504
4503 4505 With no revision specified, revert the specified files or directories
4504 4506 to the contents they had in the parent of the working directory.
4505 4507 This restores the contents of files to an unmodified
4506 4508 state and unschedules adds, removes, copies, and renames. If the
4507 4509 working directory has two parents, you must explicitly specify a
4508 4510 revision.
4509 4511
4510 4512 Using the -r/--rev or -d/--date options, revert the given files or
4511 4513 directories to their states as of a specific revision. Because
4512 4514 revert does not change the working directory parents, this will
4513 4515 cause these files to appear modified. This can be helpful to "back
4514 4516 out" some or all of an earlier change. See :hg:`backout` for a
4515 4517 related method.
4516 4518
4517 4519 Modified files are saved with a .orig suffix before reverting.
4518 4520 To disable these backups, use --no-backup. It is possible to store
4519 4521 the backup files in a custom directory relative to the root of the
4520 4522 repository by setting the ``ui.origbackuppath`` configuration
4521 4523 option.
4522 4524
4523 4525 See :hg:`help dates` for a list of formats valid for -d/--date.
4524 4526
4525 4527 See :hg:`help backout` for a way to reverse the effect of an
4526 4528 earlier changeset.
4527 4529
4528 4530 Returns 0 on success.
4529 4531 """
4530 4532
4531 4533 if opts.get("date"):
4532 4534 if opts.get("rev"):
4533 4535 raise error.Abort(_("you can't specify a revision and a date"))
4534 4536 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4535 4537
4536 4538 parent, p2 = repo.dirstate.parents()
4537 4539 if not opts.get('rev') and p2 != nullid:
4538 4540 # revert after merge is a trap for new users (issue2915)
4539 4541 raise error.Abort(_('uncommitted merge with no revision specified'),
4540 4542 hint=_("use 'hg update' or see 'hg help revert'"))
4541 4543
4542 4544 ctx = scmutil.revsingle(repo, opts.get('rev'))
4543 4545
4544 4546 if (not (pats or opts.get('include') or opts.get('exclude') or
4545 4547 opts.get('all') or opts.get('interactive'))):
4546 4548 msg = _("no files or directories specified")
4547 4549 if p2 != nullid:
4548 4550 hint = _("uncommitted merge, use --all to discard all changes,"
4549 4551 " or 'hg update -C .' to abort the merge")
4550 4552 raise error.Abort(msg, hint=hint)
4551 4553 dirty = any(repo.status())
4552 4554 node = ctx.node()
4553 4555 if node != parent:
4554 4556 if dirty:
4555 4557 hint = _("uncommitted changes, use --all to discard all"
4556 4558 " changes, or 'hg update %s' to update") % ctx.rev()
4557 4559 else:
4558 4560 hint = _("use --all to revert all files,"
4559 4561 " or 'hg update %s' to update") % ctx.rev()
4560 4562 elif dirty:
4561 4563 hint = _("uncommitted changes, use --all to discard all changes")
4562 4564 else:
4563 4565 hint = _("use --all to revert all files")
4564 4566 raise error.Abort(msg, hint=hint)
4565 4567
4566 4568 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4567 4569
4568 4570 @command('rollback', dryrunopts +
4569 4571 [('f', 'force', False, _('ignore safety measures'))])
4570 4572 def rollback(ui, repo, **opts):
4571 4573 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4572 4574
4573 4575 Please use :hg:`commit --amend` instead of rollback to correct
4574 4576 mistakes in the last commit.
4575 4577
4576 4578 This command should be used with care. There is only one level of
4577 4579 rollback, and there is no way to undo a rollback. It will also
4578 4580 restore the dirstate at the time of the last transaction, losing
4579 4581 any dirstate changes since that time. This command does not alter
4580 4582 the working directory.
4581 4583
4582 4584 Transactions are used to encapsulate the effects of all commands
4583 4585 that create new changesets or propagate existing changesets into a
4584 4586 repository.
4585 4587
4586 4588 .. container:: verbose
4587 4589
4588 4590 For example, the following commands are transactional, and their
4589 4591 effects can be rolled back:
4590 4592
4591 4593 - commit
4592 4594 - import
4593 4595 - pull
4594 4596 - push (with this repository as the destination)
4595 4597 - unbundle
4596 4598
4597 4599 To avoid permanent data loss, rollback will refuse to rollback a
4598 4600 commit transaction if it isn't checked out. Use --force to
4599 4601 override this protection.
4600 4602
4601 4603 The rollback command can be entirely disabled by setting the
4602 4604 ``ui.rollback`` configuration setting to false. If you're here
4603 4605 because you want to use rollback and it's disabled, you can
4604 4606 re-enable the command by setting ``ui.rollback`` to true.
4605 4607
4606 4608 This command is not intended for use on public repositories. Once
4607 4609 changes are visible for pull by other users, rolling a transaction
4608 4610 back locally is ineffective (someone else may already have pulled
4609 4611 the changes). Furthermore, a race is possible with readers of the
4610 4612 repository; for example an in-progress pull from the repository
4611 4613 may fail if a rollback is performed.
4612 4614
4613 4615 Returns 0 on success, 1 if no rollback data is available.
4614 4616 """
4615 4617 if not ui.configbool('ui', 'rollback', True):
4616 4618 raise error.Abort(_('rollback is disabled because it is unsafe'),
4617 4619 hint=('see `hg help -v rollback` for information'))
4618 4620 return repo.rollback(dryrun=opts.get(r'dry_run'),
4619 4621 force=opts.get(r'force'))
4620 4622
4621 4623 @command('root', [])
4622 4624 def root(ui, repo):
4623 4625 """print the root (top) of the current working directory
4624 4626
4625 4627 Print the root directory of the current repository.
4626 4628
4627 4629 Returns 0 on success.
4628 4630 """
4629 4631 ui.write(repo.root + "\n")
4630 4632
4631 4633 @command('^serve',
4632 4634 [('A', 'accesslog', '', _('name of access log file to write to'),
4633 4635 _('FILE')),
4634 4636 ('d', 'daemon', None, _('run server in background')),
4635 4637 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4636 4638 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4637 4639 # use string type, then we can check if something was passed
4638 4640 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4639 4641 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4640 4642 _('ADDR')),
4641 4643 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4642 4644 _('PREFIX')),
4643 4645 ('n', 'name', '',
4644 4646 _('name to show in web pages (default: working directory)'), _('NAME')),
4645 4647 ('', 'web-conf', '',
4646 4648 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4647 4649 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4648 4650 _('FILE')),
4649 4651 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4650 4652 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4651 4653 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4652 4654 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4653 4655 ('', 'style', '', _('template style to use'), _('STYLE')),
4654 4656 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4655 4657 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4656 4658 + subrepoopts,
4657 4659 _('[OPTION]...'),
4658 4660 optionalrepo=True)
4659 4661 def serve(ui, repo, **opts):
4660 4662 """start stand-alone webserver
4661 4663
4662 4664 Start a local HTTP repository browser and pull server. You can use
4663 4665 this for ad-hoc sharing and browsing of repositories. It is
4664 4666 recommended to use a real web server to serve a repository for
4665 4667 longer periods of time.
4666 4668
4667 4669 Please note that the server does not implement access control.
4668 4670 This means that, by default, anybody can read from the server and
4669 4671 nobody can write to it by default. Set the ``web.allow_push``
4670 4672 option to ``*`` to allow everybody to push to the server. You
4671 4673 should use a real web server if you need to authenticate users.
4672 4674
4673 4675 By default, the server logs accesses to stdout and errors to
4674 4676 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4675 4677 files.
4676 4678
4677 4679 To have the server choose a free port number to listen on, specify
4678 4680 a port number of 0; in this case, the server will print the port
4679 4681 number it uses.
4680 4682
4681 4683 Returns 0 on success.
4682 4684 """
4683 4685
4684 4686 opts = pycompat.byteskwargs(opts)
4685 4687 if opts["stdio"] and opts["cmdserver"]:
4686 4688 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4687 4689
4688 4690 if opts["stdio"]:
4689 4691 if repo is None:
4690 4692 raise error.RepoError(_("there is no Mercurial repository here"
4691 4693 " (.hg not found)"))
4692 4694 s = sshserver.sshserver(ui, repo)
4693 4695 s.serve_forever()
4694 4696
4695 4697 service = server.createservice(ui, repo, opts)
4696 4698 return server.runservice(opts, initfn=service.init, runfn=service.run)
4697 4699
4698 4700 @command('^status|st',
4699 4701 [('A', 'all', None, _('show status of all files')),
4700 4702 ('m', 'modified', None, _('show only modified files')),
4701 4703 ('a', 'added', None, _('show only added files')),
4702 4704 ('r', 'removed', None, _('show only removed files')),
4703 4705 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4704 4706 ('c', 'clean', None, _('show only files without changes')),
4705 4707 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4706 4708 ('i', 'ignored', None, _('show only ignored files')),
4707 4709 ('n', 'no-status', None, _('hide status prefix')),
4708 4710 ('C', 'copies', None, _('show source of copied files')),
4709 4711 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4710 4712 ('', 'rev', [], _('show difference from revision'), _('REV')),
4711 4713 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4712 4714 ] + walkopts + subrepoopts + formatteropts,
4713 4715 _('[OPTION]... [FILE]...'),
4714 4716 inferrepo=True)
4715 4717 def status(ui, repo, *pats, **opts):
4716 4718 """show changed files in the working directory
4717 4719
4718 4720 Show status of files in the repository. If names are given, only
4719 4721 files that match are shown. Files that are clean or ignored or
4720 4722 the source of a copy/move operation, are not listed unless
4721 4723 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4722 4724 Unless options described with "show only ..." are given, the
4723 4725 options -mardu are used.
4724 4726
4725 4727 Option -q/--quiet hides untracked (unknown and ignored) files
4726 4728 unless explicitly requested with -u/--unknown or -i/--ignored.
4727 4729
4728 4730 .. note::
4729 4731
4730 4732 :hg:`status` may appear to disagree with diff if permissions have
4731 4733 changed or a merge has occurred. The standard diff format does
4732 4734 not report permission changes and diff only reports changes
4733 4735 relative to one merge parent.
4734 4736
4735 4737 If one revision is given, it is used as the base revision.
4736 4738 If two revisions are given, the differences between them are
4737 4739 shown. The --change option can also be used as a shortcut to list
4738 4740 the changed files of a revision from its first parent.
4739 4741
4740 4742 The codes used to show the status of files are::
4741 4743
4742 4744 M = modified
4743 4745 A = added
4744 4746 R = removed
4745 4747 C = clean
4746 4748 ! = missing (deleted by non-hg command, but still tracked)
4747 4749 ? = not tracked
4748 4750 I = ignored
4749 4751 = origin of the previous file (with --copies)
4750 4752
4751 4753 .. container:: verbose
4752 4754
4753 4755 Examples:
4754 4756
4755 4757 - show changes in the working directory relative to a
4756 4758 changeset::
4757 4759
4758 4760 hg status --rev 9353
4759 4761
4760 4762 - show changes in the working directory relative to the
4761 4763 current directory (see :hg:`help patterns` for more information)::
4762 4764
4763 4765 hg status re:
4764 4766
4765 4767 - show all changes including copies in an existing changeset::
4766 4768
4767 4769 hg status --copies --change 9353
4768 4770
4769 4771 - get a NUL separated list of added files, suitable for xargs::
4770 4772
4771 4773 hg status -an0
4772 4774
4773 4775 Returns 0 on success.
4774 4776 """
4775 4777
4776 4778 opts = pycompat.byteskwargs(opts)
4777 4779 revs = opts.get('rev')
4778 4780 change = opts.get('change')
4779 4781
4780 4782 if revs and change:
4781 4783 msg = _('cannot specify --rev and --change at the same time')
4782 4784 raise error.Abort(msg)
4783 4785 elif change:
4784 4786 node2 = scmutil.revsingle(repo, change, None).node()
4785 4787 node1 = repo[node2].p1().node()
4786 4788 else:
4787 4789 node1, node2 = scmutil.revpair(repo, revs)
4788 4790
4789 4791 if pats or ui.configbool('commands', 'status.relative'):
4790 4792 cwd = repo.getcwd()
4791 4793 else:
4792 4794 cwd = ''
4793 4795
4794 4796 if opts.get('print0'):
4795 4797 end = '\0'
4796 4798 else:
4797 4799 end = '\n'
4798 4800 copy = {}
4799 4801 states = 'modified added removed deleted unknown ignored clean'.split()
4800 4802 show = [k for k in states if opts.get(k)]
4801 4803 if opts.get('all'):
4802 4804 show += ui.quiet and (states[:4] + ['clean']) or states
4803 4805 if not show:
4804 4806 if ui.quiet:
4805 4807 show = states[:4]
4806 4808 else:
4807 4809 show = states[:5]
4808 4810
4809 4811 m = scmutil.match(repo[node2], pats, opts)
4810 4812 stat = repo.status(node1, node2, m,
4811 4813 'ignored' in show, 'clean' in show, 'unknown' in show,
4812 4814 opts.get('subrepos'))
4813 4815 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4814 4816
4815 4817 if (opts.get('all') or opts.get('copies')
4816 4818 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4817 4819 copy = copies.pathcopies(repo[node1], repo[node2], m)
4818 4820
4819 4821 ui.pager('status')
4820 4822 fm = ui.formatter('status', opts)
4821 4823 fmt = '%s' + end
4822 4824 showchar = not opts.get('no_status')
4823 4825
4824 4826 for state, char, files in changestates:
4825 4827 if state in show:
4826 4828 label = 'status.' + state
4827 4829 for f in files:
4828 4830 fm.startitem()
4829 4831 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4830 4832 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4831 4833 if f in copy:
4832 4834 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4833 4835 label='status.copied')
4834 4836 fm.end()
4835 4837
4836 4838 @command('^summary|sum',
4837 4839 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4838 4840 def summary(ui, repo, **opts):
4839 4841 """summarize working directory state
4840 4842
4841 4843 This generates a brief summary of the working directory state,
4842 4844 including parents, branch, commit status, phase and available updates.
4843 4845
4844 4846 With the --remote option, this will check the default paths for
4845 4847 incoming and outgoing changes. This can be time-consuming.
4846 4848
4847 4849 Returns 0 on success.
4848 4850 """
4849 4851
4850 4852 opts = pycompat.byteskwargs(opts)
4851 4853 ui.pager('summary')
4852 4854 ctx = repo[None]
4853 4855 parents = ctx.parents()
4854 4856 pnode = parents[0].node()
4855 4857 marks = []
4856 4858
4857 4859 ms = None
4858 4860 try:
4859 4861 ms = mergemod.mergestate.read(repo)
4860 4862 except error.UnsupportedMergeRecords as e:
4861 4863 s = ' '.join(e.recordtypes)
4862 4864 ui.warn(
4863 4865 _('warning: merge state has unsupported record types: %s\n') % s)
4864 4866 unresolved = 0
4865 4867 else:
4866 4868 unresolved = [f for f in ms if ms[f] == 'u']
4867 4869
4868 4870 for p in parents:
4869 4871 # label with log.changeset (instead of log.parent) since this
4870 4872 # shows a working directory parent *changeset*:
4871 4873 # i18n: column positioning for "hg summary"
4872 4874 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4873 4875 label=cmdutil._changesetlabels(p))
4874 4876 ui.write(' '.join(p.tags()), label='log.tag')
4875 4877 if p.bookmarks():
4876 4878 marks.extend(p.bookmarks())
4877 4879 if p.rev() == -1:
4878 4880 if not len(repo):
4879 4881 ui.write(_(' (empty repository)'))
4880 4882 else:
4881 4883 ui.write(_(' (no revision checked out)'))
4882 4884 if p.obsolete():
4883 4885 ui.write(_(' (obsolete)'))
4884 4886 if p.troubled():
4885 4887 ui.write(' ('
4886 4888 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4887 4889 for trouble in p.troubles())
4888 4890 + ')')
4889 4891 ui.write('\n')
4890 4892 if p.description():
4891 4893 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4892 4894 label='log.summary')
4893 4895
4894 4896 branch = ctx.branch()
4895 4897 bheads = repo.branchheads(branch)
4896 4898 # i18n: column positioning for "hg summary"
4897 4899 m = _('branch: %s\n') % branch
4898 4900 if branch != 'default':
4899 4901 ui.write(m, label='log.branch')
4900 4902 else:
4901 4903 ui.status(m, label='log.branch')
4902 4904
4903 4905 if marks:
4904 4906 active = repo._activebookmark
4905 4907 # i18n: column positioning for "hg summary"
4906 4908 ui.write(_('bookmarks:'), label='log.bookmark')
4907 4909 if active is not None:
4908 4910 if active in marks:
4909 4911 ui.write(' *' + active, label=activebookmarklabel)
4910 4912 marks.remove(active)
4911 4913 else:
4912 4914 ui.write(' [%s]' % active, label=activebookmarklabel)
4913 4915 for m in marks:
4914 4916 ui.write(' ' + m, label='log.bookmark')
4915 4917 ui.write('\n', label='log.bookmark')
4916 4918
4917 4919 status = repo.status(unknown=True)
4918 4920
4919 4921 c = repo.dirstate.copies()
4920 4922 copied, renamed = [], []
4921 4923 for d, s in c.iteritems():
4922 4924 if s in status.removed:
4923 4925 status.removed.remove(s)
4924 4926 renamed.append(d)
4925 4927 else:
4926 4928 copied.append(d)
4927 4929 if d in status.added:
4928 4930 status.added.remove(d)
4929 4931
4930 4932 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4931 4933
4932 4934 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4933 4935 (ui.label(_('%d added'), 'status.added'), status.added),
4934 4936 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4935 4937 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4936 4938 (ui.label(_('%d copied'), 'status.copied'), copied),
4937 4939 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4938 4940 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4939 4941 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4940 4942 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4941 4943 t = []
4942 4944 for l, s in labels:
4943 4945 if s:
4944 4946 t.append(l % len(s))
4945 4947
4946 4948 t = ', '.join(t)
4947 4949 cleanworkdir = False
4948 4950
4949 4951 if repo.vfs.exists('graftstate'):
4950 4952 t += _(' (graft in progress)')
4951 4953 if repo.vfs.exists('updatestate'):
4952 4954 t += _(' (interrupted update)')
4953 4955 elif len(parents) > 1:
4954 4956 t += _(' (merge)')
4955 4957 elif branch != parents[0].branch():
4956 4958 t += _(' (new branch)')
4957 4959 elif (parents[0].closesbranch() and
4958 4960 pnode in repo.branchheads(branch, closed=True)):
4959 4961 t += _(' (head closed)')
4960 4962 elif not (status.modified or status.added or status.removed or renamed or
4961 4963 copied or subs):
4962 4964 t += _(' (clean)')
4963 4965 cleanworkdir = True
4964 4966 elif pnode not in bheads:
4965 4967 t += _(' (new branch head)')
4966 4968
4967 4969 if parents:
4968 4970 pendingphase = max(p.phase() for p in parents)
4969 4971 else:
4970 4972 pendingphase = phases.public
4971 4973
4972 4974 if pendingphase > phases.newcommitphase(ui):
4973 4975 t += ' (%s)' % phases.phasenames[pendingphase]
4974 4976
4975 4977 if cleanworkdir:
4976 4978 # i18n: column positioning for "hg summary"
4977 4979 ui.status(_('commit: %s\n') % t.strip())
4978 4980 else:
4979 4981 # i18n: column positioning for "hg summary"
4980 4982 ui.write(_('commit: %s\n') % t.strip())
4981 4983
4982 4984 # all ancestors of branch heads - all ancestors of parent = new csets
4983 4985 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4984 4986 bheads))
4985 4987
4986 4988 if new == 0:
4987 4989 # i18n: column positioning for "hg summary"
4988 4990 ui.status(_('update: (current)\n'))
4989 4991 elif pnode not in bheads:
4990 4992 # i18n: column positioning for "hg summary"
4991 4993 ui.write(_('update: %d new changesets (update)\n') % new)
4992 4994 else:
4993 4995 # i18n: column positioning for "hg summary"
4994 4996 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4995 4997 (new, len(bheads)))
4996 4998
4997 4999 t = []
4998 5000 draft = len(repo.revs('draft()'))
4999 5001 if draft:
5000 5002 t.append(_('%d draft') % draft)
5001 5003 secret = len(repo.revs('secret()'))
5002 5004 if secret:
5003 5005 t.append(_('%d secret') % secret)
5004 5006
5005 5007 if draft or secret:
5006 5008 ui.status(_('phases: %s\n') % ', '.join(t))
5007 5009
5008 5010 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5009 5011 for trouble in ("unstable", "divergent", "bumped"):
5010 5012 numtrouble = len(repo.revs(trouble + "()"))
5011 5013 # We write all the possibilities to ease translation
5012 5014 troublemsg = {
5013 5015 "unstable": _("unstable: %d changesets"),
5014 5016 "divergent": _("divergent: %d changesets"),
5015 5017 "bumped": _("bumped: %d changesets"),
5016 5018 }
5017 5019 if numtrouble > 0:
5018 5020 ui.status(troublemsg[trouble] % numtrouble + "\n")
5019 5021
5020 5022 cmdutil.summaryhooks(ui, repo)
5021 5023
5022 5024 if opts.get('remote'):
5023 5025 needsincoming, needsoutgoing = True, True
5024 5026 else:
5025 5027 needsincoming, needsoutgoing = False, False
5026 5028 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5027 5029 if i:
5028 5030 needsincoming = True
5029 5031 if o:
5030 5032 needsoutgoing = True
5031 5033 if not needsincoming and not needsoutgoing:
5032 5034 return
5033 5035
5034 5036 def getincoming():
5035 5037 source, branches = hg.parseurl(ui.expandpath('default'))
5036 5038 sbranch = branches[0]
5037 5039 try:
5038 5040 other = hg.peer(repo, {}, source)
5039 5041 except error.RepoError:
5040 5042 if opts.get('remote'):
5041 5043 raise
5042 5044 return source, sbranch, None, None, None
5043 5045 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5044 5046 if revs:
5045 5047 revs = [other.lookup(rev) for rev in revs]
5046 5048 ui.debug('comparing with %s\n' % util.hidepassword(source))
5047 5049 repo.ui.pushbuffer()
5048 5050 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5049 5051 repo.ui.popbuffer()
5050 5052 return source, sbranch, other, commoninc, commoninc[1]
5051 5053
5052 5054 if needsincoming:
5053 5055 source, sbranch, sother, commoninc, incoming = getincoming()
5054 5056 else:
5055 5057 source = sbranch = sother = commoninc = incoming = None
5056 5058
5057 5059 def getoutgoing():
5058 5060 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5059 5061 dbranch = branches[0]
5060 5062 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5061 5063 if source != dest:
5062 5064 try:
5063 5065 dother = hg.peer(repo, {}, dest)
5064 5066 except error.RepoError:
5065 5067 if opts.get('remote'):
5066 5068 raise
5067 5069 return dest, dbranch, None, None
5068 5070 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5069 5071 elif sother is None:
5070 5072 # there is no explicit destination peer, but source one is invalid
5071 5073 return dest, dbranch, None, None
5072 5074 else:
5073 5075 dother = sother
5074 5076 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5075 5077 common = None
5076 5078 else:
5077 5079 common = commoninc
5078 5080 if revs:
5079 5081 revs = [repo.lookup(rev) for rev in revs]
5080 5082 repo.ui.pushbuffer()
5081 5083 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5082 5084 commoninc=common)
5083 5085 repo.ui.popbuffer()
5084 5086 return dest, dbranch, dother, outgoing
5085 5087
5086 5088 if needsoutgoing:
5087 5089 dest, dbranch, dother, outgoing = getoutgoing()
5088 5090 else:
5089 5091 dest = dbranch = dother = outgoing = None
5090 5092
5091 5093 if opts.get('remote'):
5092 5094 t = []
5093 5095 if incoming:
5094 5096 t.append(_('1 or more incoming'))
5095 5097 o = outgoing.missing
5096 5098 if o:
5097 5099 t.append(_('%d outgoing') % len(o))
5098 5100 other = dother or sother
5099 5101 if 'bookmarks' in other.listkeys('namespaces'):
5100 5102 counts = bookmarks.summary(repo, other)
5101 5103 if counts[0] > 0:
5102 5104 t.append(_('%d incoming bookmarks') % counts[0])
5103 5105 if counts[1] > 0:
5104 5106 t.append(_('%d outgoing bookmarks') % counts[1])
5105 5107
5106 5108 if t:
5107 5109 # i18n: column positioning for "hg summary"
5108 5110 ui.write(_('remote: %s\n') % (', '.join(t)))
5109 5111 else:
5110 5112 # i18n: column positioning for "hg summary"
5111 5113 ui.status(_('remote: (synced)\n'))
5112 5114
5113 5115 cmdutil.summaryremotehooks(ui, repo, opts,
5114 5116 ((source, sbranch, sother, commoninc),
5115 5117 (dest, dbranch, dother, outgoing)))
5116 5118
5117 5119 @command('tag',
5118 5120 [('f', 'force', None, _('force tag')),
5119 5121 ('l', 'local', None, _('make the tag local')),
5120 5122 ('r', 'rev', '', _('revision to tag'), _('REV')),
5121 5123 ('', 'remove', None, _('remove a tag')),
5122 5124 # -l/--local is already there, commitopts cannot be used
5123 5125 ('e', 'edit', None, _('invoke editor on commit messages')),
5124 5126 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5125 5127 ] + commitopts2,
5126 5128 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5127 5129 def tag(ui, repo, name1, *names, **opts):
5128 5130 """add one or more tags for the current or given revision
5129 5131
5130 5132 Name a particular revision using <name>.
5131 5133
5132 5134 Tags are used to name particular revisions of the repository and are
5133 5135 very useful to compare different revisions, to go back to significant
5134 5136 earlier versions or to mark branch points as releases, etc. Changing
5135 5137 an existing tag is normally disallowed; use -f/--force to override.
5136 5138
5137 5139 If no revision is given, the parent of the working directory is
5138 5140 used.
5139 5141
5140 5142 To facilitate version control, distribution, and merging of tags,
5141 5143 they are stored as a file named ".hgtags" which is managed similarly
5142 5144 to other project files and can be hand-edited if necessary. This
5143 5145 also means that tagging creates a new commit. The file
5144 5146 ".hg/localtags" is used for local tags (not shared among
5145 5147 repositories).
5146 5148
5147 5149 Tag commits are usually made at the head of a branch. If the parent
5148 5150 of the working directory is not a branch head, :hg:`tag` aborts; use
5149 5151 -f/--force to force the tag commit to be based on a non-head
5150 5152 changeset.
5151 5153
5152 5154 See :hg:`help dates` for a list of formats valid for -d/--date.
5153 5155
5154 5156 Since tag names have priority over branch names during revision
5155 5157 lookup, using an existing branch name as a tag name is discouraged.
5156 5158
5157 5159 Returns 0 on success.
5158 5160 """
5159 5161 opts = pycompat.byteskwargs(opts)
5160 5162 wlock = lock = None
5161 5163 try:
5162 5164 wlock = repo.wlock()
5163 5165 lock = repo.lock()
5164 5166 rev_ = "."
5165 5167 names = [t.strip() for t in (name1,) + names]
5166 5168 if len(names) != len(set(names)):
5167 5169 raise error.Abort(_('tag names must be unique'))
5168 5170 for n in names:
5169 5171 scmutil.checknewlabel(repo, n, 'tag')
5170 5172 if not n:
5171 5173 raise error.Abort(_('tag names cannot consist entirely of '
5172 5174 'whitespace'))
5173 5175 if opts.get('rev') and opts.get('remove'):
5174 5176 raise error.Abort(_("--rev and --remove are incompatible"))
5175 5177 if opts.get('rev'):
5176 5178 rev_ = opts['rev']
5177 5179 message = opts.get('message')
5178 5180 if opts.get('remove'):
5179 5181 if opts.get('local'):
5180 5182 expectedtype = 'local'
5181 5183 else:
5182 5184 expectedtype = 'global'
5183 5185
5184 5186 for n in names:
5185 5187 if not repo.tagtype(n):
5186 5188 raise error.Abort(_("tag '%s' does not exist") % n)
5187 5189 if repo.tagtype(n) != expectedtype:
5188 5190 if expectedtype == 'global':
5189 5191 raise error.Abort(_("tag '%s' is not a global tag") % n)
5190 5192 else:
5191 5193 raise error.Abort(_("tag '%s' is not a local tag") % n)
5192 5194 rev_ = 'null'
5193 5195 if not message:
5194 5196 # we don't translate commit messages
5195 5197 message = 'Removed tag %s' % ', '.join(names)
5196 5198 elif not opts.get('force'):
5197 5199 for n in names:
5198 5200 if n in repo.tags():
5199 5201 raise error.Abort(_("tag '%s' already exists "
5200 5202 "(use -f to force)") % n)
5201 5203 if not opts.get('local'):
5202 5204 p1, p2 = repo.dirstate.parents()
5203 5205 if p2 != nullid:
5204 5206 raise error.Abort(_('uncommitted merge'))
5205 5207 bheads = repo.branchheads()
5206 5208 if not opts.get('force') and bheads and p1 not in bheads:
5207 5209 raise error.Abort(_('working directory is not at a branch head '
5208 5210 '(use -f to force)'))
5209 5211 r = scmutil.revsingle(repo, rev_).node()
5210 5212
5211 5213 if not message:
5212 5214 # we don't translate commit messages
5213 5215 message = ('Added tag %s for changeset %s' %
5214 5216 (', '.join(names), short(r)))
5215 5217
5216 5218 date = opts.get('date')
5217 5219 if date:
5218 5220 date = util.parsedate(date)
5219 5221
5220 5222 if opts.get('remove'):
5221 5223 editform = 'tag.remove'
5222 5224 else:
5223 5225 editform = 'tag.add'
5224 5226 editor = cmdutil.getcommiteditor(editform=editform,
5225 5227 **pycompat.strkwargs(opts))
5226 5228
5227 5229 # don't allow tagging the null rev
5228 5230 if (not opts.get('remove') and
5229 5231 scmutil.revsingle(repo, rev_).rev() == nullrev):
5230 5232 raise error.Abort(_("cannot tag null revision"))
5231 5233
5232 5234 tagsmod.tag(repo, names, r, message, opts.get('local'),
5233 5235 opts.get('user'), date, editor=editor)
5234 5236 finally:
5235 5237 release(lock, wlock)
5236 5238
5237 5239 @command('tags', formatteropts, '')
5238 5240 def tags(ui, repo, **opts):
5239 5241 """list repository tags
5240 5242
5241 5243 This lists both regular and local tags. When the -v/--verbose
5242 5244 switch is used, a third column "local" is printed for local tags.
5243 5245 When the -q/--quiet switch is used, only the tag name is printed.
5244 5246
5245 5247 Returns 0 on success.
5246 5248 """
5247 5249
5248 5250 opts = pycompat.byteskwargs(opts)
5249 5251 ui.pager('tags')
5250 5252 fm = ui.formatter('tags', opts)
5251 5253 hexfunc = fm.hexfunc
5252 5254 tagtype = ""
5253 5255
5254 5256 for t, n in reversed(repo.tagslist()):
5255 5257 hn = hexfunc(n)
5256 5258 label = 'tags.normal'
5257 5259 tagtype = ''
5258 5260 if repo.tagtype(t) == 'local':
5259 5261 label = 'tags.local'
5260 5262 tagtype = 'local'
5261 5263
5262 5264 fm.startitem()
5263 5265 fm.write('tag', '%s', t, label=label)
5264 5266 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5265 5267 fm.condwrite(not ui.quiet, 'rev node', fmt,
5266 5268 repo.changelog.rev(n), hn, label=label)
5267 5269 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5268 5270 tagtype, label=label)
5269 5271 fm.plain('\n')
5270 5272 fm.end()
5271 5273
5272 5274 @command('tip',
5273 5275 [('p', 'patch', None, _('show patch')),
5274 5276 ('g', 'git', None, _('use git extended diff format')),
5275 5277 ] + templateopts,
5276 5278 _('[-p] [-g]'))
5277 5279 def tip(ui, repo, **opts):
5278 5280 """show the tip revision (DEPRECATED)
5279 5281
5280 5282 The tip revision (usually just called the tip) is the changeset
5281 5283 most recently added to the repository (and therefore the most
5282 5284 recently changed head).
5283 5285
5284 5286 If you have just made a commit, that commit will be the tip. If
5285 5287 you have just pulled changes from another repository, the tip of
5286 5288 that repository becomes the current tip. The "tip" tag is special
5287 5289 and cannot be renamed or assigned to a different changeset.
5288 5290
5289 5291 This command is deprecated, please use :hg:`heads` instead.
5290 5292
5291 5293 Returns 0 on success.
5292 5294 """
5293 5295 opts = pycompat.byteskwargs(opts)
5294 5296 displayer = cmdutil.show_changeset(ui, repo, opts)
5295 5297 displayer.show(repo['tip'])
5296 5298 displayer.close()
5297 5299
5298 5300 @command('unbundle',
5299 5301 [('u', 'update', None,
5300 5302 _('update to new branch head if changesets were unbundled'))],
5301 5303 _('[-u] FILE...'))
5302 5304 def unbundle(ui, repo, fname1, *fnames, **opts):
5303 5305 """apply one or more bundle files
5304 5306
5305 5307 Apply one or more bundle files generated by :hg:`bundle`.
5306 5308
5307 5309 Returns 0 on success, 1 if an update has unresolved files.
5308 5310 """
5309 5311 fnames = (fname1,) + fnames
5310 5312
5311 5313 with repo.lock():
5312 5314 for fname in fnames:
5313 5315 f = hg.openpath(ui, fname)
5314 5316 gen = exchange.readbundle(ui, f, fname)
5315 5317 if isinstance(gen, bundle2.unbundle20):
5316 5318 tr = repo.transaction('unbundle')
5317 5319 try:
5318 5320 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5319 5321 url='bundle:' + fname)
5320 5322 tr.close()
5321 5323 except error.BundleUnknownFeatureError as exc:
5322 5324 raise error.Abort(_('%s: unknown bundle feature, %s')
5323 5325 % (fname, exc),
5324 5326 hint=_("see https://mercurial-scm.org/"
5325 5327 "wiki/BundleFeature for more "
5326 5328 "information"))
5327 5329 finally:
5328 5330 if tr:
5329 5331 tr.release()
5330 5332 changes = [r.get('return', 0)
5331 5333 for r in op.records['changegroup']]
5332 5334 modheads = changegroup.combineresults(changes)
5333 5335 elif isinstance(gen, streamclone.streamcloneapplier):
5334 5336 raise error.Abort(
5335 5337 _('packed bundles cannot be applied with '
5336 5338 '"hg unbundle"'),
5337 5339 hint=_('use "hg debugapplystreamclonebundle"'))
5338 5340 else:
5339 5341 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5340 5342
5341 5343 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5342 5344
5343 5345 @command('^update|up|checkout|co',
5344 5346 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5345 5347 ('c', 'check', None, _('require clean working directory')),
5346 5348 ('m', 'merge', None, _('merge uncommitted changes')),
5347 5349 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5348 5350 ('r', 'rev', '', _('revision'), _('REV'))
5349 5351 ] + mergetoolopts,
5350 5352 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5351 5353 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5352 5354 merge=None, tool=None):
5353 5355 """update working directory (or switch revisions)
5354 5356
5355 5357 Update the repository's working directory to the specified
5356 5358 changeset. If no changeset is specified, update to the tip of the
5357 5359 current named branch and move the active bookmark (see :hg:`help
5358 5360 bookmarks`).
5359 5361
5360 5362 Update sets the working directory's parent revision to the specified
5361 5363 changeset (see :hg:`help parents`).
5362 5364
5363 5365 If the changeset is not a descendant or ancestor of the working
5364 5366 directory's parent and there are uncommitted changes, the update is
5365 5367 aborted. With the -c/--check option, the working directory is checked
5366 5368 for uncommitted changes; if none are found, the working directory is
5367 5369 updated to the specified changeset.
5368 5370
5369 5371 .. container:: verbose
5370 5372
5371 5373 The -C/--clean, -c/--check, and -m/--merge options control what
5372 5374 happens if the working directory contains uncommitted changes.
5373 5375 At most of one of them can be specified.
5374 5376
5375 5377 1. If no option is specified, and if
5376 5378 the requested changeset is an ancestor or descendant of
5377 5379 the working directory's parent, the uncommitted changes
5378 5380 are merged into the requested changeset and the merged
5379 5381 result is left uncommitted. If the requested changeset is
5380 5382 not an ancestor or descendant (that is, it is on another
5381 5383 branch), the update is aborted and the uncommitted changes
5382 5384 are preserved.
5383 5385
5384 5386 2. With the -m/--merge option, the update is allowed even if the
5385 5387 requested changeset is not an ancestor or descendant of
5386 5388 the working directory's parent.
5387 5389
5388 5390 3. With the -c/--check option, the update is aborted and the
5389 5391 uncommitted changes are preserved.
5390 5392
5391 5393 4. With the -C/--clean option, uncommitted changes are discarded and
5392 5394 the working directory is updated to the requested changeset.
5393 5395
5394 5396 To cancel an uncommitted merge (and lose your changes), use
5395 5397 :hg:`update --clean .`.
5396 5398
5397 5399 Use null as the changeset to remove the working directory (like
5398 5400 :hg:`clone -U`).
5399 5401
5400 5402 If you want to revert just one file to an older revision, use
5401 5403 :hg:`revert [-r REV] NAME`.
5402 5404
5403 5405 See :hg:`help dates` for a list of formats valid for -d/--date.
5404 5406
5405 5407 Returns 0 on success, 1 if there are unresolved files.
5406 5408 """
5407 5409 if rev and node:
5408 5410 raise error.Abort(_("please specify just one revision"))
5409 5411
5410 5412 if ui.configbool('commands', 'update.requiredest'):
5411 5413 if not node and not rev and not date:
5412 5414 raise error.Abort(_('you must specify a destination'),
5413 5415 hint=_('for example: hg update ".::"'))
5414 5416
5415 5417 if rev is None or rev == '':
5416 5418 rev = node
5417 5419
5418 5420 if date and rev is not None:
5419 5421 raise error.Abort(_("you can't specify a revision and a date"))
5420 5422
5421 5423 if len([x for x in (clean, check, merge) if x]) > 1:
5422 5424 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5423 5425 "or -m/merge"))
5424 5426
5425 5427 updatecheck = None
5426 5428 if check:
5427 5429 updatecheck = 'abort'
5428 5430 elif merge:
5429 5431 updatecheck = 'none'
5430 5432
5431 5433 with repo.wlock():
5432 5434 cmdutil.clearunfinished(repo)
5433 5435
5434 5436 if date:
5435 5437 rev = cmdutil.finddate(ui, repo, date)
5436 5438
5437 5439 # if we defined a bookmark, we have to remember the original name
5438 5440 brev = rev
5439 5441 rev = scmutil.revsingle(repo, rev, rev).rev()
5440 5442
5441 5443 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5442 5444
5443 5445 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5444 5446 updatecheck=updatecheck)
5445 5447
5446 5448 @command('verify', [])
5447 5449 def verify(ui, repo):
5448 5450 """verify the integrity of the repository
5449 5451
5450 5452 Verify the integrity of the current repository.
5451 5453
5452 5454 This will perform an extensive check of the repository's
5453 5455 integrity, validating the hashes and checksums of each entry in
5454 5456 the changelog, manifest, and tracked files, as well as the
5455 5457 integrity of their crosslinks and indices.
5456 5458
5457 5459 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5458 5460 for more information about recovery from corruption of the
5459 5461 repository.
5460 5462
5461 5463 Returns 0 on success, 1 if errors are encountered.
5462 5464 """
5463 5465 return hg.verify(repo)
5464 5466
5465 5467 @command('version', [] + formatteropts, norepo=True)
5466 5468 def version_(ui, **opts):
5467 5469 """output version and copyright information"""
5468 5470 opts = pycompat.byteskwargs(opts)
5469 5471 if ui.verbose:
5470 5472 ui.pager('version')
5471 5473 fm = ui.formatter("version", opts)
5472 5474 fm.startitem()
5473 5475 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5474 5476 util.version())
5475 5477 license = _(
5476 5478 "(see https://mercurial-scm.org for more information)\n"
5477 5479 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5478 5480 "This is free software; see the source for copying conditions. "
5479 5481 "There is NO\nwarranty; "
5480 5482 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5481 5483 )
5482 5484 if not ui.quiet:
5483 5485 fm.plain(license)
5484 5486
5485 5487 if ui.verbose:
5486 5488 fm.plain(_("\nEnabled extensions:\n\n"))
5487 5489 # format names and versions into columns
5488 5490 names = []
5489 5491 vers = []
5490 5492 isinternals = []
5491 5493 for name, module in extensions.extensions():
5492 5494 names.append(name)
5493 5495 vers.append(extensions.moduleversion(module) or None)
5494 5496 isinternals.append(extensions.ismoduleinternal(module))
5495 5497 fn = fm.nested("extensions")
5496 5498 if names:
5497 5499 namefmt = " %%-%ds " % max(len(n) for n in names)
5498 5500 places = [_("external"), _("internal")]
5499 5501 for n, v, p in zip(names, vers, isinternals):
5500 5502 fn.startitem()
5501 5503 fn.condwrite(ui.verbose, "name", namefmt, n)
5502 5504 if ui.verbose:
5503 5505 fn.plain("%s " % places[p])
5504 5506 fn.data(bundled=p)
5505 5507 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5506 5508 if ui.verbose:
5507 5509 fn.plain("\n")
5508 5510 fn.end()
5509 5511 fm.end()
5510 5512
5511 5513 def loadcmdtable(ui, name, cmdtable):
5512 5514 """Load command functions from specified cmdtable
5513 5515 """
5514 5516 overrides = [cmd for cmd in cmdtable if cmd in table]
5515 5517 if overrides:
5516 5518 ui.warn(_("extension '%s' overrides commands: %s\n")
5517 5519 % (name, " ".join(overrides)))
5518 5520 table.update(cmdtable)
@@ -1,85 +1,85 b''
1 1 To merge files Mercurial uses merge tools.
2 2
3 3 A merge tool combines two different versions of a file into a merged
4 4 file. Merge tools are given the two files and the greatest common
5 5 ancestor of the two file versions, so they can determine the changes
6 6 made on both branches.
7 7
8 8 Merge tools are used both for :hg:`resolve`, :hg:`merge`, :hg:`update`,
9 9 :hg:`backout` and in several extensions.
10 10
11 11 Usually, the merge tool tries to automatically reconcile the files by
12 12 combining all non-overlapping changes that occurred separately in
13 13 the two different evolutions of the same initial base file. Furthermore, some
14 14 interactive merge programs make it easier to manually resolve
15 15 conflicting merges, either in a graphical way, or by inserting some
16 16 conflict markers. Mercurial does not include any interactive merge
17 17 programs but relies on external tools for that.
18 18
19 19 Available merge tools
20 20 =====================
21 21
22 22 External merge tools and their properties are configured in the
23 23 merge-tools configuration section - see hgrc(5) - but they can often just
24 24 be named by their executable.
25 25
26 26 A merge tool is generally usable if its executable can be found on the
27 27 system and if it can handle the merge. The executable is found if it
28 28 is an absolute or relative executable path or the name of an
29 29 application in the executable search path. The tool is assumed to be
30 30 able to handle the merge if it can handle symlinks if the file is a
31 31 symlink, if it can handle binary files if the file is binary, and if a
32 32 GUI is available if the tool requires a GUI.
33 33
34 34 There are some internal merge tools which can be used. The internal
35 35 merge tools are:
36 36
37 37 .. internaltoolsmarker
38 38
39 39 Internal tools are always available and do not require a GUI but will by default
40 40 not handle symlinks or binary files.
41 41
42 42 Choosing a merge tool
43 43 =====================
44 44
45 45 Mercurial uses these rules when deciding which merge tool to use:
46 46
47 47 1. If a tool has been specified with the --tool option to merge or resolve, it
48 48 is used. If it is the name of a tool in the merge-tools configuration, its
49 49 configuration is used. Otherwise the specified tool must be executable by
50 50 the shell.
51 51
52 52 2. If the ``HGMERGE`` environment variable is present, its value is used and
53 53 must be executable by the shell.
54 54
55 55 3. If the filename of the file to be merged matches any of the patterns in the
56 56 merge-patterns configuration section, the first usable merge tool
57 57 corresponding to a matching pattern is used. Here, binary capabilities of the
58 58 merge tool are not considered.
59 59
60 60 4. If ui.merge is set it will be considered next. If the value is not the name
61 61 of a configured tool, the specified value is used and must be executable by
62 62 the shell. Otherwise the named tool is used if it is usable.
63 63
64 64 5. If any usable merge tools are present in the merge-tools configuration
65 65 section, the one with the highest priority is used.
66 66
67 67 6. If a program named ``hgmerge`` can be found on the system, it is used - but
68 68 it will by default not be used for symlinks and binary files.
69 69
70 70 7. If the file to be merged is not binary and is not a symlink, then
71 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 75 .. note::
76 76
77 77 After selecting a merge program, Mercurial will by default attempt
78 78 to merge the files using a simple merge algorithm first. Only if it doesn't
79 79 succeed because of conflicting changes Mercurial will actually execute the
80 80 merge program. Whether to use the simple merge algorithm first can be
81 81 controlled by the premerge setting of the merge tool. Premerge is enabled by
82 82 default unless the file is binary or a symlink.
83 83
84 84 See the merge-tools and ui sections of hgrc(5) for details on the
85 85 configuration of merge tools.
@@ -1,860 +1,860 b''
1 1 # sslutil.py - SSL handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 from __future__ import absolute_import
11 11
12 12 import hashlib
13 13 import os
14 14 import re
15 15 import ssl
16 16 import sys
17 17
18 18 from .i18n import _
19 19 from . import (
20 20 error,
21 21 pycompat,
22 22 util,
23 23 )
24 24
25 25 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
26 26 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
27 27 # all exposed via the "ssl" module.
28 28 #
29 29 # Depending on the version of Python being used, SSL/TLS support is either
30 30 # modern/secure or legacy/insecure. Many operations in this module have
31 31 # separate code paths depending on support in Python.
32 32
33 33 configprotocols = set([
34 34 'tls1.0',
35 35 'tls1.1',
36 36 'tls1.2',
37 37 ])
38 38
39 39 hassni = getattr(ssl, 'HAS_SNI', False)
40 40
41 41 # TLS 1.1 and 1.2 may not be supported if the OpenSSL Python is compiled
42 42 # against doesn't support them.
43 43 supportedprotocols = set(['tls1.0'])
44 44 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_1'):
45 45 supportedprotocols.add('tls1.1')
46 46 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_2'):
47 47 supportedprotocols.add('tls1.2')
48 48
49 49 try:
50 50 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
51 51 # SSL/TLS features are available.
52 52 SSLContext = ssl.SSLContext
53 53 modernssl = True
54 54 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
55 55 except AttributeError:
56 56 modernssl = False
57 57 _canloaddefaultcerts = False
58 58
59 59 # We implement SSLContext using the interface from the standard library.
60 60 class SSLContext(object):
61 61 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
62 62 _supportsciphers = sys.version_info >= (2, 7)
63 63
64 64 def __init__(self, protocol):
65 65 # From the public interface of SSLContext
66 66 self.protocol = protocol
67 67 self.check_hostname = False
68 68 self.options = 0
69 69 self.verify_mode = ssl.CERT_NONE
70 70
71 71 # Used by our implementation.
72 72 self._certfile = None
73 73 self._keyfile = None
74 74 self._certpassword = None
75 75 self._cacerts = None
76 76 self._ciphers = None
77 77
78 78 def load_cert_chain(self, certfile, keyfile=None, password=None):
79 79 self._certfile = certfile
80 80 self._keyfile = keyfile
81 81 self._certpassword = password
82 82
83 83 def load_default_certs(self, purpose=None):
84 84 pass
85 85
86 86 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
87 87 if capath:
88 88 raise error.Abort(_('capath not supported'))
89 89 if cadata:
90 90 raise error.Abort(_('cadata not supported'))
91 91
92 92 self._cacerts = cafile
93 93
94 94 def set_ciphers(self, ciphers):
95 95 if not self._supportsciphers:
96 96 raise error.Abort(_('setting ciphers in [hostsecurity] is not '
97 97 'supported by this version of Python'),
98 98 hint=_('remove the config option or run '
99 99 'Mercurial with a modern Python '
100 100 'version (preferred)'))
101 101
102 102 self._ciphers = ciphers
103 103
104 104 def wrap_socket(self, socket, server_hostname=None, server_side=False):
105 105 # server_hostname is unique to SSLContext.wrap_socket and is used
106 106 # for SNI in that context. So there's nothing for us to do with it
107 107 # in this legacy code since we don't support SNI.
108 108
109 109 args = {
110 110 'keyfile': self._keyfile,
111 111 'certfile': self._certfile,
112 112 'server_side': server_side,
113 113 'cert_reqs': self.verify_mode,
114 114 'ssl_version': self.protocol,
115 115 'ca_certs': self._cacerts,
116 116 }
117 117
118 118 if self._supportsciphers:
119 119 args['ciphers'] = self._ciphers
120 120
121 121 return ssl.wrap_socket(socket, **args)
122 122
123 123 def _hostsettings(ui, hostname):
124 124 """Obtain security settings for a hostname.
125 125
126 126 Returns a dict of settings relevant to that hostname.
127 127 """
128 128 s = {
129 129 # Whether we should attempt to load default/available CA certs
130 130 # if an explicit ``cafile`` is not defined.
131 131 'allowloaddefaultcerts': True,
132 132 # List of 2-tuple of (hash algorithm, hash).
133 133 'certfingerprints': [],
134 134 # Path to file containing concatenated CA certs. Used by
135 135 # SSLContext.load_verify_locations().
136 136 'cafile': None,
137 137 # Whether certificate verification should be disabled.
138 138 'disablecertverification': False,
139 139 # Whether the legacy [hostfingerprints] section has data for this host.
140 140 'legacyfingerprint': False,
141 141 # PROTOCOL_* constant to use for SSLContext.__init__.
142 142 'protocol': None,
143 143 # String representation of minimum protocol to be used for UI
144 144 # presentation.
145 145 'protocolui': None,
146 146 # ssl.CERT_* constant used by SSLContext.verify_mode.
147 147 'verifymode': None,
148 148 # Defines extra ssl.OP* bitwise options to set.
149 149 'ctxoptions': None,
150 150 # OpenSSL Cipher List to use (instead of default).
151 151 'ciphers': None,
152 152 }
153 153
154 154 # Allow minimum TLS protocol to be specified in the config.
155 155 def validateprotocol(protocol, key):
156 156 if protocol not in configprotocols:
157 157 raise error.Abort(
158 158 _('unsupported protocol from hostsecurity.%s: %s') %
159 159 (key, protocol),
160 160 hint=_('valid protocols: %s') %
161 161 ' '.join(sorted(configprotocols)))
162 162
163 163 # We default to TLS 1.1+ where we can because TLS 1.0 has known
164 164 # vulnerabilities (like BEAST and POODLE). We allow users to downgrade to
165 165 # TLS 1.0+ via config options in case a legacy server is encountered.
166 166 if 'tls1.1' in supportedprotocols:
167 167 defaultprotocol = 'tls1.1'
168 168 else:
169 169 # Let people know they are borderline secure.
170 170 # We don't document this config option because we want people to see
171 171 # the bold warnings on the web site.
172 172 # internal config: hostsecurity.disabletls10warning
173 173 if not ui.configbool('hostsecurity', 'disabletls10warning'):
174 174 ui.warn(_('warning: connecting to %s using legacy security '
175 175 'technology (TLS 1.0); see '
176 176 'https://mercurial-scm.org/wiki/SecureConnections for '
177 177 'more info\n') % hostname)
178 178 defaultprotocol = 'tls1.0'
179 179
180 180 key = 'minimumprotocol'
181 181 protocol = ui.config('hostsecurity', key, defaultprotocol)
182 182 validateprotocol(protocol, key)
183 183
184 184 key = '%s:minimumprotocol' % hostname
185 185 protocol = ui.config('hostsecurity', key, protocol)
186 186 validateprotocol(protocol, key)
187 187
188 188 # If --insecure is used, we allow the use of TLS 1.0 despite config options.
189 189 # We always print a "connection security to %s is disabled..." message when
190 190 # --insecure is used. So no need to print anything more here.
191 191 if ui.insecureconnections:
192 192 protocol = 'tls1.0'
193 193
194 194 s['protocol'], s['ctxoptions'], s['protocolui'] = protocolsettings(protocol)
195 195
196 196 ciphers = ui.config('hostsecurity', 'ciphers')
197 197 ciphers = ui.config('hostsecurity', '%s:ciphers' % hostname, ciphers)
198 198 s['ciphers'] = ciphers
199 199
200 200 # Look for fingerprints in [hostsecurity] section. Value is a list
201 201 # of <alg>:<fingerprint> strings.
202 202 fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
203 203 [])
204 204 for fingerprint in fingerprints:
205 205 if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
206 206 raise error.Abort(_('invalid fingerprint for %s: %s') % (
207 207 hostname, fingerprint),
208 208 hint=_('must begin with "sha1:", "sha256:", '
209 209 'or "sha512:"'))
210 210
211 211 alg, fingerprint = fingerprint.split(':', 1)
212 212 fingerprint = fingerprint.replace(':', '').lower()
213 213 s['certfingerprints'].append((alg, fingerprint))
214 214
215 215 # Fingerprints from [hostfingerprints] are always SHA-1.
216 216 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
217 217 fingerprint = fingerprint.replace(':', '').lower()
218 218 s['certfingerprints'].append(('sha1', fingerprint))
219 219 s['legacyfingerprint'] = True
220 220
221 221 # If a host cert fingerprint is defined, it is the only thing that
222 222 # matters. No need to validate CA certs.
223 223 if s['certfingerprints']:
224 224 s['verifymode'] = ssl.CERT_NONE
225 225 s['allowloaddefaultcerts'] = False
226 226
227 227 # If --insecure is used, don't take CAs into consideration.
228 228 elif ui.insecureconnections:
229 229 s['disablecertverification'] = True
230 230 s['verifymode'] = ssl.CERT_NONE
231 231 s['allowloaddefaultcerts'] = False
232 232
233 233 if ui.configbool('devel', 'disableloaddefaultcerts'):
234 234 s['allowloaddefaultcerts'] = False
235 235
236 236 # If both fingerprints and a per-host ca file are specified, issue a warning
237 237 # because users should not be surprised about what security is or isn't
238 238 # being performed.
239 239 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname)
240 240 if s['certfingerprints'] and cafile:
241 241 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host '
242 242 'fingerprints defined; using host fingerprints for '
243 243 'verification)\n') % hostname)
244 244
245 245 # Try to hook up CA certificate validation unless something above
246 246 # makes it not necessary.
247 247 if s['verifymode'] is None:
248 248 # Look at per-host ca file first.
249 249 if cafile:
250 250 cafile = util.expandpath(cafile)
251 251 if not os.path.exists(cafile):
252 252 raise error.Abort(_('path specified by %s does not exist: %s') %
253 253 ('hostsecurity.%s:verifycertsfile' % hostname,
254 254 cafile))
255 255 s['cafile'] = cafile
256 256 else:
257 257 # Find global certificates file in config.
258 258 cafile = ui.config('web', 'cacerts')
259 259
260 260 if cafile:
261 261 cafile = util.expandpath(cafile)
262 262 if not os.path.exists(cafile):
263 263 raise error.Abort(_('could not find web.cacerts: %s') %
264 264 cafile)
265 265 elif s['allowloaddefaultcerts']:
266 266 # CAs not defined in config. Try to find system bundles.
267 267 cafile = _defaultcacerts(ui)
268 268 if cafile:
269 269 ui.debug('using %s for CA file\n' % cafile)
270 270
271 271 s['cafile'] = cafile
272 272
273 273 # Require certificate validation if CA certs are being loaded and
274 274 # verification hasn't been disabled above.
275 275 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
276 276 s['verifymode'] = ssl.CERT_REQUIRED
277 277 else:
278 278 # At this point we don't have a fingerprint, aren't being
279 279 # explicitly insecure, and can't load CA certs. Connecting
280 280 # is insecure. We allow the connection and abort during
281 281 # validation (once we have the fingerprint to print to the
282 282 # user).
283 283 s['verifymode'] = ssl.CERT_NONE
284 284
285 285 assert s['protocol'] is not None
286 286 assert s['ctxoptions'] is not None
287 287 assert s['verifymode'] is not None
288 288
289 289 return s
290 290
291 291 def protocolsettings(protocol):
292 292 """Resolve the protocol for a config value.
293 293
294 294 Returns a 3-tuple of (protocol, options, ui value) where the first
295 295 2 items are values used by SSLContext and the last is a string value
296 296 of the ``minimumprotocol`` config option equivalent.
297 297 """
298 298 if protocol not in configprotocols:
299 299 raise ValueError('protocol value not supported: %s' % protocol)
300 300
301 301 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
302 302 # that both ends support, including TLS protocols. On legacy stacks,
303 303 # the highest it likely goes is TLS 1.0. On modern stacks, it can
304 304 # support TLS 1.2.
305 305 #
306 306 # The PROTOCOL_TLSv* constants select a specific TLS version
307 307 # only (as opposed to multiple versions). So the method for
308 308 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
309 309 # disable protocols via SSLContext.options and OP_NO_* constants.
310 310 # However, SSLContext.options doesn't work unless we have the
311 311 # full/real SSLContext available to us.
312 312 if supportedprotocols == set(['tls1.0']):
313 313 if protocol != 'tls1.0':
314 314 raise error.Abort(_('current Python does not support protocol '
315 315 'setting %s') % protocol,
316 316 hint=_('upgrade Python or disable setting since '
317 317 'only TLS 1.0 is supported'))
318 318
319 319 return ssl.PROTOCOL_TLSv1, 0, 'tls1.0'
320 320
321 321 # WARNING: returned options don't work unless the modern ssl module
322 322 # is available. Be careful when adding options here.
323 323
324 324 # SSLv2 and SSLv3 are broken. We ban them outright.
325 325 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
326 326
327 327 if protocol == 'tls1.0':
328 328 # Defaults above are to use TLS 1.0+
329 329 pass
330 330 elif protocol == 'tls1.1':
331 331 options |= ssl.OP_NO_TLSv1
332 332 elif protocol == 'tls1.2':
333 333 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
334 334 else:
335 335 raise error.Abort(_('this should not happen'))
336 336
337 337 # Prevent CRIME.
338 338 # There is no guarantee this attribute is defined on the module.
339 339 options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
340 340
341 341 return ssl.PROTOCOL_SSLv23, options, protocol
342 342
343 343 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
344 344 """Add SSL/TLS to a socket.
345 345
346 346 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
347 347 choices based on what security options are available.
348 348
349 349 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
350 350 the following additional arguments:
351 351
352 352 * serverhostname - The expected hostname of the remote server. If the
353 353 server (and client) support SNI, this tells the server which certificate
354 354 to use.
355 355 """
356 356 if not serverhostname:
357 357 raise error.Abort(_('serverhostname argument is required'))
358 358
359 359 settings = _hostsettings(ui, serverhostname)
360 360
361 361 # We can't use ssl.create_default_context() because it calls
362 362 # load_default_certs() unless CA arguments are passed to it. We want to
363 363 # have explicit control over CA loading because implicitly loading
364 364 # CAs may undermine the user's intent. For example, a user may define a CA
365 365 # bundle with a specific CA cert removed. If the system/default CA bundle
366 366 # is loaded and contains that removed CA, you've just undone the user's
367 367 # choice.
368 368 sslcontext = SSLContext(settings['protocol'])
369 369
370 370 # This is a no-op unless using modern ssl.
371 371 sslcontext.options |= settings['ctxoptions']
372 372
373 373 # This still works on our fake SSLContext.
374 374 sslcontext.verify_mode = settings['verifymode']
375 375
376 376 if settings['ciphers']:
377 377 try:
378 378 sslcontext.set_ciphers(settings['ciphers'])
379 379 except ssl.SSLError as e:
380 380 raise error.Abort(_('could not set ciphers: %s') % e.args[0],
381 381 hint=_('change cipher string (%s) in config') %
382 382 settings['ciphers'])
383 383
384 384 if certfile is not None:
385 385 def password():
386 386 f = keyfile or certfile
387 387 return ui.getpass(_('passphrase for %s: ') % f, '')
388 388 sslcontext.load_cert_chain(certfile, keyfile, password)
389 389
390 390 if settings['cafile'] is not None:
391 391 try:
392 392 sslcontext.load_verify_locations(cafile=settings['cafile'])
393 393 except ssl.SSLError as e:
394 394 if len(e.args) == 1: # pypy has different SSLError args
395 395 msg = e.args[0]
396 396 else:
397 397 msg = e.args[1]
398 398 raise error.Abort(_('error loading CA file %s: %s') % (
399 399 settings['cafile'], msg),
400 400 hint=_('file is empty or malformed?'))
401 401 caloaded = True
402 402 elif settings['allowloaddefaultcerts']:
403 403 # This is a no-op on old Python.
404 404 sslcontext.load_default_certs()
405 405 caloaded = True
406 406 else:
407 407 caloaded = False
408 408
409 409 try:
410 410 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
411 411 except ssl.SSLError as e:
412 412 # If we're doing certificate verification and no CA certs are loaded,
413 413 # that is almost certainly the reason why verification failed. Provide
414 414 # a hint to the user.
415 415 # Only modern ssl module exposes SSLContext.get_ca_certs() so we can
416 416 # only show this warning if modern ssl is available.
417 417 # The exception handler is here to handle bugs around cert attributes:
418 418 # https://bugs.python.org/issue20916#msg213479. (See issues5313.)
419 419 # When the main 20916 bug occurs, 'sslcontext.get_ca_certs()' is a
420 420 # non-empty list, but the following conditional is otherwise True.
421 421 try:
422 422 if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
423 423 modernssl and not sslcontext.get_ca_certs()):
424 424 ui.warn(_('(an attempt was made to load CA certificates but '
425 425 'none were loaded; see '
426 426 'https://mercurial-scm.org/wiki/SecureConnections '
427 427 'for how to configure Mercurial to avoid this '
428 428 'error)\n'))
429 429 except ssl.SSLError:
430 430 pass
431 431 # Try to print more helpful error messages for known failures.
432 432 if util.safehasattr(e, 'reason'):
433 433 # This error occurs when the client and server don't share a
434 434 # common/supported SSL/TLS protocol. We've disabled SSLv2 and SSLv3
435 435 # outright. Hopefully the reason for this error is that we require
436 436 # TLS 1.1+ and the server only supports TLS 1.0. Whatever the
437 437 # reason, try to emit an actionable warning.
438 438 if e.reason == 'UNSUPPORTED_PROTOCOL':
439 439 # We attempted TLS 1.0+.
440 440 if settings['protocolui'] == 'tls1.0':
441 441 # We support more than just TLS 1.0+. If this happens,
442 442 # the likely scenario is either the client or the server
443 443 # is really old. (e.g. server doesn't support TLS 1.0+ or
444 444 # client doesn't support modern TLS versions introduced
445 445 # several years from when this comment was written).
446 446 if supportedprotocols != set(['tls1.0']):
447 447 ui.warn(_(
448 448 '(could not communicate with %s using security '
449 449 'protocols %s; if you are using a modern Mercurial '
450 450 'version, consider contacting the operator of this '
451 451 'server; see '
452 452 'https://mercurial-scm.org/wiki/SecureConnections '
453 453 'for more info)\n') % (
454 454 serverhostname,
455 455 ', '.join(sorted(supportedprotocols))))
456 456 else:
457 457 ui.warn(_(
458 458 '(could not communicate with %s using TLS 1.0; the '
459 459 'likely cause of this is the server no longer '
460 460 'supports TLS 1.0 because it has known security '
461 461 'vulnerabilities; see '
462 462 'https://mercurial-scm.org/wiki/SecureConnections '
463 463 'for more info)\n') % serverhostname)
464 464 else:
465 465 # We attempted TLS 1.1+. We can only get here if the client
466 466 # supports the configured protocol. So the likely reason is
467 467 # the client wants better security than the server can
468 468 # offer.
469 469 ui.warn(_(
470 470 '(could not negotiate a common security protocol (%s+) '
471 471 'with %s; the likely cause is Mercurial is configured '
472 472 'to be more secure than the server can support)\n') % (
473 473 settings['protocolui'], serverhostname))
474 474 ui.warn(_('(consider contacting the operator of this '
475 475 'server and ask them to support modern TLS '
476 476 'protocol versions; or, set '
477 477 'hostsecurity.%s:minimumprotocol=tls1.0 to allow '
478 478 'use of legacy, less secure protocols when '
479 479 'communicating with this server)\n') %
480 480 serverhostname)
481 481 ui.warn(_(
482 482 '(see https://mercurial-scm.org/wiki/SecureConnections '
483 483 'for more info)\n'))
484 484 raise
485 485
486 486 # check if wrap_socket failed silently because socket had been
487 487 # closed
488 488 # - see http://bugs.python.org/issue13721
489 489 if not sslsocket.cipher():
490 490 raise error.Abort(_('ssl connection failed'))
491 491
492 492 sslsocket._hgstate = {
493 493 'caloaded': caloaded,
494 494 'hostname': serverhostname,
495 495 'settings': settings,
496 496 'ui': ui,
497 497 }
498 498
499 499 return sslsocket
500 500
501 501 def wrapserversocket(sock, ui, certfile=None, keyfile=None, cafile=None,
502 502 requireclientcert=False):
503 503 """Wrap a socket for use by servers.
504 504
505 505 ``certfile`` and ``keyfile`` specify the files containing the certificate's
506 506 public and private keys, respectively. Both keys can be defined in the same
507 507 file via ``certfile`` (the private key must come first in the file).
508 508
509 509 ``cafile`` defines the path to certificate authorities.
510 510
511 511 ``requireclientcert`` specifies whether to require client certificates.
512 512
513 513 Typically ``cafile`` is only defined if ``requireclientcert`` is true.
514 514 """
515 515 protocol, options, _protocolui = protocolsettings('tls1.0')
516 516
517 517 # This config option is intended for use in tests only. It is a giant
518 518 # footgun to kill security. Don't define it.
519 519 exactprotocol = ui.config('devel', 'serverexactprotocol')
520 520 if exactprotocol == 'tls1.0':
521 521 protocol = ssl.PROTOCOL_TLSv1
522 522 elif exactprotocol == 'tls1.1':
523 523 if 'tls1.1' not in supportedprotocols:
524 524 raise error.Abort(_('TLS 1.1 not supported by this Python'))
525 525 protocol = ssl.PROTOCOL_TLSv1_1
526 526 elif exactprotocol == 'tls1.2':
527 527 if 'tls1.2' not in supportedprotocols:
528 528 raise error.Abort(_('TLS 1.2 not supported by this Python'))
529 529 protocol = ssl.PROTOCOL_TLSv1_2
530 530 elif exactprotocol:
531 531 raise error.Abort(_('invalid value for serverexactprotocol: %s') %
532 532 exactprotocol)
533 533
534 534 if modernssl:
535 535 # We /could/ use create_default_context() here since it doesn't load
536 536 # CAs when configured for client auth. However, it is hard-coded to
537 537 # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
538 538 sslcontext = SSLContext(protocol)
539 539 sslcontext.options |= options
540 540
541 541 # Improve forward secrecy.
542 542 sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)
543 543 sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0)
544 544
545 545 # Use the list of more secure ciphers if found in the ssl module.
546 546 if util.safehasattr(ssl, '_RESTRICTED_SERVER_CIPHERS'):
547 547 sslcontext.options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0)
548 548 sslcontext.set_ciphers(ssl._RESTRICTED_SERVER_CIPHERS)
549 549 else:
550 550 sslcontext = SSLContext(ssl.PROTOCOL_TLSv1)
551 551
552 552 if requireclientcert:
553 553 sslcontext.verify_mode = ssl.CERT_REQUIRED
554 554 else:
555 555 sslcontext.verify_mode = ssl.CERT_NONE
556 556
557 557 if certfile or keyfile:
558 558 sslcontext.load_cert_chain(certfile=certfile, keyfile=keyfile)
559 559
560 560 if cafile:
561 561 sslcontext.load_verify_locations(cafile=cafile)
562 562
563 563 return sslcontext.wrap_socket(sock, server_side=True)
564 564
565 565 class wildcarderror(Exception):
566 566 """Represents an error parsing wildcards in DNS name."""
567 567
568 568 def _dnsnamematch(dn, hostname, maxwildcards=1):
569 569 """Match DNS names according RFC 6125 section 6.4.3.
570 570
571 571 This code is effectively copied from CPython's ssl._dnsname_match.
572 572
573 573 Returns a bool indicating whether the expected hostname matches
574 574 the value in ``dn``.
575 575 """
576 576 pats = []
577 577 if not dn:
578 578 return False
579 579
580 580 pieces = dn.split(r'.')
581 581 leftmost = pieces[0]
582 582 remainder = pieces[1:]
583 583 wildcards = leftmost.count('*')
584 584 if wildcards > maxwildcards:
585 585 raise wildcarderror(
586 586 _('too many wildcards in certificate DNS name: %s') % dn)
587 587
588 588 # speed up common case w/o wildcards
589 589 if not wildcards:
590 590 return dn.lower() == hostname.lower()
591 591
592 592 # RFC 6125, section 6.4.3, subitem 1.
593 593 # The client SHOULD NOT attempt to match a presented identifier in which
594 594 # the wildcard character comprises a label other than the left-most label.
595 595 if leftmost == '*':
596 596 # When '*' is a fragment by itself, it matches a non-empty dotless
597 597 # fragment.
598 598 pats.append('[^.]+')
599 599 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
600 600 # RFC 6125, section 6.4.3, subitem 3.
601 601 # The client SHOULD NOT attempt to match a presented identifier
602 602 # where the wildcard character is embedded within an A-label or
603 603 # U-label of an internationalized domain name.
604 604 pats.append(re.escape(leftmost))
605 605 else:
606 606 # Otherwise, '*' matches any dotless string, e.g. www*
607 607 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
608 608
609 609 # add the remaining fragments, ignore any wildcards
610 610 for frag in remainder:
611 611 pats.append(re.escape(frag))
612 612
613 613 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
614 614 return pat.match(hostname) is not None
615 615
616 616 def _verifycert(cert, hostname):
617 617 '''Verify that cert (in socket.getpeercert() format) matches hostname.
618 618 CRLs is not handled.
619 619
620 620 Returns error message if any problems are found and None on success.
621 621 '''
622 622 if not cert:
623 623 return _('no certificate received')
624 624
625 625 dnsnames = []
626 626 san = cert.get('subjectAltName', [])
627 627 for key, value in san:
628 628 if key == 'DNS':
629 629 try:
630 630 if _dnsnamematch(value, hostname):
631 631 return
632 632 except wildcarderror as e:
633 633 return e.args[0]
634 634
635 635 dnsnames.append(value)
636 636
637 637 if not dnsnames:
638 638 # The subject is only checked when there is no DNS in subjectAltName.
639 639 for sub in cert.get('subject', []):
640 640 for key, value in sub:
641 641 # According to RFC 2818 the most specific Common Name must
642 642 # be used.
643 643 if key == 'commonName':
644 644 # 'subject' entries are unicode.
645 645 try:
646 646 value = value.encode('ascii')
647 647 except UnicodeEncodeError:
648 648 return _('IDN in certificate not supported')
649 649
650 650 try:
651 651 if _dnsnamematch(value, hostname):
652 652 return
653 653 except wildcarderror as e:
654 654 return e.args[0]
655 655
656 656 dnsnames.append(value)
657 657
658 658 if len(dnsnames) > 1:
659 659 return _('certificate is for %s') % ', '.join(dnsnames)
660 660 elif len(dnsnames) == 1:
661 661 return _('certificate is for %s') % dnsnames[0]
662 662 else:
663 663 return _('no commonName or subjectAltName found in certificate')
664 664
665 665 def _plainapplepython():
666 666 """return true if this seems to be a pure Apple Python that
667 667 * is unfrozen and presumably has the whole mercurial module in the file
668 668 system
669 669 * presumably is an Apple Python that uses Apple OpenSSL which has patches
670 670 for using system certificate store CAs in addition to the provided
671 671 cacerts file
672 672 """
673 673 if (pycompat.sysplatform != 'darwin' or
674 674 util.mainfrozen() or not pycompat.sysexecutable):
675 675 return False
676 676 exe = os.path.realpath(pycompat.sysexecutable).lower()
677 677 return (exe.startswith('/usr/bin/python') or
678 678 exe.startswith('/system/library/frameworks/python.framework/'))
679 679
680 680 _systemcacertpaths = [
681 681 # RHEL, CentOS, and Fedora
682 682 '/etc/pki/tls/certs/ca-bundle.trust.crt',
683 683 # Debian, Ubuntu, Gentoo
684 684 '/etc/ssl/certs/ca-certificates.crt',
685 685 ]
686 686
687 687 def _defaultcacerts(ui):
688 688 """return path to default CA certificates or None.
689 689
690 690 It is assumed this function is called when the returned certificates
691 691 file will actually be used to validate connections. Therefore this
692 692 function may print warnings or debug messages assuming this usage.
693 693
694 694 We don't print a message when the Python is able to load default
695 695 CA certs because this scenario is detected at socket connect time.
696 696 """
697 697 # The "certifi" Python package provides certificates. If it is installed
698 698 # and usable, assume the user intends it to be used and use it.
699 699 try:
700 700 import certifi
701 701 certs = certifi.where()
702 702 if os.path.exists(certs):
703 703 ui.debug('using ca certificates from certifi\n')
704 704 return certs
705 705 except (ImportError, AttributeError):
706 706 pass
707 707
708 708 # On Windows, only the modern ssl module is capable of loading the system
709 709 # CA certificates. If we're not capable of doing that, emit a warning
710 710 # because we'll get a certificate verification error later and the lack
711 711 # of loaded CA certificates will be the reason why.
712 712 # Assertion: this code is only called if certificates are being verified.
713 713 if pycompat.osname == 'nt':
714 714 if not _canloaddefaultcerts:
715 715 ui.warn(_('(unable to load Windows CA certificates; see '
716 716 'https://mercurial-scm.org/wiki/SecureConnections for '
717 717 'how to configure Mercurial to avoid this message)\n'))
718 718
719 719 return None
720 720
721 721 # Apple's OpenSSL has patches that allow a specially constructed certificate
722 722 # to load the system CA store. If we're running on Apple Python, use this
723 723 # trick.
724 724 if _plainapplepython():
725 725 dummycert = os.path.join(
726 726 os.path.dirname(pycompat.fsencode(__file__)), 'dummycert.pem')
727 727 if os.path.exists(dummycert):
728 728 return dummycert
729 729
730 730 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
731 731 # load system certs, we're out of luck.
732 732 if pycompat.sysplatform == 'darwin':
733 733 # FUTURE Consider looking for Homebrew or MacPorts installed certs
734 734 # files. Also consider exporting the keychain certs to a file during
735 735 # Mercurial install.
736 736 if not _canloaddefaultcerts:
737 737 ui.warn(_('(unable to load CA certificates; see '
738 738 'https://mercurial-scm.org/wiki/SecureConnections for '
739 739 'how to configure Mercurial to avoid this message)\n'))
740 740 return None
741 741
742 742 # / is writable on Windows. Out of an abundance of caution make sure
743 743 # we're not on Windows because paths from _systemcacerts could be installed
744 744 # by non-admin users.
745 745 assert pycompat.osname != 'nt'
746 746
747 747 # Try to find CA certificates in well-known locations. We print a warning
748 748 # when using a found file because we don't want too much silent magic
749 749 # for security settings. The expectation is that proper Mercurial
750 750 # installs will have the CA certs path defined at install time and the
751 751 # installer/packager will make an appropriate decision on the user's
752 752 # behalf. We only get here and perform this setting as a feature of
753 753 # last resort.
754 754 if not _canloaddefaultcerts:
755 755 for path in _systemcacertpaths:
756 756 if os.path.isfile(path):
757 757 ui.warn(_('(using CA certificates from %s; if you see this '
758 758 'message, your Mercurial install is not properly '
759 759 'configured; see '
760 760 'https://mercurial-scm.org/wiki/SecureConnections '
761 761 'for how to configure Mercurial to avoid this '
762 762 'message)\n') % path)
763 763 return path
764 764
765 765 ui.warn(_('(unable to load CA certificates; see '
766 766 'https://mercurial-scm.org/wiki/SecureConnections for '
767 767 'how to configure Mercurial to avoid this message)\n'))
768 768
769 769 return None
770 770
771 771 def validatesocket(sock):
772 772 """Validate a socket meets security requirements.
773 773
774 774 The passed socket must have been created with ``wrapsocket()``.
775 775 """
776 776 host = sock._hgstate['hostname']
777 777 ui = sock._hgstate['ui']
778 778 settings = sock._hgstate['settings']
779 779
780 780 try:
781 781 peercert = sock.getpeercert(True)
782 782 peercert2 = sock.getpeercert()
783 783 except AttributeError:
784 784 raise error.Abort(_('%s ssl connection error') % host)
785 785
786 786 if not peercert:
787 787 raise error.Abort(_('%s certificate error: '
788 788 'no certificate received') % host)
789 789
790 790 if settings['disablecertverification']:
791 791 # We don't print the certificate fingerprint because it shouldn't
792 792 # be necessary: if the user requested certificate verification be
793 793 # disabled, they presumably already saw a message about the inability
794 794 # to verify the certificate and this message would have printed the
795 795 # fingerprint. So printing the fingerprint here adds little to no
796 796 # value.
797 797 ui.warn(_('warning: connection security to %s is disabled per current '
798 798 'settings; communication is susceptible to eavesdropping '
799 799 'and tampering\n') % host)
800 800 return
801 801
802 802 # If a certificate fingerprint is pinned, use it and only it to
803 803 # validate the remote cert.
804 804 peerfingerprints = {
805 805 'sha1': hashlib.sha1(peercert).hexdigest(),
806 806 'sha256': hashlib.sha256(peercert).hexdigest(),
807 807 'sha512': hashlib.sha512(peercert).hexdigest(),
808 808 }
809 809
810 810 def fmtfingerprint(s):
811 811 return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
812 812
813 813 nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
814 814
815 815 if settings['certfingerprints']:
816 816 for hash, fingerprint in settings['certfingerprints']:
817 817 if peerfingerprints[hash].lower() == fingerprint:
818 818 ui.debug('%s certificate matched fingerprint %s:%s\n' %
819 819 (host, hash, fmtfingerprint(fingerprint)))
820 820 if settings['legacyfingerprint']:
821 821 ui.warn(_('(SHA-1 fingerprint for %s found in legacy '
822 822 '[hostfingerprints] section; '
823 823 'if you trust this fingerprint, set the '
824 824 'following config value in [hostsecurity] and '
825 825 'remove the old one from [hostfingerprints] '
826 826 'to upgrade to a more secure SHA-256 '
827 827 'fingerprint: '
828 '%s.fingerprints=%s)\n') % (
828 '%s:fingerprints=%s)\n') % (
829 829 host, host, nicefingerprint))
830 830 return
831 831
832 832 # Pinned fingerprint didn't match. This is a fatal error.
833 833 if settings['legacyfingerprint']:
834 834 section = 'hostfingerprint'
835 835 nice = fmtfingerprint(peerfingerprints['sha1'])
836 836 else:
837 837 section = 'hostsecurity'
838 838 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
839 839 raise error.Abort(_('certificate for %s has unexpected '
840 840 'fingerprint %s') % (host, nice),
841 841 hint=_('check %s configuration') % section)
842 842
843 843 # Security is enabled but no CAs are loaded. We can't establish trust
844 844 # for the cert so abort.
845 845 if not sock._hgstate['caloaded']:
846 846 raise error.Abort(
847 847 _('unable to verify security of %s (no loaded CA certificates); '
848 848 'refusing to connect') % host,
849 849 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
850 850 'how to configure Mercurial to avoid this error or set '
851 851 'hostsecurity.%s:fingerprints=%s to trust this server') %
852 852 (host, nicefingerprint))
853 853
854 854 msg = _verifycert(peercert2, host)
855 855 if msg:
856 856 raise error.Abort(_('%s certificate error: %s') % (host, msg),
857 857 hint=_('set hostsecurity.%s:certfingerprints=%s '
858 858 'config setting or use --insecure to connect '
859 859 'insecurely') %
860 860 (host, nicefingerprint))
@@ -1,636 +1,636 b''
1 1 from __future__ import absolute_import
2 2
3 3 import errno
4 4 import os
5 5 import re
6 6 import socket
7 7 import stat
8 8 import subprocess
9 9 import sys
10 10 import tempfile
11 11
12 12 tempprefix = 'hg-hghave-'
13 13
14 14 checks = {
15 15 "true": (lambda: True, "yak shaving"),
16 16 "false": (lambda: False, "nail clipper"),
17 17 }
18 18
19 19 def check(name, desc):
20 20 """Registers a check function for a feature."""
21 21 def decorator(func):
22 22 checks[name] = (func, desc)
23 23 return func
24 24 return decorator
25 25
26 26 def checkvers(name, desc, vers):
27 27 """Registers a check function for each of a series of versions.
28 28
29 29 vers can be a list or an iterator"""
30 30 def decorator(func):
31 31 def funcv(v):
32 32 def f():
33 33 return func(v)
34 34 return f
35 35 for v in vers:
36 36 v = str(v)
37 37 f = funcv(v)
38 38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
39 39 return func
40 40 return decorator
41 41
42 42 def checkfeatures(features):
43 43 result = {
44 44 'error': [],
45 45 'missing': [],
46 46 'skipped': [],
47 47 }
48 48
49 49 for feature in features:
50 50 negate = feature.startswith('no-')
51 51 if negate:
52 52 feature = feature[3:]
53 53
54 54 if feature not in checks:
55 55 result['missing'].append(feature)
56 56 continue
57 57
58 58 check, desc = checks[feature]
59 59 try:
60 60 available = check()
61 61 except Exception:
62 62 result['error'].append('hghave check failed: %s' % feature)
63 63 continue
64 64
65 65 if not negate and not available:
66 66 result['skipped'].append('missing feature: %s' % desc)
67 67 elif negate and available:
68 68 result['skipped'].append('system supports %s' % desc)
69 69
70 70 return result
71 71
72 72 def require(features):
73 73 """Require that features are available, exiting if not."""
74 74 result = checkfeatures(features)
75 75
76 76 for missing in result['missing']:
77 77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
78 78 for msg in result['skipped']:
79 79 sys.stderr.write('skipped: %s\n' % msg)
80 80 for msg in result['error']:
81 81 sys.stderr.write('%s\n' % msg)
82 82
83 83 if result['missing']:
84 84 sys.exit(2)
85 85
86 86 if result['skipped'] or result['error']:
87 87 sys.exit(1)
88 88
89 89 def matchoutput(cmd, regexp, ignorestatus=False):
90 90 """Return the match object if cmd executes successfully and its output
91 91 is matched by the supplied regular expression.
92 92 """
93 93 r = re.compile(regexp)
94 94 try:
95 95 p = subprocess.Popen(
96 96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
97 97 except OSError as e:
98 98 if e.errno != errno.ENOENT:
99 99 raise
100 100 ret = -1
101 101 ret = p.wait()
102 102 s = p.stdout.read()
103 103 return (ignorestatus or not ret) and r.search(s)
104 104
105 105 @check("baz", "GNU Arch baz client")
106 106 def has_baz():
107 107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
108 108
109 109 @check("bzr", "Canonical's Bazaar client")
110 110 def has_bzr():
111 111 try:
112 112 import bzrlib
113 113 import bzrlib.bzrdir
114 114 import bzrlib.errors
115 115 import bzrlib.revision
116 116 import bzrlib.revisionspec
117 117 bzrlib.revisionspec.RevisionSpec
118 118 return bzrlib.__doc__ is not None
119 119 except (AttributeError, ImportError):
120 120 return False
121 121
122 122 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
123 123 def has_bzr_range(v):
124 124 major, minor = v.split('.')[0:2]
125 125 try:
126 126 import bzrlib
127 127 return (bzrlib.__doc__ is not None
128 128 and bzrlib.version_info[:2] >= (int(major), int(minor)))
129 129 except ImportError:
130 130 return False
131 131
132 132 @check("chg", "running with chg")
133 133 def has_chg():
134 134 return 'CHGHG' in os.environ
135 135
136 136 @check("cvs", "cvs client/server")
137 137 def has_cvs():
138 138 re = br'Concurrent Versions System.*?server'
139 139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
140 140
141 141 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
142 142 def has_cvs112():
143 143 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
144 144 return matchoutput('cvs --version 2>&1', re) and not has_msys()
145 145
146 146 @check("cvsnt", "cvsnt client/server")
147 147 def has_cvsnt():
148 148 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
149 149 return matchoutput('cvsnt --version 2>&1', re)
150 150
151 151 @check("darcs", "darcs client")
152 152 def has_darcs():
153 153 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
154 154
155 155 @check("mtn", "monotone client (>= 1.0)")
156 156 def has_mtn():
157 157 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
158 158 'mtn --version', br'monotone 0\.', True)
159 159
160 160 @check("eol-in-paths", "end-of-lines in paths")
161 161 def has_eol_in_paths():
162 162 try:
163 163 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
164 164 os.close(fd)
165 165 os.remove(path)
166 166 return True
167 167 except (IOError, OSError):
168 168 return False
169 169
170 170 @check("execbit", "executable bit")
171 171 def has_executablebit():
172 172 try:
173 173 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
174 174 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
175 175 try:
176 176 os.close(fh)
177 177 m = os.stat(fn).st_mode & 0o777
178 178 new_file_has_exec = m & EXECFLAGS
179 179 os.chmod(fn, m ^ EXECFLAGS)
180 180 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
181 181 finally:
182 182 os.unlink(fn)
183 183 except (IOError, OSError):
184 184 # we don't care, the user probably won't be able to commit anyway
185 185 return False
186 186 return not (new_file_has_exec or exec_flags_cannot_flip)
187 187
188 188 @check("icasefs", "case insensitive file system")
189 189 def has_icasefs():
190 190 # Stolen from mercurial.util
191 191 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
192 192 os.close(fd)
193 193 try:
194 194 s1 = os.stat(path)
195 195 d, b = os.path.split(path)
196 196 p2 = os.path.join(d, b.upper())
197 197 if path == p2:
198 198 p2 = os.path.join(d, b.lower())
199 199 try:
200 200 s2 = os.stat(p2)
201 201 return s2 == s1
202 202 except OSError:
203 203 return False
204 204 finally:
205 205 os.remove(path)
206 206
207 207 @check("fifo", "named pipes")
208 208 def has_fifo():
209 209 if getattr(os, "mkfifo", None) is None:
210 210 return False
211 211 name = tempfile.mktemp(dir='.', prefix=tempprefix)
212 212 try:
213 213 os.mkfifo(name)
214 214 os.unlink(name)
215 215 return True
216 216 except OSError:
217 217 return False
218 218
219 219 @check("killdaemons", 'killdaemons.py support')
220 220 def has_killdaemons():
221 221 return True
222 222
223 223 @check("cacheable", "cacheable filesystem")
224 224 def has_cacheable_fs():
225 225 from mercurial import util
226 226
227 227 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
228 228 os.close(fd)
229 229 try:
230 230 return util.cachestat(path).cacheable()
231 231 finally:
232 232 os.remove(path)
233 233
234 234 @check("lsprof", "python lsprof module")
235 235 def has_lsprof():
236 236 try:
237 237 import _lsprof
238 238 _lsprof.Profiler # silence unused import warning
239 239 return True
240 240 except ImportError:
241 241 return False
242 242
243 243 def gethgversion():
244 244 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
245 245 if not m:
246 246 return (0, 0)
247 247 return (int(m.group(1)), int(m.group(2)))
248 248
249 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 251 def has_hg_range(v):
252 252 major, minor = v.split('.')[0:2]
253 253 return gethgversion() >= (int(major), int(minor))
254 254
255 255 @check("hg08", "Mercurial >= 0.8")
256 256 def has_hg08():
257 257 if checks["hg09"][0]():
258 258 return True
259 259 return matchoutput('hg help annotate 2>&1', '--date')
260 260
261 261 @check("hg07", "Mercurial >= 0.7")
262 262 def has_hg07():
263 263 if checks["hg08"][0]():
264 264 return True
265 265 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
266 266
267 267 @check("hg06", "Mercurial >= 0.6")
268 268 def has_hg06():
269 269 if checks["hg07"][0]():
270 270 return True
271 271 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
272 272
273 273 @check("gettext", "GNU Gettext (msgfmt)")
274 274 def has_gettext():
275 275 return matchoutput('msgfmt --version', br'GNU gettext-tools')
276 276
277 277 @check("git", "git command line client")
278 278 def has_git():
279 279 return matchoutput('git --version 2>&1', br'^git version')
280 280
281 281 @check("docutils", "Docutils text processing library")
282 282 def has_docutils():
283 283 try:
284 284 import docutils.core
285 285 docutils.core.publish_cmdline # silence unused import
286 286 return True
287 287 except ImportError:
288 288 return False
289 289
290 290 def getsvnversion():
291 291 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
292 292 if not m:
293 293 return (0, 0)
294 294 return (int(m.group(1)), int(m.group(2)))
295 295
296 296 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
297 297 def has_svn_range(v):
298 298 major, minor = v.split('.')[0:2]
299 299 return getsvnversion() >= (int(major), int(minor))
300 300
301 301 @check("svn", "subversion client and admin tools")
302 302 def has_svn():
303 303 return matchoutput('svn --version 2>&1', br'^svn, version') and \
304 304 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
305 305
306 306 @check("svn-bindings", "subversion python bindings")
307 307 def has_svn_bindings():
308 308 try:
309 309 import svn.core
310 310 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
311 311 if version < (1, 4):
312 312 return False
313 313 return True
314 314 except ImportError:
315 315 return False
316 316
317 317 @check("p4", "Perforce server and client")
318 318 def has_p4():
319 319 return (matchoutput('p4 -V', br'Rev\. P4/') and
320 320 matchoutput('p4d -V', br'Rev\. P4D/'))
321 321
322 322 @check("symlink", "symbolic links")
323 323 def has_symlink():
324 324 if getattr(os, "symlink", None) is None:
325 325 return False
326 326 name = tempfile.mktemp(dir='.', prefix=tempprefix)
327 327 try:
328 328 os.symlink(".", name)
329 329 os.unlink(name)
330 330 return True
331 331 except (OSError, AttributeError):
332 332 return False
333 333
334 334 @check("hardlink", "hardlinks")
335 335 def has_hardlink():
336 336 from mercurial import util
337 337 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
338 338 os.close(fh)
339 339 name = tempfile.mktemp(dir='.', prefix=tempprefix)
340 340 try:
341 341 util.oslink(fn, name)
342 342 os.unlink(name)
343 343 return True
344 344 except OSError:
345 345 return False
346 346 finally:
347 347 os.unlink(fn)
348 348
349 349 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
350 350 def has_hardlink_whitelisted():
351 351 from mercurial import util
352 352 try:
353 353 fstype = util.getfstype('.')
354 354 except OSError:
355 355 return False
356 356 return fstype in util._hardlinkfswhitelist
357 357
358 358 @check("rmcwd", "can remove current working directory")
359 359 def has_rmcwd():
360 360 ocwd = os.getcwd()
361 361 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
362 362 try:
363 363 os.chdir(temp)
364 364 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
365 365 # On Solaris and Windows, the cwd can't be removed by any names.
366 366 os.rmdir(os.getcwd())
367 367 return True
368 368 except OSError:
369 369 return False
370 370 finally:
371 371 os.chdir(ocwd)
372 372 # clean up temp dir on platforms where cwd can't be removed
373 373 try:
374 374 os.rmdir(temp)
375 375 except OSError:
376 376 pass
377 377
378 378 @check("tla", "GNU Arch tla client")
379 379 def has_tla():
380 380 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
381 381
382 382 @check("gpg", "gpg client")
383 383 def has_gpg():
384 384 return matchoutput('gpg --version 2>&1', br'GnuPG')
385 385
386 386 @check("gpg2", "gpg client v2")
387 387 def has_gpg2():
388 388 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
389 389
390 390 @check("gpg21", "gpg client v2.1+")
391 391 def has_gpg21():
392 392 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
393 393
394 394 @check("unix-permissions", "unix-style permissions")
395 395 def has_unix_permissions():
396 396 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
397 397 try:
398 398 fname = os.path.join(d, 'foo')
399 399 for umask in (0o77, 0o07, 0o22):
400 400 os.umask(umask)
401 401 f = open(fname, 'w')
402 402 f.close()
403 403 mode = os.stat(fname).st_mode
404 404 os.unlink(fname)
405 405 if mode & 0o777 != ~umask & 0o666:
406 406 return False
407 407 return True
408 408 finally:
409 409 os.rmdir(d)
410 410
411 411 @check("unix-socket", "AF_UNIX socket family")
412 412 def has_unix_socket():
413 413 return getattr(socket, 'AF_UNIX', None) is not None
414 414
415 415 @check("root", "root permissions")
416 416 def has_root():
417 417 return getattr(os, 'geteuid', None) and os.geteuid() == 0
418 418
419 419 @check("pyflakes", "Pyflakes python linter")
420 420 def has_pyflakes():
421 421 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
422 422 br"<stdin>:1: 're' imported but unused",
423 423 True)
424 424
425 425 @check("pylint", "Pylint python linter")
426 426 def has_pylint():
427 427 return matchoutput("pylint --help",
428 428 br"Usage: pylint",
429 429 True)
430 430
431 431 @check("pygments", "Pygments source highlighting library")
432 432 def has_pygments():
433 433 try:
434 434 import pygments
435 435 pygments.highlight # silence unused import warning
436 436 return True
437 437 except ImportError:
438 438 return False
439 439
440 440 @check("outer-repo", "outer repo")
441 441 def has_outer_repo():
442 442 # failing for other reasons than 'no repo' imply that there is a repo
443 443 return not matchoutput('hg root 2>&1',
444 444 br'abort: no repository found', True)
445 445
446 446 @check("ssl", "ssl module available")
447 447 def has_ssl():
448 448 try:
449 449 import ssl
450 450 ssl.CERT_NONE
451 451 return True
452 452 except ImportError:
453 453 return False
454 454
455 455 @check("sslcontext", "python >= 2.7.9 ssl")
456 456 def has_sslcontext():
457 457 try:
458 458 import ssl
459 459 ssl.SSLContext
460 460 return True
461 461 except (ImportError, AttributeError):
462 462 return False
463 463
464 464 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
465 465 def has_defaultcacerts():
466 466 from mercurial import sslutil, ui as uimod
467 467 ui = uimod.ui.load()
468 468 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
469 469
470 470 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
471 471 def has_defaultcacertsloaded():
472 472 import ssl
473 473 from mercurial import sslutil, ui as uimod
474 474
475 475 if not has_defaultcacerts():
476 476 return False
477 477 if not has_sslcontext():
478 478 return False
479 479
480 480 ui = uimod.ui.load()
481 481 cafile = sslutil._defaultcacerts(ui)
482 482 ctx = ssl.create_default_context()
483 483 if cafile:
484 484 ctx.load_verify_locations(cafile=cafile)
485 485 else:
486 486 ctx.load_default_certs()
487 487
488 488 return len(ctx.get_ca_certs()) > 0
489 489
490 490 @check("tls1.2", "TLS 1.2 protocol support")
491 491 def has_tls1_2():
492 492 from mercurial import sslutil
493 493 return 'tls1.2' in sslutil.supportedprotocols
494 494
495 495 @check("windows", "Windows")
496 496 def has_windows():
497 497 return os.name == 'nt'
498 498
499 499 @check("system-sh", "system() uses sh")
500 500 def has_system_sh():
501 501 return os.name != 'nt'
502 502
503 503 @check("serve", "platform and python can manage 'hg serve -d'")
504 504 def has_serve():
505 505 return os.name != 'nt' # gross approximation
506 506
507 507 @check("test-repo", "running tests from repository")
508 508 def has_test_repo():
509 509 t = os.environ["TESTDIR"]
510 510 return os.path.isdir(os.path.join(t, "..", ".hg"))
511 511
512 512 @check("tic", "terminfo compiler and curses module")
513 513 def has_tic():
514 514 try:
515 515 import curses
516 516 curses.COLOR_BLUE
517 517 return matchoutput('test -x "`which tic`"', br'')
518 518 except ImportError:
519 519 return False
520 520
521 521 @check("msys", "Windows with MSYS")
522 522 def has_msys():
523 523 return os.getenv('MSYSTEM')
524 524
525 525 @check("aix", "AIX")
526 526 def has_aix():
527 527 return sys.platform.startswith("aix")
528 528
529 529 @check("osx", "OS X")
530 530 def has_osx():
531 531 return sys.platform == 'darwin'
532 532
533 533 @check("osxpackaging", "OS X packaging tools")
534 534 def has_osxpackaging():
535 535 try:
536 536 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
537 537 and matchoutput(
538 538 'productbuild', br'Usage: productbuild ',
539 539 ignorestatus=1)
540 540 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
541 541 and matchoutput(
542 542 'xar --help', br'Usage: xar', ignorestatus=1))
543 543 except ImportError:
544 544 return False
545 545
546 546 @check("docker", "docker support")
547 547 def has_docker():
548 548 pat = br'A self-sufficient runtime for'
549 549 if matchoutput('docker --help', pat):
550 550 if 'linux' not in sys.platform:
551 551 # TODO: in theory we should be able to test docker-based
552 552 # package creation on non-linux using boot2docker, but in
553 553 # practice that requires extra coordination to make sure
554 554 # $TESTTEMP is going to be visible at the same path to the
555 555 # boot2docker VM. If we figure out how to verify that, we
556 556 # can use the following instead of just saying False:
557 557 # return 'DOCKER_HOST' in os.environ
558 558 return False
559 559
560 560 return True
561 561 return False
562 562
563 563 @check("debhelper", "debian packaging tools")
564 564 def has_debhelper():
565 565 dpkg = matchoutput('dpkg --version',
566 566 br"Debian `dpkg' package management program")
567 567 dh = matchoutput('dh --help',
568 568 br'dh is a part of debhelper.', ignorestatus=True)
569 569 dh_py2 = matchoutput('dh_python2 --help',
570 570 br'other supported Python versions')
571 571 return dpkg and dh and dh_py2
572 572
573 573 @check("demandimport", "demandimport enabled")
574 574 def has_demandimport():
575 575 return os.environ.get('HGDEMANDIMPORT') != 'disable'
576 576
577 577 @check("absimport", "absolute_import in __future__")
578 578 def has_absimport():
579 579 import __future__
580 580 from mercurial import util
581 581 return util.safehasattr(__future__, "absolute_import")
582 582
583 583 @check("py3k", "running with Python 3.x")
584 584 def has_py3k():
585 585 return 3 == sys.version_info[0]
586 586
587 587 @check("py3exe", "a Python 3.x interpreter is available")
588 588 def has_python3exe():
589 589 return 'PYTHON3' in os.environ
590 590
591 591 @check("py3pygments", "Pygments available on Python 3.x")
592 592 def has_py3pygments():
593 593 if has_py3k():
594 594 return has_pygments()
595 595 elif has_python3exe():
596 596 # just check exit status (ignoring output)
597 597 py3 = os.environ['PYTHON3']
598 598 return matchoutput('%s -c "import pygments"' % py3, br'')
599 599 return False
600 600
601 601 @check("pure", "running with pure Python code")
602 602 def has_pure():
603 603 return any([
604 604 os.environ.get("HGMODULEPOLICY") == "py",
605 605 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
606 606 ])
607 607
608 608 @check("slow", "allow slow tests")
609 609 def has_slow():
610 610 return os.environ.get('HGTEST_SLOW') == 'slow'
611 611
612 612 @check("hypothesis", "Hypothesis automated test generation")
613 613 def has_hypothesis():
614 614 try:
615 615 import hypothesis
616 616 hypothesis.given
617 617 return True
618 618 except ImportError:
619 619 return False
620 620
621 621 @check("unziplinks", "unzip(1) understands and extracts symlinks")
622 622 def unzip_understands_symlinks():
623 623 return matchoutput('unzip --help', br'Info-ZIP')
624 624
625 625 @check("zstd", "zstd Python module available")
626 626 def has_zstd():
627 627 try:
628 628 import mercurial.zstd
629 629 mercurial.zstd.__version__
630 630 return True
631 631 except ImportError:
632 632 return False
633 633
634 634 @check("devfull", "/dev/full special file")
635 635 def has_dev_full():
636 636 return os.path.exists('/dev/full')
@@ -1,1310 +1,1354 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Can't continue without starting:
50 50
51 51 $ hg rm -q e
52 52 $ hg graft --continue
53 53 abort: no graft in progress
54 54 [255]
55 55 $ hg revert -r . -q e
56 56
57 57 Need to specify a rev:
58 58
59 59 $ hg graft
60 60 abort: no revisions specified
61 61 [255]
62 62
63 63 Can't graft ancestor:
64 64
65 65 $ hg graft 1 2
66 66 skipping ancestor revision 1:5d205f8b35b6
67 67 skipping ancestor revision 2:5c095ad7e90f
68 68 [255]
69 69
70 70 Specify revisions with -r:
71 71
72 72 $ hg graft -r 1 -r 2
73 73 skipping ancestor revision 1:5d205f8b35b6
74 74 skipping ancestor revision 2:5c095ad7e90f
75 75 [255]
76 76
77 77 $ hg graft -r 1 2
78 78 warning: inconsistent use of --rev might give unexpected revision ordering!
79 79 skipping ancestor revision 2:5c095ad7e90f
80 80 skipping ancestor revision 1:5d205f8b35b6
81 81 [255]
82 82
83 83 Can't graft with dirty wd:
84 84
85 85 $ hg up -q 0
86 86 $ echo foo > a
87 87 $ hg graft 1
88 88 abort: uncommitted changes
89 89 [255]
90 90 $ hg revert a
91 91
92 92 Graft a rename:
93 93 (this also tests that editor is invoked if '--edit' is specified)
94 94
95 95 $ hg status --rev "2^1" --rev 2
96 96 A b
97 97 R a
98 98 $ HGEDITOR=cat hg graft 2 -u foo --edit
99 99 grafting 2:5c095ad7e90f "2"
100 100 merging a and b to b
101 101 2
102 102
103 103
104 104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
105 105 HG: Leave message empty to abort commit.
106 106 HG: --
107 107 HG: user: foo
108 108 HG: branch 'default'
109 109 HG: added b
110 110 HG: removed a
111 111 $ hg export tip --git
112 112 # HG changeset patch
113 113 # User foo
114 114 # Date 0 0
115 115 # Thu Jan 01 00:00:00 1970 +0000
116 116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
117 117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
118 118 2
119 119
120 120 diff --git a/a b/b
121 121 rename from a
122 122 rename to b
123 123
124 124 Look for extra:source
125 125
126 126 $ hg log --debug -r tip
127 127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
128 128 tag: tip
129 129 phase: draft
130 130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
131 131 parent: -1:0000000000000000000000000000000000000000
132 132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
133 133 user: foo
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 files+: b
136 136 files-: a
137 137 extra: branch=default
138 138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
139 139 description:
140 140 2
141 141
142 142
143 143
144 144 Graft out of order, skipping a merge and a duplicate
145 145 (this also tests that editor is not invoked if '--edit' is not specified)
146 146
147 147 $ hg graft 1 5 4 3 'merge()' 2 -n
148 148 skipping ungraftable merge revision 6
149 149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
150 150 grafting 1:5d205f8b35b6 "1"
151 151 grafting 5:97f8bfe72746 "5"
152 152 grafting 4:9c233e8e184d "4"
153 153 grafting 3:4c60f11aa304 "3"
154 154
155 155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
156 156 skipping ungraftable merge revision 6
157 157 scanning for duplicate grafts
158 158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
159 159 grafting 1:5d205f8b35b6 "1"
160 160 searching for copies back to rev 1
161 161 unmatched files in local:
162 162 b
163 163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 164 src: 'a' -> dst: 'b' *
165 165 checking for directory renames
166 166 resolving manifests
167 167 branchmerge: True, force: True, partial: False
168 168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
169 169 preserving b for resolve of b
170 170 starting 4 threads for background file closing (?)
171 171 b: local copied/moved from a -> m (premerge)
172 172 picked tool ':merge' for b (binary False symlink False changedelete False)
173 173 merging b and a to b
174 174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
175 175 premerge successful
176 176 committing files:
177 177 b
178 178 committing manifest
179 179 committing changelog
180 180 grafting 5:97f8bfe72746 "5"
181 181 searching for copies back to rev 1
182 182 unmatched files in other (from topological common ancestor):
183 183 c
184 184 resolving manifests
185 185 branchmerge: True, force: True, partial: False
186 186 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
187 187 e: remote is newer -> g
188 188 getting e
189 189 committing files:
190 190 e
191 191 committing manifest
192 192 committing changelog
193 193 $ HGEDITOR=cat hg graft 4 3 --log --debug
194 194 scanning for duplicate grafts
195 195 grafting 4:9c233e8e184d "4"
196 196 searching for copies back to rev 1
197 197 unmatched files in other (from topological common ancestor):
198 198 c
199 199 resolving manifests
200 200 branchmerge: True, force: True, partial: False
201 201 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
202 202 preserving e for resolve of e
203 203 d: remote is newer -> g
204 204 getting d
205 205 e: versions differ -> m (premerge)
206 206 picked tool ':merge' for e (binary False symlink False changedelete False)
207 207 merging e
208 208 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
209 209 e: versions differ -> m (merge)
210 210 picked tool ':merge' for e (binary False symlink False changedelete False)
211 211 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
212 212 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
213 213 abort: unresolved conflicts, can't continue
214 214 (use 'hg resolve' and 'hg graft --continue --log')
215 215 [255]
216 216
217 217 Summary should mention graft:
218 218
219 219 $ hg summary |grep graft
220 220 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
221 221
222 222 Commit while interrupted should fail:
223 223
224 224 $ hg ci -m 'commit interrupted graft'
225 225 abort: graft in progress
226 226 (use 'hg graft --continue' or 'hg update' to abort)
227 227 [255]
228 228
229 229 Abort the graft and try committing:
230 230
231 231 $ hg up -C .
232 232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 233 $ echo c >> e
234 234 $ hg ci -mtest
235 235
236 236 $ hg strip . --config extensions.strip=
237 237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 238 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
239 239
240 240 Graft again:
241 241
242 242 $ hg graft 1 5 4 3 'merge()' 2
243 243 skipping ungraftable merge revision 6
244 244 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
245 245 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
246 246 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
247 247 grafting 4:9c233e8e184d "4"
248 248 merging e
249 249 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
250 250 abort: unresolved conflicts, can't continue
251 251 (use 'hg resolve' and 'hg graft --continue')
252 252 [255]
253 253
254 254 Continue without resolve should fail:
255 255
256 256 $ hg graft -c
257 257 grafting 4:9c233e8e184d "4"
258 258 abort: unresolved merge conflicts (see 'hg help resolve')
259 259 [255]
260 260
261 261 Fix up:
262 262
263 263 $ echo b > e
264 264 $ hg resolve -m e
265 265 (no more unresolved files)
266 266 continue: hg graft --continue
267 267
268 268 Continue with a revision should fail:
269 269
270 270 $ hg graft -c 6
271 271 abort: can't specify --continue and revisions
272 272 [255]
273 273
274 274 $ hg graft -c -r 6
275 275 abort: can't specify --continue and revisions
276 276 [255]
277 277
278 278 Continue for real, clobber usernames
279 279
280 280 $ hg graft -c -U
281 281 grafting 4:9c233e8e184d "4"
282 282 grafting 3:4c60f11aa304 "3"
283 283
284 284 Compare with original:
285 285
286 286 $ hg diff -r 6
287 287 $ hg status --rev 0:. -C
288 288 M d
289 289 M e
290 290 A b
291 291 a
292 292 A c
293 293 a
294 294 R a
295 295
296 296 View graph:
297 297
298 298 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
299 299 @ test@11.draft: 3
300 300 |
301 301 o test@10.draft: 4
302 302 |
303 303 o test@9.draft: 5
304 304 |
305 305 o bar@8.draft: 1
306 306 |
307 307 o foo@7.draft: 2
308 308 |
309 309 | o test@6.secret: 6
310 310 | |\
311 311 | | o test@5.draft: 5
312 312 | | |
313 313 | o | test@4.draft: 4
314 314 | |/
315 315 | o baz@3.public: 3
316 316 | |
317 317 | o test@2.public: 2
318 318 | |
319 319 | o bar@1.public: 1
320 320 |/
321 321 o test@0.public: 0
322 322
323 323 Graft again onto another branch should preserve the original source
324 324 $ hg up -q 0
325 325 $ echo 'g'>g
326 326 $ hg add g
327 327 $ hg ci -m 7
328 328 created new head
329 329 $ hg graft 7
330 330 grafting 7:ef0ef43d49e7 "2"
331 331
332 332 $ hg log -r 7 --template '{rev}:{node}\n'
333 333 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
334 334 $ hg log -r 2 --template '{rev}:{node}\n'
335 335 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
336 336
337 337 $ hg log --debug -r tip
338 338 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
339 339 tag: tip
340 340 phase: draft
341 341 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
342 342 parent: -1:0000000000000000000000000000000000000000
343 343 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
344 344 user: foo
345 345 date: Thu Jan 01 00:00:00 1970 +0000
346 346 files+: b
347 347 files-: a
348 348 extra: branch=default
349 349 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
350 350 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
351 351 description:
352 352 2
353 353
354 354
355 355 Disallow grafting an already grafted cset onto its original branch
356 356 $ hg up -q 6
357 357 $ hg graft 7
358 358 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
359 359 [255]
360 360
361 361 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
362 362 --- */hg-5c095ad7e90f.patch * (glob)
363 363 +++ */hg-7a4785234d87.patch * (glob)
364 364 @@ -1,18 +1,18 @@
365 365 # HG changeset patch
366 366 -# User test
367 367 +# User foo
368 368 # Date 0 0
369 369 # Thu Jan 01 00:00:00 1970 +0000
370 370 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
371 371 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
372 372 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
373 373 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
374 374 2
375 375
376 376 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
377 377 +diff -r b592ea63bb0c -r 7a4785234d87 a
378 378 --- a/a Thu Jan 01 00:00:00 1970 +0000
379 379 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
380 380 @@ -1,1 +0,0 @@
381 381 --b
382 382 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
383 383 +-a
384 384 +diff -r b592ea63bb0c -r 7a4785234d87 b
385 385 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
386 386 +++ b/b Thu Jan 01 00:00:00 1970 +0000
387 387 @@ -0,0 +1,1 @@
388 388 -+b
389 389 ++a
390 390 [1]
391 391
392 392 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
393 393 --- */hg-5c095ad7e90f.patch * (glob)
394 394 +++ */hg-7a4785234d87.patch * (glob)
395 395 @@ -1,8 +1,8 @@
396 396 # HG changeset patch
397 397 -# User test
398 398 +# User foo
399 399 # Date 0 0
400 400 # Thu Jan 01 00:00:00 1970 +0000
401 401 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
402 402 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
403 403 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
404 404 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
405 405 2
406 406
407 407 [1]
408 408
409 409 Disallow grafting already grafted csets with the same origin onto each other
410 410 $ hg up -q 13
411 411 $ hg graft 2
412 412 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
413 413 [255]
414 414 $ hg graft 7
415 415 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
416 416 [255]
417 417
418 418 $ hg up -q 7
419 419 $ hg graft 2
420 420 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
421 421 [255]
422 422 $ hg graft tip
423 423 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
424 424 [255]
425 425
426 426 Graft with --log
427 427
428 428 $ hg up -Cq 1
429 429 $ hg graft 3 --log -u foo
430 430 grafting 3:4c60f11aa304 "3"
431 431 warning: can't find ancestor for 'c' copied from 'b'!
432 432 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
433 433 14:0c921c65ef1e 1:5d205f8b35b6 3
434 434 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
435 435
436 436 Resolve conflicted graft
437 437 $ hg up -q 0
438 438 $ echo b > a
439 439 $ hg ci -m 8
440 440 created new head
441 441 $ echo c > a
442 442 $ hg ci -m 9
443 443 $ hg graft 1 --tool internal:fail
444 444 grafting 1:5d205f8b35b6 "1"
445 445 abort: unresolved conflicts, can't continue
446 446 (use 'hg resolve' and 'hg graft --continue')
447 447 [255]
448 448 $ hg resolve --all
449 449 merging a
450 450 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
451 451 [1]
452 452 $ cat a
453 453 <<<<<<< local: aaa4406d4f0a - test: 9
454 454 c
455 455 =======
456 456 b
457 457 >>>>>>> graft: 5d205f8b35b6 - bar: 1
458 458 $ echo b > a
459 459 $ hg resolve -m a
460 460 (no more unresolved files)
461 461 continue: hg graft --continue
462 462 $ hg graft -c
463 463 grafting 1:5d205f8b35b6 "1"
464 464 $ hg export tip --git
465 465 # HG changeset patch
466 466 # User bar
467 467 # Date 0 0
468 468 # Thu Jan 01 00:00:00 1970 +0000
469 469 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
470 470 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
471 471 1
472 472
473 473 diff --git a/a b/a
474 474 --- a/a
475 475 +++ b/a
476 476 @@ -1,1 +1,1 @@
477 477 -c
478 478 +b
479 479
480 480 Resolve conflicted graft with rename
481 481 $ echo c > a
482 482 $ hg ci -m 10
483 483 $ hg graft 2 --tool internal:fail
484 484 grafting 2:5c095ad7e90f "2"
485 485 abort: unresolved conflicts, can't continue
486 486 (use 'hg resolve' and 'hg graft --continue')
487 487 [255]
488 488 $ hg resolve --all
489 489 merging a and b to b
490 490 (no more unresolved files)
491 491 continue: hg graft --continue
492 492 $ hg graft -c
493 493 grafting 2:5c095ad7e90f "2"
494 494 $ hg export tip --git
495 495 # HG changeset patch
496 496 # User test
497 497 # Date 0 0
498 498 # Thu Jan 01 00:00:00 1970 +0000
499 499 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
500 500 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
501 501 2
502 502
503 503 diff --git a/a b/b
504 504 rename from a
505 505 rename to b
506 506
507 507 Test simple origin(), with and without args
508 508 $ hg log -r 'origin()'
509 509 changeset: 1:5d205f8b35b6
510 510 user: bar
511 511 date: Thu Jan 01 00:00:00 1970 +0000
512 512 summary: 1
513 513
514 514 changeset: 2:5c095ad7e90f
515 515 user: test
516 516 date: Thu Jan 01 00:00:00 1970 +0000
517 517 summary: 2
518 518
519 519 changeset: 3:4c60f11aa304
520 520 user: baz
521 521 date: Thu Jan 01 00:00:00 1970 +0000
522 522 summary: 3
523 523
524 524 changeset: 4:9c233e8e184d
525 525 user: test
526 526 date: Thu Jan 01 00:00:00 1970 +0000
527 527 summary: 4
528 528
529 529 changeset: 5:97f8bfe72746
530 530 branch: stable
531 531 parent: 3:4c60f11aa304
532 532 user: test
533 533 date: Thu Jan 01 00:00:00 1970 +0000
534 534 summary: 5
535 535
536 536 $ hg log -r 'origin(7)'
537 537 changeset: 2:5c095ad7e90f
538 538 user: test
539 539 date: Thu Jan 01 00:00:00 1970 +0000
540 540 summary: 2
541 541
542 542 Now transplant a graft to test following through copies
543 543 $ hg up -q 0
544 544 $ hg branch -q dev
545 545 $ hg ci -qm "dev branch"
546 546 $ hg --config extensions.transplant= transplant -q 7
547 547 $ hg log -r 'origin(.)'
548 548 changeset: 2:5c095ad7e90f
549 549 user: test
550 550 date: Thu Jan 01 00:00:00 1970 +0000
551 551 summary: 2
552 552
553 553 Test that the graft and transplant markers in extra are converted, allowing
554 554 origin() to still work. Note that these recheck the immediately preceeding two
555 555 tests.
556 556 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
557 557
558 558 The graft case
559 559 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
560 560 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
561 561 branch=default
562 562 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
563 563 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
564 564 $ hg -R ../converted log -r 'origin(7)'
565 565 changeset: 2:e0213322b2c1
566 566 user: test
567 567 date: Thu Jan 01 00:00:00 1970 +0000
568 568 summary: 2
569 569
570 570 Test that template correctly expands more than one 'extra' (issue4362), and that
571 571 'intermediate-source' is converted.
572 572 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
573 573 Extra: branch=default
574 574 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
575 575 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
576 576 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
577 577
578 578 The transplant case
579 579 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
580 580 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
581 581 branch=dev
582 582 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
583 583 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
584 584 $ hg -R ../converted log -r 'origin(tip)'
585 585 changeset: 2:e0213322b2c1
586 586 user: test
587 587 date: Thu Jan 01 00:00:00 1970 +0000
588 588 summary: 2
589 589
590 590
591 591 Test simple destination
592 592 $ hg log -r 'destination()'
593 593 changeset: 7:ef0ef43d49e7
594 594 parent: 0:68795b066622
595 595 user: foo
596 596 date: Thu Jan 01 00:00:00 1970 +0000
597 597 summary: 2
598 598
599 599 changeset: 8:6b9e5368ca4e
600 600 user: bar
601 601 date: Thu Jan 01 00:00:00 1970 +0000
602 602 summary: 1
603 603
604 604 changeset: 9:1905859650ec
605 605 user: test
606 606 date: Thu Jan 01 00:00:00 1970 +0000
607 607 summary: 5
608 608
609 609 changeset: 10:52dc0b4c6907
610 610 user: test
611 611 date: Thu Jan 01 00:00:00 1970 +0000
612 612 summary: 4
613 613
614 614 changeset: 11:882b35362a6b
615 615 user: test
616 616 date: Thu Jan 01 00:00:00 1970 +0000
617 617 summary: 3
618 618
619 619 changeset: 13:7a4785234d87
620 620 user: foo
621 621 date: Thu Jan 01 00:00:00 1970 +0000
622 622 summary: 2
623 623
624 624 changeset: 14:0c921c65ef1e
625 625 parent: 1:5d205f8b35b6
626 626 user: foo
627 627 date: Thu Jan 01 00:00:00 1970 +0000
628 628 summary: 3
629 629
630 630 changeset: 17:f67661df0c48
631 631 user: bar
632 632 date: Thu Jan 01 00:00:00 1970 +0000
633 633 summary: 1
634 634
635 635 changeset: 19:9627f653b421
636 636 user: test
637 637 date: Thu Jan 01 00:00:00 1970 +0000
638 638 summary: 2
639 639
640 640 changeset: 21:7e61b508e709
641 641 branch: dev
642 642 tag: tip
643 643 user: foo
644 644 date: Thu Jan 01 00:00:00 1970 +0000
645 645 summary: 2
646 646
647 647 $ hg log -r 'destination(2)'
648 648 changeset: 7:ef0ef43d49e7
649 649 parent: 0:68795b066622
650 650 user: foo
651 651 date: Thu Jan 01 00:00:00 1970 +0000
652 652 summary: 2
653 653
654 654 changeset: 13:7a4785234d87
655 655 user: foo
656 656 date: Thu Jan 01 00:00:00 1970 +0000
657 657 summary: 2
658 658
659 659 changeset: 19:9627f653b421
660 660 user: test
661 661 date: Thu Jan 01 00:00:00 1970 +0000
662 662 summary: 2
663 663
664 664 changeset: 21:7e61b508e709
665 665 branch: dev
666 666 tag: tip
667 667 user: foo
668 668 date: Thu Jan 01 00:00:00 1970 +0000
669 669 summary: 2
670 670
671 671 Transplants of grafts can find a destination...
672 672 $ hg log -r 'destination(7)'
673 673 changeset: 21:7e61b508e709
674 674 branch: dev
675 675 tag: tip
676 676 user: foo
677 677 date: Thu Jan 01 00:00:00 1970 +0000
678 678 summary: 2
679 679
680 680 ... grafts of grafts unfortunately can't
681 681 $ hg graft -q 13
682 682 warning: can't find ancestor for 'b' copied from 'a'!
683 683 $ hg log -r 'destination(13)'
684 684 All copies of a cset
685 685 $ hg log -r 'origin(13) or destination(origin(13))'
686 686 changeset: 2:5c095ad7e90f
687 687 user: test
688 688 date: Thu Jan 01 00:00:00 1970 +0000
689 689 summary: 2
690 690
691 691 changeset: 7:ef0ef43d49e7
692 692 parent: 0:68795b066622
693 693 user: foo
694 694 date: Thu Jan 01 00:00:00 1970 +0000
695 695 summary: 2
696 696
697 697 changeset: 13:7a4785234d87
698 698 user: foo
699 699 date: Thu Jan 01 00:00:00 1970 +0000
700 700 summary: 2
701 701
702 702 changeset: 19:9627f653b421
703 703 user: test
704 704 date: Thu Jan 01 00:00:00 1970 +0000
705 705 summary: 2
706 706
707 707 changeset: 21:7e61b508e709
708 708 branch: dev
709 709 user: foo
710 710 date: Thu Jan 01 00:00:00 1970 +0000
711 711 summary: 2
712 712
713 713 changeset: 22:d1cb6591fa4b
714 714 branch: dev
715 715 tag: tip
716 716 user: foo
717 717 date: Thu Jan 01 00:00:00 1970 +0000
718 718 summary: 2
719 719
720 720
721 721 graft works on complex revset
722 722
723 723 $ hg graft 'origin(13) or destination(origin(13))'
724 724 skipping ancestor revision 21:7e61b508e709
725 725 skipping ancestor revision 22:d1cb6591fa4b
726 726 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
727 727 grafting 7:ef0ef43d49e7 "2"
728 728 warning: can't find ancestor for 'b' copied from 'a'!
729 729 grafting 13:7a4785234d87 "2"
730 730 warning: can't find ancestor for 'b' copied from 'a'!
731 731 grafting 19:9627f653b421 "2"
732 732 merging b
733 733 warning: can't find ancestor for 'b' copied from 'a'!
734 734
735 735 graft with --force (still doesn't graft merges)
736 736
737 737 $ hg graft 19 0 6
738 738 skipping ungraftable merge revision 6
739 739 skipping ancestor revision 0:68795b066622
740 740 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
741 741 [255]
742 742 $ hg graft 19 0 6 --force
743 743 skipping ungraftable merge revision 6
744 744 grafting 19:9627f653b421 "2"
745 745 merging b
746 746 warning: can't find ancestor for 'b' copied from 'a'!
747 747 grafting 0:68795b066622 "0"
748 748
749 749 graft --force after backout
750 750
751 751 $ echo abc > a
752 752 $ hg ci -m 28
753 753 $ hg backout 28
754 754 reverting a
755 755 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
756 756 $ hg graft 28
757 757 skipping ancestor revision 28:50a516bb8b57
758 758 [255]
759 759 $ hg graft 28 --force
760 760 grafting 28:50a516bb8b57 "28"
761 761 merging a
762 762 $ cat a
763 763 abc
764 764
765 765 graft --continue after --force
766 766
767 767 $ echo def > a
768 768 $ hg ci -m 31
769 769 $ hg graft 28 --force --tool internal:fail
770 770 grafting 28:50a516bb8b57 "28"
771 771 abort: unresolved conflicts, can't continue
772 772 (use 'hg resolve' and 'hg graft --continue')
773 773 [255]
774 774 $ hg resolve --all
775 775 merging a
776 776 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
777 777 [1]
778 778 $ echo abc > a
779 779 $ hg resolve -m a
780 780 (no more unresolved files)
781 781 continue: hg graft --continue
782 782 $ hg graft -c
783 783 grafting 28:50a516bb8b57 "28"
784 784 $ cat a
785 785 abc
786 786
787 787 Continue testing same origin policy, using revision numbers from test above
788 788 but do some destructive editing of the repo:
789 789
790 790 $ hg up -qC 7
791 791 $ hg tag -l -r 13 tmp
792 792 $ hg --config extensions.strip= strip 2
793 793 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
794 794 $ hg graft tmp
795 795 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
796 796 [255]
797 797
798 798 Empty graft
799 799
800 800 $ hg up -qr 26
801 801 $ hg tag -f something
802 802 $ hg graft -qr 27
803 803 $ hg graft -f 27
804 804 grafting 27:ed6c7e54e319 "28"
805 805 note: graft of 27:ed6c7e54e319 created no changes to commit
806 806
807 807 $ cd ..
808 808
809 809 Graft to duplicate a commit
810 810
811 811 $ hg init graftsibling
812 812 $ cd graftsibling
813 813 $ touch a
814 814 $ hg commit -qAm a
815 815 $ touch b
816 816 $ hg commit -qAm b
817 817 $ hg log -G -T '{rev}\n'
818 818 @ 1
819 819 |
820 820 o 0
821 821
822 822 $ hg up -q 0
823 823 $ hg graft -r 1
824 824 grafting 1:0e067c57feba "b" (tip)
825 825 $ hg log -G -T '{rev}\n'
826 826 @ 2
827 827 |
828 828 | o 1
829 829 |/
830 830 o 0
831 831
832 832 Graft to duplicate a commit twice
833 833
834 834 $ hg up -q 0
835 835 $ hg graft -r 2
836 836 grafting 2:044ec77f6389 "b" (tip)
837 837 $ hg log -G -T '{rev}\n'
838 838 @ 3
839 839 |
840 840 | o 2
841 841 |/
842 842 | o 1
843 843 |/
844 844 o 0
845 845
846 846 Graft from behind a move or rename
847 847 ==================================
848 848
849 849 NOTE: This is affected by issue5343, and will need updating when it's fixed
850 850
851 851 Possible cases during a regular graft (when ca is between cta and c2):
852 852
853 853 name | c1<-cta | cta<->ca | ca->c2
854 854 A.0 | | |
855 855 A.1 | X | |
856 856 A.2 | | X |
857 857 A.3 | | | X
858 858 A.4 | X | X |
859 859 A.5 | X | | X
860 860 A.6 | | X | X
861 861 A.7 | X | X | X
862 862
863 863 A.0 is trivial, and doesn't need copy tracking.
864 864 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
865 865 In A.2, the rename is recorded in the c2 pass and followed backwards.
866 866 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
867 867 In A.4, both passes of checkcopies record incomplete renames, which are
868 868 then joined in mergecopies to record a rename to be followed.
869 869 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
870 870 records an incomplete divergence. The incomplete rename is then joined to the
871 871 appropriate side of the incomplete divergence, and the result is recorded as a
872 872 divergence. The code doesn't distinguish at all between these two cases, since
873 873 the end result of them is the same: an incomplete divergence joined with an
874 874 incomplete rename into a divergence.
875 875 Finally, A.6 records a divergence entirely in the c2 pass.
876 876
877 877 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
878 878 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
879 879 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
880 880 incomplete divergence, which is in fact complete. This is handled later in
881 881 mergecopies.
882 882 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
883 883 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
884 884 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
885 885 case, a<-b<-c->a is treated the same as a<-b<-b->a).
886 886
887 887 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
888 888 same name on both branches, then the rename is backed out on one branch, and
889 889 the backout is grafted to the other branch. This creates a challenging rename
890 890 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
891 891 source, respectively. Since rename detection will run on the c1 side for such a
892 892 sequence (as for technical reasons, we split the c1 and c2 sides not at the
893 893 graft CA, but rather at the topological CA), it will pick up a false rename,
894 894 and cause a spurious merge conflict. This false rename is always exactly the
895 895 reverse of the true rename that would be detected on the c2 side, so we can
896 896 correct for it by detecting this condition and reversing as necessary.
897 897
898 898 First, set up the repository with commits to be grafted
899 899
900 900 $ hg init ../graftmove
901 901 $ cd ../graftmove
902 902 $ echo c1a > f1a
903 903 $ echo c2a > f2a
904 904 $ echo c3a > f3a
905 905 $ echo c4a > f4a
906 906 $ echo c5a > f5a
907 907 $ hg ci -qAm A0
908 908 $ hg mv f1a f1b
909 909 $ hg mv f3a f3b
910 910 $ hg mv f5a f5b
911 911 $ hg ci -qAm B0
912 912 $ echo c1c > f1b
913 913 $ hg mv f2a f2c
914 914 $ hg mv f5b f5a
915 915 $ echo c5c > f5a
916 916 $ hg ci -qAm C0
917 917 $ hg mv f3b f3d
918 918 $ echo c4d > f4a
919 919 $ hg ci -qAm D0
920 920 $ hg log -G
921 921 @ changeset: 3:b69f5839d2d9
922 922 | tag: tip
923 923 | user: test
924 924 | date: Thu Jan 01 00:00:00 1970 +0000
925 925 | summary: D0
926 926 |
927 927 o changeset: 2:f58c7e2b28fa
928 928 | user: test
929 929 | date: Thu Jan 01 00:00:00 1970 +0000
930 930 | summary: C0
931 931 |
932 932 o changeset: 1:3d7bba921b5d
933 933 | user: test
934 934 | date: Thu Jan 01 00:00:00 1970 +0000
935 935 | summary: B0
936 936 |
937 937 o changeset: 0:11f7a1b56675
938 938 user: test
939 939 date: Thu Jan 01 00:00:00 1970 +0000
940 940 summary: A0
941 941
942 942
943 943 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
944 944 two renames actually converge to the same name (thus no actual divergence).
945 945
946 946 $ hg up -q 'desc("A0")'
947 947 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
948 948 grafting 2:f58c7e2b28fa "C0"
949 949 merging f1a and f1b to f1a
950 950 merging f5a
951 951 warning: can't find ancestor for 'f5a' copied from 'f5b'!
952 952 $ hg status --change .
953 953 M f1a
954 954 M f5a
955 955 A f2c
956 956 R f2a
957 957 $ hg cat f1a
958 958 c1c
959 959 $ hg cat f1b
960 960 f1b: no such file in rev c9763722f9bd
961 961 [1]
962 962
963 963 Test the cases A.0 (f4x) and A.6 (f3x)
964 964
965 965 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
966 966 grafting 3:b69f5839d2d9 "D0"
967 967 note: possible conflict - f3b was renamed multiple times to:
968 968 f3d
969 969 f3a
970 970 warning: can't find ancestor for 'f3d' copied from 'f3b'!
971 971
972 972 Set up the repository for some further tests
973 973
974 974 $ hg up -q "min(desc("A0"))"
975 975 $ hg mv f1a f1e
976 976 $ echo c2e > f2a
977 977 $ hg mv f3a f3e
978 978 $ hg mv f4a f4e
979 979 $ hg mv f5a f5b
980 980 $ hg ci -qAm "E0"
981 981 $ hg log -G
982 982 @ changeset: 6:6bd1736cab86
983 983 | tag: tip
984 984 | parent: 0:11f7a1b56675
985 985 | user: test
986 986 | date: Thu Jan 01 00:00:00 1970 +0000
987 987 | summary: E0
988 988 |
989 989 | o changeset: 5:560daee679da
990 990 | | user: test
991 991 | | date: Thu Jan 01 00:00:00 1970 +0000
992 992 | | summary: D1
993 993 | |
994 994 | o changeset: 4:c9763722f9bd
995 995 |/ parent: 0:11f7a1b56675
996 996 | user: test
997 997 | date: Thu Jan 01 00:00:00 1970 +0000
998 998 | summary: C1
999 999 |
1000 1000 | o changeset: 3:b69f5839d2d9
1001 1001 | | user: test
1002 1002 | | date: Thu Jan 01 00:00:00 1970 +0000
1003 1003 | | summary: D0
1004 1004 | |
1005 1005 | o changeset: 2:f58c7e2b28fa
1006 1006 | | user: test
1007 1007 | | date: Thu Jan 01 00:00:00 1970 +0000
1008 1008 | | summary: C0
1009 1009 | |
1010 1010 | o changeset: 1:3d7bba921b5d
1011 1011 |/ user: test
1012 1012 | date: Thu Jan 01 00:00:00 1970 +0000
1013 1013 | summary: B0
1014 1014 |
1015 1015 o changeset: 0:11f7a1b56675
1016 1016 user: test
1017 1017 date: Thu Jan 01 00:00:00 1970 +0000
1018 1018 summary: A0
1019 1019
1020 1020
1021 1021 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1022 1022 and A.3 with a local content change to be preserved (f2x).
1023 1023
1024 1024 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1025 1025 grafting 2:f58c7e2b28fa "C0"
1026 1026 merging f1e and f1b to f1e
1027 1027 merging f2a and f2c to f2c
1028 1028 merging f5b and f5a to f5a
1029 1029
1030 1030 Test the cases A.1 (f4x) and A.7 (f3x).
1031 1031
1032 1032 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1033 1033 grafting 3:b69f5839d2d9 "D0"
1034 1034 note: possible conflict - f3b was renamed multiple times to:
1035 1035 f3e
1036 1036 f3d
1037 1037 merging f4e and f4a to f4e
1038 1038 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1039 1039
1040 1040 Check the results of the grafts tested
1041 1041
1042 1042 $ hg log -CGv --patch --git
1043 1043 @ changeset: 8:93ee502e8b0a
1044 1044 | tag: tip
1045 1045 | user: test
1046 1046 | date: Thu Jan 01 00:00:00 1970 +0000
1047 1047 | files: f3d f4e
1048 1048 | description:
1049 1049 | D2
1050 1050 |
1051 1051 |
1052 1052 | diff --git a/f3d b/f3d
1053 1053 | new file mode 100644
1054 1054 | --- /dev/null
1055 1055 | +++ b/f3d
1056 1056 | @@ -0,0 +1,1 @@
1057 1057 | +c3a
1058 1058 | diff --git a/f4e b/f4e
1059 1059 | --- a/f4e
1060 1060 | +++ b/f4e
1061 1061 | @@ -1,1 +1,1 @@
1062 1062 | -c4a
1063 1063 | +c4d
1064 1064 |
1065 1065 o changeset: 7:539cf145f496
1066 1066 | user: test
1067 1067 | date: Thu Jan 01 00:00:00 1970 +0000
1068 1068 | files: f1e f2a f2c f5a f5b
1069 1069 | copies: f2c (f2a) f5a (f5b)
1070 1070 | description:
1071 1071 | C2
1072 1072 |
1073 1073 |
1074 1074 | diff --git a/f1e b/f1e
1075 1075 | --- a/f1e
1076 1076 | +++ b/f1e
1077 1077 | @@ -1,1 +1,1 @@
1078 1078 | -c1a
1079 1079 | +c1c
1080 1080 | diff --git a/f2a b/f2c
1081 1081 | rename from f2a
1082 1082 | rename to f2c
1083 1083 | diff --git a/f5b b/f5a
1084 1084 | rename from f5b
1085 1085 | rename to f5a
1086 1086 | --- a/f5b
1087 1087 | +++ b/f5a
1088 1088 | @@ -1,1 +1,1 @@
1089 1089 | -c5a
1090 1090 | +c5c
1091 1091 |
1092 1092 o changeset: 6:6bd1736cab86
1093 1093 | parent: 0:11f7a1b56675
1094 1094 | user: test
1095 1095 | date: Thu Jan 01 00:00:00 1970 +0000
1096 1096 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1097 1097 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1098 1098 | description:
1099 1099 | E0
1100 1100 |
1101 1101 |
1102 1102 | diff --git a/f1a b/f1e
1103 1103 | rename from f1a
1104 1104 | rename to f1e
1105 1105 | diff --git a/f2a b/f2a
1106 1106 | --- a/f2a
1107 1107 | +++ b/f2a
1108 1108 | @@ -1,1 +1,1 @@
1109 1109 | -c2a
1110 1110 | +c2e
1111 1111 | diff --git a/f3a b/f3e
1112 1112 | rename from f3a
1113 1113 | rename to f3e
1114 1114 | diff --git a/f4a b/f4e
1115 1115 | rename from f4a
1116 1116 | rename to f4e
1117 1117 | diff --git a/f5a b/f5b
1118 1118 | rename from f5a
1119 1119 | rename to f5b
1120 1120 |
1121 1121 | o changeset: 5:560daee679da
1122 1122 | | user: test
1123 1123 | | date: Thu Jan 01 00:00:00 1970 +0000
1124 1124 | | files: f3d f4a
1125 1125 | | description:
1126 1126 | | D1
1127 1127 | |
1128 1128 | |
1129 1129 | | diff --git a/f3d b/f3d
1130 1130 | | new file mode 100644
1131 1131 | | --- /dev/null
1132 1132 | | +++ b/f3d
1133 1133 | | @@ -0,0 +1,1 @@
1134 1134 | | +c3a
1135 1135 | | diff --git a/f4a b/f4a
1136 1136 | | --- a/f4a
1137 1137 | | +++ b/f4a
1138 1138 | | @@ -1,1 +1,1 @@
1139 1139 | | -c4a
1140 1140 | | +c4d
1141 1141 | |
1142 1142 | o changeset: 4:c9763722f9bd
1143 1143 |/ parent: 0:11f7a1b56675
1144 1144 | user: test
1145 1145 | date: Thu Jan 01 00:00:00 1970 +0000
1146 1146 | files: f1a f2a f2c f5a
1147 1147 | copies: f2c (f2a)
1148 1148 | description:
1149 1149 | C1
1150 1150 |
1151 1151 |
1152 1152 | diff --git a/f1a b/f1a
1153 1153 | --- a/f1a
1154 1154 | +++ b/f1a
1155 1155 | @@ -1,1 +1,1 @@
1156 1156 | -c1a
1157 1157 | +c1c
1158 1158 | diff --git a/f2a b/f2c
1159 1159 | rename from f2a
1160 1160 | rename to f2c
1161 1161 | diff --git a/f5a b/f5a
1162 1162 | --- a/f5a
1163 1163 | +++ b/f5a
1164 1164 | @@ -1,1 +1,1 @@
1165 1165 | -c5a
1166 1166 | +c5c
1167 1167 |
1168 1168 | o changeset: 3:b69f5839d2d9
1169 1169 | | user: test
1170 1170 | | date: Thu Jan 01 00:00:00 1970 +0000
1171 1171 | | files: f3b f3d f4a
1172 1172 | | copies: f3d (f3b)
1173 1173 | | description:
1174 1174 | | D0
1175 1175 | |
1176 1176 | |
1177 1177 | | diff --git a/f3b b/f3d
1178 1178 | | rename from f3b
1179 1179 | | rename to f3d
1180 1180 | | diff --git a/f4a b/f4a
1181 1181 | | --- a/f4a
1182 1182 | | +++ b/f4a
1183 1183 | | @@ -1,1 +1,1 @@
1184 1184 | | -c4a
1185 1185 | | +c4d
1186 1186 | |
1187 1187 | o changeset: 2:f58c7e2b28fa
1188 1188 | | user: test
1189 1189 | | date: Thu Jan 01 00:00:00 1970 +0000
1190 1190 | | files: f1b f2a f2c f5a f5b
1191 1191 | | copies: f2c (f2a) f5a (f5b)
1192 1192 | | description:
1193 1193 | | C0
1194 1194 | |
1195 1195 | |
1196 1196 | | diff --git a/f1b b/f1b
1197 1197 | | --- a/f1b
1198 1198 | | +++ b/f1b
1199 1199 | | @@ -1,1 +1,1 @@
1200 1200 | | -c1a
1201 1201 | | +c1c
1202 1202 | | diff --git a/f2a b/f2c
1203 1203 | | rename from f2a
1204 1204 | | rename to f2c
1205 1205 | | diff --git a/f5b b/f5a
1206 1206 | | rename from f5b
1207 1207 | | rename to f5a
1208 1208 | | --- a/f5b
1209 1209 | | +++ b/f5a
1210 1210 | | @@ -1,1 +1,1 @@
1211 1211 | | -c5a
1212 1212 | | +c5c
1213 1213 | |
1214 1214 | o changeset: 1:3d7bba921b5d
1215 1215 |/ user: test
1216 1216 | date: Thu Jan 01 00:00:00 1970 +0000
1217 1217 | files: f1a f1b f3a f3b f5a f5b
1218 1218 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1219 1219 | description:
1220 1220 | B0
1221 1221 |
1222 1222 |
1223 1223 | diff --git a/f1a b/f1b
1224 1224 | rename from f1a
1225 1225 | rename to f1b
1226 1226 | diff --git a/f3a b/f3b
1227 1227 | rename from f3a
1228 1228 | rename to f3b
1229 1229 | diff --git a/f5a b/f5b
1230 1230 | rename from f5a
1231 1231 | rename to f5b
1232 1232 |
1233 1233 o changeset: 0:11f7a1b56675
1234 1234 user: test
1235 1235 date: Thu Jan 01 00:00:00 1970 +0000
1236 1236 files: f1a f2a f3a f4a f5a
1237 1237 description:
1238 1238 A0
1239 1239
1240 1240
1241 1241 diff --git a/f1a b/f1a
1242 1242 new file mode 100644
1243 1243 --- /dev/null
1244 1244 +++ b/f1a
1245 1245 @@ -0,0 +1,1 @@
1246 1246 +c1a
1247 1247 diff --git a/f2a b/f2a
1248 1248 new file mode 100644
1249 1249 --- /dev/null
1250 1250 +++ b/f2a
1251 1251 @@ -0,0 +1,1 @@
1252 1252 +c2a
1253 1253 diff --git a/f3a b/f3a
1254 1254 new file mode 100644
1255 1255 --- /dev/null
1256 1256 +++ b/f3a
1257 1257 @@ -0,0 +1,1 @@
1258 1258 +c3a
1259 1259 diff --git a/f4a b/f4a
1260 1260 new file mode 100644
1261 1261 --- /dev/null
1262 1262 +++ b/f4a
1263 1263 @@ -0,0 +1,1 @@
1264 1264 +c4a
1265 1265 diff --git a/f5a b/f5a
1266 1266 new file mode 100644
1267 1267 --- /dev/null
1268 1268 +++ b/f5a
1269 1269 @@ -0,0 +1,1 @@
1270 1270 +c5a
1271 1271
1272 1272 $ hg cat f2c
1273 1273 c2e
1274 1274
1275 1275 Check superfluous filemerge of files renamed in the past but untouched by graft
1276 1276
1277 1277 $ echo a > a
1278 1278 $ hg ci -qAma
1279 1279 $ hg mv a b
1280 1280 $ echo b > b
1281 1281 $ hg ci -qAmb
1282 1282 $ echo c > c
1283 1283 $ hg ci -qAmc
1284 1284 $ hg up -q .~2
1285 1285 $ hg graft tip -qt:fail
1286 1286
1287 1287 $ cd ..
1288 1288
1289 1289 Graft a change into a new file previously grafted into a renamed directory
1290 1290
1291 1291 $ hg init dirmovenewfile
1292 1292 $ cd dirmovenewfile
1293 1293 $ mkdir a
1294 1294 $ echo a > a/a
1295 1295 $ hg ci -qAma
1296 1296 $ echo x > a/x
1297 1297 $ hg ci -qAmx
1298 1298 $ hg up -q 0
1299 1299 $ hg mv -q a b
1300 1300 $ hg ci -qAmb
1301 1301 $ hg graft -q 1 # a/x grafted as b/x, but no copy information recorded
1302 1302 $ hg up -q 1
1303 1303 $ echo y > a/x
1304 1304 $ hg ci -qAmy
1305 1305 $ hg up -q 3
1306 1306 $ hg graft -q 4
1307 1307 $ hg status --change .
1308 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 1354 $ cd ..
@@ -1,3327 +1,3327 b''
1 1 Short help:
2 2
3 3 $ hg
4 4 Mercurial Distributed SCM
5 5
6 6 basic commands:
7 7
8 8 add add the specified files on the next commit
9 9 annotate show changeset information by line for each file
10 10 clone make a copy of an existing repository
11 11 commit commit the specified files or all outstanding changes
12 12 diff diff repository (or selected files)
13 13 export dump the header and diffs for one or more changesets
14 14 forget forget the specified files on the next commit
15 15 init create a new repository in the given directory
16 16 log show revision history of entire repository or files
17 17 merge merge another revision into working directory
18 18 pull pull changes from the specified source
19 19 push push changes to the specified destination
20 20 remove remove the specified files on the next commit
21 21 serve start stand-alone webserver
22 22 status show changed files in the working directory
23 23 summary summarize working directory state
24 24 update update working directory (or switch revisions)
25 25
26 26 (use 'hg help' for the full list of commands or 'hg -v' for details)
27 27
28 28 $ hg -q
29 29 add add the specified files on the next commit
30 30 annotate show changeset information by line for each file
31 31 clone make a copy of an existing repository
32 32 commit commit the specified files or all outstanding changes
33 33 diff diff repository (or selected files)
34 34 export dump the header and diffs for one or more changesets
35 35 forget forget the specified files on the next commit
36 36 init create a new repository in the given directory
37 37 log show revision history of entire repository or files
38 38 merge merge another revision into working directory
39 39 pull pull changes from the specified source
40 40 push push changes to the specified destination
41 41 remove remove the specified files on the next commit
42 42 serve start stand-alone webserver
43 43 status show changed files in the working directory
44 44 summary summarize working directory state
45 45 update update working directory (or switch revisions)
46 46
47 47 $ hg help
48 48 Mercurial Distributed SCM
49 49
50 50 list of commands:
51 51
52 52 add add the specified files on the next commit
53 53 addremove add all new files, delete all missing files
54 54 annotate show changeset information by line for each file
55 55 archive create an unversioned archive of a repository revision
56 56 backout reverse effect of earlier changeset
57 57 bisect subdivision search of changesets
58 58 bookmarks create a new bookmark or list existing bookmarks
59 59 branch set or show the current branch name
60 60 branches list repository named branches
61 61 bundle create a bundle file
62 62 cat output the current or given revision of files
63 63 clone make a copy of an existing repository
64 64 commit commit the specified files or all outstanding changes
65 65 config show combined config settings from all hgrc files
66 66 copy mark files as copied for the next commit
67 67 diff diff repository (or selected files)
68 68 export dump the header and diffs for one or more changesets
69 69 files list tracked files
70 70 forget forget the specified files on the next commit
71 71 graft copy changes from other branches onto the current branch
72 72 grep search revision history for a pattern in specified files
73 73 heads show branch heads
74 74 help show help for a given topic or a help overview
75 75 identify identify the working directory or specified revision
76 76 import import an ordered set of patches
77 77 incoming show new changesets found in source
78 78 init create a new repository in the given directory
79 79 log show revision history of entire repository or files
80 80 manifest output the current or given revision of the project manifest
81 81 merge merge another revision into working directory
82 82 outgoing show changesets not found in the destination
83 83 paths show aliases for remote repositories
84 84 phase set or show the current phase name
85 85 pull pull changes from the specified source
86 86 push push changes to the specified destination
87 87 recover roll back an interrupted transaction
88 88 remove remove the specified files on the next commit
89 89 rename rename files; equivalent of copy + remove
90 90 resolve redo merges or set/view the merge status of files
91 91 revert restore files to their checkout state
92 92 root print the root (top) of the current working directory
93 93 serve start stand-alone webserver
94 94 status show changed files in the working directory
95 95 summary summarize working directory state
96 96 tag add one or more tags for the current or given revision
97 97 tags list repository tags
98 98 unbundle apply one or more bundle files
99 99 update update working directory (or switch revisions)
100 100 verify verify the integrity of the repository
101 101 version output version and copyright information
102 102
103 103 additional help topics:
104 104
105 105 bundlespec Bundle File Formats
106 106 color Colorizing Outputs
107 107 config Configuration Files
108 108 dates Date Formats
109 109 diffs Diff Formats
110 110 environment Environment Variables
111 111 extensions Using Additional Features
112 112 filesets Specifying File Sets
113 113 glossary Glossary
114 114 hgignore Syntax for Mercurial Ignore Files
115 115 hgweb Configuring hgweb
116 116 internals Technical implementation topics
117 117 merge-tools Merge Tools
118 118 pager Pager Support
119 119 patterns File Name Patterns
120 120 phases Working with Phases
121 121 revisions Specifying Revisions
122 122 scripting Using Mercurial from scripts and automation
123 123 subrepos Subrepositories
124 124 templating Template Usage
125 125 urls URL Paths
126 126
127 127 (use 'hg help -v' to show built-in aliases and global options)
128 128
129 129 $ hg -q help
130 130 add add the specified files on the next commit
131 131 addremove add all new files, delete all missing files
132 132 annotate show changeset information by line for each file
133 133 archive create an unversioned archive of a repository revision
134 134 backout reverse effect of earlier changeset
135 135 bisect subdivision search of changesets
136 136 bookmarks create a new bookmark or list existing bookmarks
137 137 branch set or show the current branch name
138 138 branches list repository named branches
139 139 bundle create a bundle file
140 140 cat output the current or given revision of files
141 141 clone make a copy of an existing repository
142 142 commit commit the specified files or all outstanding changes
143 143 config show combined config settings from all hgrc files
144 144 copy mark files as copied for the next commit
145 145 diff diff repository (or selected files)
146 146 export dump the header and diffs for one or more changesets
147 147 files list tracked files
148 148 forget forget the specified files on the next commit
149 149 graft copy changes from other branches onto the current branch
150 150 grep search revision history for a pattern in specified files
151 151 heads show branch heads
152 152 help show help for a given topic or a help overview
153 153 identify identify the working directory or specified revision
154 154 import import an ordered set of patches
155 155 incoming show new changesets found in source
156 156 init create a new repository in the given directory
157 157 log show revision history of entire repository or files
158 158 manifest output the current or given revision of the project manifest
159 159 merge merge another revision into working directory
160 160 outgoing show changesets not found in the destination
161 161 paths show aliases for remote repositories
162 162 phase set or show the current phase name
163 163 pull pull changes from the specified source
164 164 push push changes to the specified destination
165 165 recover roll back an interrupted transaction
166 166 remove remove the specified files on the next commit
167 167 rename rename files; equivalent of copy + remove
168 168 resolve redo merges or set/view the merge status of files
169 169 revert restore files to their checkout state
170 170 root print the root (top) of the current working directory
171 171 serve start stand-alone webserver
172 172 status show changed files in the working directory
173 173 summary summarize working directory state
174 174 tag add one or more tags for the current or given revision
175 175 tags list repository tags
176 176 unbundle apply one or more bundle files
177 177 update update working directory (or switch revisions)
178 178 verify verify the integrity of the repository
179 179 version output version and copyright information
180 180
181 181 additional help topics:
182 182
183 183 bundlespec Bundle File Formats
184 184 color Colorizing Outputs
185 185 config Configuration Files
186 186 dates Date Formats
187 187 diffs Diff Formats
188 188 environment Environment Variables
189 189 extensions Using Additional Features
190 190 filesets Specifying File Sets
191 191 glossary Glossary
192 192 hgignore Syntax for Mercurial Ignore Files
193 193 hgweb Configuring hgweb
194 194 internals Technical implementation topics
195 195 merge-tools Merge Tools
196 196 pager Pager Support
197 197 patterns File Name Patterns
198 198 phases Working with Phases
199 199 revisions Specifying Revisions
200 200 scripting Using Mercurial from scripts and automation
201 201 subrepos Subrepositories
202 202 templating Template Usage
203 203 urls URL Paths
204 204
205 205 Test extension help:
206 206 $ hg help extensions --config extensions.rebase= --config extensions.children=
207 207 Using Additional Features
208 208 """""""""""""""""""""""""
209 209
210 210 Mercurial has the ability to add new features through the use of
211 211 extensions. Extensions may add new commands, add options to existing
212 212 commands, change the default behavior of commands, or implement hooks.
213 213
214 214 To enable the "foo" extension, either shipped with Mercurial or in the
215 215 Python search path, create an entry for it in your configuration file,
216 216 like this:
217 217
218 218 [extensions]
219 219 foo =
220 220
221 221 You may also specify the full path to an extension:
222 222
223 223 [extensions]
224 224 myfeature = ~/.hgext/myfeature.py
225 225
226 226 See 'hg help config' for more information on configuration files.
227 227
228 228 Extensions are not loaded by default for a variety of reasons: they can
229 229 increase startup overhead; they may be meant for advanced usage only; they
230 230 may provide potentially dangerous abilities (such as letting you destroy
231 231 or modify history); they might not be ready for prime time; or they may
232 232 alter some usual behaviors of stock Mercurial. It is thus up to the user
233 233 to activate extensions as needed.
234 234
235 235 To explicitly disable an extension enabled in a configuration file of
236 236 broader scope, prepend its path with !:
237 237
238 238 [extensions]
239 239 # disabling extension bar residing in /path/to/extension/bar.py
240 240 bar = !/path/to/extension/bar.py
241 241 # ditto, but no path was supplied for extension baz
242 242 baz = !
243 243
244 244 enabled extensions:
245 245
246 246 children command to display child changesets (DEPRECATED)
247 247 rebase command to move sets of revisions to a different ancestor
248 248
249 249 disabled extensions:
250 250
251 251 acl hooks for controlling repository access
252 252 blackbox log repository events to a blackbox for debugging
253 253 bugzilla hooks for integrating with the Bugzilla bug tracker
254 254 censor erase file content at a given revision
255 255 churn command to display statistics about repository history
256 256 clonebundles advertise pre-generated bundles to seed clones
257 257 convert import revisions from foreign VCS repositories into
258 258 Mercurial
259 259 eol automatically manage newlines in repository files
260 260 extdiff command to allow external programs to compare revisions
261 261 factotum http authentication with factotum
262 262 gpg commands to sign and verify changesets
263 263 hgk browse the repository in a graphical way
264 264 highlight syntax highlighting for hgweb (requires Pygments)
265 265 histedit interactive history editing
266 266 keyword expand keywords in tracked files
267 267 largefiles track large binary files
268 268 mq manage a stack of patches
269 269 notify hooks for sending email push notifications
270 270 patchbomb command to send changesets as (a series of) patch emails
271 271 purge command to delete untracked files from the working
272 272 directory
273 273 relink recreates hardlinks between repository clones
274 274 schemes extend schemes with shortcuts to repository swarms
275 275 share share a common history between several working directories
276 276 shelve save and restore changes to the working directory
277 277 strip strip changesets and their descendants from history
278 278 transplant command to transplant changesets from another branch
279 279 win32mbcs allow the use of MBCS paths with problematic encodings
280 280 zeroconf discover and advertise repositories on the local network
281 281
282 282 Verify that extension keywords appear in help templates
283 283
284 284 $ hg help --config extensions.transplant= templating|grep transplant > /dev/null
285 285
286 286 Test short command list with verbose option
287 287
288 288 $ hg -v help shortlist
289 289 Mercurial Distributed SCM
290 290
291 291 basic commands:
292 292
293 293 add add the specified files on the next commit
294 294 annotate, blame
295 295 show changeset information by line for each file
296 296 clone make a copy of an existing repository
297 297 commit, ci commit the specified files or all outstanding changes
298 298 diff diff repository (or selected files)
299 299 export dump the header and diffs for one or more changesets
300 300 forget forget the specified files on the next commit
301 301 init create a new repository in the given directory
302 302 log, history show revision history of entire repository or files
303 303 merge merge another revision into working directory
304 304 pull pull changes from the specified source
305 305 push push changes to the specified destination
306 306 remove, rm remove the specified files on the next commit
307 307 serve start stand-alone webserver
308 308 status, st show changed files in the working directory
309 309 summary, sum summarize working directory state
310 310 update, up, checkout, co
311 311 update working directory (or switch revisions)
312 312
313 313 global options ([+] can be repeated):
314 314
315 315 -R --repository REPO repository root directory or name of overlay bundle
316 316 file
317 317 --cwd DIR change working directory
318 318 -y --noninteractive do not prompt, automatically pick the first choice for
319 319 all prompts
320 320 -q --quiet suppress output
321 321 -v --verbose enable additional output
322 322 --color TYPE when to colorize (boolean, always, auto, never, or
323 323 debug)
324 324 --config CONFIG [+] set/override config option (use 'section.name=value')
325 325 --debug enable debugging output
326 326 --debugger start debugger
327 327 --encoding ENCODE set the charset encoding (default: ascii)
328 328 --encodingmode MODE set the charset encoding mode (default: strict)
329 329 --traceback always print a traceback on exception
330 330 --time time how long the command takes
331 331 --profile print command execution profile
332 332 --version output version information and exit
333 333 -h --help display help and exit
334 334 --hidden consider hidden changesets
335 335 --pager TYPE when to paginate (boolean, always, auto, or never)
336 336 (default: auto)
337 337
338 338 (use 'hg help' for the full list of commands)
339 339
340 340 $ hg add -h
341 341 hg add [OPTION]... [FILE]...
342 342
343 343 add the specified files on the next commit
344 344
345 345 Schedule files to be version controlled and added to the repository.
346 346
347 347 The files will be added to the repository at the next commit. To undo an
348 348 add before that, see 'hg forget'.
349 349
350 350 If no names are given, add all files to the repository (except files
351 351 matching ".hgignore").
352 352
353 353 Returns 0 if all files are successfully added.
354 354
355 355 options ([+] can be repeated):
356 356
357 357 -I --include PATTERN [+] include names matching the given patterns
358 358 -X --exclude PATTERN [+] exclude names matching the given patterns
359 359 -S --subrepos recurse into subrepositories
360 360 -n --dry-run do not perform actions, just print output
361 361
362 362 (some details hidden, use --verbose to show complete help)
363 363
364 364 Verbose help for add
365 365
366 366 $ hg add -hv
367 367 hg add [OPTION]... [FILE]...
368 368
369 369 add the specified files on the next commit
370 370
371 371 Schedule files to be version controlled and added to the repository.
372 372
373 373 The files will be added to the repository at the next commit. To undo an
374 374 add before that, see 'hg forget'.
375 375
376 376 If no names are given, add all files to the repository (except files
377 377 matching ".hgignore").
378 378
379 379 Examples:
380 380
381 381 - New (unknown) files are added automatically by 'hg add':
382 382
383 383 $ ls
384 384 foo.c
385 385 $ hg status
386 386 ? foo.c
387 387 $ hg add
388 388 adding foo.c
389 389 $ hg status
390 390 A foo.c
391 391
392 392 - Specific files to be added can be specified:
393 393
394 394 $ ls
395 395 bar.c foo.c
396 396 $ hg status
397 397 ? bar.c
398 398 ? foo.c
399 399 $ hg add bar.c
400 400 $ hg status
401 401 A bar.c
402 402 ? foo.c
403 403
404 404 Returns 0 if all files are successfully added.
405 405
406 406 options ([+] can be repeated):
407 407
408 408 -I --include PATTERN [+] include names matching the given patterns
409 409 -X --exclude PATTERN [+] exclude names matching the given patterns
410 410 -S --subrepos recurse into subrepositories
411 411 -n --dry-run do not perform actions, just print output
412 412
413 413 global options ([+] can be repeated):
414 414
415 415 -R --repository REPO repository root directory or name of overlay bundle
416 416 file
417 417 --cwd DIR change working directory
418 418 -y --noninteractive do not prompt, automatically pick the first choice for
419 419 all prompts
420 420 -q --quiet suppress output
421 421 -v --verbose enable additional output
422 422 --color TYPE when to colorize (boolean, always, auto, never, or
423 423 debug)
424 424 --config CONFIG [+] set/override config option (use 'section.name=value')
425 425 --debug enable debugging output
426 426 --debugger start debugger
427 427 --encoding ENCODE set the charset encoding (default: ascii)
428 428 --encodingmode MODE set the charset encoding mode (default: strict)
429 429 --traceback always print a traceback on exception
430 430 --time time how long the command takes
431 431 --profile print command execution profile
432 432 --version output version information and exit
433 433 -h --help display help and exit
434 434 --hidden consider hidden changesets
435 435 --pager TYPE when to paginate (boolean, always, auto, or never)
436 436 (default: auto)
437 437
438 438 Test the textwidth config option
439 439
440 440 $ hg root -h --config ui.textwidth=50
441 441 hg root
442 442
443 443 print the root (top) of the current working
444 444 directory
445 445
446 446 Print the root directory of the current
447 447 repository.
448 448
449 449 Returns 0 on success.
450 450
451 451 (some details hidden, use --verbose to show
452 452 complete help)
453 453
454 454 Test help option with version option
455 455
456 456 $ hg add -h --version
457 457 Mercurial Distributed SCM (version *) (glob)
458 458 (see https://mercurial-scm.org for more information)
459 459
460 460 Copyright (C) 2005-* Matt Mackall and others (glob)
461 461 This is free software; see the source for copying conditions. There is NO
462 462 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
463 463
464 464 $ hg add --skjdfks
465 465 hg add: option --skjdfks not recognized
466 466 hg add [OPTION]... [FILE]...
467 467
468 468 add the specified files on the next commit
469 469
470 470 options ([+] can be repeated):
471 471
472 472 -I --include PATTERN [+] include names matching the given patterns
473 473 -X --exclude PATTERN [+] exclude names matching the given patterns
474 474 -S --subrepos recurse into subrepositories
475 475 -n --dry-run do not perform actions, just print output
476 476
477 477 (use 'hg add -h' to show more help)
478 478 [255]
479 479
480 480 Test ambiguous command help
481 481
482 482 $ hg help ad
483 483 list of commands:
484 484
485 485 add add the specified files on the next commit
486 486 addremove add all new files, delete all missing files
487 487
488 488 (use 'hg help -v ad' to show built-in aliases and global options)
489 489
490 490 Test command without options
491 491
492 492 $ hg help verify
493 493 hg verify
494 494
495 495 verify the integrity of the repository
496 496
497 497 Verify the integrity of the current repository.
498 498
499 499 This will perform an extensive check of the repository's integrity,
500 500 validating the hashes and checksums of each entry in the changelog,
501 501 manifest, and tracked files, as well as the integrity of their crosslinks
502 502 and indices.
503 503
504 504 Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more
505 505 information about recovery from corruption of the repository.
506 506
507 507 Returns 0 on success, 1 if errors are encountered.
508 508
509 509 (some details hidden, use --verbose to show complete help)
510 510
511 511 $ hg help diff
512 512 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
513 513
514 514 diff repository (or selected files)
515 515
516 516 Show differences between revisions for the specified files.
517 517
518 518 Differences between files are shown using the unified diff format.
519 519
520 520 Note:
521 521 'hg diff' may generate unexpected results for merges, as it will
522 522 default to comparing against the working directory's first parent
523 523 changeset if no revisions are specified.
524 524
525 525 When two revision arguments are given, then changes are shown between
526 526 those revisions. If only one revision is specified then that revision is
527 527 compared to the working directory, and, when no revisions are specified,
528 528 the working directory files are compared to its first parent.
529 529
530 530 Alternatively you can specify -c/--change with a revision to see the
531 531 changes in that changeset relative to its first parent.
532 532
533 533 Without the -a/--text option, diff will avoid generating diffs of files it
534 534 detects as binary. With -a, diff will generate a diff anyway, probably
535 535 with undesirable results.
536 536
537 537 Use the -g/--git option to generate diffs in the git extended diff format.
538 538 For more information, read 'hg help diffs'.
539 539
540 540 Returns 0 on success.
541 541
542 542 options ([+] can be repeated):
543 543
544 544 -r --rev REV [+] revision
545 545 -c --change REV change made by revision
546 546 -a --text treat all files as text
547 547 -g --git use git extended diff format
548 548 --binary generate binary diffs in git mode (default)
549 549 --nodates omit dates from diff headers
550 550 --noprefix omit a/ and b/ prefixes from filenames
551 551 -p --show-function show which function each change is in
552 552 --reverse produce a diff that undoes the changes
553 553 -w --ignore-all-space ignore white space when comparing lines
554 554 -b --ignore-space-change ignore changes in the amount of white space
555 555 -B --ignore-blank-lines ignore changes whose lines are all blank
556 556 -U --unified NUM number of lines of context to show
557 557 --stat output diffstat-style summary of changes
558 558 --root DIR produce diffs relative to subdirectory
559 559 -I --include PATTERN [+] include names matching the given patterns
560 560 -X --exclude PATTERN [+] exclude names matching the given patterns
561 561 -S --subrepos recurse into subrepositories
562 562
563 563 (some details hidden, use --verbose to show complete help)
564 564
565 565 $ hg help status
566 566 hg status [OPTION]... [FILE]...
567 567
568 568 aliases: st
569 569
570 570 show changed files in the working directory
571 571
572 572 Show status of files in the repository. If names are given, only files
573 573 that match are shown. Files that are clean or ignored or the source of a
574 574 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
575 575 -C/--copies or -A/--all are given. Unless options described with "show
576 576 only ..." are given, the options -mardu are used.
577 577
578 578 Option -q/--quiet hides untracked (unknown and ignored) files unless
579 579 explicitly requested with -u/--unknown or -i/--ignored.
580 580
581 581 Note:
582 582 'hg status' may appear to disagree with diff if permissions have
583 583 changed or a merge has occurred. The standard diff format does not
584 584 report permission changes and diff only reports changes relative to one
585 585 merge parent.
586 586
587 587 If one revision is given, it is used as the base revision. If two
588 588 revisions are given, the differences between them are shown. The --change
589 589 option can also be used as a shortcut to list the changed files of a
590 590 revision from its first parent.
591 591
592 592 The codes used to show the status of files are:
593 593
594 594 M = modified
595 595 A = added
596 596 R = removed
597 597 C = clean
598 598 ! = missing (deleted by non-hg command, but still tracked)
599 599 ? = not tracked
600 600 I = ignored
601 601 = origin of the previous file (with --copies)
602 602
603 603 Returns 0 on success.
604 604
605 605 options ([+] can be repeated):
606 606
607 607 -A --all show status of all files
608 608 -m --modified show only modified files
609 609 -a --added show only added files
610 610 -r --removed show only removed files
611 611 -d --deleted show only deleted (but tracked) files
612 612 -c --clean show only files without changes
613 613 -u --unknown show only unknown (not tracked) files
614 614 -i --ignored show only ignored files
615 615 -n --no-status hide status prefix
616 616 -C --copies show source of copied files
617 617 -0 --print0 end filenames with NUL, for use with xargs
618 618 --rev REV [+] show difference from revision
619 619 --change REV list the changed files of a revision
620 620 -I --include PATTERN [+] include names matching the given patterns
621 621 -X --exclude PATTERN [+] exclude names matching the given patterns
622 622 -S --subrepos recurse into subrepositories
623 623
624 624 (some details hidden, use --verbose to show complete help)
625 625
626 626 $ hg -q help status
627 627 hg status [OPTION]... [FILE]...
628 628
629 629 show changed files in the working directory
630 630
631 631 $ hg help foo
632 632 abort: no such help topic: foo
633 633 (try 'hg help --keyword foo')
634 634 [255]
635 635
636 636 $ hg skjdfks
637 637 hg: unknown command 'skjdfks'
638 638 Mercurial Distributed SCM
639 639
640 640 basic commands:
641 641
642 642 add add the specified files on the next commit
643 643 annotate show changeset information by line for each file
644 644 clone make a copy of an existing repository
645 645 commit commit the specified files or all outstanding changes
646 646 diff diff repository (or selected files)
647 647 export dump the header and diffs for one or more changesets
648 648 forget forget the specified files on the next commit
649 649 init create a new repository in the given directory
650 650 log show revision history of entire repository or files
651 651 merge merge another revision into working directory
652 652 pull pull changes from the specified source
653 653 push push changes to the specified destination
654 654 remove remove the specified files on the next commit
655 655 serve start stand-alone webserver
656 656 status show changed files in the working directory
657 657 summary summarize working directory state
658 658 update update working directory (or switch revisions)
659 659
660 660 (use 'hg help' for the full list of commands or 'hg -v' for details)
661 661 [255]
662 662
663 663
664 664 Make sure that we don't run afoul of the help system thinking that
665 665 this is a section and erroring out weirdly.
666 666
667 667 $ hg .log
668 668 hg: unknown command '.log'
669 669 (did you mean log?)
670 670 [255]
671 671
672 672 $ hg log.
673 673 hg: unknown command 'log.'
674 674 (did you mean log?)
675 675 [255]
676 676 $ hg pu.lh
677 677 hg: unknown command 'pu.lh'
678 678 (did you mean one of pull, push?)
679 679 [255]
680 680
681 681 $ cat > helpext.py <<EOF
682 682 > import os
683 683 > from mercurial import cmdutil, commands
684 684 >
685 685 > cmdtable = {}
686 686 > command = cmdutil.command(cmdtable)
687 687 >
688 688 > @command('nohelp',
689 689 > [('', 'longdesc', 3, 'x'*90),
690 690 > ('n', '', None, 'normal desc'),
691 691 > ('', 'newline', '', 'line1\nline2')],
692 692 > 'hg nohelp',
693 693 > norepo=True)
694 694 > @command('debugoptADV', [('', 'aopt', None, 'option is (ADVANCED)')])
695 695 > @command('debugoptDEP', [('', 'dopt', None, 'option is (DEPRECATED)')])
696 696 > @command('debugoptEXP', [('', 'eopt', None, 'option is (EXPERIMENTAL)')])
697 697 > def nohelp(ui, *args, **kwargs):
698 698 > pass
699 699 >
700 700 > def uisetup(ui):
701 701 > ui.setconfig('alias', 'shellalias', '!echo hi', 'helpext')
702 702 > ui.setconfig('alias', 'hgalias', 'summary', 'helpext')
703 703 >
704 704 > EOF
705 705 $ echo '[extensions]' >> $HGRCPATH
706 706 $ echo "helpext = `pwd`/helpext.py" >> $HGRCPATH
707 707
708 708 Test for aliases
709 709
710 710 $ hg help hgalias
711 711 hg hgalias [--remote]
712 712
713 713 alias for: hg summary
714 714
715 715 summarize working directory state
716 716
717 717 This generates a brief summary of the working directory state, including
718 718 parents, branch, commit status, phase and available updates.
719 719
720 720 With the --remote option, this will check the default paths for incoming
721 721 and outgoing changes. This can be time-consuming.
722 722
723 723 Returns 0 on success.
724 724
725 725 defined by: helpext
726 726
727 727 options:
728 728
729 729 --remote check for push and pull
730 730
731 731 (some details hidden, use --verbose to show complete help)
732 732
733 733 $ hg help shellalias
734 734 hg shellalias
735 735
736 736 shell alias for:
737 737
738 738 echo hi
739 739
740 740 defined by: helpext
741 741
742 742 (some details hidden, use --verbose to show complete help)
743 743
744 744 Test command with no help text
745 745
746 746 $ hg help nohelp
747 747 hg nohelp
748 748
749 749 (no help text available)
750 750
751 751 options:
752 752
753 753 --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
754 754 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
755 755 -n -- normal desc
756 756 --newline VALUE line1 line2
757 757
758 758 (some details hidden, use --verbose to show complete help)
759 759
760 760 $ hg help -k nohelp
761 761 Commands:
762 762
763 763 nohelp hg nohelp
764 764
765 765 Extension Commands:
766 766
767 767 nohelp (no help text available)
768 768
769 769 Test that default list of commands omits extension commands
770 770
771 771 $ hg help
772 772 Mercurial Distributed SCM
773 773
774 774 list of commands:
775 775
776 776 add add the specified files on the next commit
777 777 addremove add all new files, delete all missing files
778 778 annotate show changeset information by line for each file
779 779 archive create an unversioned archive of a repository revision
780 780 backout reverse effect of earlier changeset
781 781 bisect subdivision search of changesets
782 782 bookmarks create a new bookmark or list existing bookmarks
783 783 branch set or show the current branch name
784 784 branches list repository named branches
785 785 bundle create a bundle file
786 786 cat output the current or given revision of files
787 787 clone make a copy of an existing repository
788 788 commit commit the specified files or all outstanding changes
789 789 config show combined config settings from all hgrc files
790 790 copy mark files as copied for the next commit
791 791 diff diff repository (or selected files)
792 792 export dump the header and diffs for one or more changesets
793 793 files list tracked files
794 794 forget forget the specified files on the next commit
795 795 graft copy changes from other branches onto the current branch
796 796 grep search revision history for a pattern in specified files
797 797 heads show branch heads
798 798 help show help for a given topic or a help overview
799 799 identify identify the working directory or specified revision
800 800 import import an ordered set of patches
801 801 incoming show new changesets found in source
802 802 init create a new repository in the given directory
803 803 log show revision history of entire repository or files
804 804 manifest output the current or given revision of the project manifest
805 805 merge merge another revision into working directory
806 806 outgoing show changesets not found in the destination
807 807 paths show aliases for remote repositories
808 808 phase set or show the current phase name
809 809 pull pull changes from the specified source
810 810 push push changes to the specified destination
811 811 recover roll back an interrupted transaction
812 812 remove remove the specified files on the next commit
813 813 rename rename files; equivalent of copy + remove
814 814 resolve redo merges or set/view the merge status of files
815 815 revert restore files to their checkout state
816 816 root print the root (top) of the current working directory
817 817 serve start stand-alone webserver
818 818 status show changed files in the working directory
819 819 summary summarize working directory state
820 820 tag add one or more tags for the current or given revision
821 821 tags list repository tags
822 822 unbundle apply one or more bundle files
823 823 update update working directory (or switch revisions)
824 824 verify verify the integrity of the repository
825 825 version output version and copyright information
826 826
827 827 enabled extensions:
828 828
829 829 helpext (no help text available)
830 830
831 831 additional help topics:
832 832
833 833 bundlespec Bundle File Formats
834 834 color Colorizing Outputs
835 835 config Configuration Files
836 836 dates Date Formats
837 837 diffs Diff Formats
838 838 environment Environment Variables
839 839 extensions Using Additional Features
840 840 filesets Specifying File Sets
841 841 glossary Glossary
842 842 hgignore Syntax for Mercurial Ignore Files
843 843 hgweb Configuring hgweb
844 844 internals Technical implementation topics
845 845 merge-tools Merge Tools
846 846 pager Pager Support
847 847 patterns File Name Patterns
848 848 phases Working with Phases
849 849 revisions Specifying Revisions
850 850 scripting Using Mercurial from scripts and automation
851 851 subrepos Subrepositories
852 852 templating Template Usage
853 853 urls URL Paths
854 854
855 855 (use 'hg help -v' to show built-in aliases and global options)
856 856
857 857
858 858 Test list of internal help commands
859 859
860 860 $ hg help debug
861 861 debug commands (internal and unsupported):
862 862
863 863 debugancestor
864 864 find the ancestor revision of two revisions in a given index
865 865 debugapplystreamclonebundle
866 866 apply a stream clone bundle file
867 867 debugbuilddag
868 868 builds a repo with a given DAG from scratch in the current
869 869 empty repo
870 870 debugbundle lists the contents of a bundle
871 871 debugcheckstate
872 872 validate the correctness of the current dirstate
873 873 debugcolor show available color, effects or style
874 874 debugcommands
875 875 list all available commands and options
876 876 debugcomplete
877 877 returns the completion list associated with the given command
878 878 debugcreatestreamclonebundle
879 879 create a stream clone bundle file
880 880 debugdag format the changelog or an index DAG as a concise textual
881 881 description
882 882 debugdata dump the contents of a data file revision
883 883 debugdate parse and display a date
884 884 debugdeltachain
885 885 dump information about delta chains in a revlog
886 886 debugdirstate
887 887 show the contents of the current dirstate
888 888 debugdiscovery
889 889 runs the changeset discovery protocol in isolation
890 890 debugextensions
891 891 show information about active extensions
892 892 debugfileset parse and apply a fileset specification
893 893 debugfsinfo show information detected about current filesystem
894 894 debuggetbundle
895 895 retrieves a bundle from a repo
896 896 debugignore display the combined ignore pattern and information about
897 897 ignored files
898 898 debugindex dump the contents of an index file
899 899 debugindexdot
900 900 dump an index DAG as a graphviz dot file
901 901 debuginstall test Mercurial installation
902 902 debugknown test whether node ids are known to a repo
903 903 debuglocks show or modify state of locks
904 904 debugmergestate
905 905 print merge state
906 906 debugnamecomplete
907 907 complete "names" - tags, open branch names, bookmark names
908 908 debugobsolete
909 909 create arbitrary obsolete marker
910 910 debugoptADV (no help text available)
911 911 debugoptDEP (no help text available)
912 912 debugoptEXP (no help text available)
913 913 debugpathcomplete
914 914 complete part or all of a tracked path
915 915 debugpushkey access the pushkey key/value protocol
916 916 debugpvec (no help text available)
917 917 debugrebuilddirstate
918 918 rebuild the dirstate as it would look like for the given
919 919 revision
920 920 debugrebuildfncache
921 921 rebuild the fncache file
922 922 debugrename dump rename information
923 923 debugrevlog show data and statistics about a revlog
924 924 debugrevspec parse and apply a revision specification
925 925 debugsetparents
926 926 manually set the parents of the current working directory
927 927 debugsub (no help text available)
928 928 debugsuccessorssets
929 929 show set of successors for revision
930 930 debugtemplate
931 931 parse and apply a template
932 932 debugupgraderepo
933 933 upgrade a repository to use different features
934 934 debugwalk show how files match on given patterns
935 935 debugwireargs
936 936 (no help text available)
937 937
938 938 (use 'hg help -v debug' to show built-in aliases and global options)
939 939
940 940 internals topic renders index of available sub-topics
941 941
942 942 $ hg help internals
943 943 Technical implementation topics
944 944 """""""""""""""""""""""""""""""
945 945
946 946 To access a subtopic, use "hg help internals.{subtopic-name}"
947 947
948 948 bundles Bundles
949 949 censor Censor
950 950 changegroups Changegroups
951 951 requirements Repository Requirements
952 952 revlogs Revision Logs
953 953 wireprotocol Wire Protocol
954 954
955 955 sub-topics can be accessed
956 956
957 957 $ hg help internals.changegroups
958 958 Changegroups
959 959 """"""""""""
960 960
961 961 Changegroups are representations of repository revlog data, specifically
962 962 the changelog data, root/flat manifest data, treemanifest data, and
963 963 filelogs.
964 964
965 965 There are 3 versions of changegroups: "1", "2", and "3". From a high-
966 966 level, versions "1" and "2" are almost exactly the same, with the only
967 967 difference being an additional item in the *delta header*. Version "3"
968 968 adds support for revlog flags in the *delta header* and optionally
969 969 exchanging treemanifests (enabled by setting an option on the
970 970 "changegroup" part in the bundle2).
971 971
972 972 Changegroups when not exchanging treemanifests consist of 3 logical
973 973 segments:
974 974
975 975 +---------------------------------+
976 976 | | | |
977 977 | changeset | manifest | filelogs |
978 978 | | | |
979 979 | | | |
980 980 +---------------------------------+
981 981
982 982 When exchanging treemanifests, there are 4 logical segments:
983 983
984 984 +-------------------------------------------------+
985 985 | | | | |
986 986 | changeset | root | treemanifests | filelogs |
987 987 | | manifest | | |
988 988 | | | | |
989 989 +-------------------------------------------------+
990 990
991 991 The principle building block of each segment is a *chunk*. A *chunk* is a
992 992 framed piece of data:
993 993
994 994 +---------------------------------------+
995 995 | | |
996 996 | length | data |
997 997 | (4 bytes) | (<length - 4> bytes) |
998 998 | | |
999 999 +---------------------------------------+
1000 1000
1001 1001 All integers are big-endian signed integers. Each chunk starts with a
1002 1002 32-bit integer indicating the length of the entire chunk (including the
1003 1003 length field itself).
1004 1004
1005 1005 There is a special case chunk that has a value of 0 for the length
1006 1006 ("0x00000000"). We call this an *empty chunk*.
1007 1007
1008 1008 Delta Groups
1009 1009 ============
1010 1010
1011 1011 A *delta group* expresses the content of a revlog as a series of deltas,
1012 1012 or patches against previous revisions.
1013 1013
1014 1014 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
1015 1015 to signal the end of the delta group:
1016 1016
1017 1017 +------------------------------------------------------------------------+
1018 1018 | | | | | |
1019 1019 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
1020 1020 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
1021 1021 | | | | | |
1022 1022 +------------------------------------------------------------------------+
1023 1023
1024 1024 Each *chunk*'s data consists of the following:
1025 1025
1026 1026 +---------------------------------------+
1027 1027 | | |
1028 1028 | delta header | delta data |
1029 1029 | (various by version) | (various) |
1030 1030 | | |
1031 1031 +---------------------------------------+
1032 1032
1033 1033 The *delta data* is a series of *delta*s that describe a diff from an
1034 1034 existing entry (either that the recipient already has, or previously
1035 1035 specified in the bundle/changegroup).
1036 1036
1037 1037 The *delta header* is different between versions "1", "2", and "3" of the
1038 1038 changegroup format.
1039 1039
1040 1040 Version 1 (headerlen=80):
1041 1041
1042 1042 +------------------------------------------------------+
1043 1043 | | | | |
1044 1044 | node | p1 node | p2 node | link node |
1045 1045 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1046 1046 | | | | |
1047 1047 +------------------------------------------------------+
1048 1048
1049 1049 Version 2 (headerlen=100):
1050 1050
1051 1051 +------------------------------------------------------------------+
1052 1052 | | | | | |
1053 1053 | node | p1 node | p2 node | base node | link node |
1054 1054 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
1055 1055 | | | | | |
1056 1056 +------------------------------------------------------------------+
1057 1057
1058 1058 Version 3 (headerlen=102):
1059 1059
1060 1060 +------------------------------------------------------------------------------+
1061 1061 | | | | | | |
1062 1062 | node | p1 node | p2 node | base node | link node | flags |
1063 1063 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
1064 1064 | | | | | | |
1065 1065 +------------------------------------------------------------------------------+
1066 1066
1067 1067 The *delta data* consists of "chunklen - 4 - headerlen" bytes, which
1068 1068 contain a series of *delta*s, densely packed (no separators). These deltas
1069 1069 describe a diff from an existing entry (either that the recipient already
1070 1070 has, or previously specified in the bundle/changegroup). The format is
1071 1071 described more fully in "hg help internals.bdiff", but briefly:
1072 1072
1073 1073 +---------------------------------------------------------------+
1074 1074 | | | | |
1075 1075 | start offset | end offset | new length | content |
1076 1076 | (4 bytes) | (4 bytes) | (4 bytes) | (<new length> bytes) |
1077 1077 | | | | |
1078 1078 +---------------------------------------------------------------+
1079 1079
1080 1080 Please note that the length field in the delta data does *not* include
1081 1081 itself.
1082 1082
1083 1083 In version 1, the delta is always applied against the previous node from
1084 1084 the changegroup or the first parent if this is the first entry in the
1085 1085 changegroup.
1086 1086
1087 1087 In version 2 and up, the delta base node is encoded in the entry in the
1088 1088 changegroup. This allows the delta to be expressed against any parent,
1089 1089 which can result in smaller deltas and more efficient encoding of data.
1090 1090
1091 1091 Changeset Segment
1092 1092 =================
1093 1093
1094 1094 The *changeset segment* consists of a single *delta group* holding
1095 1095 changelog data. The *empty chunk* at the end of the *delta group* denotes
1096 1096 the boundary to the *manifest segment*.
1097 1097
1098 1098 Manifest Segment
1099 1099 ================
1100 1100
1101 1101 The *manifest segment* consists of a single *delta group* holding manifest
1102 1102 data. If treemanifests are in use, it contains only the manifest for the
1103 1103 root directory of the repository. Otherwise, it contains the entire
1104 1104 manifest data. The *empty chunk* at the end of the *delta group* denotes
1105 1105 the boundary to the next segment (either the *treemanifests segment* or
1106 1106 the *filelogs segment*, depending on version and the request options).
1107 1107
1108 1108 Treemanifests Segment
1109 1109 ---------------------
1110 1110
1111 1111 The *treemanifests segment* only exists in changegroup version "3", and
1112 1112 only if the 'treemanifest' param is part of the bundle2 changegroup part
1113 1113 (it is not possible to use changegroup version 3 outside of bundle2).
1114 1114 Aside from the filenames in the *treemanifests segment* containing a
1115 1115 trailing "/" character, it behaves identically to the *filelogs segment*
1116 1116 (see below). The final sub-segment is followed by an *empty chunk*
1117 1117 (logically, a sub-segment with filename size 0). This denotes the boundary
1118 1118 to the *filelogs segment*.
1119 1119
1120 1120 Filelogs Segment
1121 1121 ================
1122 1122
1123 1123 The *filelogs segment* consists of multiple sub-segments, each
1124 1124 corresponding to an individual file whose data is being described:
1125 1125
1126 1126 +--------------------------------------------------+
1127 1127 | | | | | |
1128 1128 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
1129 1129 | | | | | (4 bytes) |
1130 1130 | | | | | |
1131 1131 +--------------------------------------------------+
1132 1132
1133 1133 The final filelog sub-segment is followed by an *empty chunk* (logically,
1134 1134 a sub-segment with filename size 0). This denotes the end of the segment
1135 1135 and of the overall changegroup.
1136 1136
1137 1137 Each filelog sub-segment consists of the following:
1138 1138
1139 1139 +------------------------------------------------------+
1140 1140 | | | |
1141 1141 | filename length | filename | delta group |
1142 1142 | (4 bytes) | (<length - 4> bytes) | (various) |
1143 1143 | | | |
1144 1144 +------------------------------------------------------+
1145 1145
1146 1146 That is, a *chunk* consisting of the filename (not terminated or padded)
1147 1147 followed by N chunks constituting the *delta group* for this file. The
1148 1148 *empty chunk* at the end of each *delta group* denotes the boundary to the
1149 1149 next filelog sub-segment.
1150 1150
1151 1151 Test list of commands with command with no help text
1152 1152
1153 1153 $ hg help helpext
1154 1154 helpext extension - no help text available
1155 1155
1156 1156 list of commands:
1157 1157
1158 1158 nohelp (no help text available)
1159 1159
1160 1160 (use 'hg help -v helpext' to show built-in aliases and global options)
1161 1161
1162 1162
1163 1163 test advanced, deprecated and experimental options are hidden in command help
1164 1164 $ hg help debugoptADV
1165 1165 hg debugoptADV
1166 1166
1167 1167 (no help text available)
1168 1168
1169 1169 options:
1170 1170
1171 1171 (some details hidden, use --verbose to show complete help)
1172 1172 $ hg help debugoptDEP
1173 1173 hg debugoptDEP
1174 1174
1175 1175 (no help text available)
1176 1176
1177 1177 options:
1178 1178
1179 1179 (some details hidden, use --verbose to show complete help)
1180 1180
1181 1181 $ hg help debugoptEXP
1182 1182 hg debugoptEXP
1183 1183
1184 1184 (no help text available)
1185 1185
1186 1186 options:
1187 1187
1188 1188 (some details hidden, use --verbose to show complete help)
1189 1189
1190 1190 test advanced, deprecated and experimental options are shown with -v
1191 1191 $ hg help -v debugoptADV | grep aopt
1192 1192 --aopt option is (ADVANCED)
1193 1193 $ hg help -v debugoptDEP | grep dopt
1194 1194 --dopt option is (DEPRECATED)
1195 1195 $ hg help -v debugoptEXP | grep eopt
1196 1196 --eopt option is (EXPERIMENTAL)
1197 1197
1198 1198 #if gettext
1199 1199 test deprecated option is hidden with translation with untranslated description
1200 1200 (use many globy for not failing on changed transaction)
1201 1201 $ LANGUAGE=sv hg help debugoptDEP
1202 1202 hg debugoptDEP
1203 1203
1204 1204 (*) (glob)
1205 1205
1206 1206 options:
1207 1207
1208 1208 (some details hidden, use --verbose to show complete help)
1209 1209 #endif
1210 1210
1211 1211 Test commands that collide with topics (issue4240)
1212 1212
1213 1213 $ hg config -hq
1214 1214 hg config [-u] [NAME]...
1215 1215
1216 1216 show combined config settings from all hgrc files
1217 1217 $ hg showconfig -hq
1218 1218 hg config [-u] [NAME]...
1219 1219
1220 1220 show combined config settings from all hgrc files
1221 1221
1222 1222 Test a help topic
1223 1223
1224 1224 $ hg help dates
1225 1225 Date Formats
1226 1226 """"""""""""
1227 1227
1228 1228 Some commands allow the user to specify a date, e.g.:
1229 1229
1230 1230 - backout, commit, import, tag: Specify the commit date.
1231 1231 - log, revert, update: Select revision(s) by date.
1232 1232
1233 1233 Many date formats are valid. Here are some examples:
1234 1234
1235 1235 - "Wed Dec 6 13:18:29 2006" (local timezone assumed)
1236 1236 - "Dec 6 13:18 -0600" (year assumed, time offset provided)
1237 1237 - "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
1238 1238 - "Dec 6" (midnight)
1239 1239 - "13:18" (today assumed)
1240 1240 - "3:39" (3:39AM assumed)
1241 1241 - "3:39pm" (15:39)
1242 1242 - "2006-12-06 13:18:29" (ISO 8601 format)
1243 1243 - "2006-12-6 13:18"
1244 1244 - "2006-12-6"
1245 1245 - "12-6"
1246 1246 - "12/6"
1247 1247 - "12/6/6" (Dec 6 2006)
1248 1248 - "today" (midnight)
1249 1249 - "yesterday" (midnight)
1250 1250 - "now" - right now
1251 1251
1252 1252 Lastly, there is Mercurial's internal format:
1253 1253
1254 1254 - "1165411109 0" (Wed Dec 6 13:18:29 2006 UTC)
1255 1255
1256 1256 This is the internal representation format for dates. The first number is
1257 1257 the number of seconds since the epoch (1970-01-01 00:00 UTC). The second
1258 1258 is the offset of the local timezone, in seconds west of UTC (negative if
1259 1259 the timezone is east of UTC).
1260 1260
1261 1261 The log command also accepts date ranges:
1262 1262
1263 1263 - "<DATE" - at or before a given date/time
1264 1264 - ">DATE" - on or after a given date/time
1265 1265 - "DATE to DATE" - a date range, inclusive
1266 1266 - "-DAYS" - within a given number of days of today
1267 1267
1268 1268 Test repeated config section name
1269 1269
1270 1270 $ hg help config.host
1271 1271 "http_proxy.host"
1272 1272 Host name and (optional) port of the proxy server, for example
1273 1273 "myproxy:8000".
1274 1274
1275 1275 "smtp.host"
1276 1276 Host name of mail server, e.g. "mail.example.com".
1277 1277
1278 1278 Unrelated trailing paragraphs shouldn't be included
1279 1279
1280 1280 $ hg help config.extramsg | grep '^$'
1281 1281
1282 1282
1283 1283 Test capitalized section name
1284 1284
1285 1285 $ hg help scripting.HGPLAIN > /dev/null
1286 1286
1287 1287 Help subsection:
1288 1288
1289 1289 $ hg help config.charsets |grep "Email example:" > /dev/null
1290 1290 [1]
1291 1291
1292 1292 Show nested definitions
1293 1293 ("profiling.type"[break]"ls"[break]"stat"[break])
1294 1294
1295 1295 $ hg help config.type | egrep '^$'|wc -l
1296 1296 \s*3 (re)
1297 1297
1298 1298 Separate sections from subsections
1299 1299
1300 1300 $ hg help config.format | egrep '^ ("|-)|^\s*$' | uniq
1301 1301 "format"
1302 1302 --------
1303 1303
1304 1304 "usegeneraldelta"
1305 1305
1306 1306 "dotencode"
1307 1307
1308 1308 "usefncache"
1309 1309
1310 1310 "usestore"
1311 1311
1312 1312 "profiling"
1313 1313 -----------
1314 1314
1315 1315 "format"
1316 1316
1317 1317 "progress"
1318 1318 ----------
1319 1319
1320 1320 "format"
1321 1321
1322 1322
1323 1323 Last item in help config.*:
1324 1324
1325 1325 $ hg help config.`hg help config|grep '^ "'| \
1326 1326 > tail -1|sed 's![ "]*!!g'`| \
1327 1327 > grep 'hg help -c config' > /dev/null
1328 1328 [1]
1329 1329
1330 1330 note to use help -c for general hg help config:
1331 1331
1332 1332 $ hg help config |grep 'hg help -c config' > /dev/null
1333 1333
1334 1334 Test templating help
1335 1335
1336 1336 $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
1337 1337 desc String. The text of the changeset description.
1338 1338 diffstat String. Statistics of changes with the following format:
1339 1339 firstline Any text. Returns the first line of text.
1340 1340 nonempty Any text. Returns '(none)' if the string is empty.
1341 1341
1342 1342 Test deprecated items
1343 1343
1344 1344 $ hg help -v templating | grep currentbookmark
1345 1345 currentbookmark
1346 1346 $ hg help templating | (grep currentbookmark || true)
1347 1347
1348 1348 Test help hooks
1349 1349
1350 1350 $ cat > helphook1.py <<EOF
1351 1351 > from mercurial import help
1352 1352 >
1353 1353 > def rewrite(ui, topic, doc):
1354 1354 > return doc + '\nhelphook1\n'
1355 1355 >
1356 1356 > def extsetup(ui):
1357 1357 > help.addtopichook('revisions', rewrite)
1358 1358 > EOF
1359 1359 $ cat > helphook2.py <<EOF
1360 1360 > from mercurial import help
1361 1361 >
1362 1362 > def rewrite(ui, topic, doc):
1363 1363 > return doc + '\nhelphook2\n'
1364 1364 >
1365 1365 > def extsetup(ui):
1366 1366 > help.addtopichook('revisions', rewrite)
1367 1367 > EOF
1368 1368 $ echo '[extensions]' >> $HGRCPATH
1369 1369 $ echo "helphook1 = `pwd`/helphook1.py" >> $HGRCPATH
1370 1370 $ echo "helphook2 = `pwd`/helphook2.py" >> $HGRCPATH
1371 1371 $ hg help revsets | grep helphook
1372 1372 helphook1
1373 1373 helphook2
1374 1374
1375 1375 help -c should only show debug --debug
1376 1376
1377 1377 $ hg help -c --debug|egrep debug|wc -l|egrep '^\s*0\s*$'
1378 1378 [1]
1379 1379
1380 1380 help -c should only show deprecated for -v
1381 1381
1382 1382 $ hg help -c -v|egrep DEPRECATED|wc -l|egrep '^\s*0\s*$'
1383 1383 [1]
1384 1384
1385 1385 Test -s / --system
1386 1386
1387 1387 $ hg help config.files -s windows |grep 'etc/mercurial' | \
1388 1388 > wc -l | sed -e 's/ //g'
1389 1389 0
1390 1390 $ hg help config.files --system unix | grep 'USER' | \
1391 1391 > wc -l | sed -e 's/ //g'
1392 1392 0
1393 1393
1394 1394 Test -e / -c / -k combinations
1395 1395
1396 1396 $ hg help -c|egrep '^[A-Z].*:|^ debug'
1397 1397 Commands:
1398 1398 $ hg help -e|egrep '^[A-Z].*:|^ debug'
1399 1399 Extensions:
1400 1400 $ hg help -k|egrep '^[A-Z].*:|^ debug'
1401 1401 Topics:
1402 1402 Commands:
1403 1403 Extensions:
1404 1404 Extension Commands:
1405 1405 $ hg help -c schemes
1406 1406 abort: no such help topic: schemes
1407 1407 (try 'hg help --keyword schemes')
1408 1408 [255]
1409 1409 $ hg help -e schemes |head -1
1410 1410 schemes extension - extend schemes with shortcuts to repository swarms
1411 1411 $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):'
1412 1412 Commands:
1413 1413 $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):'
1414 1414 Extensions:
1415 1415 $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):'
1416 1416 Extensions:
1417 1417 Commands:
1418 1418 $ hg help -c commit > /dev/null
1419 1419 $ hg help -e -c commit > /dev/null
1420 1420 $ hg help -e commit > /dev/null
1421 1421 abort: no such help topic: commit
1422 1422 (try 'hg help --keyword commit')
1423 1423 [255]
1424 1424
1425 1425 Test keyword search help
1426 1426
1427 1427 $ cat > prefixedname.py <<EOF
1428 1428 > '''matched against word "clone"
1429 1429 > '''
1430 1430 > EOF
1431 1431 $ echo '[extensions]' >> $HGRCPATH
1432 1432 $ echo "dot.dot.prefixedname = `pwd`/prefixedname.py" >> $HGRCPATH
1433 1433 $ hg help -k clone
1434 1434 Topics:
1435 1435
1436 1436 config Configuration Files
1437 1437 extensions Using Additional Features
1438 1438 glossary Glossary
1439 1439 phases Working with Phases
1440 1440 subrepos Subrepositories
1441 1441 urls URL Paths
1442 1442
1443 1443 Commands:
1444 1444
1445 1445 bookmarks create a new bookmark or list existing bookmarks
1446 1446 clone make a copy of an existing repository
1447 1447 paths show aliases for remote repositories
1448 1448 update update working directory (or switch revisions)
1449 1449
1450 1450 Extensions:
1451 1451
1452 1452 clonebundles advertise pre-generated bundles to seed clones
1453 1453 prefixedname matched against word "clone"
1454 1454 relink recreates hardlinks between repository clones
1455 1455
1456 1456 Extension Commands:
1457 1457
1458 1458 qclone clone main and patch repository at same time
1459 1459
1460 1460 Test unfound topic
1461 1461
1462 1462 $ hg help nonexistingtopicthatwillneverexisteverever
1463 1463 abort: no such help topic: nonexistingtopicthatwillneverexisteverever
1464 1464 (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever')
1465 1465 [255]
1466 1466
1467 1467 Test unfound keyword
1468 1468
1469 1469 $ hg help --keyword nonexistingwordthatwillneverexisteverever
1470 1470 abort: no matches
1471 1471 (try 'hg help' for a list of topics)
1472 1472 [255]
1473 1473
1474 1474 Test omit indicating for help
1475 1475
1476 1476 $ cat > addverboseitems.py <<EOF
1477 1477 > '''extension to test omit indicating.
1478 1478 >
1479 1479 > This paragraph is never omitted (for extension)
1480 1480 >
1481 1481 > .. container:: verbose
1482 1482 >
1483 1483 > This paragraph is omitted,
1484 1484 > if :hg:\`help\` is invoked without \`\`-v\`\` (for extension)
1485 1485 >
1486 1486 > This paragraph is never omitted, too (for extension)
1487 1487 > '''
1488 1488 >
1489 1489 > from mercurial import help, commands
1490 1490 > testtopic = """This paragraph is never omitted (for topic).
1491 1491 >
1492 1492 > .. container:: verbose
1493 1493 >
1494 1494 > This paragraph is omitted,
1495 1495 > if :hg:\`help\` is invoked without \`\`-v\`\` (for topic)
1496 1496 >
1497 1497 > This paragraph is never omitted, too (for topic)
1498 1498 > """
1499 1499 > def extsetup(ui):
1500 1500 > help.helptable.append((["topic-containing-verbose"],
1501 1501 > "This is the topic to test omit indicating.",
1502 1502 > lambda ui: testtopic))
1503 1503 > EOF
1504 1504 $ echo '[extensions]' >> $HGRCPATH
1505 1505 $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH
1506 1506 $ hg help addverboseitems
1507 1507 addverboseitems extension - extension to test omit indicating.
1508 1508
1509 1509 This paragraph is never omitted (for extension)
1510 1510
1511 1511 This paragraph is never omitted, too (for extension)
1512 1512
1513 1513 (some details hidden, use --verbose to show complete help)
1514 1514
1515 1515 no commands defined
1516 1516 $ hg help -v addverboseitems
1517 1517 addverboseitems extension - extension to test omit indicating.
1518 1518
1519 1519 This paragraph is never omitted (for extension)
1520 1520
1521 1521 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1522 1522 extension)
1523 1523
1524 1524 This paragraph is never omitted, too (for extension)
1525 1525
1526 1526 no commands defined
1527 1527 $ hg help topic-containing-verbose
1528 1528 This is the topic to test omit indicating.
1529 1529 """"""""""""""""""""""""""""""""""""""""""
1530 1530
1531 1531 This paragraph is never omitted (for topic).
1532 1532
1533 1533 This paragraph is never omitted, too (for topic)
1534 1534
1535 1535 (some details hidden, use --verbose to show complete help)
1536 1536 $ hg help -v topic-containing-verbose
1537 1537 This is the topic to test omit indicating.
1538 1538 """"""""""""""""""""""""""""""""""""""""""
1539 1539
1540 1540 This paragraph is never omitted (for topic).
1541 1541
1542 1542 This paragraph is omitted, if 'hg help' is invoked without "-v" (for
1543 1543 topic)
1544 1544
1545 1545 This paragraph is never omitted, too (for topic)
1546 1546
1547 1547 Test section lookup
1548 1548
1549 1549 $ hg help revset.merge
1550 1550 "merge()"
1551 1551 Changeset is a merge changeset.
1552 1552
1553 1553 $ hg help glossary.dag
1554 1554 DAG
1555 1555 The repository of changesets of a distributed version control system
1556 1556 (DVCS) can be described as a directed acyclic graph (DAG), consisting
1557 1557 of nodes and edges, where nodes correspond to changesets and edges
1558 1558 imply a parent -> child relation. This graph can be visualized by
1559 1559 graphical tools such as 'hg log --graph'. In Mercurial, the DAG is
1560 1560 limited by the requirement for children to have at most two parents.
1561 1561
1562 1562
1563 1563 $ hg help hgrc.paths
1564 1564 "paths"
1565 1565 -------
1566 1566
1567 1567 Assigns symbolic names and behavior to repositories.
1568 1568
1569 1569 Options are symbolic names defining the URL or directory that is the
1570 1570 location of the repository. Example:
1571 1571
1572 1572 [paths]
1573 1573 my_server = https://example.com/my_repo
1574 1574 local_path = /home/me/repo
1575 1575
1576 1576 These symbolic names can be used from the command line. To pull from
1577 1577 "my_server": 'hg pull my_server'. To push to "local_path": 'hg push
1578 1578 local_path'.
1579 1579
1580 1580 Options containing colons (":") denote sub-options that can influence
1581 1581 behavior for that specific path. Example:
1582 1582
1583 1583 [paths]
1584 1584 my_server = https://example.com/my_path
1585 1585 my_server:pushurl = ssh://example.com/my_path
1586 1586
1587 1587 The following sub-options can be defined:
1588 1588
1589 1589 "pushurl"
1590 1590 The URL to use for push operations. If not defined, the location
1591 1591 defined by the path's main entry is used.
1592 1592
1593 1593 "pushrev"
1594 1594 A revset defining which revisions to push by default.
1595 1595
1596 1596 When 'hg push' is executed without a "-r" argument, the revset defined
1597 1597 by this sub-option is evaluated to determine what to push.
1598 1598
1599 1599 For example, a value of "." will push the working directory's revision
1600 1600 by default.
1601 1601
1602 1602 Revsets specifying bookmarks will not result in the bookmark being
1603 1603 pushed.
1604 1604
1605 1605 The following special named paths exist:
1606 1606
1607 1607 "default"
1608 1608 The URL or directory to use when no source or remote is specified.
1609 1609
1610 1610 'hg clone' will automatically define this path to the location the
1611 1611 repository was cloned from.
1612 1612
1613 1613 "default-push"
1614 1614 (deprecated) The URL or directory for the default 'hg push' location.
1615 1615 "default:pushurl" should be used instead.
1616 1616
1617 1617 $ hg help glossary.mcguffin
1618 1618 abort: help section not found: glossary.mcguffin
1619 1619 [255]
1620 1620
1621 1621 $ hg help glossary.mc.guffin
1622 1622 abort: help section not found: glossary.mc.guffin
1623 1623 [255]
1624 1624
1625 1625 $ hg help template.files
1626 1626 files List of strings. All files modified, added, or removed by
1627 1627 this changeset.
1628 1628 files(pattern)
1629 1629 All files of the current changeset matching the pattern. See
1630 1630 'hg help patterns'.
1631 1631
1632 1632 Test section lookup by translated message
1633 1633
1634 1634 str.lower() instead of encoding.lower(str) on translated message might
1635 1635 make message meaningless, because some encoding uses 0x41(A) - 0x5a(Z)
1636 1636 as the second or later byte of multi-byte character.
1637 1637
1638 1638 For example, "\x8bL\x98^" (translation of "record" in ja_JP.cp932)
1639 1639 contains 0x4c (L). str.lower() replaces 0x4c(L) by 0x6c(l) and this
1640 1640 replacement makes message meaningless.
1641 1641
1642 1642 This tests that section lookup by translated string isn't broken by
1643 1643 such str.lower().
1644 1644
1645 1645 $ python <<EOF
1646 1646 > def escape(s):
1647 1647 > return ''.join('\u%x' % ord(uc) for uc in s.decode('cp932'))
1648 1648 > # translation of "record" in ja_JP.cp932
1649 1649 > upper = "\x8bL\x98^"
1650 1650 > # str.lower()-ed section name should be treated as different one
1651 1651 > lower = "\x8bl\x98^"
1652 1652 > with open('ambiguous.py', 'w') as fp:
1653 1653 > fp.write("""# ambiguous section names in ja_JP.cp932
1654 1654 > u'''summary of extension
1655 1655 >
1656 1656 > %s
1657 1657 > ----
1658 1658 >
1659 1659 > Upper name should show only this message
1660 1660 >
1661 1661 > %s
1662 1662 > ----
1663 1663 >
1664 1664 > Lower name should show only this message
1665 1665 >
1666 1666 > subsequent section
1667 1667 > ------------------
1668 1668 >
1669 1669 > This should be hidden at 'hg help ambiguous' with section name.
1670 1670 > '''
1671 1671 > """ % (escape(upper), escape(lower)))
1672 1672 > EOF
1673 1673
1674 1674 $ cat >> $HGRCPATH <<EOF
1675 1675 > [extensions]
1676 1676 > ambiguous = ./ambiguous.py
1677 1677 > EOF
1678 1678
1679 1679 $ python <<EOF | sh
1680 1680 > upper = "\x8bL\x98^"
1681 1681 > print "hg --encoding cp932 help -e ambiguous.%s" % upper
1682 1682 > EOF
1683 1683 \x8bL\x98^ (esc)
1684 1684 ----
1685 1685
1686 1686 Upper name should show only this message
1687 1687
1688 1688
1689 1689 $ python <<EOF | sh
1690 1690 > lower = "\x8bl\x98^"
1691 1691 > print "hg --encoding cp932 help -e ambiguous.%s" % lower
1692 1692 > EOF
1693 1693 \x8bl\x98^ (esc)
1694 1694 ----
1695 1695
1696 1696 Lower name should show only this message
1697 1697
1698 1698
1699 1699 $ cat >> $HGRCPATH <<EOF
1700 1700 > [extensions]
1701 1701 > ambiguous = !
1702 1702 > EOF
1703 1703
1704 1704 Show help content of disabled extensions
1705 1705
1706 1706 $ cat >> $HGRCPATH <<EOF
1707 1707 > [extensions]
1708 1708 > ambiguous = !./ambiguous.py
1709 1709 > EOF
1710 1710 $ hg help -e ambiguous
1711 1711 ambiguous extension - (no help text available)
1712 1712
1713 1713 (use 'hg help extensions' for information on enabling extensions)
1714 1714
1715 1715 Test dynamic list of merge tools only shows up once
1716 1716 $ hg help merge-tools
1717 1717 Merge Tools
1718 1718 """""""""""
1719 1719
1720 1720 To merge files Mercurial uses merge tools.
1721 1721
1722 1722 A merge tool combines two different versions of a file into a merged file.
1723 1723 Merge tools are given the two files and the greatest common ancestor of
1724 1724 the two file versions, so they can determine the changes made on both
1725 1725 branches.
1726 1726
1727 1727 Merge tools are used both for 'hg resolve', 'hg merge', 'hg update', 'hg
1728 1728 backout' and in several extensions.
1729 1729
1730 1730 Usually, the merge tool tries to automatically reconcile the files by
1731 1731 combining all non-overlapping changes that occurred separately in the two
1732 1732 different evolutions of the same initial base file. Furthermore, some
1733 1733 interactive merge programs make it easier to manually resolve conflicting
1734 1734 merges, either in a graphical way, or by inserting some conflict markers.
1735 1735 Mercurial does not include any interactive merge programs but relies on
1736 1736 external tools for that.
1737 1737
1738 1738 Available merge tools
1739 1739 =====================
1740 1740
1741 1741 External merge tools and their properties are configured in the merge-
1742 1742 tools configuration section - see hgrc(5) - but they can often just be
1743 1743 named by their executable.
1744 1744
1745 1745 A merge tool is generally usable if its executable can be found on the
1746 1746 system and if it can handle the merge. The executable is found if it is an
1747 1747 absolute or relative executable path or the name of an application in the
1748 1748 executable search path. The tool is assumed to be able to handle the merge
1749 1749 if it can handle symlinks if the file is a symlink, if it can handle
1750 1750 binary files if the file is binary, and if a GUI is available if the tool
1751 1751 requires a GUI.
1752 1752
1753 1753 There are some internal merge tools which can be used. The internal merge
1754 1754 tools are:
1755 1755
1756 1756 ":dump"
1757 1757 Creates three versions of the files to merge, containing the contents of
1758 1758 local, other and base. These files can then be used to perform a merge
1759 1759 manually. If the file to be merged is named "a.txt", these files will
1760 1760 accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
1761 1761 they will be placed in the same directory as "a.txt".
1762 1762
1763 1763 ":fail"
1764 1764 Rather than attempting to merge files that were modified on both
1765 1765 branches, it marks them as unresolved. The resolve command must be used
1766 1766 to resolve these conflicts.
1767 1767
1768 1768 ":local"
1769 1769 Uses the local 'p1()' version of files as the merged version.
1770 1770
1771 1771 ":merge"
1772 1772 Uses the internal non-interactive simple merge algorithm for merging
1773 1773 files. It will fail if there are any conflicts and leave markers in the
1774 1774 partially merged file. Markers will have two sections, one for each side
1775 1775 of merge.
1776 1776
1777 1777 ":merge-local"
1778 1778 Like :merge, but resolve all conflicts non-interactively in favor of the
1779 1779 local 'p1()' changes.
1780 1780
1781 1781 ":merge-other"
1782 1782 Like :merge, but resolve all conflicts non-interactively in favor of the
1783 1783 other 'p2()' changes.
1784 1784
1785 1785 ":merge3"
1786 1786 Uses the internal non-interactive simple merge algorithm for merging
1787 1787 files. It will fail if there are any conflicts and leave markers in the
1788 1788 partially merged file. Marker will have three sections, one from each
1789 1789 side of the merge and one for the base content.
1790 1790
1791 1791 ":other"
1792 1792 Uses the other 'p2()' version of files as the merged version.
1793 1793
1794 1794 ":prompt"
1795 1795 Asks the user which of the local 'p1()' or the other 'p2()' version to
1796 1796 keep as the merged version.
1797 1797
1798 1798 ":tagmerge"
1799 1799 Uses the internal tag merge algorithm (experimental).
1800 1800
1801 1801 ":union"
1802 1802 Uses the internal non-interactive simple merge algorithm for merging
1803 1803 files. It will use both left and right sides for conflict regions. No
1804 1804 markers are inserted.
1805 1805
1806 1806 Internal tools are always available and do not require a GUI but will by
1807 1807 default not handle symlinks or binary files.
1808 1808
1809 1809 Choosing a merge tool
1810 1810 =====================
1811 1811
1812 1812 Mercurial uses these rules when deciding which merge tool to use:
1813 1813
1814 1814 1. If a tool has been specified with the --tool option to merge or
1815 1815 resolve, it is used. If it is the name of a tool in the merge-tools
1816 1816 configuration, its configuration is used. Otherwise the specified tool
1817 1817 must be executable by the shell.
1818 1818 2. If the "HGMERGE" environment variable is present, its value is used and
1819 1819 must be executable by the shell.
1820 1820 3. If the filename of the file to be merged matches any of the patterns in
1821 1821 the merge-patterns configuration section, the first usable merge tool
1822 1822 corresponding to a matching pattern is used. Here, binary capabilities
1823 1823 of the merge tool are not considered.
1824 1824 4. If ui.merge is set it will be considered next. If the value is not the
1825 1825 name of a configured tool, the specified value is used and must be
1826 1826 executable by the shell. Otherwise the named tool is used if it is
1827 1827 usable.
1828 1828 5. If any usable merge tools are present in the merge-tools configuration
1829 1829 section, the one with the highest priority is used.
1830 1830 6. If a program named "hgmerge" can be found on the system, it is used -
1831 1831 but it will by default not be used for symlinks and binary files.
1832 1832 7. If the file to be merged is not binary and is not a symlink, then
1833 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 1836 Note:
1837 1837 After selecting a merge program, Mercurial will by default attempt to
1838 1838 merge the files using a simple merge algorithm first. Only if it
1839 1839 doesn't succeed because of conflicting changes Mercurial will actually
1840 1840 execute the merge program. Whether to use the simple merge algorithm
1841 1841 first can be controlled by the premerge setting of the merge tool.
1842 1842 Premerge is enabled by default unless the file is binary or a symlink.
1843 1843
1844 1844 See the merge-tools and ui sections of hgrc(5) for details on the
1845 1845 configuration of merge tools.
1846 1846
1847 1847 Compression engines listed in `hg help bundlespec`
1848 1848
1849 1849 $ hg help bundlespec | grep gzip
1850 1850 "v1" bundles can only use the "gzip", "bzip2", and "none" compression
1851 1851 An algorithm that produces smaller bundles than "gzip".
1852 1852 This engine will likely produce smaller bundles than "gzip" but will be
1853 1853 "gzip"
1854 1854 better compression than "gzip". It also frequently yields better (?)
1855 1855
1856 1856 Test usage of section marks in help documents
1857 1857
1858 1858 $ cd "$TESTDIR"/../doc
1859 1859 $ python check-seclevel.py
1860 1860 $ cd $TESTTMP
1861 1861
1862 1862 #if serve
1863 1863
1864 1864 Test the help pages in hgweb.
1865 1865
1866 1866 Dish up an empty repo; serve it cold.
1867 1867
1868 1868 $ hg init "$TESTTMP/test"
1869 1869 $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid
1870 1870 $ cat hg.pid >> $DAEMON_PIDS
1871 1871
1872 1872 $ get-with-headers.py $LOCALIP:$HGPORT "help"
1873 1873 200 Script output follows
1874 1874
1875 1875 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1876 1876 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1877 1877 <head>
1878 1878 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1879 1879 <meta name="robots" content="index, nofollow" />
1880 1880 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1881 1881 <script type="text/javascript" src="/static/mercurial.js"></script>
1882 1882
1883 1883 <title>Help: Index</title>
1884 1884 </head>
1885 1885 <body>
1886 1886
1887 1887 <div class="container">
1888 1888 <div class="menu">
1889 1889 <div class="logo">
1890 1890 <a href="https://mercurial-scm.org/">
1891 1891 <img src="/static/hglogo.png" alt="mercurial" /></a>
1892 1892 </div>
1893 1893 <ul>
1894 1894 <li><a href="/shortlog">log</a></li>
1895 1895 <li><a href="/graph">graph</a></li>
1896 1896 <li><a href="/tags">tags</a></li>
1897 1897 <li><a href="/bookmarks">bookmarks</a></li>
1898 1898 <li><a href="/branches">branches</a></li>
1899 1899 </ul>
1900 1900 <ul>
1901 1901 <li class="active">help</li>
1902 1902 </ul>
1903 1903 </div>
1904 1904
1905 1905 <div class="main">
1906 1906 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1907 1907 <form class="search" action="/log">
1908 1908
1909 1909 <p><input name="rev" id="search1" type="text" size="30" /></p>
1910 1910 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1911 1911 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1912 1912 </form>
1913 1913 <table class="bigtable">
1914 1914 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
1915 1915
1916 1916 <tr><td>
1917 1917 <a href="/help/bundlespec">
1918 1918 bundlespec
1919 1919 </a>
1920 1920 </td><td>
1921 1921 Bundle File Formats
1922 1922 </td></tr>
1923 1923 <tr><td>
1924 1924 <a href="/help/color">
1925 1925 color
1926 1926 </a>
1927 1927 </td><td>
1928 1928 Colorizing Outputs
1929 1929 </td></tr>
1930 1930 <tr><td>
1931 1931 <a href="/help/config">
1932 1932 config
1933 1933 </a>
1934 1934 </td><td>
1935 1935 Configuration Files
1936 1936 </td></tr>
1937 1937 <tr><td>
1938 1938 <a href="/help/dates">
1939 1939 dates
1940 1940 </a>
1941 1941 </td><td>
1942 1942 Date Formats
1943 1943 </td></tr>
1944 1944 <tr><td>
1945 1945 <a href="/help/diffs">
1946 1946 diffs
1947 1947 </a>
1948 1948 </td><td>
1949 1949 Diff Formats
1950 1950 </td></tr>
1951 1951 <tr><td>
1952 1952 <a href="/help/environment">
1953 1953 environment
1954 1954 </a>
1955 1955 </td><td>
1956 1956 Environment Variables
1957 1957 </td></tr>
1958 1958 <tr><td>
1959 1959 <a href="/help/extensions">
1960 1960 extensions
1961 1961 </a>
1962 1962 </td><td>
1963 1963 Using Additional Features
1964 1964 </td></tr>
1965 1965 <tr><td>
1966 1966 <a href="/help/filesets">
1967 1967 filesets
1968 1968 </a>
1969 1969 </td><td>
1970 1970 Specifying File Sets
1971 1971 </td></tr>
1972 1972 <tr><td>
1973 1973 <a href="/help/glossary">
1974 1974 glossary
1975 1975 </a>
1976 1976 </td><td>
1977 1977 Glossary
1978 1978 </td></tr>
1979 1979 <tr><td>
1980 1980 <a href="/help/hgignore">
1981 1981 hgignore
1982 1982 </a>
1983 1983 </td><td>
1984 1984 Syntax for Mercurial Ignore Files
1985 1985 </td></tr>
1986 1986 <tr><td>
1987 1987 <a href="/help/hgweb">
1988 1988 hgweb
1989 1989 </a>
1990 1990 </td><td>
1991 1991 Configuring hgweb
1992 1992 </td></tr>
1993 1993 <tr><td>
1994 1994 <a href="/help/internals">
1995 1995 internals
1996 1996 </a>
1997 1997 </td><td>
1998 1998 Technical implementation topics
1999 1999 </td></tr>
2000 2000 <tr><td>
2001 2001 <a href="/help/merge-tools">
2002 2002 merge-tools
2003 2003 </a>
2004 2004 </td><td>
2005 2005 Merge Tools
2006 2006 </td></tr>
2007 2007 <tr><td>
2008 2008 <a href="/help/pager">
2009 2009 pager
2010 2010 </a>
2011 2011 </td><td>
2012 2012 Pager Support
2013 2013 </td></tr>
2014 2014 <tr><td>
2015 2015 <a href="/help/patterns">
2016 2016 patterns
2017 2017 </a>
2018 2018 </td><td>
2019 2019 File Name Patterns
2020 2020 </td></tr>
2021 2021 <tr><td>
2022 2022 <a href="/help/phases">
2023 2023 phases
2024 2024 </a>
2025 2025 </td><td>
2026 2026 Working with Phases
2027 2027 </td></tr>
2028 2028 <tr><td>
2029 2029 <a href="/help/revisions">
2030 2030 revisions
2031 2031 </a>
2032 2032 </td><td>
2033 2033 Specifying Revisions
2034 2034 </td></tr>
2035 2035 <tr><td>
2036 2036 <a href="/help/scripting">
2037 2037 scripting
2038 2038 </a>
2039 2039 </td><td>
2040 2040 Using Mercurial from scripts and automation
2041 2041 </td></tr>
2042 2042 <tr><td>
2043 2043 <a href="/help/subrepos">
2044 2044 subrepos
2045 2045 </a>
2046 2046 </td><td>
2047 2047 Subrepositories
2048 2048 </td></tr>
2049 2049 <tr><td>
2050 2050 <a href="/help/templating">
2051 2051 templating
2052 2052 </a>
2053 2053 </td><td>
2054 2054 Template Usage
2055 2055 </td></tr>
2056 2056 <tr><td>
2057 2057 <a href="/help/urls">
2058 2058 urls
2059 2059 </a>
2060 2060 </td><td>
2061 2061 URL Paths
2062 2062 </td></tr>
2063 2063 <tr><td>
2064 2064 <a href="/help/topic-containing-verbose">
2065 2065 topic-containing-verbose
2066 2066 </a>
2067 2067 </td><td>
2068 2068 This is the topic to test omit indicating.
2069 2069 </td></tr>
2070 2070
2071 2071
2072 2072 <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr>
2073 2073
2074 2074 <tr><td>
2075 2075 <a href="/help/add">
2076 2076 add
2077 2077 </a>
2078 2078 </td><td>
2079 2079 add the specified files on the next commit
2080 2080 </td></tr>
2081 2081 <tr><td>
2082 2082 <a href="/help/annotate">
2083 2083 annotate
2084 2084 </a>
2085 2085 </td><td>
2086 2086 show changeset information by line for each file
2087 2087 </td></tr>
2088 2088 <tr><td>
2089 2089 <a href="/help/clone">
2090 2090 clone
2091 2091 </a>
2092 2092 </td><td>
2093 2093 make a copy of an existing repository
2094 2094 </td></tr>
2095 2095 <tr><td>
2096 2096 <a href="/help/commit">
2097 2097 commit
2098 2098 </a>
2099 2099 </td><td>
2100 2100 commit the specified files or all outstanding changes
2101 2101 </td></tr>
2102 2102 <tr><td>
2103 2103 <a href="/help/diff">
2104 2104 diff
2105 2105 </a>
2106 2106 </td><td>
2107 2107 diff repository (or selected files)
2108 2108 </td></tr>
2109 2109 <tr><td>
2110 2110 <a href="/help/export">
2111 2111 export
2112 2112 </a>
2113 2113 </td><td>
2114 2114 dump the header and diffs for one or more changesets
2115 2115 </td></tr>
2116 2116 <tr><td>
2117 2117 <a href="/help/forget">
2118 2118 forget
2119 2119 </a>
2120 2120 </td><td>
2121 2121 forget the specified files on the next commit
2122 2122 </td></tr>
2123 2123 <tr><td>
2124 2124 <a href="/help/init">
2125 2125 init
2126 2126 </a>
2127 2127 </td><td>
2128 2128 create a new repository in the given directory
2129 2129 </td></tr>
2130 2130 <tr><td>
2131 2131 <a href="/help/log">
2132 2132 log
2133 2133 </a>
2134 2134 </td><td>
2135 2135 show revision history of entire repository or files
2136 2136 </td></tr>
2137 2137 <tr><td>
2138 2138 <a href="/help/merge">
2139 2139 merge
2140 2140 </a>
2141 2141 </td><td>
2142 2142 merge another revision into working directory
2143 2143 </td></tr>
2144 2144 <tr><td>
2145 2145 <a href="/help/pull">
2146 2146 pull
2147 2147 </a>
2148 2148 </td><td>
2149 2149 pull changes from the specified source
2150 2150 </td></tr>
2151 2151 <tr><td>
2152 2152 <a href="/help/push">
2153 2153 push
2154 2154 </a>
2155 2155 </td><td>
2156 2156 push changes to the specified destination
2157 2157 </td></tr>
2158 2158 <tr><td>
2159 2159 <a href="/help/remove">
2160 2160 remove
2161 2161 </a>
2162 2162 </td><td>
2163 2163 remove the specified files on the next commit
2164 2164 </td></tr>
2165 2165 <tr><td>
2166 2166 <a href="/help/serve">
2167 2167 serve
2168 2168 </a>
2169 2169 </td><td>
2170 2170 start stand-alone webserver
2171 2171 </td></tr>
2172 2172 <tr><td>
2173 2173 <a href="/help/status">
2174 2174 status
2175 2175 </a>
2176 2176 </td><td>
2177 2177 show changed files in the working directory
2178 2178 </td></tr>
2179 2179 <tr><td>
2180 2180 <a href="/help/summary">
2181 2181 summary
2182 2182 </a>
2183 2183 </td><td>
2184 2184 summarize working directory state
2185 2185 </td></tr>
2186 2186 <tr><td>
2187 2187 <a href="/help/update">
2188 2188 update
2189 2189 </a>
2190 2190 </td><td>
2191 2191 update working directory (or switch revisions)
2192 2192 </td></tr>
2193 2193
2194 2194
2195 2195
2196 2196 <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr>
2197 2197
2198 2198 <tr><td>
2199 2199 <a href="/help/addremove">
2200 2200 addremove
2201 2201 </a>
2202 2202 </td><td>
2203 2203 add all new files, delete all missing files
2204 2204 </td></tr>
2205 2205 <tr><td>
2206 2206 <a href="/help/archive">
2207 2207 archive
2208 2208 </a>
2209 2209 </td><td>
2210 2210 create an unversioned archive of a repository revision
2211 2211 </td></tr>
2212 2212 <tr><td>
2213 2213 <a href="/help/backout">
2214 2214 backout
2215 2215 </a>
2216 2216 </td><td>
2217 2217 reverse effect of earlier changeset
2218 2218 </td></tr>
2219 2219 <tr><td>
2220 2220 <a href="/help/bisect">
2221 2221 bisect
2222 2222 </a>
2223 2223 </td><td>
2224 2224 subdivision search of changesets
2225 2225 </td></tr>
2226 2226 <tr><td>
2227 2227 <a href="/help/bookmarks">
2228 2228 bookmarks
2229 2229 </a>
2230 2230 </td><td>
2231 2231 create a new bookmark or list existing bookmarks
2232 2232 </td></tr>
2233 2233 <tr><td>
2234 2234 <a href="/help/branch">
2235 2235 branch
2236 2236 </a>
2237 2237 </td><td>
2238 2238 set or show the current branch name
2239 2239 </td></tr>
2240 2240 <tr><td>
2241 2241 <a href="/help/branches">
2242 2242 branches
2243 2243 </a>
2244 2244 </td><td>
2245 2245 list repository named branches
2246 2246 </td></tr>
2247 2247 <tr><td>
2248 2248 <a href="/help/bundle">
2249 2249 bundle
2250 2250 </a>
2251 2251 </td><td>
2252 2252 create a bundle file
2253 2253 </td></tr>
2254 2254 <tr><td>
2255 2255 <a href="/help/cat">
2256 2256 cat
2257 2257 </a>
2258 2258 </td><td>
2259 2259 output the current or given revision of files
2260 2260 </td></tr>
2261 2261 <tr><td>
2262 2262 <a href="/help/config">
2263 2263 config
2264 2264 </a>
2265 2265 </td><td>
2266 2266 show combined config settings from all hgrc files
2267 2267 </td></tr>
2268 2268 <tr><td>
2269 2269 <a href="/help/copy">
2270 2270 copy
2271 2271 </a>
2272 2272 </td><td>
2273 2273 mark files as copied for the next commit
2274 2274 </td></tr>
2275 2275 <tr><td>
2276 2276 <a href="/help/files">
2277 2277 files
2278 2278 </a>
2279 2279 </td><td>
2280 2280 list tracked files
2281 2281 </td></tr>
2282 2282 <tr><td>
2283 2283 <a href="/help/graft">
2284 2284 graft
2285 2285 </a>
2286 2286 </td><td>
2287 2287 copy changes from other branches onto the current branch
2288 2288 </td></tr>
2289 2289 <tr><td>
2290 2290 <a href="/help/grep">
2291 2291 grep
2292 2292 </a>
2293 2293 </td><td>
2294 2294 search revision history for a pattern in specified files
2295 2295 </td></tr>
2296 2296 <tr><td>
2297 2297 <a href="/help/heads">
2298 2298 heads
2299 2299 </a>
2300 2300 </td><td>
2301 2301 show branch heads
2302 2302 </td></tr>
2303 2303 <tr><td>
2304 2304 <a href="/help/help">
2305 2305 help
2306 2306 </a>
2307 2307 </td><td>
2308 2308 show help for a given topic or a help overview
2309 2309 </td></tr>
2310 2310 <tr><td>
2311 2311 <a href="/help/hgalias">
2312 2312 hgalias
2313 2313 </a>
2314 2314 </td><td>
2315 2315 summarize working directory state
2316 2316 </td></tr>
2317 2317 <tr><td>
2318 2318 <a href="/help/identify">
2319 2319 identify
2320 2320 </a>
2321 2321 </td><td>
2322 2322 identify the working directory or specified revision
2323 2323 </td></tr>
2324 2324 <tr><td>
2325 2325 <a href="/help/import">
2326 2326 import
2327 2327 </a>
2328 2328 </td><td>
2329 2329 import an ordered set of patches
2330 2330 </td></tr>
2331 2331 <tr><td>
2332 2332 <a href="/help/incoming">
2333 2333 incoming
2334 2334 </a>
2335 2335 </td><td>
2336 2336 show new changesets found in source
2337 2337 </td></tr>
2338 2338 <tr><td>
2339 2339 <a href="/help/manifest">
2340 2340 manifest
2341 2341 </a>
2342 2342 </td><td>
2343 2343 output the current or given revision of the project manifest
2344 2344 </td></tr>
2345 2345 <tr><td>
2346 2346 <a href="/help/nohelp">
2347 2347 nohelp
2348 2348 </a>
2349 2349 </td><td>
2350 2350 (no help text available)
2351 2351 </td></tr>
2352 2352 <tr><td>
2353 2353 <a href="/help/outgoing">
2354 2354 outgoing
2355 2355 </a>
2356 2356 </td><td>
2357 2357 show changesets not found in the destination
2358 2358 </td></tr>
2359 2359 <tr><td>
2360 2360 <a href="/help/paths">
2361 2361 paths
2362 2362 </a>
2363 2363 </td><td>
2364 2364 show aliases for remote repositories
2365 2365 </td></tr>
2366 2366 <tr><td>
2367 2367 <a href="/help/phase">
2368 2368 phase
2369 2369 </a>
2370 2370 </td><td>
2371 2371 set or show the current phase name
2372 2372 </td></tr>
2373 2373 <tr><td>
2374 2374 <a href="/help/recover">
2375 2375 recover
2376 2376 </a>
2377 2377 </td><td>
2378 2378 roll back an interrupted transaction
2379 2379 </td></tr>
2380 2380 <tr><td>
2381 2381 <a href="/help/rename">
2382 2382 rename
2383 2383 </a>
2384 2384 </td><td>
2385 2385 rename files; equivalent of copy + remove
2386 2386 </td></tr>
2387 2387 <tr><td>
2388 2388 <a href="/help/resolve">
2389 2389 resolve
2390 2390 </a>
2391 2391 </td><td>
2392 2392 redo merges or set/view the merge status of files
2393 2393 </td></tr>
2394 2394 <tr><td>
2395 2395 <a href="/help/revert">
2396 2396 revert
2397 2397 </a>
2398 2398 </td><td>
2399 2399 restore files to their checkout state
2400 2400 </td></tr>
2401 2401 <tr><td>
2402 2402 <a href="/help/root">
2403 2403 root
2404 2404 </a>
2405 2405 </td><td>
2406 2406 print the root (top) of the current working directory
2407 2407 </td></tr>
2408 2408 <tr><td>
2409 2409 <a href="/help/shellalias">
2410 2410 shellalias
2411 2411 </a>
2412 2412 </td><td>
2413 2413 (no help text available)
2414 2414 </td></tr>
2415 2415 <tr><td>
2416 2416 <a href="/help/tag">
2417 2417 tag
2418 2418 </a>
2419 2419 </td><td>
2420 2420 add one or more tags for the current or given revision
2421 2421 </td></tr>
2422 2422 <tr><td>
2423 2423 <a href="/help/tags">
2424 2424 tags
2425 2425 </a>
2426 2426 </td><td>
2427 2427 list repository tags
2428 2428 </td></tr>
2429 2429 <tr><td>
2430 2430 <a href="/help/unbundle">
2431 2431 unbundle
2432 2432 </a>
2433 2433 </td><td>
2434 2434 apply one or more bundle files
2435 2435 </td></tr>
2436 2436 <tr><td>
2437 2437 <a href="/help/verify">
2438 2438 verify
2439 2439 </a>
2440 2440 </td><td>
2441 2441 verify the integrity of the repository
2442 2442 </td></tr>
2443 2443 <tr><td>
2444 2444 <a href="/help/version">
2445 2445 version
2446 2446 </a>
2447 2447 </td><td>
2448 2448 output version and copyright information
2449 2449 </td></tr>
2450 2450
2451 2451
2452 2452 </table>
2453 2453 </div>
2454 2454 </div>
2455 2455
2456 2456
2457 2457
2458 2458 </body>
2459 2459 </html>
2460 2460
2461 2461
2462 2462 $ get-with-headers.py $LOCALIP:$HGPORT "help/add"
2463 2463 200 Script output follows
2464 2464
2465 2465 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2466 2466 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2467 2467 <head>
2468 2468 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2469 2469 <meta name="robots" content="index, nofollow" />
2470 2470 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2471 2471 <script type="text/javascript" src="/static/mercurial.js"></script>
2472 2472
2473 2473 <title>Help: add</title>
2474 2474 </head>
2475 2475 <body>
2476 2476
2477 2477 <div class="container">
2478 2478 <div class="menu">
2479 2479 <div class="logo">
2480 2480 <a href="https://mercurial-scm.org/">
2481 2481 <img src="/static/hglogo.png" alt="mercurial" /></a>
2482 2482 </div>
2483 2483 <ul>
2484 2484 <li><a href="/shortlog">log</a></li>
2485 2485 <li><a href="/graph">graph</a></li>
2486 2486 <li><a href="/tags">tags</a></li>
2487 2487 <li><a href="/bookmarks">bookmarks</a></li>
2488 2488 <li><a href="/branches">branches</a></li>
2489 2489 </ul>
2490 2490 <ul>
2491 2491 <li class="active"><a href="/help">help</a></li>
2492 2492 </ul>
2493 2493 </div>
2494 2494
2495 2495 <div class="main">
2496 2496 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2497 2497 <h3>Help: add</h3>
2498 2498
2499 2499 <form class="search" action="/log">
2500 2500
2501 2501 <p><input name="rev" id="search1" type="text" size="30" /></p>
2502 2502 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2503 2503 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2504 2504 </form>
2505 2505 <div id="doc">
2506 2506 <p>
2507 2507 hg add [OPTION]... [FILE]...
2508 2508 </p>
2509 2509 <p>
2510 2510 add the specified files on the next commit
2511 2511 </p>
2512 2512 <p>
2513 2513 Schedule files to be version controlled and added to the
2514 2514 repository.
2515 2515 </p>
2516 2516 <p>
2517 2517 The files will be added to the repository at the next commit. To
2518 2518 undo an add before that, see 'hg forget'.
2519 2519 </p>
2520 2520 <p>
2521 2521 If no names are given, add all files to the repository (except
2522 2522 files matching &quot;.hgignore&quot;).
2523 2523 </p>
2524 2524 <p>
2525 2525 Examples:
2526 2526 </p>
2527 2527 <ul>
2528 2528 <li> New (unknown) files are added automatically by 'hg add':
2529 2529 <pre>
2530 2530 \$ ls (re)
2531 2531 foo.c
2532 2532 \$ hg status (re)
2533 2533 ? foo.c
2534 2534 \$ hg add (re)
2535 2535 adding foo.c
2536 2536 \$ hg status (re)
2537 2537 A foo.c
2538 2538 </pre>
2539 2539 <li> Specific files to be added can be specified:
2540 2540 <pre>
2541 2541 \$ ls (re)
2542 2542 bar.c foo.c
2543 2543 \$ hg status (re)
2544 2544 ? bar.c
2545 2545 ? foo.c
2546 2546 \$ hg add bar.c (re)
2547 2547 \$ hg status (re)
2548 2548 A bar.c
2549 2549 ? foo.c
2550 2550 </pre>
2551 2551 </ul>
2552 2552 <p>
2553 2553 Returns 0 if all files are successfully added.
2554 2554 </p>
2555 2555 <p>
2556 2556 options ([+] can be repeated):
2557 2557 </p>
2558 2558 <table>
2559 2559 <tr><td>-I</td>
2560 2560 <td>--include PATTERN [+]</td>
2561 2561 <td>include names matching the given patterns</td></tr>
2562 2562 <tr><td>-X</td>
2563 2563 <td>--exclude PATTERN [+]</td>
2564 2564 <td>exclude names matching the given patterns</td></tr>
2565 2565 <tr><td>-S</td>
2566 2566 <td>--subrepos</td>
2567 2567 <td>recurse into subrepositories</td></tr>
2568 2568 <tr><td>-n</td>
2569 2569 <td>--dry-run</td>
2570 2570 <td>do not perform actions, just print output</td></tr>
2571 2571 </table>
2572 2572 <p>
2573 2573 global options ([+] can be repeated):
2574 2574 </p>
2575 2575 <table>
2576 2576 <tr><td>-R</td>
2577 2577 <td>--repository REPO</td>
2578 2578 <td>repository root directory or name of overlay bundle file</td></tr>
2579 2579 <tr><td></td>
2580 2580 <td>--cwd DIR</td>
2581 2581 <td>change working directory</td></tr>
2582 2582 <tr><td>-y</td>
2583 2583 <td>--noninteractive</td>
2584 2584 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2585 2585 <tr><td>-q</td>
2586 2586 <td>--quiet</td>
2587 2587 <td>suppress output</td></tr>
2588 2588 <tr><td>-v</td>
2589 2589 <td>--verbose</td>
2590 2590 <td>enable additional output</td></tr>
2591 2591 <tr><td></td>
2592 2592 <td>--color TYPE</td>
2593 2593 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2594 2594 <tr><td></td>
2595 2595 <td>--config CONFIG [+]</td>
2596 2596 <td>set/override config option (use 'section.name=value')</td></tr>
2597 2597 <tr><td></td>
2598 2598 <td>--debug</td>
2599 2599 <td>enable debugging output</td></tr>
2600 2600 <tr><td></td>
2601 2601 <td>--debugger</td>
2602 2602 <td>start debugger</td></tr>
2603 2603 <tr><td></td>
2604 2604 <td>--encoding ENCODE</td>
2605 2605 <td>set the charset encoding (default: ascii)</td></tr>
2606 2606 <tr><td></td>
2607 2607 <td>--encodingmode MODE</td>
2608 2608 <td>set the charset encoding mode (default: strict)</td></tr>
2609 2609 <tr><td></td>
2610 2610 <td>--traceback</td>
2611 2611 <td>always print a traceback on exception</td></tr>
2612 2612 <tr><td></td>
2613 2613 <td>--time</td>
2614 2614 <td>time how long the command takes</td></tr>
2615 2615 <tr><td></td>
2616 2616 <td>--profile</td>
2617 2617 <td>print command execution profile</td></tr>
2618 2618 <tr><td></td>
2619 2619 <td>--version</td>
2620 2620 <td>output version information and exit</td></tr>
2621 2621 <tr><td>-h</td>
2622 2622 <td>--help</td>
2623 2623 <td>display help and exit</td></tr>
2624 2624 <tr><td></td>
2625 2625 <td>--hidden</td>
2626 2626 <td>consider hidden changesets</td></tr>
2627 2627 <tr><td></td>
2628 2628 <td>--pager TYPE</td>
2629 2629 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2630 2630 </table>
2631 2631
2632 2632 </div>
2633 2633 </div>
2634 2634 </div>
2635 2635
2636 2636
2637 2637
2638 2638 </body>
2639 2639 </html>
2640 2640
2641 2641
2642 2642 $ get-with-headers.py $LOCALIP:$HGPORT "help/remove"
2643 2643 200 Script output follows
2644 2644
2645 2645 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2646 2646 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2647 2647 <head>
2648 2648 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2649 2649 <meta name="robots" content="index, nofollow" />
2650 2650 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2651 2651 <script type="text/javascript" src="/static/mercurial.js"></script>
2652 2652
2653 2653 <title>Help: remove</title>
2654 2654 </head>
2655 2655 <body>
2656 2656
2657 2657 <div class="container">
2658 2658 <div class="menu">
2659 2659 <div class="logo">
2660 2660 <a href="https://mercurial-scm.org/">
2661 2661 <img src="/static/hglogo.png" alt="mercurial" /></a>
2662 2662 </div>
2663 2663 <ul>
2664 2664 <li><a href="/shortlog">log</a></li>
2665 2665 <li><a href="/graph">graph</a></li>
2666 2666 <li><a href="/tags">tags</a></li>
2667 2667 <li><a href="/bookmarks">bookmarks</a></li>
2668 2668 <li><a href="/branches">branches</a></li>
2669 2669 </ul>
2670 2670 <ul>
2671 2671 <li class="active"><a href="/help">help</a></li>
2672 2672 </ul>
2673 2673 </div>
2674 2674
2675 2675 <div class="main">
2676 2676 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2677 2677 <h3>Help: remove</h3>
2678 2678
2679 2679 <form class="search" action="/log">
2680 2680
2681 2681 <p><input name="rev" id="search1" type="text" size="30" /></p>
2682 2682 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2683 2683 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2684 2684 </form>
2685 2685 <div id="doc">
2686 2686 <p>
2687 2687 hg remove [OPTION]... FILE...
2688 2688 </p>
2689 2689 <p>
2690 2690 aliases: rm
2691 2691 </p>
2692 2692 <p>
2693 2693 remove the specified files on the next commit
2694 2694 </p>
2695 2695 <p>
2696 2696 Schedule the indicated files for removal from the current branch.
2697 2697 </p>
2698 2698 <p>
2699 2699 This command schedules the files to be removed at the next commit.
2700 2700 To undo a remove before that, see 'hg revert'. To undo added
2701 2701 files, see 'hg forget'.
2702 2702 </p>
2703 2703 <p>
2704 2704 -A/--after can be used to remove only files that have already
2705 2705 been deleted, -f/--force can be used to force deletion, and -Af
2706 2706 can be used to remove files from the next revision without
2707 2707 deleting them from the working directory.
2708 2708 </p>
2709 2709 <p>
2710 2710 The following table details the behavior of remove for different
2711 2711 file states (columns) and option combinations (rows). The file
2712 2712 states are Added [A], Clean [C], Modified [M] and Missing [!]
2713 2713 (as reported by 'hg status'). The actions are Warn, Remove
2714 2714 (from branch) and Delete (from disk):
2715 2715 </p>
2716 2716 <table>
2717 2717 <tr><td>opt/state</td>
2718 2718 <td>A</td>
2719 2719 <td>C</td>
2720 2720 <td>M</td>
2721 2721 <td>!</td></tr>
2722 2722 <tr><td>none</td>
2723 2723 <td>W</td>
2724 2724 <td>RD</td>
2725 2725 <td>W</td>
2726 2726 <td>R</td></tr>
2727 2727 <tr><td>-f</td>
2728 2728 <td>R</td>
2729 2729 <td>RD</td>
2730 2730 <td>RD</td>
2731 2731 <td>R</td></tr>
2732 2732 <tr><td>-A</td>
2733 2733 <td>W</td>
2734 2734 <td>W</td>
2735 2735 <td>W</td>
2736 2736 <td>R</td></tr>
2737 2737 <tr><td>-Af</td>
2738 2738 <td>R</td>
2739 2739 <td>R</td>
2740 2740 <td>R</td>
2741 2741 <td>R</td></tr>
2742 2742 </table>
2743 2743 <p>
2744 2744 <b>Note:</b>
2745 2745 </p>
2746 2746 <p>
2747 2747 'hg remove' never deletes files in Added [A] state from the
2748 2748 working directory, not even if &quot;--force&quot; is specified.
2749 2749 </p>
2750 2750 <p>
2751 2751 Returns 0 on success, 1 if any warnings encountered.
2752 2752 </p>
2753 2753 <p>
2754 2754 options ([+] can be repeated):
2755 2755 </p>
2756 2756 <table>
2757 2757 <tr><td>-A</td>
2758 2758 <td>--after</td>
2759 2759 <td>record delete for missing files</td></tr>
2760 2760 <tr><td>-f</td>
2761 2761 <td>--force</td>
2762 2762 <td>forget added files, delete modified files</td></tr>
2763 2763 <tr><td>-S</td>
2764 2764 <td>--subrepos</td>
2765 2765 <td>recurse into subrepositories</td></tr>
2766 2766 <tr><td>-I</td>
2767 2767 <td>--include PATTERN [+]</td>
2768 2768 <td>include names matching the given patterns</td></tr>
2769 2769 <tr><td>-X</td>
2770 2770 <td>--exclude PATTERN [+]</td>
2771 2771 <td>exclude names matching the given patterns</td></tr>
2772 2772 </table>
2773 2773 <p>
2774 2774 global options ([+] can be repeated):
2775 2775 </p>
2776 2776 <table>
2777 2777 <tr><td>-R</td>
2778 2778 <td>--repository REPO</td>
2779 2779 <td>repository root directory or name of overlay bundle file</td></tr>
2780 2780 <tr><td></td>
2781 2781 <td>--cwd DIR</td>
2782 2782 <td>change working directory</td></tr>
2783 2783 <tr><td>-y</td>
2784 2784 <td>--noninteractive</td>
2785 2785 <td>do not prompt, automatically pick the first choice for all prompts</td></tr>
2786 2786 <tr><td>-q</td>
2787 2787 <td>--quiet</td>
2788 2788 <td>suppress output</td></tr>
2789 2789 <tr><td>-v</td>
2790 2790 <td>--verbose</td>
2791 2791 <td>enable additional output</td></tr>
2792 2792 <tr><td></td>
2793 2793 <td>--color TYPE</td>
2794 2794 <td>when to colorize (boolean, always, auto, never, or debug)</td></tr>
2795 2795 <tr><td></td>
2796 2796 <td>--config CONFIG [+]</td>
2797 2797 <td>set/override config option (use 'section.name=value')</td></tr>
2798 2798 <tr><td></td>
2799 2799 <td>--debug</td>
2800 2800 <td>enable debugging output</td></tr>
2801 2801 <tr><td></td>
2802 2802 <td>--debugger</td>
2803 2803 <td>start debugger</td></tr>
2804 2804 <tr><td></td>
2805 2805 <td>--encoding ENCODE</td>
2806 2806 <td>set the charset encoding (default: ascii)</td></tr>
2807 2807 <tr><td></td>
2808 2808 <td>--encodingmode MODE</td>
2809 2809 <td>set the charset encoding mode (default: strict)</td></tr>
2810 2810 <tr><td></td>
2811 2811 <td>--traceback</td>
2812 2812 <td>always print a traceback on exception</td></tr>
2813 2813 <tr><td></td>
2814 2814 <td>--time</td>
2815 2815 <td>time how long the command takes</td></tr>
2816 2816 <tr><td></td>
2817 2817 <td>--profile</td>
2818 2818 <td>print command execution profile</td></tr>
2819 2819 <tr><td></td>
2820 2820 <td>--version</td>
2821 2821 <td>output version information and exit</td></tr>
2822 2822 <tr><td>-h</td>
2823 2823 <td>--help</td>
2824 2824 <td>display help and exit</td></tr>
2825 2825 <tr><td></td>
2826 2826 <td>--hidden</td>
2827 2827 <td>consider hidden changesets</td></tr>
2828 2828 <tr><td></td>
2829 2829 <td>--pager TYPE</td>
2830 2830 <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
2831 2831 </table>
2832 2832
2833 2833 </div>
2834 2834 </div>
2835 2835 </div>
2836 2836
2837 2837
2838 2838
2839 2839 </body>
2840 2840 </html>
2841 2841
2842 2842
2843 2843 $ get-with-headers.py $LOCALIP:$HGPORT "help/dates"
2844 2844 200 Script output follows
2845 2845
2846 2846 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2847 2847 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2848 2848 <head>
2849 2849 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2850 2850 <meta name="robots" content="index, nofollow" />
2851 2851 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2852 2852 <script type="text/javascript" src="/static/mercurial.js"></script>
2853 2853
2854 2854 <title>Help: dates</title>
2855 2855 </head>
2856 2856 <body>
2857 2857
2858 2858 <div class="container">
2859 2859 <div class="menu">
2860 2860 <div class="logo">
2861 2861 <a href="https://mercurial-scm.org/">
2862 2862 <img src="/static/hglogo.png" alt="mercurial" /></a>
2863 2863 </div>
2864 2864 <ul>
2865 2865 <li><a href="/shortlog">log</a></li>
2866 2866 <li><a href="/graph">graph</a></li>
2867 2867 <li><a href="/tags">tags</a></li>
2868 2868 <li><a href="/bookmarks">bookmarks</a></li>
2869 2869 <li><a href="/branches">branches</a></li>
2870 2870 </ul>
2871 2871 <ul>
2872 2872 <li class="active"><a href="/help">help</a></li>
2873 2873 </ul>
2874 2874 </div>
2875 2875
2876 2876 <div class="main">
2877 2877 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2878 2878 <h3>Help: dates</h3>
2879 2879
2880 2880 <form class="search" action="/log">
2881 2881
2882 2882 <p><input name="rev" id="search1" type="text" size="30" /></p>
2883 2883 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2884 2884 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2885 2885 </form>
2886 2886 <div id="doc">
2887 2887 <h1>Date Formats</h1>
2888 2888 <p>
2889 2889 Some commands allow the user to specify a date, e.g.:
2890 2890 </p>
2891 2891 <ul>
2892 2892 <li> backout, commit, import, tag: Specify the commit date.
2893 2893 <li> log, revert, update: Select revision(s) by date.
2894 2894 </ul>
2895 2895 <p>
2896 2896 Many date formats are valid. Here are some examples:
2897 2897 </p>
2898 2898 <ul>
2899 2899 <li> &quot;Wed Dec 6 13:18:29 2006&quot; (local timezone assumed)
2900 2900 <li> &quot;Dec 6 13:18 -0600&quot; (year assumed, time offset provided)
2901 2901 <li> &quot;Dec 6 13:18 UTC&quot; (UTC and GMT are aliases for +0000)
2902 2902 <li> &quot;Dec 6&quot; (midnight)
2903 2903 <li> &quot;13:18&quot; (today assumed)
2904 2904 <li> &quot;3:39&quot; (3:39AM assumed)
2905 2905 <li> &quot;3:39pm&quot; (15:39)
2906 2906 <li> &quot;2006-12-06 13:18:29&quot; (ISO 8601 format)
2907 2907 <li> &quot;2006-12-6 13:18&quot;
2908 2908 <li> &quot;2006-12-6&quot;
2909 2909 <li> &quot;12-6&quot;
2910 2910 <li> &quot;12/6&quot;
2911 2911 <li> &quot;12/6/6&quot; (Dec 6 2006)
2912 2912 <li> &quot;today&quot; (midnight)
2913 2913 <li> &quot;yesterday&quot; (midnight)
2914 2914 <li> &quot;now&quot; - right now
2915 2915 </ul>
2916 2916 <p>
2917 2917 Lastly, there is Mercurial's internal format:
2918 2918 </p>
2919 2919 <ul>
2920 2920 <li> &quot;1165411109 0&quot; (Wed Dec 6 13:18:29 2006 UTC)
2921 2921 </ul>
2922 2922 <p>
2923 2923 This is the internal representation format for dates. The first number
2924 2924 is the number of seconds since the epoch (1970-01-01 00:00 UTC). The
2925 2925 second is the offset of the local timezone, in seconds west of UTC
2926 2926 (negative if the timezone is east of UTC).
2927 2927 </p>
2928 2928 <p>
2929 2929 The log command also accepts date ranges:
2930 2930 </p>
2931 2931 <ul>
2932 2932 <li> &quot;&lt;DATE&quot; - at or before a given date/time
2933 2933 <li> &quot;&gt;DATE&quot; - on or after a given date/time
2934 2934 <li> &quot;DATE to DATE&quot; - a date range, inclusive
2935 2935 <li> &quot;-DAYS&quot; - within a given number of days of today
2936 2936 </ul>
2937 2937
2938 2938 </div>
2939 2939 </div>
2940 2940 </div>
2941 2941
2942 2942
2943 2943
2944 2944 </body>
2945 2945 </html>
2946 2946
2947 2947
2948 2948 Sub-topic indexes rendered properly
2949 2949
2950 2950 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals"
2951 2951 200 Script output follows
2952 2952
2953 2953 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2954 2954 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
2955 2955 <head>
2956 2956 <link rel="icon" href="/static/hgicon.png" type="image/png" />
2957 2957 <meta name="robots" content="index, nofollow" />
2958 2958 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
2959 2959 <script type="text/javascript" src="/static/mercurial.js"></script>
2960 2960
2961 2961 <title>Help: internals</title>
2962 2962 </head>
2963 2963 <body>
2964 2964
2965 2965 <div class="container">
2966 2966 <div class="menu">
2967 2967 <div class="logo">
2968 2968 <a href="https://mercurial-scm.org/">
2969 2969 <img src="/static/hglogo.png" alt="mercurial" /></a>
2970 2970 </div>
2971 2971 <ul>
2972 2972 <li><a href="/shortlog">log</a></li>
2973 2973 <li><a href="/graph">graph</a></li>
2974 2974 <li><a href="/tags">tags</a></li>
2975 2975 <li><a href="/bookmarks">bookmarks</a></li>
2976 2976 <li><a href="/branches">branches</a></li>
2977 2977 </ul>
2978 2978 <ul>
2979 2979 <li><a href="/help">help</a></li>
2980 2980 </ul>
2981 2981 </div>
2982 2982
2983 2983 <div class="main">
2984 2984 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
2985 2985 <form class="search" action="/log">
2986 2986
2987 2987 <p><input name="rev" id="search1" type="text" size="30" /></p>
2988 2988 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
2989 2989 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
2990 2990 </form>
2991 2991 <table class="bigtable">
2992 2992 <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr>
2993 2993
2994 2994 <tr><td>
2995 2995 <a href="/help/internals.bundles">
2996 2996 bundles
2997 2997 </a>
2998 2998 </td><td>
2999 2999 Bundles
3000 3000 </td></tr>
3001 3001 <tr><td>
3002 3002 <a href="/help/internals.censor">
3003 3003 censor
3004 3004 </a>
3005 3005 </td><td>
3006 3006 Censor
3007 3007 </td></tr>
3008 3008 <tr><td>
3009 3009 <a href="/help/internals.changegroups">
3010 3010 changegroups
3011 3011 </a>
3012 3012 </td><td>
3013 3013 Changegroups
3014 3014 </td></tr>
3015 3015 <tr><td>
3016 3016 <a href="/help/internals.requirements">
3017 3017 requirements
3018 3018 </a>
3019 3019 </td><td>
3020 3020 Repository Requirements
3021 3021 </td></tr>
3022 3022 <tr><td>
3023 3023 <a href="/help/internals.revlogs">
3024 3024 revlogs
3025 3025 </a>
3026 3026 </td><td>
3027 3027 Revision Logs
3028 3028 </td></tr>
3029 3029 <tr><td>
3030 3030 <a href="/help/internals.wireprotocol">
3031 3031 wireprotocol
3032 3032 </a>
3033 3033 </td><td>
3034 3034 Wire Protocol
3035 3035 </td></tr>
3036 3036
3037 3037
3038 3038
3039 3039
3040 3040
3041 3041 </table>
3042 3042 </div>
3043 3043 </div>
3044 3044
3045 3045
3046 3046
3047 3047 </body>
3048 3048 </html>
3049 3049
3050 3050
3051 3051 Sub-topic topics rendered properly
3052 3052
3053 3053 $ get-with-headers.py $LOCALIP:$HGPORT "help/internals.changegroups"
3054 3054 200 Script output follows
3055 3055
3056 3056 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3057 3057 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
3058 3058 <head>
3059 3059 <link rel="icon" href="/static/hgicon.png" type="image/png" />
3060 3060 <meta name="robots" content="index, nofollow" />
3061 3061 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
3062 3062 <script type="text/javascript" src="/static/mercurial.js"></script>
3063 3063
3064 3064 <title>Help: internals.changegroups</title>
3065 3065 </head>
3066 3066 <body>
3067 3067
3068 3068 <div class="container">
3069 3069 <div class="menu">
3070 3070 <div class="logo">
3071 3071 <a href="https://mercurial-scm.org/">
3072 3072 <img src="/static/hglogo.png" alt="mercurial" /></a>
3073 3073 </div>
3074 3074 <ul>
3075 3075 <li><a href="/shortlog">log</a></li>
3076 3076 <li><a href="/graph">graph</a></li>
3077 3077 <li><a href="/tags">tags</a></li>
3078 3078 <li><a href="/bookmarks">bookmarks</a></li>
3079 3079 <li><a href="/branches">branches</a></li>
3080 3080 </ul>
3081 3081 <ul>
3082 3082 <li class="active"><a href="/help">help</a></li>
3083 3083 </ul>
3084 3084 </div>
3085 3085
3086 3086 <div class="main">
3087 3087 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
3088 3088 <h3>Help: internals.changegroups</h3>
3089 3089
3090 3090 <form class="search" action="/log">
3091 3091
3092 3092 <p><input name="rev" id="search1" type="text" size="30" /></p>
3093 3093 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
3094 3094 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
3095 3095 </form>
3096 3096 <div id="doc">
3097 3097 <h1>Changegroups</h1>
3098 3098 <p>
3099 3099 Changegroups are representations of repository revlog data, specifically
3100 3100 the changelog data, root/flat manifest data, treemanifest data, and
3101 3101 filelogs.
3102 3102 </p>
3103 3103 <p>
3104 3104 There are 3 versions of changegroups: &quot;1&quot;, &quot;2&quot;, and &quot;3&quot;. From a
3105 3105 high-level, versions &quot;1&quot; and &quot;2&quot; are almost exactly the same, with the
3106 3106 only difference being an additional item in the *delta header*. Version
3107 3107 &quot;3&quot; adds support for revlog flags in the *delta header* and optionally
3108 3108 exchanging treemanifests (enabled by setting an option on the
3109 3109 &quot;changegroup&quot; part in the bundle2).
3110 3110 </p>
3111 3111 <p>
3112 3112 Changegroups when not exchanging treemanifests consist of 3 logical
3113 3113 segments:
3114 3114 </p>
3115 3115 <pre>
3116 3116 +---------------------------------+
3117 3117 | | | |
3118 3118 | changeset | manifest | filelogs |
3119 3119 | | | |
3120 3120 | | | |
3121 3121 +---------------------------------+
3122 3122 </pre>
3123 3123 <p>
3124 3124 When exchanging treemanifests, there are 4 logical segments:
3125 3125 </p>
3126 3126 <pre>
3127 3127 +-------------------------------------------------+
3128 3128 | | | | |
3129 3129 | changeset | root | treemanifests | filelogs |
3130 3130 | | manifest | | |
3131 3131 | | | | |
3132 3132 +-------------------------------------------------+
3133 3133 </pre>
3134 3134 <p>
3135 3135 The principle building block of each segment is a *chunk*. A *chunk*
3136 3136 is a framed piece of data:
3137 3137 </p>
3138 3138 <pre>
3139 3139 +---------------------------------------+
3140 3140 | | |
3141 3141 | length | data |
3142 3142 | (4 bytes) | (&lt;length - 4&gt; bytes) |
3143 3143 | | |
3144 3144 +---------------------------------------+
3145 3145 </pre>
3146 3146 <p>
3147 3147 All integers are big-endian signed integers. Each chunk starts with a 32-bit
3148 3148 integer indicating the length of the entire chunk (including the length field
3149 3149 itself).
3150 3150 </p>
3151 3151 <p>
3152 3152 There is a special case chunk that has a value of 0 for the length
3153 3153 (&quot;0x00000000&quot;). We call this an *empty chunk*.
3154 3154 </p>
3155 3155 <h2>Delta Groups</h2>
3156 3156 <p>
3157 3157 A *delta group* expresses the content of a revlog as a series of deltas,
3158 3158 or patches against previous revisions.
3159 3159 </p>
3160 3160 <p>
3161 3161 Delta groups consist of 0 or more *chunks* followed by the *empty chunk*
3162 3162 to signal the end of the delta group:
3163 3163 </p>
3164 3164 <pre>
3165 3165 +------------------------------------------------------------------------+
3166 3166 | | | | | |
3167 3167 | chunk0 length | chunk0 data | chunk1 length | chunk1 data | 0x0 |
3168 3168 | (4 bytes) | (various) | (4 bytes) | (various) | (4 bytes) |
3169 3169 | | | | | |
3170 3170 +------------------------------------------------------------------------+
3171 3171 </pre>
3172 3172 <p>
3173 3173 Each *chunk*'s data consists of the following:
3174 3174 </p>
3175 3175 <pre>
3176 3176 +---------------------------------------+
3177 3177 | | |
3178 3178 | delta header | delta data |
3179 3179 | (various by version) | (various) |
3180 3180 | | |
3181 3181 +---------------------------------------+
3182 3182 </pre>
3183 3183 <p>
3184 3184 The *delta data* is a series of *delta*s that describe a diff from an existing
3185 3185 entry (either that the recipient already has, or previously specified in the
3186 3186 bundle/changegroup).
3187 3187 </p>
3188 3188 <p>
3189 3189 The *delta header* is different between versions &quot;1&quot;, &quot;2&quot;, and
3190 3190 &quot;3&quot; of the changegroup format.
3191 3191 </p>
3192 3192 <p>
3193 3193 Version 1 (headerlen=80):
3194 3194 </p>
3195 3195 <pre>
3196 3196 +------------------------------------------------------+
3197 3197 | | | | |
3198 3198 | node | p1 node | p2 node | link node |
3199 3199 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3200 3200 | | | | |
3201 3201 +------------------------------------------------------+
3202 3202 </pre>
3203 3203 <p>
3204 3204 Version 2 (headerlen=100):
3205 3205 </p>
3206 3206 <pre>
3207 3207 +------------------------------------------------------------------+
3208 3208 | | | | | |
3209 3209 | node | p1 node | p2 node | base node | link node |
3210 3210 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) |
3211 3211 | | | | | |
3212 3212 +------------------------------------------------------------------+
3213 3213 </pre>
3214 3214 <p>
3215 3215 Version 3 (headerlen=102):
3216 3216 </p>
3217 3217 <pre>
3218 3218 +------------------------------------------------------------------------------+
3219 3219 | | | | | | |
3220 3220 | node | p1 node | p2 node | base node | link node | flags |
3221 3221 | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (20 bytes) | (2 bytes) |
3222 3222 | | | | | | |
3223 3223 +------------------------------------------------------------------------------+
3224 3224 </pre>
3225 3225 <p>
3226 3226 The *delta data* consists of &quot;chunklen - 4 - headerlen&quot; bytes, which contain a
3227 3227 series of *delta*s, densely packed (no separators). These deltas describe a diff
3228 3228 from an existing entry (either that the recipient already has, or previously
3229 3229 specified in the bundle/changegroup). The format is described more fully in
3230 3230 &quot;hg help internals.bdiff&quot;, but briefly:
3231 3231 </p>
3232 3232 <pre>
3233 3233 +---------------------------------------------------------------+
3234 3234 | | | | |
3235 3235 | start offset | end offset | new length | content |
3236 3236 | (4 bytes) | (4 bytes) | (4 bytes) | (&lt;new length&gt; bytes) |
3237 3237 | | | | |
3238 3238 +---------------------------------------------------------------+
3239 3239 </pre>
3240 3240 <p>
3241 3241 Please note that the length field in the delta data does *not* include itself.
3242 3242 </p>
3243 3243 <p>
3244 3244 In version 1, the delta is always applied against the previous node from
3245 3245 the changegroup or the first parent if this is the first entry in the
3246 3246 changegroup.
3247 3247 </p>
3248 3248 <p>
3249 3249 In version 2 and up, the delta base node is encoded in the entry in the
3250 3250 changegroup. This allows the delta to be expressed against any parent,
3251 3251 which can result in smaller deltas and more efficient encoding of data.
3252 3252 </p>
3253 3253 <h2>Changeset Segment</h2>
3254 3254 <p>
3255 3255 The *changeset segment* consists of a single *delta group* holding
3256 3256 changelog data. The *empty chunk* at the end of the *delta group* denotes
3257 3257 the boundary to the *manifest segment*.
3258 3258 </p>
3259 3259 <h2>Manifest Segment</h2>
3260 3260 <p>
3261 3261 The *manifest segment* consists of a single *delta group* holding manifest
3262 3262 data. If treemanifests are in use, it contains only the manifest for the
3263 3263 root directory of the repository. Otherwise, it contains the entire
3264 3264 manifest data. The *empty chunk* at the end of the *delta group* denotes
3265 3265 the boundary to the next segment (either the *treemanifests segment* or the
3266 3266 *filelogs segment*, depending on version and the request options).
3267 3267 </p>
3268 3268 <h3>Treemanifests Segment</h3>
3269 3269 <p>
3270 3270 The *treemanifests segment* only exists in changegroup version &quot;3&quot;, and
3271 3271 only if the 'treemanifest' param is part of the bundle2 changegroup part
3272 3272 (it is not possible to use changegroup version 3 outside of bundle2).
3273 3273 Aside from the filenames in the *treemanifests segment* containing a
3274 3274 trailing &quot;/&quot; character, it behaves identically to the *filelogs segment*
3275 3275 (see below). The final sub-segment is followed by an *empty chunk* (logically,
3276 3276 a sub-segment with filename size 0). This denotes the boundary to the
3277 3277 *filelogs segment*.
3278 3278 </p>
3279 3279 <h2>Filelogs Segment</h2>
3280 3280 <p>
3281 3281 The *filelogs segment* consists of multiple sub-segments, each
3282 3282 corresponding to an individual file whose data is being described:
3283 3283 </p>
3284 3284 <pre>
3285 3285 +--------------------------------------------------+
3286 3286 | | | | | |
3287 3287 | filelog0 | filelog1 | filelog2 | ... | 0x0 |
3288 3288 | | | | | (4 bytes) |
3289 3289 | | | | | |
3290 3290 +--------------------------------------------------+
3291 3291 </pre>
3292 3292 <p>
3293 3293 The final filelog sub-segment is followed by an *empty chunk* (logically,
3294 3294 a sub-segment with filename size 0). This denotes the end of the segment
3295 3295 and of the overall changegroup.
3296 3296 </p>
3297 3297 <p>
3298 3298 Each filelog sub-segment consists of the following:
3299 3299 </p>
3300 3300 <pre>
3301 3301 +------------------------------------------------------+
3302 3302 | | | |
3303 3303 | filename length | filename | delta group |
3304 3304 | (4 bytes) | (&lt;length - 4&gt; bytes) | (various) |
3305 3305 | | | |
3306 3306 +------------------------------------------------------+
3307 3307 </pre>
3308 3308 <p>
3309 3309 That is, a *chunk* consisting of the filename (not terminated or padded)
3310 3310 followed by N chunks constituting the *delta group* for this file. The
3311 3311 *empty chunk* at the end of each *delta group* denotes the boundary to the
3312 3312 next filelog sub-segment.
3313 3313 </p>
3314 3314
3315 3315 </div>
3316 3316 </div>
3317 3317 </div>
3318 3318
3319 3319
3320 3320
3321 3321 </body>
3322 3322 </html>
3323 3323
3324 3324
3325 3325 $ killdaemons.py
3326 3326
3327 3327 #endif
@@ -1,634 +1,634 b''
1 1 #require serve ssl
2 2
3 3 Proper https client requires the built-in ssl from Python 2.6.
4 4
5 5 Make server certificates:
6 6
7 7 $ CERTSDIR="$TESTDIR/sslcerts"
8 8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
9 9 $ PRIV=`pwd`/server.pem
10 10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
11 11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
12 12
13 13 $ hg init test
14 14 $ cd test
15 15 $ echo foo>foo
16 16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
17 17 $ echo foo>foo.d/foo
18 18 $ echo bar>foo.d/bAr.hg.d/BaR
19 19 $ echo bar>foo.d/baR.d.hg/bAR
20 20 $ hg commit -A -m 1
21 21 adding foo
22 22 adding foo.d/bAr.hg.d/BaR
23 23 adding foo.d/baR.d.hg/bAR
24 24 adding foo.d/foo
25 25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
26 26 $ cat ../hg0.pid >> $DAEMON_PIDS
27 27
28 28 cacert not found
29 29
30 30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
31 31 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
32 32 abort: could not find web.cacerts: no-such.pem
33 33 [255]
34 34
35 35 Test server address cannot be reused
36 36
37 37 #if windows
38 38 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
39 39 abort: cannot start server at 'localhost:$HGPORT': * (glob)
40 40 [255]
41 41 #else
42 42 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
43 43 abort: cannot start server at 'localhost:$HGPORT': Address already in use
44 44 [255]
45 45 #endif
46 46 $ cd ..
47 47
48 48 Our test cert is not signed by a trusted CA. It should fail to verify if
49 49 we are able to load CA certs.
50 50
51 51 #if sslcontext defaultcacerts no-defaultcacertsloaded
52 52 $ hg clone https://localhost:$HGPORT/ copy-pull
53 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 54 abort: error: *certificate verify failed* (glob)
55 55 [255]
56 56 #endif
57 57
58 58 #if no-sslcontext defaultcacerts
59 59 $ hg clone https://localhost:$HGPORT/ copy-pull
60 60 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
61 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 62 abort: error: *certificate verify failed* (glob)
63 63 [255]
64 64 #endif
65 65
66 66 #if no-sslcontext windows
67 67 $ hg clone https://localhost:$HGPORT/ copy-pull
68 68 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
69 69 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
70 70 abort: error: *certificate verify failed* (glob)
71 71 [255]
72 72 #endif
73 73
74 74 #if no-sslcontext osx
75 75 $ hg clone https://localhost:$HGPORT/ copy-pull
76 76 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
77 77 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
78 78 abort: localhost certificate error: no certificate received
79 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 80 [255]
81 81 #endif
82 82
83 83 #if defaultcacertsloaded
84 84 $ hg clone https://localhost:$HGPORT/ copy-pull
85 85 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
86 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 87 abort: error: *certificate verify failed* (glob)
88 88 [255]
89 89 #endif
90 90
91 91 #if no-defaultcacerts
92 92 $ hg clone https://localhost:$HGPORT/ copy-pull
93 93 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
94 94 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
95 95 abort: localhost certificate error: no certificate received
96 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 97 [255]
98 98 #endif
99 99
100 100 Specifying a per-host certificate file that doesn't exist will abort. The full
101 101 C:/path/to/msysroot will print on Windows.
102 102
103 103 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
104 104 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
105 105 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: */does/not/exist (glob)
106 106 [255]
107 107
108 108 A malformed per-host certificate file will raise an error
109 109
110 110 $ echo baddata > badca.pem
111 111 #if sslcontext
112 112 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
113 113 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
114 114 abort: error loading CA file badca.pem: * (glob)
115 115 (file is empty or malformed?)
116 116 [255]
117 117 #else
118 118 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
119 119 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
120 120 abort: error: * (glob)
121 121 [255]
122 122 #endif
123 123
124 124 A per-host certificate mismatching the server will fail verification
125 125
126 126 (modern ssl is able to discern whether the loaded cert is a CA cert)
127 127 #if sslcontext
128 128 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
129 129 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
130 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 131 abort: error: *certificate verify failed* (glob)
132 132 [255]
133 133 #else
134 134 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
135 135 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
136 136 abort: error: *certificate verify failed* (glob)
137 137 [255]
138 138 #endif
139 139
140 140 A per-host certificate matching the server's cert will be accepted
141 141
142 142 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
143 143 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
144 144 requesting all changes
145 145 adding changesets
146 146 adding manifests
147 147 adding file changes
148 148 added 1 changesets with 4 changes to 4 files
149 149
150 150 A per-host certificate with multiple certs and one matching will be accepted
151 151
152 152 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
153 153 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
154 154 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
155 155 requesting all changes
156 156 adding changesets
157 157 adding manifests
158 158 adding file changes
159 159 added 1 changesets with 4 changes to 4 files
160 160
161 161 Defining both per-host certificate and a fingerprint will print a warning
162 162
163 163 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
164 164 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
165 165 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
166 166 requesting all changes
167 167 adding changesets
168 168 adding manifests
169 169 adding file changes
170 170 added 1 changesets with 4 changes to 4 files
171 171
172 172 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
173 173
174 174 Inability to verify peer certificate will result in abort
175 175
176 176 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
177 177 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
178 178 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
179 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 180 [255]
181 181
182 182 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
183 183 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
184 184 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
185 185 requesting all changes
186 186 adding changesets
187 187 adding manifests
188 188 adding file changes
189 189 added 1 changesets with 4 changes to 4 files
190 190 updating to branch default
191 191 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 192 $ hg verify -R copy-pull
193 193 checking changesets
194 194 checking manifests
195 195 crosschecking files in changesets and manifests
196 196 checking files
197 197 4 files, 1 changesets, 4 total revisions
198 198 $ cd test
199 199 $ echo bar > bar
200 200 $ hg commit -A -d '1 0' -m 2
201 201 adding bar
202 202 $ cd ..
203 203
204 204 pull without cacert
205 205
206 206 $ cd copy-pull
207 207 $ cat >> .hg/hgrc <<EOF
208 208 > [hooks]
209 209 > changegroup = sh -c "printenv.py changegroup"
210 210 > EOF
211 211 $ hg pull $DISABLECACERTS
212 212 pulling from https://localhost:$HGPORT/
213 213 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
214 214 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
215 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 216 [255]
217 217
218 218 $ hg pull --insecure
219 219 pulling from https://localhost:$HGPORT/
220 220 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
221 221 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
222 222 searching for changes
223 223 adding changesets
224 224 adding manifests
225 225 adding file changes
226 226 added 1 changesets with 1 changes to 1 files
227 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 228 (run 'hg update' to get a working copy)
229 229 $ cd ..
230 230
231 231 cacert configured in local repo
232 232
233 233 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
234 234 $ echo "[web]" >> copy-pull/.hg/hgrc
235 235 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
236 236 $ hg -R copy-pull pull
237 237 pulling from https://localhost:$HGPORT/
238 238 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
239 239 searching for changes
240 240 no changes found
241 241 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
242 242
243 243 cacert configured globally, also testing expansion of environment
244 244 variables in the filename
245 245
246 246 $ echo "[web]" >> $HGRCPATH
247 247 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
248 248 $ P="$CERTSDIR" hg -R copy-pull pull
249 249 pulling from https://localhost:$HGPORT/
250 250 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
251 251 searching for changes
252 252 no changes found
253 253 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
254 254 pulling from https://localhost:$HGPORT/
255 255 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
256 256 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
257 257 searching for changes
258 258 no changes found
259 259
260 260 empty cacert file
261 261
262 262 $ touch emptycafile
263 263
264 264 #if sslcontext
265 265 $ hg --config web.cacerts=emptycafile -R copy-pull pull
266 266 pulling from https://localhost:$HGPORT/
267 267 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
268 268 abort: error loading CA file emptycafile: * (glob)
269 269 (file is empty or malformed?)
270 270 [255]
271 271 #else
272 272 $ hg --config web.cacerts=emptycafile -R copy-pull pull
273 273 pulling from https://localhost:$HGPORT/
274 274 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
275 275 abort: error: * (glob)
276 276 [255]
277 277 #endif
278 278
279 279 cacert mismatch
280 280
281 281 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
282 282 > https://$LOCALIP:$HGPORT/
283 283 pulling from https://*:$HGPORT/ (glob)
284 284 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
285 285 abort: $LOCALIP certificate error: certificate is for localhost (glob)
286 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 287 [255]
288 288 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
289 289 > https://$LOCALIP:$HGPORT/ --insecure
290 290 pulling from https://*:$HGPORT/ (glob)
291 291 warning: connecting to $LOCALIP using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
292 292 warning: connection security to $LOCALIP is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
293 293 searching for changes
294 294 no changes found
295 295 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
296 296 pulling from https://localhost:$HGPORT/
297 297 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
298 298 abort: error: *certificate verify failed* (glob)
299 299 [255]
300 300 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
301 301 > --insecure
302 302 pulling from https://localhost:$HGPORT/
303 303 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
304 304 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
305 305 searching for changes
306 306 no changes found
307 307
308 308 Test server cert which isn't valid yet
309 309
310 310 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
311 311 $ cat hg1.pid >> $DAEMON_PIDS
312 312 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
313 313 > https://localhost:$HGPORT1/
314 314 pulling from https://localhost:$HGPORT1/
315 315 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
316 316 abort: error: *certificate verify failed* (glob)
317 317 [255]
318 318
319 319 Test server cert which no longer is valid
320 320
321 321 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
322 322 $ cat hg2.pid >> $DAEMON_PIDS
323 323 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
324 324 > https://localhost:$HGPORT2/
325 325 pulling from https://localhost:$HGPORT2/
326 326 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
327 327 abort: error: *certificate verify failed* (glob)
328 328 [255]
329 329
330 330 Disabling the TLS 1.0 warning works
331 331 $ hg -R copy-pull id https://localhost:$HGPORT/ \
332 332 > --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 \
333 333 > --config hostsecurity.disabletls10warning=true
334 334 5fed3813f7f5
335 335
336 336 Error message for setting ciphers is different depending on SSLContext support
337 337
338 338 #if no-sslcontext
339 339 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
340 340 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
341 341 abort: *No cipher can be selected. (glob)
342 342 [255]
343 343
344 344 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
345 345 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
346 346 5fed3813f7f5
347 347 #endif
348 348
349 349 #if sslcontext
350 350 Setting ciphers to an invalid value aborts
351 351 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
352 352 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
353 353 abort: could not set ciphers: No cipher can be selected.
354 354 (change cipher string (invalid) in config)
355 355 [255]
356 356
357 357 $ P="$CERTSDIR" hg --config hostsecurity.localhost:ciphers=invalid -R copy-pull id https://localhost:$HGPORT/
358 358 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
359 359 abort: could not set ciphers: No cipher can be selected.
360 360 (change cipher string (invalid) in config)
361 361 [255]
362 362
363 363 Changing the cipher string works
364 364
365 365 $ P="$CERTSDIR" hg --config hostsecurity.ciphers=HIGH -R copy-pull id https://localhost:$HGPORT/
366 366 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
367 367 5fed3813f7f5
368 368 #endif
369 369
370 370 Fingerprints
371 371
372 372 - works without cacerts (hostfingerprints)
373 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 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 376 5fed3813f7f5
377 377
378 378 - works without cacerts (hostsecurity)
379 379 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
380 380 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
381 381 5fed3813f7f5
382 382
383 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 384 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
385 385 5fed3813f7f5
386 386
387 387 - multiple fingerprints specified and first matches
388 388 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
389 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 391 5fed3813f7f5
392 392
393 393 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
394 394 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
395 395 5fed3813f7f5
396 396
397 397 - multiple fingerprints specified and last matches
398 398 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
399 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 401 5fed3813f7f5
402 402
403 403 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
404 404 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
405 405 5fed3813f7f5
406 406
407 407 - multiple fingerprints specified and none match
408 408
409 409 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
410 410 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
411 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 412 (check hostfingerprint configuration)
413 413 [255]
414 414
415 415 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
416 416 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
417 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 418 (check hostsecurity configuration)
419 419 [255]
420 420
421 421 - fails when cert doesn't match hostname (port is ignored)
422 422 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
423 423 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
424 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 425 (check hostfingerprint configuration)
426 426 [255]
427 427
428 428
429 429 - ignores that certificate doesn't match hostname
430 430 $ hg -R copy-pull id https://$LOCALIP:$HGPORT/ --config hostfingerprints.$LOCALIP=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
431 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 433 5fed3813f7f5
434 434
435 435 Ports used by next test. Kill servers.
436 436
437 437 $ killdaemons.py hg0.pid
438 438 $ killdaemons.py hg1.pid
439 439 $ killdaemons.py hg2.pid
440 440
441 441 #if sslcontext tls1.2
442 442 Start servers running supported TLS versions
443 443
444 444 $ cd test
445 445 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
446 446 > --config devel.serverexactprotocol=tls1.0
447 447 $ cat ../hg0.pid >> $DAEMON_PIDS
448 448 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
449 449 > --config devel.serverexactprotocol=tls1.1
450 450 $ cat ../hg1.pid >> $DAEMON_PIDS
451 451 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
452 452 > --config devel.serverexactprotocol=tls1.2
453 453 $ cat ../hg2.pid >> $DAEMON_PIDS
454 454 $ cd ..
455 455
456 456 Clients talking same TLS versions work
457 457
458 458 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
459 459 5fed3813f7f5
460 460 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
461 461 5fed3813f7f5
462 462 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
463 463 5fed3813f7f5
464 464
465 465 Clients requiring newer TLS version than what server supports fail
466 466
467 467 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
468 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 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 470 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
471 471 abort: error: *unsupported protocol* (glob)
472 472 [255]
473 473
474 474 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
475 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 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 477 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
478 478 abort: error: *unsupported protocol* (glob)
479 479 [255]
480 480 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
481 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 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 483 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
484 484 abort: error: *unsupported protocol* (glob)
485 485 [255]
486 486 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
487 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 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 489 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
490 490 abort: error: *unsupported protocol* (glob)
491 491 [255]
492 492
493 493 --insecure will allow TLS 1.0 connections and override configs
494 494
495 495 $ hg --config hostsecurity.minimumprotocol=tls1.2 id --insecure https://localhost:$HGPORT1/
496 496 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
497 497 5fed3813f7f5
498 498
499 499 The per-host config option overrides the default
500 500
501 501 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
502 502 > --config hostsecurity.minimumprotocol=tls1.2 \
503 503 > --config hostsecurity.localhost:minimumprotocol=tls1.0
504 504 5fed3813f7f5
505 505
506 506 The per-host config option by itself works
507 507
508 508 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
509 509 > --config hostsecurity.localhost:minimumprotocol=tls1.2
510 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 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 512 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
513 513 abort: error: *unsupported protocol* (glob)
514 514 [255]
515 515
516 516 .hg/hgrc file [hostsecurity] settings are applied to remote ui instances (issue5305)
517 517
518 518 $ cat >> copy-pull/.hg/hgrc << EOF
519 519 > [hostsecurity]
520 520 > localhost:minimumprotocol=tls1.2
521 521 > EOF
522 522 $ P="$CERTSDIR" hg -R copy-pull id https://localhost:$HGPORT/
523 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 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 525 (see https://mercurial-scm.org/wiki/SecureConnections for more info)
526 526 abort: error: *unsupported protocol* (glob)
527 527 [255]
528 528
529 529 $ killdaemons.py hg0.pid
530 530 $ killdaemons.py hg1.pid
531 531 $ killdaemons.py hg2.pid
532 532 #endif
533 533
534 534 Prepare for connecting through proxy
535 535
536 536 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
537 537 $ cat hg0.pid >> $DAEMON_PIDS
538 538 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
539 539 $ cat hg2.pid >> $DAEMON_PIDS
540 540 tinyproxy.py doesn't fully detach, so killing it may result in extra output
541 541 from the shell. So don't kill it.
542 542 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
543 543 $ while [ ! -f proxy.pid ]; do sleep 0; done
544 544 $ cat proxy.pid >> $DAEMON_PIDS
545 545
546 546 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
547 547 $ echo "always=True" >> copy-pull/.hg/hgrc
548 548 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
549 549 $ echo "localhost =" >> copy-pull/.hg/hgrc
550 550
551 551 Test unvalidated https through proxy
552 552
553 553 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure
554 554 pulling from https://localhost:$HGPORT/
555 555 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
556 556 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
557 557 searching for changes
558 558 no changes found
559 559
560 560 Test https with cacert and fingerprint through proxy
561 561
562 562 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
563 563 > --config web.cacerts="$CERTSDIR/pub.pem"
564 564 pulling from https://localhost:$HGPORT/
565 565 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
566 566 searching for changes
567 567 no changes found
568 568 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://localhost:$HGPORT/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 --trace
569 569 pulling from https://*:$HGPORT/ (glob)
570 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 572 searching for changes
573 573 no changes found
574 574
575 575 Test https with cert problems through proxy
576 576
577 577 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
578 578 > --config web.cacerts="$CERTSDIR/pub-other.pem"
579 579 pulling from https://localhost:$HGPORT/
580 580 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
581 581 abort: error: *certificate verify failed* (glob)
582 582 [255]
583 583 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
584 584 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
585 585 pulling from https://localhost:$HGPORT2/
586 586 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
587 587 abort: error: *certificate verify failed* (glob)
588 588 [255]
589 589
590 590
591 591 $ killdaemons.py hg0.pid
592 592
593 593 #if sslcontext
594 594
595 595 Start hgweb that requires client certificates:
596 596
597 597 $ cd test
598 598 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
599 599 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
600 600 $ cat ../hg0.pid >> $DAEMON_PIDS
601 601 $ cd ..
602 602
603 603 without client certificate:
604 604
605 605 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
606 606 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
607 607 abort: error: *handshake failure* (glob)
608 608 [255]
609 609
610 610 with client certificate:
611 611
612 612 $ cat << EOT >> $HGRCPATH
613 613 > [auth]
614 614 > l.prefix = localhost
615 615 > l.cert = $CERTSDIR/client-cert.pem
616 616 > l.key = $CERTSDIR/client-key.pem
617 617 > EOT
618 618
619 619 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
620 620 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
621 621 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
622 622 5fed3813f7f5
623 623
624 624 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
625 625 > --config ui.interactive=True --config ui.nontty=True
626 626 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
627 627 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
628 628
629 629 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
630 630 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
631 631 abort: error: * (glob)
632 632 [255]
633 633
634 634 #endif
@@ -1,1862 +1,1881 b''
1 1 This file used to contains all largefile tests.
2 2 Do not add any new tests in this file as it his already far too long to run.
3 3
4 4 It contains all the testing of the basic concepts of large file in a single block.
5 5
6 6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 7 $ mkdir "${USERCACHE}"
8 8 $ cat >> $HGRCPATH <<EOF
9 9 > [extensions]
10 10 > largefiles=
11 11 > purge=
12 12 > rebase=
13 13 > transplant=
14 14 > [phases]
15 15 > publish=False
16 16 > [largefiles]
17 17 > minsize=2
18 18 > patterns=glob:**.dat
19 19 > usercache=${USERCACHE}
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24 Create the repo with a couple of revisions of both large and normal
25 25 files.
26 26 Test status and dirstate of largefiles and that summary output is correct.
27 27
28 28 $ hg init a
29 29 $ cd a
30 30 $ mkdir sub
31 31 $ echo normal1 > normal1
32 32 $ echo normal2 > sub/normal2
33 33 $ echo large1 > large1
34 34 $ echo large2 > sub/large2
35 35 $ hg add normal1 sub/normal2
36 36 $ hg add --large large1 sub/large2
37 37 $ hg commit -m "add files"
38 38 Invoking status precommit hook
39 39 A large1
40 40 A normal1
41 41 A sub/large2
42 42 A sub/normal2
43 43 $ touch large1 sub/large2
44 44 $ sleep 1
45 45 $ hg st
46 46 $ hg debugstate --nodates
47 47 n 644 41 set .hglf/large1
48 48 n 644 41 set .hglf/sub/large2
49 49 n 644 8 set normal1
50 50 n 644 8 set sub/normal2
51 51 $ hg debugstate --large --nodates
52 52 n 644 7 set large1
53 53 n 644 7 set sub/large2
54 54 $ echo normal11 > normal1
55 55 $ echo normal22 > sub/normal2
56 56 $ echo large11 > large1
57 57 $ echo large22 > sub/large2
58 58 $ hg commit -m "edit files"
59 59 Invoking status precommit hook
60 60 M large1
61 61 M normal1
62 62 M sub/large2
63 63 M sub/normal2
64 64 $ hg sum --large
65 65 parent: 1:ce8896473775 tip
66 66 edit files
67 67 branch: default
68 68 commit: (clean)
69 69 update: (current)
70 70 phases: 2 draft
71 71 largefiles: (no remote repo)
72 72
73 73 Commit preserved largefile contents.
74 74
75 75 $ cat normal1
76 76 normal11
77 77 $ cat large1
78 78 large11
79 79 $ cat sub/normal2
80 80 normal22
81 81 $ cat sub/large2
82 82 large22
83 83
84 84 Test status, subdir and unknown files
85 85
86 86 $ echo unknown > sub/unknown
87 87 $ hg st --all
88 88 ? sub/unknown
89 89 C large1
90 90 C normal1
91 91 C sub/large2
92 92 C sub/normal2
93 93 $ hg st --all sub
94 94 ? sub/unknown
95 95 C sub/large2
96 96 C sub/normal2
97 97 $ rm sub/unknown
98 98
99 99 Test messages and exit codes for remove warning cases
100 100
101 101 $ hg remove -A large1
102 102 not removing large1: file still exists
103 103 [1]
104 104 $ echo 'modified' > large1
105 105 $ hg remove large1
106 106 not removing large1: file is modified (use -f to force removal)
107 107 [1]
108 108 $ echo 'new' > normalnew
109 109 $ hg add normalnew
110 110 $ echo 'new' > largenew
111 111 $ hg add --large normalnew
112 112 normalnew already tracked!
113 113 $ hg remove normalnew largenew
114 114 not removing largenew: file is untracked
115 115 not removing normalnew: file has been marked for add (use 'hg forget' to undo add)
116 116 [1]
117 117 $ rm normalnew largenew
118 118 $ hg up -Cq
119 119
120 120 Remove both largefiles and normal files.
121 121
122 122 $ hg remove normal1 large1
123 123 $ hg status large1
124 124 R large1
125 125 $ hg commit -m "remove files"
126 126 Invoking status precommit hook
127 127 R large1
128 128 R normal1
129 129 $ ls
130 130 sub
131 131 $ echo "testlargefile" > large1-test
132 132 $ hg add --large large1-test
133 133 $ hg st
134 134 A large1-test
135 135 $ hg rm large1-test
136 136 not removing large1-test: file has been marked for add (use forget to undo)
137 137 [1]
138 138 $ hg st
139 139 A large1-test
140 140 $ hg forget large1-test
141 141 $ hg st
142 142 ? large1-test
143 143 $ hg remove large1-test
144 144 not removing large1-test: file is untracked
145 145 [1]
146 146 $ hg forget large1-test
147 147 not removing large1-test: file is already untracked
148 148 [1]
149 149 $ rm large1-test
150 150
151 151 Copy both largefiles and normal files (testing that status output is correct).
152 152
153 153 $ hg cp sub/normal2 normal1
154 154 $ hg cp sub/large2 large1
155 155 $ hg commit -m "copy files"
156 156 Invoking status precommit hook
157 157 A large1
158 158 A normal1
159 159 $ cat normal1
160 160 normal22
161 161 $ cat large1
162 162 large22
163 163
164 164 Test moving largefiles and verify that normal files are also unaffected.
165 165
166 166 $ hg mv normal1 normal3
167 167 $ hg mv large1 large3
168 168 $ hg mv sub/normal2 sub/normal4
169 169 $ hg mv sub/large2 sub/large4
170 170 $ hg commit -m "move files"
171 171 Invoking status precommit hook
172 172 A large3
173 173 A normal3
174 174 A sub/large4
175 175 A sub/normal4
176 176 R large1
177 177 R normal1
178 178 R sub/large2
179 179 R sub/normal2
180 180 $ cat normal3
181 181 normal22
182 182 $ cat large3
183 183 large22
184 184 $ cat sub/normal4
185 185 normal22
186 186 $ cat sub/large4
187 187 large22
188 188
189 189
190 190 #if serve
191 191 Test display of largefiles in hgweb
192 192
193 193 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
194 194 $ cat ../hg.pid >> $DAEMON_PIDS
195 195 $ get-with-headers.py $LOCALIP:$HGPORT 'file/tip/?style=raw'
196 196 200 Script output follows
197 197
198 198
199 199 drwxr-xr-x sub
200 200 -rw-r--r-- 41 large3
201 201 -rw-r--r-- 9 normal3
202 202
203 203
204 204 $ get-with-headers.py $LOCALIP:$HGPORT 'file/tip/sub/?style=raw'
205 205 200 Script output follows
206 206
207 207
208 208 -rw-r--r-- 41 large4
209 209 -rw-r--r-- 9 normal4
210 210
211 211
212 212 $ killdaemons.py
213 213 #endif
214 214
215 Test largefiles can be loaded in hgweb (wrapcommand() shouldn't fail)
216
217 $ cat <<EOF > "$TESTTMP/hgweb.cgi"
218 > #!/usr/bin/env python
219 > from mercurial import demandimport; demandimport.enable()
220 > from mercurial.hgweb import hgweb
221 > from mercurial.hgweb import wsgicgi
222 > application = hgweb('.', 'test repo')
223 > wsgicgi.launch(application)
224 > EOF
225
226 $ PATH_INFO='/' \
227 > QUERY_STRING='' \
228 > REQUEST_METHOD='GET' \
229 > SCRIPT_NAME='' \
230 > SERVER_NAME='localhost' \
231 > SERVER_PORT='80' \
232 > python "$TESTTMP/hgweb.cgi" > /dev/null
233
215 234 Test archiving the various revisions. These hit corner cases known with
216 235 archiving.
217 236
218 237 $ hg archive -r 0 ../archive0
219 238 $ hg archive -r 1 ../archive1
220 239 $ hg archive -r 2 ../archive2
221 240 $ hg archive -r 3 ../archive3
222 241 $ hg archive -r 4 ../archive4
223 242 $ cd ../archive0
224 243 $ cat normal1
225 244 normal1
226 245 $ cat large1
227 246 large1
228 247 $ cat sub/normal2
229 248 normal2
230 249 $ cat sub/large2
231 250 large2
232 251 $ cd ../archive1
233 252 $ cat normal1
234 253 normal11
235 254 $ cat large1
236 255 large11
237 256 $ cat sub/normal2
238 257 normal22
239 258 $ cat sub/large2
240 259 large22
241 260 $ cd ../archive2
242 261 $ ls
243 262 sub
244 263 $ cat sub/normal2
245 264 normal22
246 265 $ cat sub/large2
247 266 large22
248 267 $ cd ../archive3
249 268 $ cat normal1
250 269 normal22
251 270 $ cat large1
252 271 large22
253 272 $ cat sub/normal2
254 273 normal22
255 274 $ cat sub/large2
256 275 large22
257 276 $ cd ../archive4
258 277 $ cat normal3
259 278 normal22
260 279 $ cat large3
261 280 large22
262 281 $ cat sub/normal4
263 282 normal22
264 283 $ cat sub/large4
265 284 large22
266 285
267 286 Commit corner case: specify files to commit.
268 287
269 288 $ cd ../a
270 289 $ echo normal3 > normal3
271 290 $ echo large3 > large3
272 291 $ echo normal4 > sub/normal4
273 292 $ echo large4 > sub/large4
274 293 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
275 294 Invoking status precommit hook
276 295 M large3
277 296 M normal3
278 297 M sub/large4
279 298 M sub/normal4
280 299 $ cat normal3
281 300 normal3
282 301 $ cat large3
283 302 large3
284 303 $ cat sub/normal4
285 304 normal4
286 305 $ cat sub/large4
287 306 large4
288 307
289 308 One more commit corner case: commit from a subdirectory.
290 309
291 310 $ cd ../a
292 311 $ echo normal33 > normal3
293 312 $ echo large33 > large3
294 313 $ echo normal44 > sub/normal4
295 314 $ echo large44 > sub/large4
296 315 $ cd sub
297 316 $ hg commit -m "edit files yet again"
298 317 Invoking status precommit hook
299 318 M large3
300 319 M normal3
301 320 M sub/large4
302 321 M sub/normal4
303 322 $ cat ../normal3
304 323 normal33
305 324 $ cat ../large3
306 325 large33
307 326 $ cat normal4
308 327 normal44
309 328 $ cat large4
310 329 large44
311 330
312 331 Committing standins is not allowed.
313 332
314 333 $ cd ..
315 334 $ echo large3 > large3
316 335 $ hg commit .hglf/large3 -m "try to commit standin"
317 336 abort: file ".hglf/large3" is a largefile standin
318 337 (commit the largefile itself instead)
319 338 [255]
320 339
321 340 Corner cases for adding largefiles.
322 341
323 342 $ echo large5 > large5
324 343 $ hg add --large large5
325 344 $ hg add --large large5
326 345 large5 already a largefile
327 346 $ mkdir sub2
328 347 $ echo large6 > sub2/large6
329 348 $ echo large7 > sub2/large7
330 349 $ hg add --large sub2
331 350 adding sub2/large6 as a largefile (glob)
332 351 adding sub2/large7 as a largefile (glob)
333 352 $ hg st
334 353 M large3
335 354 A large5
336 355 A sub2/large6
337 356 A sub2/large7
338 357
339 358 Committing directories containing only largefiles.
340 359
341 360 $ mkdir -p z/y/x/m
342 361 $ touch z/y/x/m/large1
343 362 $ touch z/y/x/large2
344 363 $ hg add --large z/y/x/m/large1 z/y/x/large2
345 364 $ hg commit -m "Subdir with directory only containing largefiles" z
346 365 Invoking status precommit hook
347 366 M large3
348 367 A large5
349 368 A sub2/large6
350 369 A sub2/large7
351 370 A z/y/x/large2
352 371 A z/y/x/m/large1
353 372
354 373 (and a bit of log testing)
355 374
356 375 $ hg log -T '{rev}\n' z/y/x/m/large1
357 376 7
358 377 $ hg log -T '{rev}\n' z/y/x/m # with only a largefile
359 378 7
360 379
361 380 $ hg rollback --quiet
362 381 $ touch z/y/x/m/normal
363 382 $ hg add z/y/x/m/normal
364 383 $ hg commit -m "Subdir with mixed contents" z
365 384 Invoking status precommit hook
366 385 M large3
367 386 A large5
368 387 A sub2/large6
369 388 A sub2/large7
370 389 A z/y/x/large2
371 390 A z/y/x/m/large1
372 391 A z/y/x/m/normal
373 392 $ hg st
374 393 M large3
375 394 A large5
376 395 A sub2/large6
377 396 A sub2/large7
378 397 $ hg rollback --quiet
379 398 $ hg revert z/y/x/large2 z/y/x/m/large1
380 399 $ rm z/y/x/large2 z/y/x/m/large1
381 400 $ hg commit -m "Subdir with normal contents" z
382 401 Invoking status precommit hook
383 402 M large3
384 403 A large5
385 404 A sub2/large6
386 405 A sub2/large7
387 406 A z/y/x/m/normal
388 407 $ hg st
389 408 M large3
390 409 A large5
391 410 A sub2/large6
392 411 A sub2/large7
393 412 $ hg rollback --quiet
394 413 $ hg revert --quiet z
395 414 $ hg commit -m "Empty subdir" z
396 415 abort: z: no match under directory!
397 416 [255]
398 417 $ rm -rf z
399 418 $ hg ci -m "standin" .hglf
400 419 abort: file ".hglf" is a largefile standin
401 420 (commit the largefile itself instead)
402 421 [255]
403 422
404 423 Test "hg status" with combination of 'file pattern' and 'directory
405 424 pattern' for largefiles:
406 425
407 426 $ hg status sub2/large6 sub2
408 427 A sub2/large6
409 428 A sub2/large7
410 429
411 430 Config settings (pattern **.dat, minsize 2 MB) are respected.
412 431
413 432 $ echo testdata > test.dat
414 433 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
415 434 $ hg add
416 435 adding reallylarge as a largefile
417 436 adding test.dat as a largefile
418 437
419 438 Test that minsize and --lfsize handle float values;
420 439 also tests that --lfsize overrides largefiles.minsize.
421 440 (0.250 MB = 256 kB = 262144 B)
422 441
423 442 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
424 443 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
425 444 $ hg --config largefiles.minsize=.25 add
426 445 adding ratherlarge as a largefile
427 446 adding medium
428 447 $ hg forget medium
429 448 $ hg --config largefiles.minsize=.25 add --lfsize=.125
430 449 adding medium as a largefile
431 450 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
432 451 $ hg --config largefiles.minsize=.25 add --lfsize=.125
433 452 adding notlarge
434 453 $ hg forget notlarge
435 454
436 455 Test forget on largefiles.
437 456
438 457 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
439 458 $ hg commit -m "add/edit more largefiles"
440 459 Invoking status precommit hook
441 460 A sub2/large6
442 461 A sub2/large7
443 462 R large3
444 463 ? large5
445 464 ? medium
446 465 ? notlarge
447 466 ? ratherlarge
448 467 ? reallylarge
449 468 ? test.dat
450 469 $ hg st
451 470 ? large3
452 471 ? large5
453 472 ? medium
454 473 ? notlarge
455 474 ? ratherlarge
456 475 ? reallylarge
457 476 ? test.dat
458 477
459 478 Purge with largefiles: verify that largefiles are still in the working
460 479 dir after a purge.
461 480
462 481 $ hg purge --all
463 482 $ cat sub/large4
464 483 large44
465 484 $ cat sub2/large6
466 485 large6
467 486 $ cat sub2/large7
468 487 large7
469 488
470 489 Test addremove: verify that files that should be added as largefiles are added as
471 490 such and that already-existing largefiles are not added as normal files by
472 491 accident.
473 492
474 493 $ rm normal3
475 494 $ rm sub/large4
476 495 $ echo "testing addremove with patterns" > testaddremove.dat
477 496 $ echo "normaladdremove" > normaladdremove
478 497 $ hg addremove
479 498 removing sub/large4
480 499 adding testaddremove.dat as a largefile
481 500 removing normal3
482 501 adding normaladdremove
483 502
484 503 Test addremove with -R
485 504
486 505 $ hg up -C
487 506 getting changed largefiles
488 507 1 largefiles updated, 0 removed
489 508 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 509 $ rm normal3
491 510 $ rm sub/large4
492 511 $ echo "testing addremove with patterns" > testaddremove.dat
493 512 $ echo "normaladdremove" > normaladdremove
494 513 $ cd ..
495 514 $ hg -R a -v addremove
496 515 removing sub/large4
497 516 adding testaddremove.dat as a largefile
498 517 removing normal3
499 518 adding normaladdremove
500 519 $ cd a
501 520
502 521 Test 3364
503 522 $ hg clone . ../addrm
504 523 updating to branch default
505 524 getting changed largefiles
506 525 3 largefiles updated, 0 removed
507 526 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
508 527 $ cd ../addrm
509 528 $ cat >> .hg/hgrc <<EOF
510 529 > [hooks]
511 530 > post-commit.stat=sh -c "echo \\"Invoking status postcommit hook\\"; hg status -A"
512 531 > EOF
513 532 $ touch foo
514 533 $ hg add --large foo
515 534 $ hg ci -m "add foo"
516 535 Invoking status precommit hook
517 536 A foo
518 537 Invoking status postcommit hook
519 538 C foo
520 539 C normal3
521 540 C sub/large4
522 541 C sub/normal4
523 542 C sub2/large6
524 543 C sub2/large7
525 544 $ rm foo
526 545 $ hg st
527 546 ! foo
528 547 hmm.. no precommit invoked, but there is a postcommit??
529 548 $ hg ci -m "will not checkin"
530 549 nothing changed (1 missing files, see 'hg status')
531 550 Invoking status postcommit hook
532 551 ! foo
533 552 C normal3
534 553 C sub/large4
535 554 C sub/normal4
536 555 C sub2/large6
537 556 C sub2/large7
538 557 [1]
539 558 $ hg addremove
540 559 removing foo
541 560 $ hg st
542 561 R foo
543 562 $ hg ci -m "used to say nothing changed"
544 563 Invoking status precommit hook
545 564 R foo
546 565 Invoking status postcommit hook
547 566 C normal3
548 567 C sub/large4
549 568 C sub/normal4
550 569 C sub2/large6
551 570 C sub2/large7
552 571 $ hg st
553 572
554 573 Test 3507 (both normal files and largefiles were a problem)
555 574
556 575 $ touch normal
557 576 $ touch large
558 577 $ hg add normal
559 578 $ hg add --large large
560 579 $ hg ci -m "added"
561 580 Invoking status precommit hook
562 581 A large
563 582 A normal
564 583 Invoking status postcommit hook
565 584 C large
566 585 C normal
567 586 C normal3
568 587 C sub/large4
569 588 C sub/normal4
570 589 C sub2/large6
571 590 C sub2/large7
572 591 $ hg remove normal
573 592 $ hg addremove --traceback
574 593 $ hg ci -m "addremoved normal"
575 594 Invoking status precommit hook
576 595 R normal
577 596 Invoking status postcommit hook
578 597 C large
579 598 C normal3
580 599 C sub/large4
581 600 C sub/normal4
582 601 C sub2/large6
583 602 C sub2/large7
584 603 $ hg up -C '.^'
585 604 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 605 $ hg remove large
587 606 $ hg addremove --traceback
588 607 $ hg ci -m "removed large"
589 608 Invoking status precommit hook
590 609 R large
591 610 created new head
592 611 Invoking status postcommit hook
593 612 C normal
594 613 C normal3
595 614 C sub/large4
596 615 C sub/normal4
597 616 C sub2/large6
598 617 C sub2/large7
599 618
600 619 Test commit -A (issue3542)
601 620 $ echo large8 > large8
602 621 $ hg add --large large8
603 622 $ hg ci -Am 'this used to add large8 as normal and commit both'
604 623 Invoking status precommit hook
605 624 A large8
606 625 Invoking status postcommit hook
607 626 C large8
608 627 C normal
609 628 C normal3
610 629 C sub/large4
611 630 C sub/normal4
612 631 C sub2/large6
613 632 C sub2/large7
614 633 $ rm large8
615 634 $ hg ci -Am 'this used to not notice the rm'
616 635 removing large8
617 636 Invoking status precommit hook
618 637 R large8
619 638 Invoking status postcommit hook
620 639 C normal
621 640 C normal3
622 641 C sub/large4
623 642 C sub/normal4
624 643 C sub2/large6
625 644 C sub2/large7
626 645
627 646 Test that a standin can't be added as a large file
628 647
629 648 $ touch large
630 649 $ hg add --large large
631 650 $ hg ci -m "add"
632 651 Invoking status precommit hook
633 652 A large
634 653 Invoking status postcommit hook
635 654 C large
636 655 C normal
637 656 C normal3
638 657 C sub/large4
639 658 C sub/normal4
640 659 C sub2/large6
641 660 C sub2/large7
642 661 $ hg remove large
643 662 $ touch large
644 663 $ hg addremove --config largefiles.patterns=**large --traceback
645 664 adding large as a largefile
646 665
647 666 Test that outgoing --large works (with revsets too)
648 667 $ hg outgoing --rev '.^' --large
649 668 comparing with $TESTTMP/a (glob)
650 669 searching for changes
651 670 changeset: 8:c02fd3b77ec4
652 671 user: test
653 672 date: Thu Jan 01 00:00:00 1970 +0000
654 673 summary: add foo
655 674
656 675 changeset: 9:289dd08c9bbb
657 676 user: test
658 677 date: Thu Jan 01 00:00:00 1970 +0000
659 678 summary: used to say nothing changed
660 679
661 680 changeset: 10:34f23ac6ac12
662 681 user: test
663 682 date: Thu Jan 01 00:00:00 1970 +0000
664 683 summary: added
665 684
666 685 changeset: 12:710c1b2f523c
667 686 parent: 10:34f23ac6ac12
668 687 user: test
669 688 date: Thu Jan 01 00:00:00 1970 +0000
670 689 summary: removed large
671 690
672 691 changeset: 13:0a3e75774479
673 692 user: test
674 693 date: Thu Jan 01 00:00:00 1970 +0000
675 694 summary: this used to add large8 as normal and commit both
676 695
677 696 changeset: 14:84f3d378175c
678 697 user: test
679 698 date: Thu Jan 01 00:00:00 1970 +0000
680 699 summary: this used to not notice the rm
681 700
682 701 largefiles to upload (1 entities):
683 702 large8
684 703
685 704 $ cd ../a
686 705
687 706 Clone a largefiles repo.
688 707
689 708 $ hg clone . ../b
690 709 updating to branch default
691 710 getting changed largefiles
692 711 3 largefiles updated, 0 removed
693 712 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
694 713 $ cd ../b
695 714 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
696 715 7:daea875e9014 add/edit more largefiles
697 716 6:4355d653f84f edit files yet again
698 717 5:9d5af5072dbd edit files again
699 718 4:74c02385b94c move files
700 719 3:9e8fbc4bce62 copy files
701 720 2:51a0ae4d5864 remove files
702 721 1:ce8896473775 edit files
703 722 0:30d30fe6a5be add files
704 723 $ cat normal3
705 724 normal33
706 725
707 726 Test graph log
708 727
709 728 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
710 729 @ 7:daea875e9014 add/edit more largefiles
711 730 |
712 731 o 6:4355d653f84f edit files yet again
713 732 |
714 733 o 5:9d5af5072dbd edit files again
715 734 |
716 735 o 4:74c02385b94c move files
717 736 |
718 737 o 3:9e8fbc4bce62 copy files
719 738 |
720 739 o 2:51a0ae4d5864 remove files
721 740 |
722 741 o 1:ce8896473775 edit files
723 742 |
724 743 o 0:30d30fe6a5be add files
725 744
726 745
727 746 Test log with --patch
728 747
729 748 $ hg log --patch -r 6::7
730 749 changeset: 6:4355d653f84f
731 750 user: test
732 751 date: Thu Jan 01 00:00:00 1970 +0000
733 752 summary: edit files yet again
734 753
735 754 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
736 755 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
737 756 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
738 757 @@ -1,1 +1,1 @@
739 758 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
740 759 +7838695e10da2bb75ac1156565f40a2595fa2fa0
741 760 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
742 761 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
743 762 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
744 763 @@ -1,1 +1,1 @@
745 764 -aeb2210d19f02886dde00dac279729a48471e2f9
746 765 +971fb41e78fea4f8e0ba5244784239371cb00591
747 766 diff -r 9d5af5072dbd -r 4355d653f84f normal3
748 767 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
749 768 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
750 769 @@ -1,1 +1,1 @@
751 770 -normal3
752 771 +normal33
753 772 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
754 773 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
755 774 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
756 775 @@ -1,1 +1,1 @@
757 776 -normal4
758 777 +normal44
759 778
760 779 changeset: 7:daea875e9014
761 780 tag: tip
762 781 user: test
763 782 date: Thu Jan 01 00:00:00 1970 +0000
764 783 summary: add/edit more largefiles
765 784
766 785 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
767 786 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
768 787 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
769 788 @@ -1,1 +0,0 @@
770 789 -7838695e10da2bb75ac1156565f40a2595fa2fa0
771 790 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
772 791 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
773 792 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
774 793 @@ -0,0 +1,1 @@
775 794 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
776 795 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
777 796 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
778 797 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
779 798 @@ -0,0 +1,1 @@
780 799 +bb3151689acb10f0c3125c560d5e63df914bc1af
781 800
782 801
783 802 $ hg log --patch -r 6::7 sub/
784 803 changeset: 6:4355d653f84f
785 804 user: test
786 805 date: Thu Jan 01 00:00:00 1970 +0000
787 806 summary: edit files yet again
788 807
789 808 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
790 809 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
791 810 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
792 811 @@ -1,1 +1,1 @@
793 812 -aeb2210d19f02886dde00dac279729a48471e2f9
794 813 +971fb41e78fea4f8e0ba5244784239371cb00591
795 814 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
796 815 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
797 816 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
798 817 @@ -1,1 +1,1 @@
799 818 -normal4
800 819 +normal44
801 820
802 821
803 822 log with both --follow and --patch
804 823
805 824 $ hg log --follow --patch --limit 2
806 825 changeset: 7:daea875e9014
807 826 tag: tip
808 827 user: test
809 828 date: Thu Jan 01 00:00:00 1970 +0000
810 829 summary: add/edit more largefiles
811 830
812 831 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
813 832 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
814 833 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
815 834 @@ -1,1 +0,0 @@
816 835 -7838695e10da2bb75ac1156565f40a2595fa2fa0
817 836 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
818 837 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
819 838 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
820 839 @@ -0,0 +1,1 @@
821 840 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
822 841 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
823 842 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
824 843 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
825 844 @@ -0,0 +1,1 @@
826 845 +bb3151689acb10f0c3125c560d5e63df914bc1af
827 846
828 847 changeset: 6:4355d653f84f
829 848 user: test
830 849 date: Thu Jan 01 00:00:00 1970 +0000
831 850 summary: edit files yet again
832 851
833 852 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
834 853 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
835 854 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
836 855 @@ -1,1 +1,1 @@
837 856 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
838 857 +7838695e10da2bb75ac1156565f40a2595fa2fa0
839 858 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
840 859 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
841 860 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
842 861 @@ -1,1 +1,1 @@
843 862 -aeb2210d19f02886dde00dac279729a48471e2f9
844 863 +971fb41e78fea4f8e0ba5244784239371cb00591
845 864 diff -r 9d5af5072dbd -r 4355d653f84f normal3
846 865 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
847 866 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
848 867 @@ -1,1 +1,1 @@
849 868 -normal3
850 869 +normal33
851 870 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
852 871 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
853 872 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
854 873 @@ -1,1 +1,1 @@
855 874 -normal4
856 875 +normal44
857 876
858 877 $ hg log --follow --patch sub/large4
859 878 changeset: 6:4355d653f84f
860 879 user: test
861 880 date: Thu Jan 01 00:00:00 1970 +0000
862 881 summary: edit files yet again
863 882
864 883 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
865 884 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
866 885 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
867 886 @@ -1,1 +1,1 @@
868 887 -aeb2210d19f02886dde00dac279729a48471e2f9
869 888 +971fb41e78fea4f8e0ba5244784239371cb00591
870 889
871 890 changeset: 5:9d5af5072dbd
872 891 user: test
873 892 date: Thu Jan 01 00:00:00 1970 +0000
874 893 summary: edit files again
875 894
876 895 diff -r 74c02385b94c -r 9d5af5072dbd .hglf/sub/large4
877 896 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
878 897 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
879 898 @@ -1,1 +1,1 @@
880 899 -eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
881 900 +aeb2210d19f02886dde00dac279729a48471e2f9
882 901
883 902 changeset: 4:74c02385b94c
884 903 user: test
885 904 date: Thu Jan 01 00:00:00 1970 +0000
886 905 summary: move files
887 906
888 907 diff -r 9e8fbc4bce62 -r 74c02385b94c .hglf/sub/large4
889 908 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
890 909 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
891 910 @@ -0,0 +1,1 @@
892 911 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
893 912
894 913 changeset: 1:ce8896473775
895 914 user: test
896 915 date: Thu Jan 01 00:00:00 1970 +0000
897 916 summary: edit files
898 917
899 918 diff -r 30d30fe6a5be -r ce8896473775 .hglf/sub/large2
900 919 --- a/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
901 920 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
902 921 @@ -1,1 +1,1 @@
903 922 -1deebade43c8c498a3c8daddac0244dc55d1331d
904 923 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
905 924
906 925 changeset: 0:30d30fe6a5be
907 926 user: test
908 927 date: Thu Jan 01 00:00:00 1970 +0000
909 928 summary: add files
910 929
911 930 diff -r 000000000000 -r 30d30fe6a5be .hglf/sub/large2
912 931 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
913 932 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
914 933 @@ -0,0 +1,1 @@
915 934 +1deebade43c8c498a3c8daddac0244dc55d1331d
916 935
917 936 $ cat sub/normal4
918 937 normal44
919 938 $ cat sub/large4
920 939 large44
921 940 $ cat sub2/large6
922 941 large6
923 942 $ cat sub2/large7
924 943 large7
925 944 $ hg log -qf sub2/large7
926 945 7:daea875e9014
927 946 $ hg log -Gqf sub2/large7
928 947 @ 7:daea875e9014
929 948 |
930 949 ~
931 950 $ cd ..
932 951
933 952 Test log from outside repo
934 953
935 954 $ hg log b/sub -T '{rev}:{node|short} {desc|firstline}\n'
936 955 6:4355d653f84f edit files yet again
937 956 5:9d5af5072dbd edit files again
938 957 4:74c02385b94c move files
939 958 1:ce8896473775 edit files
940 959 0:30d30fe6a5be add files
941 960
942 961 Test clone at revision
943 962
944 963 $ hg clone a -r 3 c
945 964 adding changesets
946 965 adding manifests
947 966 adding file changes
948 967 added 4 changesets with 10 changes to 4 files
949 968 updating to branch default
950 969 getting changed largefiles
951 970 2 largefiles updated, 0 removed
952 971 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
953 972 $ cd c
954 973 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
955 974 3:9e8fbc4bce62 copy files
956 975 2:51a0ae4d5864 remove files
957 976 1:ce8896473775 edit files
958 977 0:30d30fe6a5be add files
959 978 $ cat normal1
960 979 normal22
961 980 $ cat large1
962 981 large22
963 982 $ cat sub/normal2
964 983 normal22
965 984 $ cat sub/large2
966 985 large22
967 986
968 987 Old revisions of a clone have correct largefiles content (this also
969 988 tests update).
970 989
971 990 $ hg update -r 1
972 991 getting changed largefiles
973 992 1 largefiles updated, 0 removed
974 993 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
975 994 $ cat large1
976 995 large11
977 996 $ cat sub/large2
978 997 large22
979 998 $ cd ..
980 999
981 1000 Test cloning with --all-largefiles flag
982 1001
983 1002 $ rm "${USERCACHE}"/*
984 1003 $ hg clone --all-largefiles a a-backup
985 1004 updating to branch default
986 1005 getting changed largefiles
987 1006 3 largefiles updated, 0 removed
988 1007 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
989 1008 8 additional largefiles cached
990 1009
991 1010 $ rm "${USERCACHE}"/*
992 1011 $ hg clone --all-largefiles -u 0 a a-clone0
993 1012 updating to branch default
994 1013 getting changed largefiles
995 1014 2 largefiles updated, 0 removed
996 1015 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
997 1016 9 additional largefiles cached
998 1017 $ hg -R a-clone0 sum
999 1018 parent: 0:30d30fe6a5be
1000 1019 add files
1001 1020 branch: default
1002 1021 commit: (clean)
1003 1022 update: 7 new changesets (update)
1004 1023 phases: 8 draft
1005 1024
1006 1025 $ rm "${USERCACHE}"/*
1007 1026 $ hg clone --all-largefiles -u 1 a a-clone1
1008 1027 updating to branch default
1009 1028 getting changed largefiles
1010 1029 2 largefiles updated, 0 removed
1011 1030 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1012 1031 8 additional largefiles cached
1013 1032 $ hg -R a-clone1 verify --large --lfa --lfc
1014 1033 checking changesets
1015 1034 checking manifests
1016 1035 crosschecking files in changesets and manifests
1017 1036 checking files
1018 1037 10 files, 8 changesets, 24 total revisions
1019 1038 searching 8 changesets for largefiles
1020 1039 verified contents of 13 revisions of 6 largefiles
1021 1040 $ hg -R a-clone1 sum
1022 1041 parent: 1:ce8896473775
1023 1042 edit files
1024 1043 branch: default
1025 1044 commit: (clean)
1026 1045 update: 6 new changesets (update)
1027 1046 phases: 8 draft
1028 1047
1029 1048 $ rm "${USERCACHE}"/*
1030 1049 $ hg clone --all-largefiles -U a a-clone-u
1031 1050 11 additional largefiles cached
1032 1051 $ hg -R a-clone-u sum
1033 1052 parent: -1:000000000000 (no revision checked out)
1034 1053 branch: default
1035 1054 commit: (clean)
1036 1055 update: 8 new changesets (update)
1037 1056 phases: 8 draft
1038 1057
1039 1058 Show computed destination directory:
1040 1059
1041 1060 $ mkdir xyz
1042 1061 $ cd xyz
1043 1062 $ hg clone ../a
1044 1063 destination directory: a
1045 1064 updating to branch default
1046 1065 getting changed largefiles
1047 1066 3 largefiles updated, 0 removed
1048 1067 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1049 1068 $ cd ..
1050 1069
1051 1070 Clone URL without path:
1052 1071
1053 1072 $ hg clone file://
1054 1073 abort: repository / not found!
1055 1074 [255]
1056 1075
1057 1076 Ensure base clone command argument validation
1058 1077
1059 1078 $ hg clone -U -u 0 a a-clone-failure
1060 1079 abort: cannot specify both --noupdate and --updaterev
1061 1080 [255]
1062 1081
1063 1082 $ hg clone --all-largefiles a ssh://localhost/a
1064 1083 abort: --all-largefiles is incompatible with non-local destination ssh://localhost/a
1065 1084 [255]
1066 1085
1067 1086 Test pulling with --all-largefiles flag. Also test that the largefiles are
1068 1087 downloaded from 'default' instead of 'default-push' when no source is specified
1069 1088 (issue3584)
1070 1089
1071 1090 $ rm -Rf a-backup
1072 1091 $ hg clone -r 1 a a-backup
1073 1092 adding changesets
1074 1093 adding manifests
1075 1094 adding file changes
1076 1095 added 2 changesets with 8 changes to 4 files
1077 1096 updating to branch default
1078 1097 getting changed largefiles
1079 1098 2 largefiles updated, 0 removed
1080 1099 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1081 1100 $ rm "${USERCACHE}"/*
1082 1101 $ cd a-backup
1083 1102 $ hg pull --all-largefiles --config paths.default-push=bogus/path
1084 1103 pulling from $TESTTMP/a (glob)
1085 1104 searching for changes
1086 1105 adding changesets
1087 1106 adding manifests
1088 1107 adding file changes
1089 1108 added 6 changesets with 16 changes to 8 files
1090 1109 (run 'hg update' to get a working copy)
1091 1110 6 largefiles cached
1092 1111
1093 1112 redo pull with --lfrev and check it pulls largefiles for the right revs
1094 1113
1095 1114 $ hg rollback
1096 1115 repository tip rolled back to revision 1 (undo pull)
1097 1116 $ hg pull -v --lfrev 'heads(pulled())+min(pulled())'
1098 1117 pulling from $TESTTMP/a (glob)
1099 1118 searching for changes
1100 1119 all local heads known remotely
1101 1120 6 changesets found
1102 1121 uncompressed size of bundle content:
1103 1122 1389 (changelog)
1104 1123 1599 (manifests)
1105 1124 254 .hglf/large1
1106 1125 564 .hglf/large3
1107 1126 572 .hglf/sub/large4
1108 1127 182 .hglf/sub2/large6
1109 1128 182 .hglf/sub2/large7
1110 1129 212 normal1
1111 1130 457 normal3
1112 1131 465 sub/normal4
1113 1132 adding changesets
1114 1133 adding manifests
1115 1134 adding file changes
1116 1135 added 6 changesets with 16 changes to 8 files
1117 1136 calling hook changegroup.lfiles: hgext.largefiles.reposetup.checkrequireslfiles
1118 1137 (run 'hg update' to get a working copy)
1119 1138 pulling largefiles for revision 7
1120 1139 found 971fb41e78fea4f8e0ba5244784239371cb00591 in store
1121 1140 found 0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30 in store
1122 1141 found bb3151689acb10f0c3125c560d5e63df914bc1af in store
1123 1142 pulling largefiles for revision 2
1124 1143 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1125 1144 0 largefiles cached
1126 1145
1127 1146 lfpull
1128 1147
1129 1148 $ hg lfpull -r : --config largefiles.usercache=usercache-lfpull
1130 1149 2 largefiles cached
1131 1150 $ hg lfpull -v -r 4+2 --config largefiles.usercache=usercache-lfpull
1132 1151 pulling largefiles for revision 4
1133 1152 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1134 1153 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1135 1154 pulling largefiles for revision 2
1136 1155 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1137 1156 0 largefiles cached
1138 1157
1139 1158 $ ls usercache-lfpull/* | sort
1140 1159 usercache-lfpull/1deebade43c8c498a3c8daddac0244dc55d1331d
1141 1160 usercache-lfpull/4669e532d5b2c093a78eca010077e708a071bb64
1142 1161
1143 1162 $ cd ..
1144 1163
1145 1164 Rebasing between two repositories does not revert largefiles to old
1146 1165 revisions (this was a very bad bug that took a lot of work to fix).
1147 1166
1148 1167 $ hg clone a d
1149 1168 updating to branch default
1150 1169 getting changed largefiles
1151 1170 3 largefiles updated, 0 removed
1152 1171 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1153 1172 $ cd b
1154 1173 $ echo large4-modified > sub/large4
1155 1174 $ echo normal3-modified > normal3
1156 1175 $ hg commit -m "modify normal file and largefile in repo b"
1157 1176 Invoking status precommit hook
1158 1177 M normal3
1159 1178 M sub/large4
1160 1179 $ cd ../d
1161 1180 $ echo large6-modified > sub2/large6
1162 1181 $ echo normal4-modified > sub/normal4
1163 1182 $ hg commit -m "modify normal file largefile in repo d"
1164 1183 Invoking status precommit hook
1165 1184 M sub/normal4
1166 1185 M sub2/large6
1167 1186 $ cd ..
1168 1187 $ hg clone d e
1169 1188 updating to branch default
1170 1189 getting changed largefiles
1171 1190 3 largefiles updated, 0 removed
1172 1191 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1173 1192 $ cd d
1174 1193
1175 1194 More rebase testing, but also test that the largefiles are downloaded from
1176 1195 'default-push' when no source is specified (issue3584). (The largefile from the
1177 1196 pulled revision is however not downloaded but found in the local cache.)
1178 1197 Largefiles are fetched for the new pulled revision, not for existing revisions,
1179 1198 rebased or not.
1180 1199
1181 1200 $ [ ! -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1182 1201 $ hg pull --rebase --all-largefiles --config paths.default-push=bogus/path --config paths.default=../b
1183 1202 pulling from $TESTTMP/b (glob)
1184 1203 searching for changes
1185 1204 adding changesets
1186 1205 adding manifests
1187 1206 adding file changes
1188 1207 added 1 changesets with 2 changes to 2 files (+1 heads)
1189 1208 rebasing 8:f574fb32bb45 "modify normal file largefile in repo d"
1190 1209 Invoking status precommit hook
1191 1210 M sub/normal4
1192 1211 M sub2/large6
1193 1212 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-dd1d9f80-backup.hg (glob)
1194 1213 0 largefiles cached
1195 1214 $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1196 1215 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1197 1216 9:598410d3eb9a modify normal file largefile in repo d
1198 1217 8:a381d2c8c80e modify normal file and largefile in repo b
1199 1218 7:daea875e9014 add/edit more largefiles
1200 1219 6:4355d653f84f edit files yet again
1201 1220 5:9d5af5072dbd edit files again
1202 1221 4:74c02385b94c move files
1203 1222 3:9e8fbc4bce62 copy files
1204 1223 2:51a0ae4d5864 remove files
1205 1224 1:ce8896473775 edit files
1206 1225 0:30d30fe6a5be add files
1207 1226 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
1208 1227 @ 9:598410d3eb9a modify normal file largefile in repo d
1209 1228 |
1210 1229 o 8:a381d2c8c80e modify normal file and largefile in repo b
1211 1230 |
1212 1231 o 7:daea875e9014 add/edit more largefiles
1213 1232 |
1214 1233 o 6:4355d653f84f edit files yet again
1215 1234 |
1216 1235 o 5:9d5af5072dbd edit files again
1217 1236 |
1218 1237 o 4:74c02385b94c move files
1219 1238 |
1220 1239 o 3:9e8fbc4bce62 copy files
1221 1240 |
1222 1241 o 2:51a0ae4d5864 remove files
1223 1242 |
1224 1243 o 1:ce8896473775 edit files
1225 1244 |
1226 1245 o 0:30d30fe6a5be add files
1227 1246
1228 1247 $ cat normal3
1229 1248 normal3-modified
1230 1249 $ cat sub/normal4
1231 1250 normal4-modified
1232 1251 $ cat sub/large4
1233 1252 large4-modified
1234 1253 $ cat sub2/large6
1235 1254 large6-modified
1236 1255 $ cat sub2/large7
1237 1256 large7
1238 1257 $ cd ../e
1239 1258 $ hg pull ../b
1240 1259 pulling from ../b
1241 1260 searching for changes
1242 1261 adding changesets
1243 1262 adding manifests
1244 1263 adding file changes
1245 1264 added 1 changesets with 2 changes to 2 files (+1 heads)
1246 1265 (run 'hg heads' to see heads, 'hg merge' to merge)
1247 1266 $ hg rebase
1248 1267 rebasing 8:f574fb32bb45 "modify normal file largefile in repo d"
1249 1268 Invoking status precommit hook
1250 1269 M sub/normal4
1251 1270 M sub2/large6
1252 1271 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-dd1d9f80-backup.hg (glob)
1253 1272 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1254 1273 9:598410d3eb9a modify normal file largefile in repo d
1255 1274 8:a381d2c8c80e modify normal file and largefile in repo b
1256 1275 7:daea875e9014 add/edit more largefiles
1257 1276 6:4355d653f84f edit files yet again
1258 1277 5:9d5af5072dbd edit files again
1259 1278 4:74c02385b94c move files
1260 1279 3:9e8fbc4bce62 copy files
1261 1280 2:51a0ae4d5864 remove files
1262 1281 1:ce8896473775 edit files
1263 1282 0:30d30fe6a5be add files
1264 1283 $ cat normal3
1265 1284 normal3-modified
1266 1285 $ cat sub/normal4
1267 1286 normal4-modified
1268 1287 $ cat sub/large4
1269 1288 large4-modified
1270 1289 $ cat sub2/large6
1271 1290 large6-modified
1272 1291 $ cat sub2/large7
1273 1292 large7
1274 1293
1275 1294 Log on largefiles
1276 1295
1277 1296 - same output
1278 1297 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1279 1298 8:a381d2c8c80e modify normal file and largefile in repo b
1280 1299 6:4355d653f84f edit files yet again
1281 1300 5:9d5af5072dbd edit files again
1282 1301 4:74c02385b94c move files
1283 1302 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1284 1303 o 8:a381d2c8c80e modify normal file and largefile in repo b
1285 1304 :
1286 1305 o 6:4355d653f84f edit files yet again
1287 1306 |
1288 1307 o 5:9d5af5072dbd edit files again
1289 1308 |
1290 1309 o 4:74c02385b94c move files
1291 1310 |
1292 1311 ~
1293 1312 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub/large4
1294 1313 8:a381d2c8c80e modify normal file and largefile in repo b
1295 1314 6:4355d653f84f edit files yet again
1296 1315 5:9d5af5072dbd edit files again
1297 1316 4:74c02385b94c move files
1298 1317 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1299 1318 o 8:a381d2c8c80e modify normal file and largefile in repo b
1300 1319 :
1301 1320 o 6:4355d653f84f edit files yet again
1302 1321 |
1303 1322 o 5:9d5af5072dbd edit files again
1304 1323 |
1305 1324 o 4:74c02385b94c move files
1306 1325 |
1307 1326 ~
1308 1327
1309 1328 - .hglf only matches largefiles, without .hglf it matches 9 bco sub/normal
1310 1329 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1311 1330 8:a381d2c8c80e modify normal file and largefile in repo b
1312 1331 6:4355d653f84f edit files yet again
1313 1332 5:9d5af5072dbd edit files again
1314 1333 4:74c02385b94c move files
1315 1334 1:ce8896473775 edit files
1316 1335 0:30d30fe6a5be add files
1317 1336 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1318 1337 o 8:a381d2c8c80e modify normal file and largefile in repo b
1319 1338 :
1320 1339 o 6:4355d653f84f edit files yet again
1321 1340 |
1322 1341 o 5:9d5af5072dbd edit files again
1323 1342 |
1324 1343 o 4:74c02385b94c move files
1325 1344 :
1326 1345 o 1:ce8896473775 edit files
1327 1346 |
1328 1347 o 0:30d30fe6a5be add files
1329 1348
1330 1349 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub
1331 1350 9:598410d3eb9a modify normal file largefile in repo d
1332 1351 8:a381d2c8c80e modify normal file and largefile in repo b
1333 1352 6:4355d653f84f edit files yet again
1334 1353 5:9d5af5072dbd edit files again
1335 1354 4:74c02385b94c move files
1336 1355 1:ce8896473775 edit files
1337 1356 0:30d30fe6a5be add files
1338 1357 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' sub
1339 1358 @ 9:598410d3eb9a modify normal file largefile in repo d
1340 1359 |
1341 1360 o 8:a381d2c8c80e modify normal file and largefile in repo b
1342 1361 :
1343 1362 o 6:4355d653f84f edit files yet again
1344 1363 |
1345 1364 o 5:9d5af5072dbd edit files again
1346 1365 |
1347 1366 o 4:74c02385b94c move files
1348 1367 :
1349 1368 o 1:ce8896473775 edit files
1350 1369 |
1351 1370 o 0:30d30fe6a5be add files
1352 1371
1353 1372 - globbing gives same result
1354 1373 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1355 1374 9:598410d3eb9a modify normal file largefile in repo d
1356 1375 8:a381d2c8c80e modify normal file and largefile in repo b
1357 1376 6:4355d653f84f edit files yet again
1358 1377 5:9d5af5072dbd edit files again
1359 1378 4:74c02385b94c move files
1360 1379 1:ce8896473775 edit files
1361 1380 0:30d30fe6a5be add files
1362 1381 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1363 1382 @ 9:598410d3eb9a modify normal file largefile in repo d
1364 1383 |
1365 1384 o 8:a381d2c8c80e modify normal file and largefile in repo b
1366 1385 :
1367 1386 o 6:4355d653f84f edit files yet again
1368 1387 |
1369 1388 o 5:9d5af5072dbd edit files again
1370 1389 |
1371 1390 o 4:74c02385b94c move files
1372 1391 :
1373 1392 o 1:ce8896473775 edit files
1374 1393 |
1375 1394 o 0:30d30fe6a5be add files
1376 1395
1377 1396 Rollback on largefiles.
1378 1397
1379 1398 $ echo large4-modified-again > sub/large4
1380 1399 $ hg commit -m "Modify large4 again"
1381 1400 Invoking status precommit hook
1382 1401 M sub/large4
1383 1402 $ hg rollback
1384 1403 repository tip rolled back to revision 9 (undo commit)
1385 1404 working directory now based on revision 9
1386 1405 $ hg st
1387 1406 M sub/large4
1388 1407 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1389 1408 9:598410d3eb9a modify normal file largefile in repo d
1390 1409 8:a381d2c8c80e modify normal file and largefile in repo b
1391 1410 7:daea875e9014 add/edit more largefiles
1392 1411 6:4355d653f84f edit files yet again
1393 1412 5:9d5af5072dbd edit files again
1394 1413 4:74c02385b94c move files
1395 1414 3:9e8fbc4bce62 copy files
1396 1415 2:51a0ae4d5864 remove files
1397 1416 1:ce8896473775 edit files
1398 1417 0:30d30fe6a5be add files
1399 1418 $ cat sub/large4
1400 1419 large4-modified-again
1401 1420
1402 1421 "update --check" refuses to update with uncommitted changes.
1403 1422 $ hg update --check 8
1404 1423 abort: uncommitted changes
1405 1424 [255]
1406 1425
1407 1426 "update --clean" leaves correct largefiles in working copy, even when there is
1408 1427 .orig files from revert in .hglf.
1409 1428
1410 1429 $ echo mistake > sub2/large7
1411 1430 $ hg revert sub2/large7
1412 1431 $ cat sub2/large7
1413 1432 large7
1414 1433 $ cat sub2/large7.orig
1415 1434 mistake
1416 1435 $ test ! -f .hglf/sub2/large7.orig
1417 1436
1418 1437 $ hg -q update --clean -r null
1419 1438 $ hg update --clean
1420 1439 getting changed largefiles
1421 1440 3 largefiles updated, 0 removed
1422 1441 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1423 1442 $ cat normal3
1424 1443 normal3-modified
1425 1444 $ cat sub/normal4
1426 1445 normal4-modified
1427 1446 $ cat sub/large4
1428 1447 large4-modified
1429 1448 $ cat sub2/large6
1430 1449 large6-modified
1431 1450 $ cat sub2/large7
1432 1451 large7
1433 1452 $ cat sub2/large7.orig
1434 1453 mistake
1435 1454 $ test ! -f .hglf/sub2/large7.orig
1436 1455
1437 1456 verify that largefile .orig file no longer is overwritten on every update -C:
1438 1457 $ hg update --clean
1439 1458 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1440 1459 $ cat sub2/large7.orig
1441 1460 mistake
1442 1461 $ rm sub2/large7.orig
1443 1462
1444 1463 Now "update check" is happy.
1445 1464 $ hg update --check 8
1446 1465 getting changed largefiles
1447 1466 1 largefiles updated, 0 removed
1448 1467 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1449 1468 $ hg update --check
1450 1469 getting changed largefiles
1451 1470 1 largefiles updated, 0 removed
1452 1471 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1453 1472
1454 1473 Test removing empty largefiles directories on update
1455 1474 $ test -d sub2 && echo "sub2 exists"
1456 1475 sub2 exists
1457 1476 $ hg update -q null
1458 1477 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1459 1478 [1]
1460 1479 $ hg update -q
1461 1480
1462 1481 Test hg remove removes empty largefiles directories
1463 1482 $ test -d sub2 && echo "sub2 exists"
1464 1483 sub2 exists
1465 1484 $ hg remove sub2/*
1466 1485 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1467 1486 [1]
1468 1487 $ hg revert sub2/large6 sub2/large7
1469 1488
1470 1489 "revert" works on largefiles (and normal files too).
1471 1490 $ echo hack3 >> normal3
1472 1491 $ echo hack4 >> sub/normal4
1473 1492 $ echo hack4 >> sub/large4
1474 1493 $ rm sub2/large6
1475 1494 $ hg revert sub2/large6
1476 1495 $ hg rm sub2/large6
1477 1496 $ echo new >> sub2/large8
1478 1497 $ hg add --large sub2/large8
1479 1498 # XXX we don't really want to report that we're reverting the standin;
1480 1499 # that's just an implementation detail. But I don't see an obvious fix. ;-(
1481 1500 $ hg revert sub
1482 1501 reverting .hglf/sub/large4 (glob)
1483 1502 reverting sub/normal4 (glob)
1484 1503 $ hg status
1485 1504 M normal3
1486 1505 A sub2/large8
1487 1506 R sub2/large6
1488 1507 ? sub/large4.orig
1489 1508 ? sub/normal4.orig
1490 1509 $ cat sub/normal4
1491 1510 normal4-modified
1492 1511 $ cat sub/large4
1493 1512 large4-modified
1494 1513 $ hg revert -a --no-backup
1495 1514 undeleting .hglf/sub2/large6 (glob)
1496 1515 forgetting .hglf/sub2/large8 (glob)
1497 1516 reverting normal3
1498 1517 $ hg status
1499 1518 ? sub/large4.orig
1500 1519 ? sub/normal4.orig
1501 1520 ? sub2/large8
1502 1521 $ cat normal3
1503 1522 normal3-modified
1504 1523 $ cat sub2/large6
1505 1524 large6-modified
1506 1525 $ rm sub/*.orig sub2/large8
1507 1526
1508 1527 revert some files to an older revision
1509 1528 $ hg revert --no-backup -r 8 sub2
1510 1529 reverting .hglf/sub2/large6 (glob)
1511 1530 $ cat sub2/large6
1512 1531 large6
1513 1532 $ hg revert --no-backup -C -r '.^' sub2
1514 1533 $ hg revert --no-backup sub2
1515 1534 reverting .hglf/sub2/large6 (glob)
1516 1535 $ hg status
1517 1536
1518 1537 "verify --large" actually verifies largefiles
1519 1538
1520 1539 - Where Do We Come From? What Are We? Where Are We Going?
1521 1540 $ pwd
1522 1541 $TESTTMP/e
1523 1542 $ hg paths
1524 1543 default = $TESTTMP/d (glob)
1525 1544
1526 1545 $ hg verify --large
1527 1546 checking changesets
1528 1547 checking manifests
1529 1548 crosschecking files in changesets and manifests
1530 1549 checking files
1531 1550 10 files, 10 changesets, 28 total revisions
1532 1551 searching 1 changesets for largefiles
1533 1552 verified existence of 3 revisions of 3 largefiles
1534 1553
1535 1554 - introduce missing blob in local store repo and remote store
1536 1555 and make sure that this is caught:
1537 1556
1538 1557 $ mv $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 .
1539 1558 $ rm .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1540 1559 $ hg verify --large
1541 1560 checking changesets
1542 1561 checking manifests
1543 1562 crosschecking files in changesets and manifests
1544 1563 checking files
1545 1564 10 files, 10 changesets, 28 total revisions
1546 1565 searching 1 changesets for largefiles
1547 1566 changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1548 1567 verified existence of 3 revisions of 3 largefiles
1549 1568 [1]
1550 1569
1551 1570 - introduce corruption and make sure that it is caught when checking content:
1552 1571 $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1553 1572 $ hg verify -q --large --lfc
1554 1573 changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
1555 1574 [1]
1556 1575
1557 1576 - cleanup
1558 1577 $ cp e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/
1559 1578 $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 .hg/largefiles/
1560 1579
1561 1580 - verifying all revisions will fail because we didn't clone all largefiles to d:
1562 1581 $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1563 1582 $ hg verify -q --lfa --lfc
1564 1583 changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64 (glob)
1565 1584 changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d (glob)
1566 1585 changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f (glob)
1567 1586 changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1568 1587 changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1569 1588 changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1570 1589 changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
1571 1590 changeset 5:9d5af5072dbd: large3 references missing $TESTTMP/d/.hg/largefiles/baaf12afde9d8d67f25dab6dced0d2bf77dba47c (glob)
1572 1591 changeset 5:9d5af5072dbd: sub/large4 references missing $TESTTMP/d/.hg/largefiles/aeb2210d19f02886dde00dac279729a48471e2f9 (glob)
1573 1592 changeset 6:4355d653f84f: large3 references missing $TESTTMP/d/.hg/largefiles/7838695e10da2bb75ac1156565f40a2595fa2fa0 (glob)
1574 1593 [1]
1575 1594
1576 1595 - cleanup
1577 1596 $ rm $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1578 1597 $ rm -f .hglf/sub/*.orig
1579 1598
1580 1599 Update to revision with missing largefile - and make sure it really is missing
1581 1600
1582 1601 $ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0
1583 1602 $ hg up -r 6
1584 1603 getting changed largefiles
1585 1604 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1586 1605 1 largefiles updated, 2 removed
1587 1606 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
1588 1607 $ rm normal3
1589 1608 $ echo >> sub/normal4
1590 1609 $ hg ci -m 'commit with missing files'
1591 1610 Invoking status precommit hook
1592 1611 M sub/normal4
1593 1612 ! large3
1594 1613 ! normal3
1595 1614 created new head
1596 1615 $ hg st
1597 1616 ! large3
1598 1617 ! normal3
1599 1618 $ hg up -r.
1600 1619 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1601 1620 $ hg st
1602 1621 ! large3
1603 1622 ! normal3
1604 1623 $ hg up -Cr.
1605 1624 getting changed largefiles
1606 1625 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1607 1626 0 largefiles updated, 0 removed
1608 1627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1609 1628 $ hg st
1610 1629 ! large3
1611 1630 $ hg rollback
1612 1631 repository tip rolled back to revision 9 (undo commit)
1613 1632 working directory now based on revision 6
1614 1633
1615 1634 Merge with revision with missing largefile - and make sure it tries to fetch it.
1616 1635
1617 1636 $ hg up -Cqr null
1618 1637 $ echo f > f
1619 1638 $ hg ci -Am branch
1620 1639 adding f
1621 1640 Invoking status precommit hook
1622 1641 A f
1623 1642 created new head
1624 1643 $ hg merge -r 6
1625 1644 getting changed largefiles
1626 1645 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1627 1646 1 largefiles updated, 0 removed
1628 1647 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1629 1648 (branch merge, don't forget to commit)
1630 1649
1631 1650 $ hg rollback -q
1632 1651 $ hg up -Cq
1633 1652
1634 1653 Pulling 0 revisions with --all-largefiles should not fetch for all revisions
1635 1654
1636 1655 $ hg pull --all-largefiles
1637 1656 pulling from $TESTTMP/d (glob)
1638 1657 searching for changes
1639 1658 no changes found
1640 1659
1641 1660 Merging does not revert to old versions of largefiles and also check
1642 1661 that merging after having pulled from a non-default remote works
1643 1662 correctly.
1644 1663
1645 1664 $ cd ..
1646 1665 $ hg clone -r 7 e temp
1647 1666 adding changesets
1648 1667 adding manifests
1649 1668 adding file changes
1650 1669 added 8 changesets with 24 changes to 10 files
1651 1670 updating to branch default
1652 1671 getting changed largefiles
1653 1672 3 largefiles updated, 0 removed
1654 1673 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1655 1674 $ hg clone temp f
1656 1675 updating to branch default
1657 1676 getting changed largefiles
1658 1677 3 largefiles updated, 0 removed
1659 1678 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1660 1679 # Delete the largefiles in the largefiles system cache so that we have an
1661 1680 # opportunity to test that caching after a pull works.
1662 1681 $ rm "${USERCACHE}"/*
1663 1682 $ cd f
1664 1683 $ echo "large4-merge-test" > sub/large4
1665 1684 $ hg commit -m "Modify large4 to test merge"
1666 1685 Invoking status precommit hook
1667 1686 M sub/large4
1668 1687 # Test --cache-largefiles flag
1669 1688 $ hg pull --lfrev 'heads(pulled())' ../e
1670 1689 pulling from ../e
1671 1690 searching for changes
1672 1691 adding changesets
1673 1692 adding manifests
1674 1693 adding file changes
1675 1694 added 2 changesets with 4 changes to 4 files (+1 heads)
1676 1695 (run 'hg heads' to see heads, 'hg merge' to merge)
1677 1696 2 largefiles cached
1678 1697 $ hg merge
1679 1698 largefile sub/large4 has a merge conflict
1680 1699 ancestor was 971fb41e78fea4f8e0ba5244784239371cb00591
1681 1700 keep (l)ocal d846f26643bfa8ec210be40cc93cc6b7ff1128ea or
1682 1701 take (o)ther e166e74c7303192238d60af5a9c4ce9bef0b7928? l
1683 1702 getting changed largefiles
1684 1703 1 largefiles updated, 0 removed
1685 1704 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
1686 1705 (branch merge, don't forget to commit)
1687 1706 $ hg commit -m "Merge repos e and f"
1688 1707 Invoking status precommit hook
1689 1708 M normal3
1690 1709 M sub/normal4
1691 1710 M sub2/large6
1692 1711 $ cat normal3
1693 1712 normal3-modified
1694 1713 $ cat sub/normal4
1695 1714 normal4-modified
1696 1715 $ cat sub/large4
1697 1716 large4-merge-test
1698 1717 $ cat sub2/large6
1699 1718 large6-modified
1700 1719 $ cat sub2/large7
1701 1720 large7
1702 1721
1703 1722 Test status after merging with a branch that introduces a new largefile:
1704 1723
1705 1724 $ echo large > large
1706 1725 $ hg add --large large
1707 1726 $ hg commit -m 'add largefile'
1708 1727 Invoking status precommit hook
1709 1728 A large
1710 1729 $ hg update -q ".^"
1711 1730 $ echo change >> normal3
1712 1731 $ hg commit -m 'some change'
1713 1732 Invoking status precommit hook
1714 1733 M normal3
1715 1734 created new head
1716 1735 $ hg merge
1717 1736 getting changed largefiles
1718 1737 1 largefiles updated, 0 removed
1719 1738 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1720 1739 (branch merge, don't forget to commit)
1721 1740 $ hg status
1722 1741 M large
1723 1742
1724 1743 - make sure update of merge with removed largefiles fails as expected
1725 1744 $ hg rm sub2/large6
1726 1745 $ hg up -r.
1727 1746 abort: outstanding uncommitted merge
1728 1747 [255]
1729 1748
1730 1749 - revert should be able to revert files introduced in a pending merge
1731 1750 $ hg revert --all -r .
1732 1751 removing .hglf/large (glob)
1733 1752 undeleting .hglf/sub2/large6 (glob)
1734 1753
1735 1754 Test that a normal file and a largefile with the same name and path cannot
1736 1755 coexist.
1737 1756
1738 1757 $ rm sub2/large7
1739 1758 $ echo "largeasnormal" > sub2/large7
1740 1759 $ hg add sub2/large7
1741 1760 sub2/large7 already a largefile (glob)
1742 1761
1743 1762 Test that transplanting a largefile change works correctly.
1744 1763
1745 1764 $ cd ..
1746 1765 $ hg clone -r 8 d g
1747 1766 adding changesets
1748 1767 adding manifests
1749 1768 adding file changes
1750 1769 added 9 changesets with 26 changes to 10 files
1751 1770 updating to branch default
1752 1771 getting changed largefiles
1753 1772 3 largefiles updated, 0 removed
1754 1773 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1755 1774 $ cd g
1756 1775 $ hg transplant -s ../d 598410d3eb9a
1757 1776 searching for changes
1758 1777 searching for changes
1759 1778 adding changesets
1760 1779 adding manifests
1761 1780 adding file changes
1762 1781 added 1 changesets with 2 changes to 2 files
1763 1782 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1764 1783 9:598410d3eb9a modify normal file largefile in repo d
1765 1784 8:a381d2c8c80e modify normal file and largefile in repo b
1766 1785 7:daea875e9014 add/edit more largefiles
1767 1786 6:4355d653f84f edit files yet again
1768 1787 5:9d5af5072dbd edit files again
1769 1788 4:74c02385b94c move files
1770 1789 3:9e8fbc4bce62 copy files
1771 1790 2:51a0ae4d5864 remove files
1772 1791 1:ce8896473775 edit files
1773 1792 0:30d30fe6a5be add files
1774 1793 $ cat normal3
1775 1794 normal3-modified
1776 1795 $ cat sub/normal4
1777 1796 normal4-modified
1778 1797 $ cat sub/large4
1779 1798 large4-modified
1780 1799 $ cat sub2/large6
1781 1800 large6-modified
1782 1801 $ cat sub2/large7
1783 1802 large7
1784 1803
1785 1804 Cat a largefile
1786 1805 $ hg cat normal3
1787 1806 normal3-modified
1788 1807 $ hg cat sub/large4
1789 1808 large4-modified
1790 1809 $ rm "${USERCACHE}"/*
1791 1810 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
1792 1811 $ cat cat.out
1793 1812 large4-modified
1794 1813 $ rm cat.out
1795 1814 $ hg cat -r a381d2c8c80e normal3
1796 1815 normal3-modified
1797 1816 $ hg cat -r '.^' normal3
1798 1817 normal3-modified
1799 1818 $ hg cat -r '.^' sub/large4 doesntexist
1800 1819 large4-modified
1801 1820 doesntexist: no such file in rev a381d2c8c80e
1802 1821 $ hg --cwd sub cat -r '.^' large4
1803 1822 large4-modified
1804 1823 $ hg --cwd sub cat -r '.^' ../normal3
1805 1824 normal3-modified
1806 1825 Cat a standin
1807 1826 $ hg cat .hglf/sub/large4
1808 1827 e166e74c7303192238d60af5a9c4ce9bef0b7928
1809 1828 $ hg cat .hglf/normal3
1810 1829 .hglf/normal3: no such file in rev 598410d3eb9a (glob)
1811 1830 [1]
1812 1831
1813 1832 Test that renaming a largefile results in correct output for status
1814 1833
1815 1834 $ hg rename sub/large4 large4-renamed
1816 1835 $ hg commit -m "test rename output"
1817 1836 Invoking status precommit hook
1818 1837 A large4-renamed
1819 1838 R sub/large4
1820 1839 $ cat large4-renamed
1821 1840 large4-modified
1822 1841 $ cd sub2
1823 1842 $ hg rename large6 large6-renamed
1824 1843 $ hg st
1825 1844 A sub2/large6-renamed
1826 1845 R sub2/large6
1827 1846 $ cd ..
1828 1847
1829 1848 Test --normal flag
1830 1849
1831 1850 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
1832 1851 $ hg add --normal --large new-largefile
1833 1852 abort: --normal cannot be used with --large
1834 1853 [255]
1835 1854 $ hg add --normal new-largefile
1836 1855 new-largefile: up to 69 MB of RAM may be required to manage this file
1837 1856 (use 'hg revert new-largefile' to cancel the pending addition)
1838 1857
1839 1858 Test explicit commit of switch between normal and largefile - make sure both
1840 1859 the add and the remove is committed.
1841 1860
1842 1861 $ hg up -qC
1843 1862 $ hg forget normal3 large4-renamed
1844 1863 $ hg add --large normal3
1845 1864 $ hg add large4-renamed
1846 1865 $ hg commit -m 'swap' normal3 large4-renamed
1847 1866 Invoking status precommit hook
1848 1867 A large4-renamed
1849 1868 A normal3
1850 1869 ? new-largefile
1851 1870 ? sub2/large6-renamed
1852 1871 $ hg mani
1853 1872 .hglf/normal3
1854 1873 .hglf/sub2/large6
1855 1874 .hglf/sub2/large7
1856 1875 large4-renamed
1857 1876 sub/normal4
1858 1877
1859 1878 $ cd ..
1860 1879
1861 1880
1862 1881
General Comments 0
You need to be logged in to leave comments. Login now