##// END OF EJS Templates
localrepo: add "editor" argument to "tag()"...
FUJIWARA Katsunori -
r21237:0054a77f default
parent child Browse files
Show More
@@ -1,5924 +1,5927 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import sys
12 import sys
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 from hgweb import server as hgweb_server
17 from hgweb import server as hgweb_server
18 import merge as mergemod
18 import merge as mergemod
19 import minirst, revset, fileset
19 import minirst, revset, fileset
20 import dagparser, context, simplemerge, graphmod
20 import dagparser, context, simplemerge, graphmod
21 import random
21 import random
22 import setdiscovery, treediscovery, dagutil, pvec, localrepo
22 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import phases, obsolete, exchange
23 import phases, obsolete, exchange
24
24
25 table = {}
25 table = {}
26
26
27 command = cmdutil.command(table)
27 command = cmdutil.command(table)
28
28
29 # common command options
29 # common command options
30
30
31 globalopts = [
31 globalopts = [
32 ('R', 'repository', '',
32 ('R', 'repository', '',
33 _('repository root directory or name of overlay bundle file'),
33 _('repository root directory or name of overlay bundle file'),
34 _('REPO')),
34 _('REPO')),
35 ('', 'cwd', '',
35 ('', 'cwd', '',
36 _('change working directory'), _('DIR')),
36 _('change working directory'), _('DIR')),
37 ('y', 'noninteractive', None,
37 ('y', 'noninteractive', None,
38 _('do not prompt, automatically pick the first choice for all prompts')),
38 _('do not prompt, automatically pick the first choice for all prompts')),
39 ('q', 'quiet', None, _('suppress output')),
39 ('q', 'quiet', None, _('suppress output')),
40 ('v', 'verbose', None, _('enable additional output')),
40 ('v', 'verbose', None, _('enable additional output')),
41 ('', 'config', [],
41 ('', 'config', [],
42 _('set/override config option (use \'section.name=value\')'),
42 _('set/override config option (use \'section.name=value\')'),
43 _('CONFIG')),
43 _('CONFIG')),
44 ('', 'debug', None, _('enable debugging output')),
44 ('', 'debug', None, _('enable debugging output')),
45 ('', 'debugger', None, _('start debugger')),
45 ('', 'debugger', None, _('start debugger')),
46 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
46 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
47 _('ENCODE')),
47 _('ENCODE')),
48 ('', 'encodingmode', encoding.encodingmode,
48 ('', 'encodingmode', encoding.encodingmode,
49 _('set the charset encoding mode'), _('MODE')),
49 _('set the charset encoding mode'), _('MODE')),
50 ('', 'traceback', None, _('always print a traceback on exception')),
50 ('', 'traceback', None, _('always print a traceback on exception')),
51 ('', 'time', None, _('time how long the command takes')),
51 ('', 'time', None, _('time how long the command takes')),
52 ('', 'profile', None, _('print command execution profile')),
52 ('', 'profile', None, _('print command execution profile')),
53 ('', 'version', None, _('output version information and exit')),
53 ('', 'version', None, _('output version information and exit')),
54 ('h', 'help', None, _('display help and exit')),
54 ('h', 'help', None, _('display help and exit')),
55 ('', 'hidden', False, _('consider hidden changesets')),
55 ('', 'hidden', False, _('consider hidden changesets')),
56 ]
56 ]
57
57
58 dryrunopts = [('n', 'dry-run', None,
58 dryrunopts = [('n', 'dry-run', None,
59 _('do not perform actions, just print output'))]
59 _('do not perform actions, just print output'))]
60
60
61 remoteopts = [
61 remoteopts = [
62 ('e', 'ssh', '',
62 ('e', 'ssh', '',
63 _('specify ssh command to use'), _('CMD')),
63 _('specify ssh command to use'), _('CMD')),
64 ('', 'remotecmd', '',
64 ('', 'remotecmd', '',
65 _('specify hg command to run on the remote side'), _('CMD')),
65 _('specify hg command to run on the remote side'), _('CMD')),
66 ('', 'insecure', None,
66 ('', 'insecure', None,
67 _('do not verify server certificate (ignoring web.cacerts config)')),
67 _('do not verify server certificate (ignoring web.cacerts config)')),
68 ]
68 ]
69
69
70 walkopts = [
70 walkopts = [
71 ('I', 'include', [],
71 ('I', 'include', [],
72 _('include names matching the given patterns'), _('PATTERN')),
72 _('include names matching the given patterns'), _('PATTERN')),
73 ('X', 'exclude', [],
73 ('X', 'exclude', [],
74 _('exclude names matching the given patterns'), _('PATTERN')),
74 _('exclude names matching the given patterns'), _('PATTERN')),
75 ]
75 ]
76
76
77 commitopts = [
77 commitopts = [
78 ('m', 'message', '',
78 ('m', 'message', '',
79 _('use text as commit message'), _('TEXT')),
79 _('use text as commit message'), _('TEXT')),
80 ('l', 'logfile', '',
80 ('l', 'logfile', '',
81 _('read commit message from file'), _('FILE')),
81 _('read commit message from file'), _('FILE')),
82 ]
82 ]
83
83
84 commitopts2 = [
84 commitopts2 = [
85 ('d', 'date', '',
85 ('d', 'date', '',
86 _('record the specified date as commit date'), _('DATE')),
86 _('record the specified date as commit date'), _('DATE')),
87 ('u', 'user', '',
87 ('u', 'user', '',
88 _('record the specified user as committer'), _('USER')),
88 _('record the specified user as committer'), _('USER')),
89 ]
89 ]
90
90
91 templateopts = [
91 templateopts = [
92 ('', 'style', '',
92 ('', 'style', '',
93 _('display using template map file (DEPRECATED)'), _('STYLE')),
93 _('display using template map file (DEPRECATED)'), _('STYLE')),
94 ('T', 'template', '',
94 ('T', 'template', '',
95 _('display with template'), _('TEMPLATE')),
95 _('display with template'), _('TEMPLATE')),
96 ]
96 ]
97
97
98 logopts = [
98 logopts = [
99 ('p', 'patch', None, _('show patch')),
99 ('p', 'patch', None, _('show patch')),
100 ('g', 'git', None, _('use git extended diff format')),
100 ('g', 'git', None, _('use git extended diff format')),
101 ('l', 'limit', '',
101 ('l', 'limit', '',
102 _('limit number of changes displayed'), _('NUM')),
102 _('limit number of changes displayed'), _('NUM')),
103 ('M', 'no-merges', None, _('do not show merges')),
103 ('M', 'no-merges', None, _('do not show merges')),
104 ('', 'stat', None, _('output diffstat-style summary of changes')),
104 ('', 'stat', None, _('output diffstat-style summary of changes')),
105 ('G', 'graph', None, _("show the revision DAG")),
105 ('G', 'graph', None, _("show the revision DAG")),
106 ] + templateopts
106 ] + templateopts
107
107
108 diffopts = [
108 diffopts = [
109 ('a', 'text', None, _('treat all files as text')),
109 ('a', 'text', None, _('treat all files as text')),
110 ('g', 'git', None, _('use git extended diff format')),
110 ('g', 'git', None, _('use git extended diff format')),
111 ('', 'nodates', None, _('omit dates from diff headers'))
111 ('', 'nodates', None, _('omit dates from diff headers'))
112 ]
112 ]
113
113
114 diffwsopts = [
114 diffwsopts = [
115 ('w', 'ignore-all-space', None,
115 ('w', 'ignore-all-space', None,
116 _('ignore white space when comparing lines')),
116 _('ignore white space when comparing lines')),
117 ('b', 'ignore-space-change', None,
117 ('b', 'ignore-space-change', None,
118 _('ignore changes in the amount of white space')),
118 _('ignore changes in the amount of white space')),
119 ('B', 'ignore-blank-lines', None,
119 ('B', 'ignore-blank-lines', None,
120 _('ignore changes whose lines are all blank')),
120 _('ignore changes whose lines are all blank')),
121 ]
121 ]
122
122
123 diffopts2 = [
123 diffopts2 = [
124 ('p', 'show-function', None, _('show which function each change is in')),
124 ('p', 'show-function', None, _('show which function each change is in')),
125 ('', 'reverse', None, _('produce a diff that undoes the changes')),
125 ('', 'reverse', None, _('produce a diff that undoes the changes')),
126 ] + diffwsopts + [
126 ] + diffwsopts + [
127 ('U', 'unified', '',
127 ('U', 'unified', '',
128 _('number of lines of context to show'), _('NUM')),
128 _('number of lines of context to show'), _('NUM')),
129 ('', 'stat', None, _('output diffstat-style summary of changes')),
129 ('', 'stat', None, _('output diffstat-style summary of changes')),
130 ]
130 ]
131
131
132 mergetoolopts = [
132 mergetoolopts = [
133 ('t', 'tool', '', _('specify merge tool')),
133 ('t', 'tool', '', _('specify merge tool')),
134 ]
134 ]
135
135
136 similarityopts = [
136 similarityopts = [
137 ('s', 'similarity', '',
137 ('s', 'similarity', '',
138 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
138 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
139 ]
139 ]
140
140
141 subrepoopts = [
141 subrepoopts = [
142 ('S', 'subrepos', None,
142 ('S', 'subrepos', None,
143 _('recurse into subrepositories'))
143 _('recurse into subrepositories'))
144 ]
144 ]
145
145
146 # Commands start here, listed alphabetically
146 # Commands start here, listed alphabetically
147
147
148 @command('^add',
148 @command('^add',
149 walkopts + subrepoopts + dryrunopts,
149 walkopts + subrepoopts + dryrunopts,
150 _('[OPTION]... [FILE]...'))
150 _('[OPTION]... [FILE]...'))
151 def add(ui, repo, *pats, **opts):
151 def add(ui, repo, *pats, **opts):
152 """add the specified files on the next commit
152 """add the specified files on the next commit
153
153
154 Schedule files to be version controlled and added to the
154 Schedule files to be version controlled and added to the
155 repository.
155 repository.
156
156
157 The files will be added to the repository at the next commit. To
157 The files will be added to the repository at the next commit. To
158 undo an add before that, see :hg:`forget`.
158 undo an add before that, see :hg:`forget`.
159
159
160 If no names are given, add all files to the repository.
160 If no names are given, add all files to the repository.
161
161
162 .. container:: verbose
162 .. container:: verbose
163
163
164 An example showing how new (unknown) files are added
164 An example showing how new (unknown) files are added
165 automatically by :hg:`add`::
165 automatically by :hg:`add`::
166
166
167 $ ls
167 $ ls
168 foo.c
168 foo.c
169 $ hg status
169 $ hg status
170 ? foo.c
170 ? foo.c
171 $ hg add
171 $ hg add
172 adding foo.c
172 adding foo.c
173 $ hg status
173 $ hg status
174 A foo.c
174 A foo.c
175
175
176 Returns 0 if all files are successfully added.
176 Returns 0 if all files are successfully added.
177 """
177 """
178
178
179 m = scmutil.match(repo[None], pats, opts)
179 m = scmutil.match(repo[None], pats, opts)
180 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
180 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
181 opts.get('subrepos'), prefix="", explicitonly=False)
181 opts.get('subrepos'), prefix="", explicitonly=False)
182 return rejected and 1 or 0
182 return rejected and 1 or 0
183
183
184 @command('addremove',
184 @command('addremove',
185 similarityopts + walkopts + dryrunopts,
185 similarityopts + walkopts + dryrunopts,
186 _('[OPTION]... [FILE]...'))
186 _('[OPTION]... [FILE]...'))
187 def addremove(ui, repo, *pats, **opts):
187 def addremove(ui, repo, *pats, **opts):
188 """add all new files, delete all missing files
188 """add all new files, delete all missing files
189
189
190 Add all new files and remove all missing files from the
190 Add all new files and remove all missing files from the
191 repository.
191 repository.
192
192
193 New files are ignored if they match any of the patterns in
193 New files are ignored if they match any of the patterns in
194 ``.hgignore``. As with add, these changes take effect at the next
194 ``.hgignore``. As with add, these changes take effect at the next
195 commit.
195 commit.
196
196
197 Use the -s/--similarity option to detect renamed files. This
197 Use the -s/--similarity option to detect renamed files. This
198 option takes a percentage between 0 (disabled) and 100 (files must
198 option takes a percentage between 0 (disabled) and 100 (files must
199 be identical) as its parameter. With a parameter greater than 0,
199 be identical) as its parameter. With a parameter greater than 0,
200 this compares every removed file with every added file and records
200 this compares every removed file with every added file and records
201 those similar enough as renames. Detecting renamed files this way
201 those similar enough as renames. Detecting renamed files this way
202 can be expensive. After using this option, :hg:`status -C` can be
202 can be expensive. After using this option, :hg:`status -C` can be
203 used to check which files were identified as moved or renamed. If
203 used to check which files were identified as moved or renamed. If
204 not specified, -s/--similarity defaults to 100 and only renames of
204 not specified, -s/--similarity defaults to 100 and only renames of
205 identical files are detected.
205 identical files are detected.
206
206
207 Returns 0 if all files are successfully added.
207 Returns 0 if all files are successfully added.
208 """
208 """
209 try:
209 try:
210 sim = float(opts.get('similarity') or 100)
210 sim = float(opts.get('similarity') or 100)
211 except ValueError:
211 except ValueError:
212 raise util.Abort(_('similarity must be a number'))
212 raise util.Abort(_('similarity must be a number'))
213 if sim < 0 or sim > 100:
213 if sim < 0 or sim > 100:
214 raise util.Abort(_('similarity must be between 0 and 100'))
214 raise util.Abort(_('similarity must be between 0 and 100'))
215 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
215 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
216
216
217 @command('^annotate|blame',
217 @command('^annotate|blame',
218 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
218 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
219 ('', 'follow', None,
219 ('', 'follow', None,
220 _('follow copies/renames and list the filename (DEPRECATED)')),
220 _('follow copies/renames and list the filename (DEPRECATED)')),
221 ('', 'no-follow', None, _("don't follow copies and renames")),
221 ('', 'no-follow', None, _("don't follow copies and renames")),
222 ('a', 'text', None, _('treat all files as text')),
222 ('a', 'text', None, _('treat all files as text')),
223 ('u', 'user', None, _('list the author (long with -v)')),
223 ('u', 'user', None, _('list the author (long with -v)')),
224 ('f', 'file', None, _('list the filename')),
224 ('f', 'file', None, _('list the filename')),
225 ('d', 'date', None, _('list the date (short with -q)')),
225 ('d', 'date', None, _('list the date (short with -q)')),
226 ('n', 'number', None, _('list the revision number (default)')),
226 ('n', 'number', None, _('list the revision number (default)')),
227 ('c', 'changeset', None, _('list the changeset')),
227 ('c', 'changeset', None, _('list the changeset')),
228 ('l', 'line-number', None, _('show line number at the first appearance'))
228 ('l', 'line-number', None, _('show line number at the first appearance'))
229 ] + diffwsopts + walkopts,
229 ] + diffwsopts + walkopts,
230 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
230 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
231 def annotate(ui, repo, *pats, **opts):
231 def annotate(ui, repo, *pats, **opts):
232 """show changeset information by line for each file
232 """show changeset information by line for each file
233
233
234 List changes in files, showing the revision id responsible for
234 List changes in files, showing the revision id responsible for
235 each line
235 each line
236
236
237 This command is useful for discovering when a change was made and
237 This command is useful for discovering when a change was made and
238 by whom.
238 by whom.
239
239
240 Without the -a/--text option, annotate will avoid processing files
240 Without the -a/--text option, annotate will avoid processing files
241 it detects as binary. With -a, annotate will annotate the file
241 it detects as binary. With -a, annotate will annotate the file
242 anyway, although the results will probably be neither useful
242 anyway, although the results will probably be neither useful
243 nor desirable.
243 nor desirable.
244
244
245 Returns 0 on success.
245 Returns 0 on success.
246 """
246 """
247 if opts.get('follow'):
247 if opts.get('follow'):
248 # --follow is deprecated and now just an alias for -f/--file
248 # --follow is deprecated and now just an alias for -f/--file
249 # to mimic the behavior of Mercurial before version 1.5
249 # to mimic the behavior of Mercurial before version 1.5
250 opts['file'] = True
250 opts['file'] = True
251
251
252 datefunc = ui.quiet and util.shortdate or util.datestr
252 datefunc = ui.quiet and util.shortdate or util.datestr
253 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
253 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
254
254
255 if not pats:
255 if not pats:
256 raise util.Abort(_('at least one filename or pattern is required'))
256 raise util.Abort(_('at least one filename or pattern is required'))
257
257
258 hexfn = ui.debugflag and hex or short
258 hexfn = ui.debugflag and hex or short
259
259
260 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
260 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
261 ('number', ' ', lambda x: str(x[0].rev())),
261 ('number', ' ', lambda x: str(x[0].rev())),
262 ('changeset', ' ', lambda x: hexfn(x[0].node())),
262 ('changeset', ' ', lambda x: hexfn(x[0].node())),
263 ('date', ' ', getdate),
263 ('date', ' ', getdate),
264 ('file', ' ', lambda x: x[0].path()),
264 ('file', ' ', lambda x: x[0].path()),
265 ('line_number', ':', lambda x: str(x[1])),
265 ('line_number', ':', lambda x: str(x[1])),
266 ]
266 ]
267
267
268 if (not opts.get('user') and not opts.get('changeset')
268 if (not opts.get('user') and not opts.get('changeset')
269 and not opts.get('date') and not opts.get('file')):
269 and not opts.get('date') and not opts.get('file')):
270 opts['number'] = True
270 opts['number'] = True
271
271
272 linenumber = opts.get('line_number') is not None
272 linenumber = opts.get('line_number') is not None
273 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
273 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
274 raise util.Abort(_('at least one of -n/-c is required for -l'))
274 raise util.Abort(_('at least one of -n/-c is required for -l'))
275
275
276 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
276 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
277 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
277 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
278
278
279 def bad(x, y):
279 def bad(x, y):
280 raise util.Abort("%s: %s" % (x, y))
280 raise util.Abort("%s: %s" % (x, y))
281
281
282 ctx = scmutil.revsingle(repo, opts.get('rev'))
282 ctx = scmutil.revsingle(repo, opts.get('rev'))
283 m = scmutil.match(ctx, pats, opts)
283 m = scmutil.match(ctx, pats, opts)
284 m.bad = bad
284 m.bad = bad
285 follow = not opts.get('no_follow')
285 follow = not opts.get('no_follow')
286 diffopts = patch.diffopts(ui, opts, section='annotate')
286 diffopts = patch.diffopts(ui, opts, section='annotate')
287 for abs in ctx.walk(m):
287 for abs in ctx.walk(m):
288 fctx = ctx[abs]
288 fctx = ctx[abs]
289 if not opts.get('text') and util.binary(fctx.data()):
289 if not opts.get('text') and util.binary(fctx.data()):
290 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
290 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
291 continue
291 continue
292
292
293 lines = fctx.annotate(follow=follow, linenumber=linenumber,
293 lines = fctx.annotate(follow=follow, linenumber=linenumber,
294 diffopts=diffopts)
294 diffopts=diffopts)
295 pieces = []
295 pieces = []
296
296
297 for f, sep in funcmap:
297 for f, sep in funcmap:
298 l = [f(n) for n, dummy in lines]
298 l = [f(n) for n, dummy in lines]
299 if l:
299 if l:
300 sized = [(x, encoding.colwidth(x)) for x in l]
300 sized = [(x, encoding.colwidth(x)) for x in l]
301 ml = max([w for x, w in sized])
301 ml = max([w for x, w in sized])
302 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
302 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
303 for x, w in sized])
303 for x, w in sized])
304
304
305 if pieces:
305 if pieces:
306 for p, l in zip(zip(*pieces), lines):
306 for p, l in zip(zip(*pieces), lines):
307 ui.write("%s: %s" % ("".join(p), l[1]))
307 ui.write("%s: %s" % ("".join(p), l[1]))
308
308
309 if lines and not lines[-1][1].endswith('\n'):
309 if lines and not lines[-1][1].endswith('\n'):
310 ui.write('\n')
310 ui.write('\n')
311
311
312 @command('archive',
312 @command('archive',
313 [('', 'no-decode', None, _('do not pass files through decoders')),
313 [('', 'no-decode', None, _('do not pass files through decoders')),
314 ('p', 'prefix', '', _('directory prefix for files in archive'),
314 ('p', 'prefix', '', _('directory prefix for files in archive'),
315 _('PREFIX')),
315 _('PREFIX')),
316 ('r', 'rev', '', _('revision to distribute'), _('REV')),
316 ('r', 'rev', '', _('revision to distribute'), _('REV')),
317 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
317 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
318 ] + subrepoopts + walkopts,
318 ] + subrepoopts + walkopts,
319 _('[OPTION]... DEST'))
319 _('[OPTION]... DEST'))
320 def archive(ui, repo, dest, **opts):
320 def archive(ui, repo, dest, **opts):
321 '''create an unversioned archive of a repository revision
321 '''create an unversioned archive of a repository revision
322
322
323 By default, the revision used is the parent of the working
323 By default, the revision used is the parent of the working
324 directory; use -r/--rev to specify a different revision.
324 directory; use -r/--rev to specify a different revision.
325
325
326 The archive type is automatically detected based on file
326 The archive type is automatically detected based on file
327 extension (or override using -t/--type).
327 extension (or override using -t/--type).
328
328
329 .. container:: verbose
329 .. container:: verbose
330
330
331 Examples:
331 Examples:
332
332
333 - create a zip file containing the 1.0 release::
333 - create a zip file containing the 1.0 release::
334
334
335 hg archive -r 1.0 project-1.0.zip
335 hg archive -r 1.0 project-1.0.zip
336
336
337 - create a tarball excluding .hg files::
337 - create a tarball excluding .hg files::
338
338
339 hg archive project.tar.gz -X ".hg*"
339 hg archive project.tar.gz -X ".hg*"
340
340
341 Valid types are:
341 Valid types are:
342
342
343 :``files``: a directory full of files (default)
343 :``files``: a directory full of files (default)
344 :``tar``: tar archive, uncompressed
344 :``tar``: tar archive, uncompressed
345 :``tbz2``: tar archive, compressed using bzip2
345 :``tbz2``: tar archive, compressed using bzip2
346 :``tgz``: tar archive, compressed using gzip
346 :``tgz``: tar archive, compressed using gzip
347 :``uzip``: zip archive, uncompressed
347 :``uzip``: zip archive, uncompressed
348 :``zip``: zip archive, compressed using deflate
348 :``zip``: zip archive, compressed using deflate
349
349
350 The exact name of the destination archive or directory is given
350 The exact name of the destination archive or directory is given
351 using a format string; see :hg:`help export` for details.
351 using a format string; see :hg:`help export` for details.
352
352
353 Each member added to an archive file has a directory prefix
353 Each member added to an archive file has a directory prefix
354 prepended. Use -p/--prefix to specify a format string for the
354 prepended. Use -p/--prefix to specify a format string for the
355 prefix. The default is the basename of the archive, with suffixes
355 prefix. The default is the basename of the archive, with suffixes
356 removed.
356 removed.
357
357
358 Returns 0 on success.
358 Returns 0 on success.
359 '''
359 '''
360
360
361 ctx = scmutil.revsingle(repo, opts.get('rev'))
361 ctx = scmutil.revsingle(repo, opts.get('rev'))
362 if not ctx:
362 if not ctx:
363 raise util.Abort(_('no working directory: please specify a revision'))
363 raise util.Abort(_('no working directory: please specify a revision'))
364 node = ctx.node()
364 node = ctx.node()
365 dest = cmdutil.makefilename(repo, dest, node)
365 dest = cmdutil.makefilename(repo, dest, node)
366 if os.path.realpath(dest) == repo.root:
366 if os.path.realpath(dest) == repo.root:
367 raise util.Abort(_('repository root cannot be destination'))
367 raise util.Abort(_('repository root cannot be destination'))
368
368
369 kind = opts.get('type') or archival.guesskind(dest) or 'files'
369 kind = opts.get('type') or archival.guesskind(dest) or 'files'
370 prefix = opts.get('prefix')
370 prefix = opts.get('prefix')
371
371
372 if dest == '-':
372 if dest == '-':
373 if kind == 'files':
373 if kind == 'files':
374 raise util.Abort(_('cannot archive plain files to stdout'))
374 raise util.Abort(_('cannot archive plain files to stdout'))
375 dest = cmdutil.makefileobj(repo, dest)
375 dest = cmdutil.makefileobj(repo, dest)
376 if not prefix:
376 if not prefix:
377 prefix = os.path.basename(repo.root) + '-%h'
377 prefix = os.path.basename(repo.root) + '-%h'
378
378
379 prefix = cmdutil.makefilename(repo, prefix, node)
379 prefix = cmdutil.makefilename(repo, prefix, node)
380 matchfn = scmutil.match(ctx, [], opts)
380 matchfn = scmutil.match(ctx, [], opts)
381 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
381 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
382 matchfn, prefix, subrepos=opts.get('subrepos'))
382 matchfn, prefix, subrepos=opts.get('subrepos'))
383
383
384 @command('backout',
384 @command('backout',
385 [('', 'merge', None, _('merge with old dirstate parent after backout')),
385 [('', 'merge', None, _('merge with old dirstate parent after backout')),
386 ('', 'parent', '',
386 ('', 'parent', '',
387 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
387 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
388 ('r', 'rev', '', _('revision to backout'), _('REV')),
388 ('r', 'rev', '', _('revision to backout'), _('REV')),
389 ] + mergetoolopts + walkopts + commitopts + commitopts2,
389 ] + mergetoolopts + walkopts + commitopts + commitopts2,
390 _('[OPTION]... [-r] REV'))
390 _('[OPTION]... [-r] REV'))
391 def backout(ui, repo, node=None, rev=None, **opts):
391 def backout(ui, repo, node=None, rev=None, **opts):
392 '''reverse effect of earlier changeset
392 '''reverse effect of earlier changeset
393
393
394 Prepare a new changeset with the effect of REV undone in the
394 Prepare a new changeset with the effect of REV undone in the
395 current working directory.
395 current working directory.
396
396
397 If REV is the parent of the working directory, then this new changeset
397 If REV is the parent of the working directory, then this new changeset
398 is committed automatically. Otherwise, hg needs to merge the
398 is committed automatically. Otherwise, hg needs to merge the
399 changes and the merged result is left uncommitted.
399 changes and the merged result is left uncommitted.
400
400
401 .. note::
401 .. note::
402
402
403 backout cannot be used to fix either an unwanted or
403 backout cannot be used to fix either an unwanted or
404 incorrect merge.
404 incorrect merge.
405
405
406 .. container:: verbose
406 .. container:: verbose
407
407
408 By default, the pending changeset will have one parent,
408 By default, the pending changeset will have one parent,
409 maintaining a linear history. With --merge, the pending
409 maintaining a linear history. With --merge, the pending
410 changeset will instead have two parents: the old parent of the
410 changeset will instead have two parents: the old parent of the
411 working directory and a new child of REV that simply undoes REV.
411 working directory and a new child of REV that simply undoes REV.
412
412
413 Before version 1.7, the behavior without --merge was equivalent
413 Before version 1.7, the behavior without --merge was equivalent
414 to specifying --merge followed by :hg:`update --clean .` to
414 to specifying --merge followed by :hg:`update --clean .` to
415 cancel the merge and leave the child of REV as a head to be
415 cancel the merge and leave the child of REV as a head to be
416 merged separately.
416 merged separately.
417
417
418 See :hg:`help dates` for a list of formats valid for -d/--date.
418 See :hg:`help dates` for a list of formats valid for -d/--date.
419
419
420 Returns 0 on success, 1 if nothing to backout or there are unresolved
420 Returns 0 on success, 1 if nothing to backout or there are unresolved
421 files.
421 files.
422 '''
422 '''
423 if rev and node:
423 if rev and node:
424 raise util.Abort(_("please specify just one revision"))
424 raise util.Abort(_("please specify just one revision"))
425
425
426 if not rev:
426 if not rev:
427 rev = node
427 rev = node
428
428
429 if not rev:
429 if not rev:
430 raise util.Abort(_("please specify a revision to backout"))
430 raise util.Abort(_("please specify a revision to backout"))
431
431
432 date = opts.get('date')
432 date = opts.get('date')
433 if date:
433 if date:
434 opts['date'] = util.parsedate(date)
434 opts['date'] = util.parsedate(date)
435
435
436 cmdutil.checkunfinished(repo)
436 cmdutil.checkunfinished(repo)
437 cmdutil.bailifchanged(repo)
437 cmdutil.bailifchanged(repo)
438 node = scmutil.revsingle(repo, rev).node()
438 node = scmutil.revsingle(repo, rev).node()
439
439
440 op1, op2 = repo.dirstate.parents()
440 op1, op2 = repo.dirstate.parents()
441 if node not in repo.changelog.commonancestorsheads(op1, node):
441 if node not in repo.changelog.commonancestorsheads(op1, node):
442 raise util.Abort(_('cannot backout change that is not an ancestor'))
442 raise util.Abort(_('cannot backout change that is not an ancestor'))
443
443
444 p1, p2 = repo.changelog.parents(node)
444 p1, p2 = repo.changelog.parents(node)
445 if p1 == nullid:
445 if p1 == nullid:
446 raise util.Abort(_('cannot backout a change with no parents'))
446 raise util.Abort(_('cannot backout a change with no parents'))
447 if p2 != nullid:
447 if p2 != nullid:
448 if not opts.get('parent'):
448 if not opts.get('parent'):
449 raise util.Abort(_('cannot backout a merge changeset'))
449 raise util.Abort(_('cannot backout a merge changeset'))
450 p = repo.lookup(opts['parent'])
450 p = repo.lookup(opts['parent'])
451 if p not in (p1, p2):
451 if p not in (p1, p2):
452 raise util.Abort(_('%s is not a parent of %s') %
452 raise util.Abort(_('%s is not a parent of %s') %
453 (short(p), short(node)))
453 (short(p), short(node)))
454 parent = p
454 parent = p
455 else:
455 else:
456 if opts.get('parent'):
456 if opts.get('parent'):
457 raise util.Abort(_('cannot use --parent on non-merge changeset'))
457 raise util.Abort(_('cannot use --parent on non-merge changeset'))
458 parent = p1
458 parent = p1
459
459
460 # the backout should appear on the same branch
460 # the backout should appear on the same branch
461 wlock = repo.wlock()
461 wlock = repo.wlock()
462 try:
462 try:
463 branch = repo.dirstate.branch()
463 branch = repo.dirstate.branch()
464 bheads = repo.branchheads(branch)
464 bheads = repo.branchheads(branch)
465 rctx = scmutil.revsingle(repo, hex(parent))
465 rctx = scmutil.revsingle(repo, hex(parent))
466 if not opts.get('merge') and op1 != node:
466 if not opts.get('merge') and op1 != node:
467 try:
467 try:
468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
469 'backout')
469 'backout')
470 stats = mergemod.update(repo, parent, True, True, False,
470 stats = mergemod.update(repo, parent, True, True, False,
471 node, False)
471 node, False)
472 repo.setparents(op1, op2)
472 repo.setparents(op1, op2)
473 hg._showstats(repo, stats)
473 hg._showstats(repo, stats)
474 if stats[3]:
474 if stats[3]:
475 repo.ui.status(_("use 'hg resolve' to retry unresolved "
475 repo.ui.status(_("use 'hg resolve' to retry unresolved "
476 "file merges\n"))
476 "file merges\n"))
477 else:
477 else:
478 msg = _("changeset %s backed out, "
478 msg = _("changeset %s backed out, "
479 "don't forget to commit.\n")
479 "don't forget to commit.\n")
480 ui.status(msg % short(node))
480 ui.status(msg % short(node))
481 return stats[3] > 0
481 return stats[3] > 0
482 finally:
482 finally:
483 ui.setconfig('ui', 'forcemerge', '', '')
483 ui.setconfig('ui', 'forcemerge', '', '')
484 else:
484 else:
485 hg.clean(repo, node, show_stats=False)
485 hg.clean(repo, node, show_stats=False)
486 repo.dirstate.setbranch(branch)
486 repo.dirstate.setbranch(branch)
487 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
487 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
488
488
489
489
490 e = cmdutil.commiteditor
490 e = cmdutil.commiteditor
491 if not opts['message'] and not opts['logfile']:
491 if not opts['message'] and not opts['logfile']:
492 # we don't translate commit messages
492 # we don't translate commit messages
493 opts['message'] = "Backed out changeset %s" % short(node)
493 opts['message'] = "Backed out changeset %s" % short(node)
494 e = cmdutil.commitforceeditor
494 e = cmdutil.commitforceeditor
495
495
496 def commitfunc(ui, repo, message, match, opts):
496 def commitfunc(ui, repo, message, match, opts):
497 return repo.commit(message, opts.get('user'), opts.get('date'),
497 return repo.commit(message, opts.get('user'), opts.get('date'),
498 match, editor=e)
498 match, editor=e)
499 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
499 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
500 if not newnode:
500 if not newnode:
501 ui.status(_("nothing changed\n"))
501 ui.status(_("nothing changed\n"))
502 return 1
502 return 1
503 cmdutil.commitstatus(repo, newnode, branch, bheads)
503 cmdutil.commitstatus(repo, newnode, branch, bheads)
504
504
505 def nice(node):
505 def nice(node):
506 return '%d:%s' % (repo.changelog.rev(node), short(node))
506 return '%d:%s' % (repo.changelog.rev(node), short(node))
507 ui.status(_('changeset %s backs out changeset %s\n') %
507 ui.status(_('changeset %s backs out changeset %s\n') %
508 (nice(repo.changelog.tip()), nice(node)))
508 (nice(repo.changelog.tip()), nice(node)))
509 if opts.get('merge') and op1 != node:
509 if opts.get('merge') and op1 != node:
510 hg.clean(repo, op1, show_stats=False)
510 hg.clean(repo, op1, show_stats=False)
511 ui.status(_('merging with changeset %s\n')
511 ui.status(_('merging with changeset %s\n')
512 % nice(repo.changelog.tip()))
512 % nice(repo.changelog.tip()))
513 try:
513 try:
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
514 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
515 'backout')
515 'backout')
516 return hg.merge(repo, hex(repo.changelog.tip()))
516 return hg.merge(repo, hex(repo.changelog.tip()))
517 finally:
517 finally:
518 ui.setconfig('ui', 'forcemerge', '', '')
518 ui.setconfig('ui', 'forcemerge', '', '')
519 finally:
519 finally:
520 wlock.release()
520 wlock.release()
521 return 0
521 return 0
522
522
523 @command('bisect',
523 @command('bisect',
524 [('r', 'reset', False, _('reset bisect state')),
524 [('r', 'reset', False, _('reset bisect state')),
525 ('g', 'good', False, _('mark changeset good')),
525 ('g', 'good', False, _('mark changeset good')),
526 ('b', 'bad', False, _('mark changeset bad')),
526 ('b', 'bad', False, _('mark changeset bad')),
527 ('s', 'skip', False, _('skip testing changeset')),
527 ('s', 'skip', False, _('skip testing changeset')),
528 ('e', 'extend', False, _('extend the bisect range')),
528 ('e', 'extend', False, _('extend the bisect range')),
529 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
529 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
530 ('U', 'noupdate', False, _('do not update to target'))],
530 ('U', 'noupdate', False, _('do not update to target'))],
531 _("[-gbsr] [-U] [-c CMD] [REV]"))
531 _("[-gbsr] [-U] [-c CMD] [REV]"))
532 def bisect(ui, repo, rev=None, extra=None, command=None,
532 def bisect(ui, repo, rev=None, extra=None, command=None,
533 reset=None, good=None, bad=None, skip=None, extend=None,
533 reset=None, good=None, bad=None, skip=None, extend=None,
534 noupdate=None):
534 noupdate=None):
535 """subdivision search of changesets
535 """subdivision search of changesets
536
536
537 This command helps to find changesets which introduce problems. To
537 This command helps to find changesets which introduce problems. To
538 use, mark the earliest changeset you know exhibits the problem as
538 use, mark the earliest changeset you know exhibits the problem as
539 bad, then mark the latest changeset which is free from the problem
539 bad, then mark the latest changeset which is free from the problem
540 as good. Bisect will update your working directory to a revision
540 as good. Bisect will update your working directory to a revision
541 for testing (unless the -U/--noupdate option is specified). Once
541 for testing (unless the -U/--noupdate option is specified). Once
542 you have performed tests, mark the working directory as good or
542 you have performed tests, mark the working directory as good or
543 bad, and bisect will either update to another candidate changeset
543 bad, and bisect will either update to another candidate changeset
544 or announce that it has found the bad revision.
544 or announce that it has found the bad revision.
545
545
546 As a shortcut, you can also use the revision argument to mark a
546 As a shortcut, you can also use the revision argument to mark a
547 revision as good or bad without checking it out first.
547 revision as good or bad without checking it out first.
548
548
549 If you supply a command, it will be used for automatic bisection.
549 If you supply a command, it will be used for automatic bisection.
550 The environment variable HG_NODE will contain the ID of the
550 The environment variable HG_NODE will contain the ID of the
551 changeset being tested. The exit status of the command will be
551 changeset being tested. The exit status of the command will be
552 used to mark revisions as good or bad: status 0 means good, 125
552 used to mark revisions as good or bad: status 0 means good, 125
553 means to skip the revision, 127 (command not found) will abort the
553 means to skip the revision, 127 (command not found) will abort the
554 bisection, and any other non-zero exit status means the revision
554 bisection, and any other non-zero exit status means the revision
555 is bad.
555 is bad.
556
556
557 .. container:: verbose
557 .. container:: verbose
558
558
559 Some examples:
559 Some examples:
560
560
561 - start a bisection with known bad revision 34, and good revision 12::
561 - start a bisection with known bad revision 34, and good revision 12::
562
562
563 hg bisect --bad 34
563 hg bisect --bad 34
564 hg bisect --good 12
564 hg bisect --good 12
565
565
566 - advance the current bisection by marking current revision as good or
566 - advance the current bisection by marking current revision as good or
567 bad::
567 bad::
568
568
569 hg bisect --good
569 hg bisect --good
570 hg bisect --bad
570 hg bisect --bad
571
571
572 - mark the current revision, or a known revision, to be skipped (e.g. if
572 - mark the current revision, or a known revision, to be skipped (e.g. if
573 that revision is not usable because of another issue)::
573 that revision is not usable because of another issue)::
574
574
575 hg bisect --skip
575 hg bisect --skip
576 hg bisect --skip 23
576 hg bisect --skip 23
577
577
578 - skip all revisions that do not touch directories ``foo`` or ``bar``::
578 - skip all revisions that do not touch directories ``foo`` or ``bar``::
579
579
580 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
580 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
581
581
582 - forget the current bisection::
582 - forget the current bisection::
583
583
584 hg bisect --reset
584 hg bisect --reset
585
585
586 - use 'make && make tests' to automatically find the first broken
586 - use 'make && make tests' to automatically find the first broken
587 revision::
587 revision::
588
588
589 hg bisect --reset
589 hg bisect --reset
590 hg bisect --bad 34
590 hg bisect --bad 34
591 hg bisect --good 12
591 hg bisect --good 12
592 hg bisect --command "make && make tests"
592 hg bisect --command "make && make tests"
593
593
594 - see all changesets whose states are already known in the current
594 - see all changesets whose states are already known in the current
595 bisection::
595 bisection::
596
596
597 hg log -r "bisect(pruned)"
597 hg log -r "bisect(pruned)"
598
598
599 - see the changeset currently being bisected (especially useful
599 - see the changeset currently being bisected (especially useful
600 if running with -U/--noupdate)::
600 if running with -U/--noupdate)::
601
601
602 hg log -r "bisect(current)"
602 hg log -r "bisect(current)"
603
603
604 - see all changesets that took part in the current bisection::
604 - see all changesets that took part in the current bisection::
605
605
606 hg log -r "bisect(range)"
606 hg log -r "bisect(range)"
607
607
608 - you can even get a nice graph::
608 - you can even get a nice graph::
609
609
610 hg log --graph -r "bisect(range)"
610 hg log --graph -r "bisect(range)"
611
611
612 See :hg:`help revsets` for more about the `bisect()` keyword.
612 See :hg:`help revsets` for more about the `bisect()` keyword.
613
613
614 Returns 0 on success.
614 Returns 0 on success.
615 """
615 """
616 def extendbisectrange(nodes, good):
616 def extendbisectrange(nodes, good):
617 # bisect is incomplete when it ends on a merge node and
617 # bisect is incomplete when it ends on a merge node and
618 # one of the parent was not checked.
618 # one of the parent was not checked.
619 parents = repo[nodes[0]].parents()
619 parents = repo[nodes[0]].parents()
620 if len(parents) > 1:
620 if len(parents) > 1:
621 side = good and state['bad'] or state['good']
621 side = good and state['bad'] or state['good']
622 num = len(set(i.node() for i in parents) & set(side))
622 num = len(set(i.node() for i in parents) & set(side))
623 if num == 1:
623 if num == 1:
624 return parents[0].ancestor(parents[1])
624 return parents[0].ancestor(parents[1])
625 return None
625 return None
626
626
627 def print_result(nodes, good):
627 def print_result(nodes, good):
628 displayer = cmdutil.show_changeset(ui, repo, {})
628 displayer = cmdutil.show_changeset(ui, repo, {})
629 if len(nodes) == 1:
629 if len(nodes) == 1:
630 # narrowed it down to a single revision
630 # narrowed it down to a single revision
631 if good:
631 if good:
632 ui.write(_("The first good revision is:\n"))
632 ui.write(_("The first good revision is:\n"))
633 else:
633 else:
634 ui.write(_("The first bad revision is:\n"))
634 ui.write(_("The first bad revision is:\n"))
635 displayer.show(repo[nodes[0]])
635 displayer.show(repo[nodes[0]])
636 extendnode = extendbisectrange(nodes, good)
636 extendnode = extendbisectrange(nodes, good)
637 if extendnode is not None:
637 if extendnode is not None:
638 ui.write(_('Not all ancestors of this changeset have been'
638 ui.write(_('Not all ancestors of this changeset have been'
639 ' checked.\nUse bisect --extend to continue the '
639 ' checked.\nUse bisect --extend to continue the '
640 'bisection from\nthe common ancestor, %s.\n')
640 'bisection from\nthe common ancestor, %s.\n')
641 % extendnode)
641 % extendnode)
642 else:
642 else:
643 # multiple possible revisions
643 # multiple possible revisions
644 if good:
644 if good:
645 ui.write(_("Due to skipped revisions, the first "
645 ui.write(_("Due to skipped revisions, the first "
646 "good revision could be any of:\n"))
646 "good revision could be any of:\n"))
647 else:
647 else:
648 ui.write(_("Due to skipped revisions, the first "
648 ui.write(_("Due to skipped revisions, the first "
649 "bad revision could be any of:\n"))
649 "bad revision could be any of:\n"))
650 for n in nodes:
650 for n in nodes:
651 displayer.show(repo[n])
651 displayer.show(repo[n])
652 displayer.close()
652 displayer.close()
653
653
654 def check_state(state, interactive=True):
654 def check_state(state, interactive=True):
655 if not state['good'] or not state['bad']:
655 if not state['good'] or not state['bad']:
656 if (good or bad or skip or reset) and interactive:
656 if (good or bad or skip or reset) and interactive:
657 return
657 return
658 if not state['good']:
658 if not state['good']:
659 raise util.Abort(_('cannot bisect (no known good revisions)'))
659 raise util.Abort(_('cannot bisect (no known good revisions)'))
660 else:
660 else:
661 raise util.Abort(_('cannot bisect (no known bad revisions)'))
661 raise util.Abort(_('cannot bisect (no known bad revisions)'))
662 return True
662 return True
663
663
664 # backward compatibility
664 # backward compatibility
665 if rev in "good bad reset init".split():
665 if rev in "good bad reset init".split():
666 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
666 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
667 cmd, rev, extra = rev, extra, None
667 cmd, rev, extra = rev, extra, None
668 if cmd == "good":
668 if cmd == "good":
669 good = True
669 good = True
670 elif cmd == "bad":
670 elif cmd == "bad":
671 bad = True
671 bad = True
672 else:
672 else:
673 reset = True
673 reset = True
674 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
674 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
675 raise util.Abort(_('incompatible arguments'))
675 raise util.Abort(_('incompatible arguments'))
676
676
677 cmdutil.checkunfinished(repo)
677 cmdutil.checkunfinished(repo)
678
678
679 if reset:
679 if reset:
680 p = repo.join("bisect.state")
680 p = repo.join("bisect.state")
681 if os.path.exists(p):
681 if os.path.exists(p):
682 os.unlink(p)
682 os.unlink(p)
683 return
683 return
684
684
685 state = hbisect.load_state(repo)
685 state = hbisect.load_state(repo)
686
686
687 if command:
687 if command:
688 changesets = 1
688 changesets = 1
689 if noupdate:
689 if noupdate:
690 try:
690 try:
691 node = state['current'][0]
691 node = state['current'][0]
692 except LookupError:
692 except LookupError:
693 raise util.Abort(_('current bisect revision is unknown - '
693 raise util.Abort(_('current bisect revision is unknown - '
694 'start a new bisect to fix'))
694 'start a new bisect to fix'))
695 else:
695 else:
696 node, p2 = repo.dirstate.parents()
696 node, p2 = repo.dirstate.parents()
697 if p2 != nullid:
697 if p2 != nullid:
698 raise util.Abort(_('current bisect revision is a merge'))
698 raise util.Abort(_('current bisect revision is a merge'))
699 try:
699 try:
700 while changesets:
700 while changesets:
701 # update state
701 # update state
702 state['current'] = [node]
702 state['current'] = [node]
703 hbisect.save_state(repo, state)
703 hbisect.save_state(repo, state)
704 status = util.system(command,
704 status = util.system(command,
705 environ={'HG_NODE': hex(node)},
705 environ={'HG_NODE': hex(node)},
706 out=ui.fout)
706 out=ui.fout)
707 if status == 125:
707 if status == 125:
708 transition = "skip"
708 transition = "skip"
709 elif status == 0:
709 elif status == 0:
710 transition = "good"
710 transition = "good"
711 # status < 0 means process was killed
711 # status < 0 means process was killed
712 elif status == 127:
712 elif status == 127:
713 raise util.Abort(_("failed to execute %s") % command)
713 raise util.Abort(_("failed to execute %s") % command)
714 elif status < 0:
714 elif status < 0:
715 raise util.Abort(_("%s killed") % command)
715 raise util.Abort(_("%s killed") % command)
716 else:
716 else:
717 transition = "bad"
717 transition = "bad"
718 ctx = scmutil.revsingle(repo, rev, node)
718 ctx = scmutil.revsingle(repo, rev, node)
719 rev = None # clear for future iterations
719 rev = None # clear for future iterations
720 state[transition].append(ctx.node())
720 state[transition].append(ctx.node())
721 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
721 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
722 check_state(state, interactive=False)
722 check_state(state, interactive=False)
723 # bisect
723 # bisect
724 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
724 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
725 # update to next check
725 # update to next check
726 node = nodes[0]
726 node = nodes[0]
727 if not noupdate:
727 if not noupdate:
728 cmdutil.bailifchanged(repo)
728 cmdutil.bailifchanged(repo)
729 hg.clean(repo, node, show_stats=False)
729 hg.clean(repo, node, show_stats=False)
730 finally:
730 finally:
731 state['current'] = [node]
731 state['current'] = [node]
732 hbisect.save_state(repo, state)
732 hbisect.save_state(repo, state)
733 print_result(nodes, bgood)
733 print_result(nodes, bgood)
734 return
734 return
735
735
736 # update state
736 # update state
737
737
738 if rev:
738 if rev:
739 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
739 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
740 else:
740 else:
741 nodes = [repo.lookup('.')]
741 nodes = [repo.lookup('.')]
742
742
743 if good or bad or skip:
743 if good or bad or skip:
744 if good:
744 if good:
745 state['good'] += nodes
745 state['good'] += nodes
746 elif bad:
746 elif bad:
747 state['bad'] += nodes
747 state['bad'] += nodes
748 elif skip:
748 elif skip:
749 state['skip'] += nodes
749 state['skip'] += nodes
750 hbisect.save_state(repo, state)
750 hbisect.save_state(repo, state)
751
751
752 if not check_state(state):
752 if not check_state(state):
753 return
753 return
754
754
755 # actually bisect
755 # actually bisect
756 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
756 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
757 if extend:
757 if extend:
758 if not changesets:
758 if not changesets:
759 extendnode = extendbisectrange(nodes, good)
759 extendnode = extendbisectrange(nodes, good)
760 if extendnode is not None:
760 if extendnode is not None:
761 ui.write(_("Extending search to changeset %d:%s\n")
761 ui.write(_("Extending search to changeset %d:%s\n")
762 % (extendnode.rev(), extendnode))
762 % (extendnode.rev(), extendnode))
763 state['current'] = [extendnode.node()]
763 state['current'] = [extendnode.node()]
764 hbisect.save_state(repo, state)
764 hbisect.save_state(repo, state)
765 if noupdate:
765 if noupdate:
766 return
766 return
767 cmdutil.bailifchanged(repo)
767 cmdutil.bailifchanged(repo)
768 return hg.clean(repo, extendnode.node())
768 return hg.clean(repo, extendnode.node())
769 raise util.Abort(_("nothing to extend"))
769 raise util.Abort(_("nothing to extend"))
770
770
771 if changesets == 0:
771 if changesets == 0:
772 print_result(nodes, good)
772 print_result(nodes, good)
773 else:
773 else:
774 assert len(nodes) == 1 # only a single node can be tested next
774 assert len(nodes) == 1 # only a single node can be tested next
775 node = nodes[0]
775 node = nodes[0]
776 # compute the approximate number of remaining tests
776 # compute the approximate number of remaining tests
777 tests, size = 0, 2
777 tests, size = 0, 2
778 while size <= changesets:
778 while size <= changesets:
779 tests, size = tests + 1, size * 2
779 tests, size = tests + 1, size * 2
780 rev = repo.changelog.rev(node)
780 rev = repo.changelog.rev(node)
781 ui.write(_("Testing changeset %d:%s "
781 ui.write(_("Testing changeset %d:%s "
782 "(%d changesets remaining, ~%d tests)\n")
782 "(%d changesets remaining, ~%d tests)\n")
783 % (rev, short(node), changesets, tests))
783 % (rev, short(node), changesets, tests))
784 state['current'] = [node]
784 state['current'] = [node]
785 hbisect.save_state(repo, state)
785 hbisect.save_state(repo, state)
786 if not noupdate:
786 if not noupdate:
787 cmdutil.bailifchanged(repo)
787 cmdutil.bailifchanged(repo)
788 return hg.clean(repo, node)
788 return hg.clean(repo, node)
789
789
790 @command('bookmarks|bookmark',
790 @command('bookmarks|bookmark',
791 [('f', 'force', False, _('force')),
791 [('f', 'force', False, _('force')),
792 ('r', 'rev', '', _('revision'), _('REV')),
792 ('r', 'rev', '', _('revision'), _('REV')),
793 ('d', 'delete', False, _('delete a given bookmark')),
793 ('d', 'delete', False, _('delete a given bookmark')),
794 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
794 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
795 ('i', 'inactive', False, _('mark a bookmark inactive'))],
795 ('i', 'inactive', False, _('mark a bookmark inactive'))],
796 _('hg bookmarks [OPTIONS]... [NAME]...'))
796 _('hg bookmarks [OPTIONS]... [NAME]...'))
797 def bookmark(ui, repo, *names, **opts):
797 def bookmark(ui, repo, *names, **opts):
798 '''track a line of development with movable markers
798 '''track a line of development with movable markers
799
799
800 Bookmarks are pointers to certain commits that move when committing.
800 Bookmarks are pointers to certain commits that move when committing.
801 Bookmarks are local. They can be renamed, copied and deleted. It is
801 Bookmarks are local. They can be renamed, copied and deleted. It is
802 possible to use :hg:`merge NAME` to merge from a given bookmark, and
802 possible to use :hg:`merge NAME` to merge from a given bookmark, and
803 :hg:`update NAME` to update to a given bookmark.
803 :hg:`update NAME` to update to a given bookmark.
804
804
805 You can use :hg:`bookmark NAME` to set a bookmark on the working
805 You can use :hg:`bookmark NAME` to set a bookmark on the working
806 directory's parent revision with the given name. If you specify
806 directory's parent revision with the given name. If you specify
807 a revision using -r REV (where REV may be an existing bookmark),
807 a revision using -r REV (where REV may be an existing bookmark),
808 the bookmark is assigned to that revision.
808 the bookmark is assigned to that revision.
809
809
810 Bookmarks can be pushed and pulled between repositories (see :hg:`help
810 Bookmarks can be pushed and pulled between repositories (see :hg:`help
811 push` and :hg:`help pull`). This requires both the local and remote
811 push` and :hg:`help pull`). This requires both the local and remote
812 repositories to support bookmarks. For versions prior to 1.8, this means
812 repositories to support bookmarks. For versions prior to 1.8, this means
813 the bookmarks extension must be enabled.
813 the bookmarks extension must be enabled.
814
814
815 If you set a bookmark called '@', new clones of the repository will
815 If you set a bookmark called '@', new clones of the repository will
816 have that revision checked out (and the bookmark made active) by
816 have that revision checked out (and the bookmark made active) by
817 default.
817 default.
818
818
819 With -i/--inactive, the new bookmark will not be made the active
819 With -i/--inactive, the new bookmark will not be made the active
820 bookmark. If -r/--rev is given, the new bookmark will not be made
820 bookmark. If -r/--rev is given, the new bookmark will not be made
821 active even if -i/--inactive is not given. If no NAME is given, the
821 active even if -i/--inactive is not given. If no NAME is given, the
822 current active bookmark will be marked inactive.
822 current active bookmark will be marked inactive.
823 '''
823 '''
824 force = opts.get('force')
824 force = opts.get('force')
825 rev = opts.get('rev')
825 rev = opts.get('rev')
826 delete = opts.get('delete')
826 delete = opts.get('delete')
827 rename = opts.get('rename')
827 rename = opts.get('rename')
828 inactive = opts.get('inactive')
828 inactive = opts.get('inactive')
829
829
830 def checkformat(mark):
830 def checkformat(mark):
831 mark = mark.strip()
831 mark = mark.strip()
832 if not mark:
832 if not mark:
833 raise util.Abort(_("bookmark names cannot consist entirely of "
833 raise util.Abort(_("bookmark names cannot consist entirely of "
834 "whitespace"))
834 "whitespace"))
835 scmutil.checknewlabel(repo, mark, 'bookmark')
835 scmutil.checknewlabel(repo, mark, 'bookmark')
836 return mark
836 return mark
837
837
838 def checkconflict(repo, mark, cur, force=False, target=None):
838 def checkconflict(repo, mark, cur, force=False, target=None):
839 if mark in marks and not force:
839 if mark in marks and not force:
840 if target:
840 if target:
841 if marks[mark] == target and target == cur:
841 if marks[mark] == target and target == cur:
842 # re-activating a bookmark
842 # re-activating a bookmark
843 return
843 return
844 anc = repo.changelog.ancestors([repo[target].rev()])
844 anc = repo.changelog.ancestors([repo[target].rev()])
845 bmctx = repo[marks[mark]]
845 bmctx = repo[marks[mark]]
846 divs = [repo[b].node() for b in marks
846 divs = [repo[b].node() for b in marks
847 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
847 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
848
848
849 # allow resolving a single divergent bookmark even if moving
849 # allow resolving a single divergent bookmark even if moving
850 # the bookmark across branches when a revision is specified
850 # the bookmark across branches when a revision is specified
851 # that contains a divergent bookmark
851 # that contains a divergent bookmark
852 if bmctx.rev() not in anc and target in divs:
852 if bmctx.rev() not in anc and target in divs:
853 bookmarks.deletedivergent(repo, [target], mark)
853 bookmarks.deletedivergent(repo, [target], mark)
854 return
854 return
855
855
856 deletefrom = [b for b in divs
856 deletefrom = [b for b in divs
857 if repo[b].rev() in anc or b == target]
857 if repo[b].rev() in anc or b == target]
858 bookmarks.deletedivergent(repo, deletefrom, mark)
858 bookmarks.deletedivergent(repo, deletefrom, mark)
859 if bookmarks.validdest(repo, bmctx, repo[target]):
859 if bookmarks.validdest(repo, bmctx, repo[target]):
860 ui.status(_("moving bookmark '%s' forward from %s\n") %
860 ui.status(_("moving bookmark '%s' forward from %s\n") %
861 (mark, short(bmctx.node())))
861 (mark, short(bmctx.node())))
862 return
862 return
863 raise util.Abort(_("bookmark '%s' already exists "
863 raise util.Abort(_("bookmark '%s' already exists "
864 "(use -f to force)") % mark)
864 "(use -f to force)") % mark)
865 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
865 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
866 and not force):
866 and not force):
867 raise util.Abort(
867 raise util.Abort(
868 _("a bookmark cannot have the name of an existing branch"))
868 _("a bookmark cannot have the name of an existing branch"))
869
869
870 if delete and rename:
870 if delete and rename:
871 raise util.Abort(_("--delete and --rename are incompatible"))
871 raise util.Abort(_("--delete and --rename are incompatible"))
872 if delete and rev:
872 if delete and rev:
873 raise util.Abort(_("--rev is incompatible with --delete"))
873 raise util.Abort(_("--rev is incompatible with --delete"))
874 if rename and rev:
874 if rename and rev:
875 raise util.Abort(_("--rev is incompatible with --rename"))
875 raise util.Abort(_("--rev is incompatible with --rename"))
876 if not names and (delete or rev):
876 if not names and (delete or rev):
877 raise util.Abort(_("bookmark name required"))
877 raise util.Abort(_("bookmark name required"))
878
878
879 if delete or rename or names or inactive:
879 if delete or rename or names or inactive:
880 wlock = repo.wlock()
880 wlock = repo.wlock()
881 try:
881 try:
882 cur = repo.changectx('.').node()
882 cur = repo.changectx('.').node()
883 marks = repo._bookmarks
883 marks = repo._bookmarks
884 if delete:
884 if delete:
885 for mark in names:
885 for mark in names:
886 if mark not in marks:
886 if mark not in marks:
887 raise util.Abort(_("bookmark '%s' does not exist") %
887 raise util.Abort(_("bookmark '%s' does not exist") %
888 mark)
888 mark)
889 if mark == repo._bookmarkcurrent:
889 if mark == repo._bookmarkcurrent:
890 bookmarks.unsetcurrent(repo)
890 bookmarks.unsetcurrent(repo)
891 del marks[mark]
891 del marks[mark]
892 marks.write()
892 marks.write()
893
893
894 elif rename:
894 elif rename:
895 if not names:
895 if not names:
896 raise util.Abort(_("new bookmark name required"))
896 raise util.Abort(_("new bookmark name required"))
897 elif len(names) > 1:
897 elif len(names) > 1:
898 raise util.Abort(_("only one new bookmark name allowed"))
898 raise util.Abort(_("only one new bookmark name allowed"))
899 mark = checkformat(names[0])
899 mark = checkformat(names[0])
900 if rename not in marks:
900 if rename not in marks:
901 raise util.Abort(_("bookmark '%s' does not exist") % rename)
901 raise util.Abort(_("bookmark '%s' does not exist") % rename)
902 checkconflict(repo, mark, cur, force)
902 checkconflict(repo, mark, cur, force)
903 marks[mark] = marks[rename]
903 marks[mark] = marks[rename]
904 if repo._bookmarkcurrent == rename and not inactive:
904 if repo._bookmarkcurrent == rename and not inactive:
905 bookmarks.setcurrent(repo, mark)
905 bookmarks.setcurrent(repo, mark)
906 del marks[rename]
906 del marks[rename]
907 marks.write()
907 marks.write()
908
908
909 elif names:
909 elif names:
910 newact = None
910 newact = None
911 for mark in names:
911 for mark in names:
912 mark = checkformat(mark)
912 mark = checkformat(mark)
913 if newact is None:
913 if newact is None:
914 newact = mark
914 newact = mark
915 if inactive and mark == repo._bookmarkcurrent:
915 if inactive and mark == repo._bookmarkcurrent:
916 bookmarks.unsetcurrent(repo)
916 bookmarks.unsetcurrent(repo)
917 return
917 return
918 tgt = cur
918 tgt = cur
919 if rev:
919 if rev:
920 tgt = scmutil.revsingle(repo, rev).node()
920 tgt = scmutil.revsingle(repo, rev).node()
921 checkconflict(repo, mark, cur, force, tgt)
921 checkconflict(repo, mark, cur, force, tgt)
922 marks[mark] = tgt
922 marks[mark] = tgt
923 if not inactive and cur == marks[newact] and not rev:
923 if not inactive and cur == marks[newact] and not rev:
924 bookmarks.setcurrent(repo, newact)
924 bookmarks.setcurrent(repo, newact)
925 elif cur != tgt and newact == repo._bookmarkcurrent:
925 elif cur != tgt and newact == repo._bookmarkcurrent:
926 bookmarks.unsetcurrent(repo)
926 bookmarks.unsetcurrent(repo)
927 marks.write()
927 marks.write()
928
928
929 elif inactive:
929 elif inactive:
930 if len(marks) == 0:
930 if len(marks) == 0:
931 ui.status(_("no bookmarks set\n"))
931 ui.status(_("no bookmarks set\n"))
932 elif not repo._bookmarkcurrent:
932 elif not repo._bookmarkcurrent:
933 ui.status(_("no active bookmark\n"))
933 ui.status(_("no active bookmark\n"))
934 else:
934 else:
935 bookmarks.unsetcurrent(repo)
935 bookmarks.unsetcurrent(repo)
936 finally:
936 finally:
937 wlock.release()
937 wlock.release()
938 else: # show bookmarks
938 else: # show bookmarks
939 hexfn = ui.debugflag and hex or short
939 hexfn = ui.debugflag and hex or short
940 marks = repo._bookmarks
940 marks = repo._bookmarks
941 if len(marks) == 0:
941 if len(marks) == 0:
942 ui.status(_("no bookmarks set\n"))
942 ui.status(_("no bookmarks set\n"))
943 else:
943 else:
944 for bmark, n in sorted(marks.iteritems()):
944 for bmark, n in sorted(marks.iteritems()):
945 current = repo._bookmarkcurrent
945 current = repo._bookmarkcurrent
946 if bmark == current:
946 if bmark == current:
947 prefix, label = '*', 'bookmarks.current'
947 prefix, label = '*', 'bookmarks.current'
948 else:
948 else:
949 prefix, label = ' ', ''
949 prefix, label = ' ', ''
950
950
951 if ui.quiet:
951 if ui.quiet:
952 ui.write("%s\n" % bmark, label=label)
952 ui.write("%s\n" % bmark, label=label)
953 else:
953 else:
954 ui.write(" %s %-25s %d:%s\n" % (
954 ui.write(" %s %-25s %d:%s\n" % (
955 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
955 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
956 label=label)
956 label=label)
957
957
958 @command('branch',
958 @command('branch',
959 [('f', 'force', None,
959 [('f', 'force', None,
960 _('set branch name even if it shadows an existing branch')),
960 _('set branch name even if it shadows an existing branch')),
961 ('C', 'clean', None, _('reset branch name to parent branch name'))],
961 ('C', 'clean', None, _('reset branch name to parent branch name'))],
962 _('[-fC] [NAME]'))
962 _('[-fC] [NAME]'))
963 def branch(ui, repo, label=None, **opts):
963 def branch(ui, repo, label=None, **opts):
964 """set or show the current branch name
964 """set or show the current branch name
965
965
966 .. note::
966 .. note::
967
967
968 Branch names are permanent and global. Use :hg:`bookmark` to create a
968 Branch names are permanent and global. Use :hg:`bookmark` to create a
969 light-weight bookmark instead. See :hg:`help glossary` for more
969 light-weight bookmark instead. See :hg:`help glossary` for more
970 information about named branches and bookmarks.
970 information about named branches and bookmarks.
971
971
972 With no argument, show the current branch name. With one argument,
972 With no argument, show the current branch name. With one argument,
973 set the working directory branch name (the branch will not exist
973 set the working directory branch name (the branch will not exist
974 in the repository until the next commit). Standard practice
974 in the repository until the next commit). Standard practice
975 recommends that primary development take place on the 'default'
975 recommends that primary development take place on the 'default'
976 branch.
976 branch.
977
977
978 Unless -f/--force is specified, branch will not let you set a
978 Unless -f/--force is specified, branch will not let you set a
979 branch name that already exists, even if it's inactive.
979 branch name that already exists, even if it's inactive.
980
980
981 Use -C/--clean to reset the working directory branch to that of
981 Use -C/--clean to reset the working directory branch to that of
982 the parent of the working directory, negating a previous branch
982 the parent of the working directory, negating a previous branch
983 change.
983 change.
984
984
985 Use the command :hg:`update` to switch to an existing branch. Use
985 Use the command :hg:`update` to switch to an existing branch. Use
986 :hg:`commit --close-branch` to mark this branch as closed.
986 :hg:`commit --close-branch` to mark this branch as closed.
987
987
988 Returns 0 on success.
988 Returns 0 on success.
989 """
989 """
990 if label:
990 if label:
991 label = label.strip()
991 label = label.strip()
992
992
993 if not opts.get('clean') and not label:
993 if not opts.get('clean') and not label:
994 ui.write("%s\n" % repo.dirstate.branch())
994 ui.write("%s\n" % repo.dirstate.branch())
995 return
995 return
996
996
997 wlock = repo.wlock()
997 wlock = repo.wlock()
998 try:
998 try:
999 if opts.get('clean'):
999 if opts.get('clean'):
1000 label = repo[None].p1().branch()
1000 label = repo[None].p1().branch()
1001 repo.dirstate.setbranch(label)
1001 repo.dirstate.setbranch(label)
1002 ui.status(_('reset working directory to branch %s\n') % label)
1002 ui.status(_('reset working directory to branch %s\n') % label)
1003 elif label:
1003 elif label:
1004 if not opts.get('force') and label in repo.branchmap():
1004 if not opts.get('force') and label in repo.branchmap():
1005 if label not in [p.branch() for p in repo.parents()]:
1005 if label not in [p.branch() for p in repo.parents()]:
1006 raise util.Abort(_('a branch of the same name already'
1006 raise util.Abort(_('a branch of the same name already'
1007 ' exists'),
1007 ' exists'),
1008 # i18n: "it" refers to an existing branch
1008 # i18n: "it" refers to an existing branch
1009 hint=_("use 'hg update' to switch to it"))
1009 hint=_("use 'hg update' to switch to it"))
1010 scmutil.checknewlabel(repo, label, 'branch')
1010 scmutil.checknewlabel(repo, label, 'branch')
1011 repo.dirstate.setbranch(label)
1011 repo.dirstate.setbranch(label)
1012 ui.status(_('marked working directory as branch %s\n') % label)
1012 ui.status(_('marked working directory as branch %s\n') % label)
1013 ui.status(_('(branches are permanent and global, '
1013 ui.status(_('(branches are permanent and global, '
1014 'did you want a bookmark?)\n'))
1014 'did you want a bookmark?)\n'))
1015 finally:
1015 finally:
1016 wlock.release()
1016 wlock.release()
1017
1017
1018 @command('branches',
1018 @command('branches',
1019 [('a', 'active', False, _('show only branches that have unmerged heads')),
1019 [('a', 'active', False, _('show only branches that have unmerged heads')),
1020 ('c', 'closed', False, _('show normal and closed branches'))],
1020 ('c', 'closed', False, _('show normal and closed branches'))],
1021 _('[-ac]'))
1021 _('[-ac]'))
1022 def branches(ui, repo, active=False, closed=False):
1022 def branches(ui, repo, active=False, closed=False):
1023 """list repository named branches
1023 """list repository named branches
1024
1024
1025 List the repository's named branches, indicating which ones are
1025 List the repository's named branches, indicating which ones are
1026 inactive. If -c/--closed is specified, also list branches which have
1026 inactive. If -c/--closed is specified, also list branches which have
1027 been marked closed (see :hg:`commit --close-branch`).
1027 been marked closed (see :hg:`commit --close-branch`).
1028
1028
1029 If -a/--active is specified, only show active branches. A branch
1029 If -a/--active is specified, only show active branches. A branch
1030 is considered active if it contains repository heads.
1030 is considered active if it contains repository heads.
1031
1031
1032 Use the command :hg:`update` to switch to an existing branch.
1032 Use the command :hg:`update` to switch to an existing branch.
1033
1033
1034 Returns 0.
1034 Returns 0.
1035 """
1035 """
1036
1036
1037 hexfunc = ui.debugflag and hex or short
1037 hexfunc = ui.debugflag and hex or short
1038
1038
1039 allheads = set(repo.heads())
1039 allheads = set(repo.heads())
1040 branches = []
1040 branches = []
1041 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1041 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1042 isactive = not isclosed and bool(set(heads) & allheads)
1042 isactive = not isclosed and bool(set(heads) & allheads)
1043 branches.append((tag, repo[tip], isactive, not isclosed))
1043 branches.append((tag, repo[tip], isactive, not isclosed))
1044 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1044 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1045 reverse=True)
1045 reverse=True)
1046
1046
1047 for tag, ctx, isactive, isopen in branches:
1047 for tag, ctx, isactive, isopen in branches:
1048 if (not active) or isactive:
1048 if (not active) or isactive:
1049 if isactive:
1049 if isactive:
1050 label = 'branches.active'
1050 label = 'branches.active'
1051 notice = ''
1051 notice = ''
1052 elif not isopen:
1052 elif not isopen:
1053 if not closed:
1053 if not closed:
1054 continue
1054 continue
1055 label = 'branches.closed'
1055 label = 'branches.closed'
1056 notice = _(' (closed)')
1056 notice = _(' (closed)')
1057 else:
1057 else:
1058 label = 'branches.inactive'
1058 label = 'branches.inactive'
1059 notice = _(' (inactive)')
1059 notice = _(' (inactive)')
1060 if tag == repo.dirstate.branch():
1060 if tag == repo.dirstate.branch():
1061 label = 'branches.current'
1061 label = 'branches.current'
1062 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(tag))
1062 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(tag))
1063 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1063 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1064 'log.changeset changeset.%s' % ctx.phasestr())
1064 'log.changeset changeset.%s' % ctx.phasestr())
1065 labeledtag = ui.label(tag, label)
1065 labeledtag = ui.label(tag, label)
1066 if ui.quiet:
1066 if ui.quiet:
1067 ui.write("%s\n" % labeledtag)
1067 ui.write("%s\n" % labeledtag)
1068 else:
1068 else:
1069 ui.write("%s %s%s\n" % (labeledtag, rev, notice))
1069 ui.write("%s %s%s\n" % (labeledtag, rev, notice))
1070
1070
1071 @command('bundle',
1071 @command('bundle',
1072 [('f', 'force', None, _('run even when the destination is unrelated')),
1072 [('f', 'force', None, _('run even when the destination is unrelated')),
1073 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1073 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1074 _('REV')),
1074 _('REV')),
1075 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1075 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1076 _('BRANCH')),
1076 _('BRANCH')),
1077 ('', 'base', [],
1077 ('', 'base', [],
1078 _('a base changeset assumed to be available at the destination'),
1078 _('a base changeset assumed to be available at the destination'),
1079 _('REV')),
1079 _('REV')),
1080 ('a', 'all', None, _('bundle all changesets in the repository')),
1080 ('a', 'all', None, _('bundle all changesets in the repository')),
1081 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1081 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1082 ] + remoteopts,
1082 ] + remoteopts,
1083 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1083 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1084 def bundle(ui, repo, fname, dest=None, **opts):
1084 def bundle(ui, repo, fname, dest=None, **opts):
1085 """create a changegroup file
1085 """create a changegroup file
1086
1086
1087 Generate a compressed changegroup file collecting changesets not
1087 Generate a compressed changegroup file collecting changesets not
1088 known to be in another repository.
1088 known to be in another repository.
1089
1089
1090 If you omit the destination repository, then hg assumes the
1090 If you omit the destination repository, then hg assumes the
1091 destination will have all the nodes you specify with --base
1091 destination will have all the nodes you specify with --base
1092 parameters. To create a bundle containing all changesets, use
1092 parameters. To create a bundle containing all changesets, use
1093 -a/--all (or --base null).
1093 -a/--all (or --base null).
1094
1094
1095 You can change compression method with the -t/--type option.
1095 You can change compression method with the -t/--type option.
1096 The available compression methods are: none, bzip2, and
1096 The available compression methods are: none, bzip2, and
1097 gzip (by default, bundles are compressed using bzip2).
1097 gzip (by default, bundles are compressed using bzip2).
1098
1098
1099 The bundle file can then be transferred using conventional means
1099 The bundle file can then be transferred using conventional means
1100 and applied to another repository with the unbundle or pull
1100 and applied to another repository with the unbundle or pull
1101 command. This is useful when direct push and pull are not
1101 command. This is useful when direct push and pull are not
1102 available or when exporting an entire repository is undesirable.
1102 available or when exporting an entire repository is undesirable.
1103
1103
1104 Applying bundles preserves all changeset contents including
1104 Applying bundles preserves all changeset contents including
1105 permissions, copy/rename information, and revision history.
1105 permissions, copy/rename information, and revision history.
1106
1106
1107 Returns 0 on success, 1 if no changes found.
1107 Returns 0 on success, 1 if no changes found.
1108 """
1108 """
1109 revs = None
1109 revs = None
1110 if 'rev' in opts:
1110 if 'rev' in opts:
1111 revs = scmutil.revrange(repo, opts['rev'])
1111 revs = scmutil.revrange(repo, opts['rev'])
1112
1112
1113 bundletype = opts.get('type', 'bzip2').lower()
1113 bundletype = opts.get('type', 'bzip2').lower()
1114 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1114 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1115 bundletype = btypes.get(bundletype)
1115 bundletype = btypes.get(bundletype)
1116 if bundletype not in changegroup.bundletypes:
1116 if bundletype not in changegroup.bundletypes:
1117 raise util.Abort(_('unknown bundle type specified with --type'))
1117 raise util.Abort(_('unknown bundle type specified with --type'))
1118
1118
1119 if opts.get('all'):
1119 if opts.get('all'):
1120 base = ['null']
1120 base = ['null']
1121 else:
1121 else:
1122 base = scmutil.revrange(repo, opts.get('base'))
1122 base = scmutil.revrange(repo, opts.get('base'))
1123 # TODO: get desired bundlecaps from command line.
1123 # TODO: get desired bundlecaps from command line.
1124 bundlecaps = None
1124 bundlecaps = None
1125 if base:
1125 if base:
1126 if dest:
1126 if dest:
1127 raise util.Abort(_("--base is incompatible with specifying "
1127 raise util.Abort(_("--base is incompatible with specifying "
1128 "a destination"))
1128 "a destination"))
1129 common = [repo.lookup(rev) for rev in base]
1129 common = [repo.lookup(rev) for rev in base]
1130 heads = revs and map(repo.lookup, revs) or revs
1130 heads = revs and map(repo.lookup, revs) or revs
1131 cg = changegroup.getbundle(repo, 'bundle', heads=heads, common=common,
1131 cg = changegroup.getbundle(repo, 'bundle', heads=heads, common=common,
1132 bundlecaps=bundlecaps)
1132 bundlecaps=bundlecaps)
1133 outgoing = None
1133 outgoing = None
1134 else:
1134 else:
1135 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1135 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1136 dest, branches = hg.parseurl(dest, opts.get('branch'))
1136 dest, branches = hg.parseurl(dest, opts.get('branch'))
1137 other = hg.peer(repo, opts, dest)
1137 other = hg.peer(repo, opts, dest)
1138 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1138 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1139 heads = revs and map(repo.lookup, revs) or revs
1139 heads = revs and map(repo.lookup, revs) or revs
1140 outgoing = discovery.findcommonoutgoing(repo, other,
1140 outgoing = discovery.findcommonoutgoing(repo, other,
1141 onlyheads=heads,
1141 onlyheads=heads,
1142 force=opts.get('force'),
1142 force=opts.get('force'),
1143 portable=True)
1143 portable=True)
1144 cg = changegroup.getlocalbundle(repo, 'bundle', outgoing, bundlecaps)
1144 cg = changegroup.getlocalbundle(repo, 'bundle', outgoing, bundlecaps)
1145 if not cg:
1145 if not cg:
1146 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1146 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1147 return 1
1147 return 1
1148
1148
1149 changegroup.writebundle(cg, fname, bundletype)
1149 changegroup.writebundle(cg, fname, bundletype)
1150
1150
1151 @command('cat',
1151 @command('cat',
1152 [('o', 'output', '',
1152 [('o', 'output', '',
1153 _('print output to file with formatted name'), _('FORMAT')),
1153 _('print output to file with formatted name'), _('FORMAT')),
1154 ('r', 'rev', '', _('print the given revision'), _('REV')),
1154 ('r', 'rev', '', _('print the given revision'), _('REV')),
1155 ('', 'decode', None, _('apply any matching decode filter')),
1155 ('', 'decode', None, _('apply any matching decode filter')),
1156 ] + walkopts,
1156 ] + walkopts,
1157 _('[OPTION]... FILE...'))
1157 _('[OPTION]... FILE...'))
1158 def cat(ui, repo, file1, *pats, **opts):
1158 def cat(ui, repo, file1, *pats, **opts):
1159 """output the current or given revision of files
1159 """output the current or given revision of files
1160
1160
1161 Print the specified files as they were at the given revision. If
1161 Print the specified files as they were at the given revision. If
1162 no revision is given, the parent of the working directory is used.
1162 no revision is given, the parent of the working directory is used.
1163
1163
1164 Output may be to a file, in which case the name of the file is
1164 Output may be to a file, in which case the name of the file is
1165 given using a format string. The formatting rules as follows:
1165 given using a format string. The formatting rules as follows:
1166
1166
1167 :``%%``: literal "%" character
1167 :``%%``: literal "%" character
1168 :``%s``: basename of file being printed
1168 :``%s``: basename of file being printed
1169 :``%d``: dirname of file being printed, or '.' if in repository root
1169 :``%d``: dirname of file being printed, or '.' if in repository root
1170 :``%p``: root-relative path name of file being printed
1170 :``%p``: root-relative path name of file being printed
1171 :``%H``: changeset hash (40 hexadecimal digits)
1171 :``%H``: changeset hash (40 hexadecimal digits)
1172 :``%R``: changeset revision number
1172 :``%R``: changeset revision number
1173 :``%h``: short-form changeset hash (12 hexadecimal digits)
1173 :``%h``: short-form changeset hash (12 hexadecimal digits)
1174 :``%r``: zero-padded changeset revision number
1174 :``%r``: zero-padded changeset revision number
1175 :``%b``: basename of the exporting repository
1175 :``%b``: basename of the exporting repository
1176
1176
1177 Returns 0 on success.
1177 Returns 0 on success.
1178 """
1178 """
1179 ctx = scmutil.revsingle(repo, opts.get('rev'))
1179 ctx = scmutil.revsingle(repo, opts.get('rev'))
1180 m = scmutil.match(ctx, (file1,) + pats, opts)
1180 m = scmutil.match(ctx, (file1,) + pats, opts)
1181
1181
1182 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1182 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1183
1183
1184 @command('^clone',
1184 @command('^clone',
1185 [('U', 'noupdate', None,
1185 [('U', 'noupdate', None,
1186 _('the clone will include an empty working copy (only a repository)')),
1186 _('the clone will include an empty working copy (only a repository)')),
1187 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1187 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1188 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1188 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1189 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1189 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1190 ('', 'pull', None, _('use pull protocol to copy metadata')),
1190 ('', 'pull', None, _('use pull protocol to copy metadata')),
1191 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1191 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1192 ] + remoteopts,
1192 ] + remoteopts,
1193 _('[OPTION]... SOURCE [DEST]'))
1193 _('[OPTION]... SOURCE [DEST]'))
1194 def clone(ui, source, dest=None, **opts):
1194 def clone(ui, source, dest=None, **opts):
1195 """make a copy of an existing repository
1195 """make a copy of an existing repository
1196
1196
1197 Create a copy of an existing repository in a new directory.
1197 Create a copy of an existing repository in a new directory.
1198
1198
1199 If no destination directory name is specified, it defaults to the
1199 If no destination directory name is specified, it defaults to the
1200 basename of the source.
1200 basename of the source.
1201
1201
1202 The location of the source is added to the new repository's
1202 The location of the source is added to the new repository's
1203 ``.hg/hgrc`` file, as the default to be used for future pulls.
1203 ``.hg/hgrc`` file, as the default to be used for future pulls.
1204
1204
1205 Only local paths and ``ssh://`` URLs are supported as
1205 Only local paths and ``ssh://`` URLs are supported as
1206 destinations. For ``ssh://`` destinations, no working directory or
1206 destinations. For ``ssh://`` destinations, no working directory or
1207 ``.hg/hgrc`` will be created on the remote side.
1207 ``.hg/hgrc`` will be created on the remote side.
1208
1208
1209 To pull only a subset of changesets, specify one or more revisions
1209 To pull only a subset of changesets, specify one or more revisions
1210 identifiers with -r/--rev or branches with -b/--branch. The
1210 identifiers with -r/--rev or branches with -b/--branch. The
1211 resulting clone will contain only the specified changesets and
1211 resulting clone will contain only the specified changesets and
1212 their ancestors. These options (or 'clone src#rev dest') imply
1212 their ancestors. These options (or 'clone src#rev dest') imply
1213 --pull, even for local source repositories. Note that specifying a
1213 --pull, even for local source repositories. Note that specifying a
1214 tag will include the tagged changeset but not the changeset
1214 tag will include the tagged changeset but not the changeset
1215 containing the tag.
1215 containing the tag.
1216
1216
1217 If the source repository has a bookmark called '@' set, that
1217 If the source repository has a bookmark called '@' set, that
1218 revision will be checked out in the new repository by default.
1218 revision will be checked out in the new repository by default.
1219
1219
1220 To check out a particular version, use -u/--update, or
1220 To check out a particular version, use -u/--update, or
1221 -U/--noupdate to create a clone with no working directory.
1221 -U/--noupdate to create a clone with no working directory.
1222
1222
1223 .. container:: verbose
1223 .. container:: verbose
1224
1224
1225 For efficiency, hardlinks are used for cloning whenever the
1225 For efficiency, hardlinks are used for cloning whenever the
1226 source and destination are on the same filesystem (note this
1226 source and destination are on the same filesystem (note this
1227 applies only to the repository data, not to the working
1227 applies only to the repository data, not to the working
1228 directory). Some filesystems, such as AFS, implement hardlinking
1228 directory). Some filesystems, such as AFS, implement hardlinking
1229 incorrectly, but do not report errors. In these cases, use the
1229 incorrectly, but do not report errors. In these cases, use the
1230 --pull option to avoid hardlinking.
1230 --pull option to avoid hardlinking.
1231
1231
1232 In some cases, you can clone repositories and the working
1232 In some cases, you can clone repositories and the working
1233 directory using full hardlinks with ::
1233 directory using full hardlinks with ::
1234
1234
1235 $ cp -al REPO REPOCLONE
1235 $ cp -al REPO REPOCLONE
1236
1236
1237 This is the fastest way to clone, but it is not always safe. The
1237 This is the fastest way to clone, but it is not always safe. The
1238 operation is not atomic (making sure REPO is not modified during
1238 operation is not atomic (making sure REPO is not modified during
1239 the operation is up to you) and you have to make sure your
1239 the operation is up to you) and you have to make sure your
1240 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1240 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1241 so). Also, this is not compatible with certain extensions that
1241 so). Also, this is not compatible with certain extensions that
1242 place their metadata under the .hg directory, such as mq.
1242 place their metadata under the .hg directory, such as mq.
1243
1243
1244 Mercurial will update the working directory to the first applicable
1244 Mercurial will update the working directory to the first applicable
1245 revision from this list:
1245 revision from this list:
1246
1246
1247 a) null if -U or the source repository has no changesets
1247 a) null if -U or the source repository has no changesets
1248 b) if -u . and the source repository is local, the first parent of
1248 b) if -u . and the source repository is local, the first parent of
1249 the source repository's working directory
1249 the source repository's working directory
1250 c) the changeset specified with -u (if a branch name, this means the
1250 c) the changeset specified with -u (if a branch name, this means the
1251 latest head of that branch)
1251 latest head of that branch)
1252 d) the changeset specified with -r
1252 d) the changeset specified with -r
1253 e) the tipmost head specified with -b
1253 e) the tipmost head specified with -b
1254 f) the tipmost head specified with the url#branch source syntax
1254 f) the tipmost head specified with the url#branch source syntax
1255 g) the revision marked with the '@' bookmark, if present
1255 g) the revision marked with the '@' bookmark, if present
1256 h) the tipmost head of the default branch
1256 h) the tipmost head of the default branch
1257 i) tip
1257 i) tip
1258
1258
1259 Examples:
1259 Examples:
1260
1260
1261 - clone a remote repository to a new directory named hg/::
1261 - clone a remote repository to a new directory named hg/::
1262
1262
1263 hg clone http://selenic.com/hg
1263 hg clone http://selenic.com/hg
1264
1264
1265 - create a lightweight local clone::
1265 - create a lightweight local clone::
1266
1266
1267 hg clone project/ project-feature/
1267 hg clone project/ project-feature/
1268
1268
1269 - clone from an absolute path on an ssh server (note double-slash)::
1269 - clone from an absolute path on an ssh server (note double-slash)::
1270
1270
1271 hg clone ssh://user@server//home/projects/alpha/
1271 hg clone ssh://user@server//home/projects/alpha/
1272
1272
1273 - do a high-speed clone over a LAN while checking out a
1273 - do a high-speed clone over a LAN while checking out a
1274 specified version::
1274 specified version::
1275
1275
1276 hg clone --uncompressed http://server/repo -u 1.5
1276 hg clone --uncompressed http://server/repo -u 1.5
1277
1277
1278 - create a repository without changesets after a particular revision::
1278 - create a repository without changesets after a particular revision::
1279
1279
1280 hg clone -r 04e544 experimental/ good/
1280 hg clone -r 04e544 experimental/ good/
1281
1281
1282 - clone (and track) a particular named branch::
1282 - clone (and track) a particular named branch::
1283
1283
1284 hg clone http://selenic.com/hg#stable
1284 hg clone http://selenic.com/hg#stable
1285
1285
1286 See :hg:`help urls` for details on specifying URLs.
1286 See :hg:`help urls` for details on specifying URLs.
1287
1287
1288 Returns 0 on success.
1288 Returns 0 on success.
1289 """
1289 """
1290 if opts.get('noupdate') and opts.get('updaterev'):
1290 if opts.get('noupdate') and opts.get('updaterev'):
1291 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1291 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1292
1292
1293 r = hg.clone(ui, opts, source, dest,
1293 r = hg.clone(ui, opts, source, dest,
1294 pull=opts.get('pull'),
1294 pull=opts.get('pull'),
1295 stream=opts.get('uncompressed'),
1295 stream=opts.get('uncompressed'),
1296 rev=opts.get('rev'),
1296 rev=opts.get('rev'),
1297 update=opts.get('updaterev') or not opts.get('noupdate'),
1297 update=opts.get('updaterev') or not opts.get('noupdate'),
1298 branch=opts.get('branch'))
1298 branch=opts.get('branch'))
1299
1299
1300 return r is None
1300 return r is None
1301
1301
1302 @command('^commit|ci',
1302 @command('^commit|ci',
1303 [('A', 'addremove', None,
1303 [('A', 'addremove', None,
1304 _('mark new/missing files as added/removed before committing')),
1304 _('mark new/missing files as added/removed before committing')),
1305 ('', 'close-branch', None,
1305 ('', 'close-branch', None,
1306 _('mark a branch as closed, hiding it from the branch list')),
1306 _('mark a branch as closed, hiding it from the branch list')),
1307 ('', 'amend', None, _('amend the parent of the working dir')),
1307 ('', 'amend', None, _('amend the parent of the working dir')),
1308 ('s', 'secret', None, _('use the secret phase for committing')),
1308 ('s', 'secret', None, _('use the secret phase for committing')),
1309 ('e', 'edit', None,
1309 ('e', 'edit', None,
1310 _('further edit commit message already specified')),
1310 _('further edit commit message already specified')),
1311 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1311 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1312 _('[OPTION]... [FILE]...'))
1312 _('[OPTION]... [FILE]...'))
1313 def commit(ui, repo, *pats, **opts):
1313 def commit(ui, repo, *pats, **opts):
1314 """commit the specified files or all outstanding changes
1314 """commit the specified files or all outstanding changes
1315
1315
1316 Commit changes to the given files into the repository. Unlike a
1316 Commit changes to the given files into the repository. Unlike a
1317 centralized SCM, this operation is a local operation. See
1317 centralized SCM, this operation is a local operation. See
1318 :hg:`push` for a way to actively distribute your changes.
1318 :hg:`push` for a way to actively distribute your changes.
1319
1319
1320 If a list of files is omitted, all changes reported by :hg:`status`
1320 If a list of files is omitted, all changes reported by :hg:`status`
1321 will be committed.
1321 will be committed.
1322
1322
1323 If you are committing the result of a merge, do not provide any
1323 If you are committing the result of a merge, do not provide any
1324 filenames or -I/-X filters.
1324 filenames or -I/-X filters.
1325
1325
1326 If no commit message is specified, Mercurial starts your
1326 If no commit message is specified, Mercurial starts your
1327 configured editor where you can enter a message. In case your
1327 configured editor where you can enter a message. In case your
1328 commit fails, you will find a backup of your message in
1328 commit fails, you will find a backup of your message in
1329 ``.hg/last-message.txt``.
1329 ``.hg/last-message.txt``.
1330
1330
1331 The --amend flag can be used to amend the parent of the
1331 The --amend flag can be used to amend the parent of the
1332 working directory with a new commit that contains the changes
1332 working directory with a new commit that contains the changes
1333 in the parent in addition to those currently reported by :hg:`status`,
1333 in the parent in addition to those currently reported by :hg:`status`,
1334 if there are any. The old commit is stored in a backup bundle in
1334 if there are any. The old commit is stored in a backup bundle in
1335 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1335 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1336 on how to restore it).
1336 on how to restore it).
1337
1337
1338 Message, user and date are taken from the amended commit unless
1338 Message, user and date are taken from the amended commit unless
1339 specified. When a message isn't specified on the command line,
1339 specified. When a message isn't specified on the command line,
1340 the editor will open with the message of the amended commit.
1340 the editor will open with the message of the amended commit.
1341
1341
1342 It is not possible to amend public changesets (see :hg:`help phases`)
1342 It is not possible to amend public changesets (see :hg:`help phases`)
1343 or changesets that have children.
1343 or changesets that have children.
1344
1344
1345 See :hg:`help dates` for a list of formats valid for -d/--date.
1345 See :hg:`help dates` for a list of formats valid for -d/--date.
1346
1346
1347 Returns 0 on success, 1 if nothing changed.
1347 Returns 0 on success, 1 if nothing changed.
1348 """
1348 """
1349 forceeditor = opts.get('edit')
1349 forceeditor = opts.get('edit')
1350
1350
1351 if opts.get('subrepos'):
1351 if opts.get('subrepos'):
1352 if opts.get('amend'):
1352 if opts.get('amend'):
1353 raise util.Abort(_('cannot amend with --subrepos'))
1353 raise util.Abort(_('cannot amend with --subrepos'))
1354 # Let --subrepos on the command line override config setting.
1354 # Let --subrepos on the command line override config setting.
1355 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1355 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1356
1356
1357 # Save this for restoring it later
1357 # Save this for restoring it later
1358 oldcommitphase = ui.config('phases', 'new-commit')
1358 oldcommitphase = ui.config('phases', 'new-commit')
1359
1359
1360 cmdutil.checkunfinished(repo, commit=True)
1360 cmdutil.checkunfinished(repo, commit=True)
1361
1361
1362 branch = repo[None].branch()
1362 branch = repo[None].branch()
1363 bheads = repo.branchheads(branch)
1363 bheads = repo.branchheads(branch)
1364
1364
1365 extra = {}
1365 extra = {}
1366 if opts.get('close_branch'):
1366 if opts.get('close_branch'):
1367 extra['close'] = 1
1367 extra['close'] = 1
1368
1368
1369 if not bheads:
1369 if not bheads:
1370 raise util.Abort(_('can only close branch heads'))
1370 raise util.Abort(_('can only close branch heads'))
1371 elif opts.get('amend'):
1371 elif opts.get('amend'):
1372 if repo.parents()[0].p1().branch() != branch and \
1372 if repo.parents()[0].p1().branch() != branch and \
1373 repo.parents()[0].p2().branch() != branch:
1373 repo.parents()[0].p2().branch() != branch:
1374 raise util.Abort(_('can only close branch heads'))
1374 raise util.Abort(_('can only close branch heads'))
1375
1375
1376 if opts.get('amend'):
1376 if opts.get('amend'):
1377 if ui.configbool('ui', 'commitsubrepos'):
1377 if ui.configbool('ui', 'commitsubrepos'):
1378 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1378 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1379
1379
1380 old = repo['.']
1380 old = repo['.']
1381 if old.phase() == phases.public:
1381 if old.phase() == phases.public:
1382 raise util.Abort(_('cannot amend public changesets'))
1382 raise util.Abort(_('cannot amend public changesets'))
1383 if len(repo[None].parents()) > 1:
1383 if len(repo[None].parents()) > 1:
1384 raise util.Abort(_('cannot amend while merging'))
1384 raise util.Abort(_('cannot amend while merging'))
1385 if (not obsolete._enabled) and old.children():
1385 if (not obsolete._enabled) and old.children():
1386 raise util.Abort(_('cannot amend changeset with children'))
1386 raise util.Abort(_('cannot amend changeset with children'))
1387
1387
1388 # commitfunc is used only for temporary amend commit by cmdutil.amend
1388 # commitfunc is used only for temporary amend commit by cmdutil.amend
1389 def commitfunc(ui, repo, message, match, opts):
1389 def commitfunc(ui, repo, message, match, opts):
1390 return repo.commit(message,
1390 return repo.commit(message,
1391 opts.get('user') or old.user(),
1391 opts.get('user') or old.user(),
1392 opts.get('date') or old.date(),
1392 opts.get('date') or old.date(),
1393 match,
1393 match,
1394 extra=extra)
1394 extra=extra)
1395
1395
1396 current = repo._bookmarkcurrent
1396 current = repo._bookmarkcurrent
1397 marks = old.bookmarks()
1397 marks = old.bookmarks()
1398 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1398 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1399 if node == old.node():
1399 if node == old.node():
1400 ui.status(_("nothing changed\n"))
1400 ui.status(_("nothing changed\n"))
1401 return 1
1401 return 1
1402 elif marks:
1402 elif marks:
1403 ui.debug('moving bookmarks %r from %s to %s\n' %
1403 ui.debug('moving bookmarks %r from %s to %s\n' %
1404 (marks, old.hex(), hex(node)))
1404 (marks, old.hex(), hex(node)))
1405 newmarks = repo._bookmarks
1405 newmarks = repo._bookmarks
1406 for bm in marks:
1406 for bm in marks:
1407 newmarks[bm] = node
1407 newmarks[bm] = node
1408 if bm == current:
1408 if bm == current:
1409 bookmarks.setcurrent(repo, bm)
1409 bookmarks.setcurrent(repo, bm)
1410 newmarks.write()
1410 newmarks.write()
1411 else:
1411 else:
1412 e = cmdutil.commiteditor
1412 e = cmdutil.commiteditor
1413 if forceeditor:
1413 if forceeditor:
1414 e = cmdutil.commitforceeditor
1414 e = cmdutil.commitforceeditor
1415
1415
1416 def commitfunc(ui, repo, message, match, opts):
1416 def commitfunc(ui, repo, message, match, opts):
1417 try:
1417 try:
1418 if opts.get('secret'):
1418 if opts.get('secret'):
1419 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1419 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1420 # Propagate to subrepos
1420 # Propagate to subrepos
1421 repo.baseui.setconfig('phases', 'new-commit', 'secret',
1421 repo.baseui.setconfig('phases', 'new-commit', 'secret',
1422 'commit')
1422 'commit')
1423
1423
1424 return repo.commit(message, opts.get('user'), opts.get('date'),
1424 return repo.commit(message, opts.get('user'), opts.get('date'),
1425 match, editor=e, extra=extra)
1425 match, editor=e, extra=extra)
1426 finally:
1426 finally:
1427 ui.setconfig('phases', 'new-commit', oldcommitphase, 'commit')
1427 ui.setconfig('phases', 'new-commit', oldcommitphase, 'commit')
1428 repo.baseui.setconfig('phases', 'new-commit', oldcommitphase,
1428 repo.baseui.setconfig('phases', 'new-commit', oldcommitphase,
1429 'commit')
1429 'commit')
1430
1430
1431
1431
1432 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1432 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1433
1433
1434 if not node:
1434 if not node:
1435 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1435 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1436 if stat[3]:
1436 if stat[3]:
1437 ui.status(_("nothing changed (%d missing files, see "
1437 ui.status(_("nothing changed (%d missing files, see "
1438 "'hg status')\n") % len(stat[3]))
1438 "'hg status')\n") % len(stat[3]))
1439 else:
1439 else:
1440 ui.status(_("nothing changed\n"))
1440 ui.status(_("nothing changed\n"))
1441 return 1
1441 return 1
1442
1442
1443 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1443 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1444
1444
1445 @command('config|showconfig|debugconfig',
1445 @command('config|showconfig|debugconfig',
1446 [('u', 'untrusted', None, _('show untrusted configuration options')),
1446 [('u', 'untrusted', None, _('show untrusted configuration options')),
1447 ('e', 'edit', None, _('edit user config')),
1447 ('e', 'edit', None, _('edit user config')),
1448 ('l', 'local', None, _('edit repository config')),
1448 ('l', 'local', None, _('edit repository config')),
1449 ('g', 'global', None, _('edit global config'))],
1449 ('g', 'global', None, _('edit global config'))],
1450 _('[-u] [NAME]...'))
1450 _('[-u] [NAME]...'))
1451 def config(ui, repo, *values, **opts):
1451 def config(ui, repo, *values, **opts):
1452 """show combined config settings from all hgrc files
1452 """show combined config settings from all hgrc files
1453
1453
1454 With no arguments, print names and values of all config items.
1454 With no arguments, print names and values of all config items.
1455
1455
1456 With one argument of the form section.name, print just the value
1456 With one argument of the form section.name, print just the value
1457 of that config item.
1457 of that config item.
1458
1458
1459 With multiple arguments, print names and values of all config
1459 With multiple arguments, print names and values of all config
1460 items with matching section names.
1460 items with matching section names.
1461
1461
1462 With --edit, start an editor on the user-level config file. With
1462 With --edit, start an editor on the user-level config file. With
1463 --global, edit the system-wide config file. With --local, edit the
1463 --global, edit the system-wide config file. With --local, edit the
1464 repository-level config file.
1464 repository-level config file.
1465
1465
1466 With --debug, the source (filename and line number) is printed
1466 With --debug, the source (filename and line number) is printed
1467 for each config item.
1467 for each config item.
1468
1468
1469 See :hg:`help config` for more information about config files.
1469 See :hg:`help config` for more information about config files.
1470
1470
1471 Returns 0 on success.
1471 Returns 0 on success.
1472
1472
1473 """
1473 """
1474
1474
1475 if opts.get('edit') or opts.get('local') or opts.get('global'):
1475 if opts.get('edit') or opts.get('local') or opts.get('global'):
1476 if opts.get('local') and opts.get('global'):
1476 if opts.get('local') and opts.get('global'):
1477 raise util.Abort(_("can't use --local and --global together"))
1477 raise util.Abort(_("can't use --local and --global together"))
1478
1478
1479 if opts.get('local'):
1479 if opts.get('local'):
1480 if not repo:
1480 if not repo:
1481 raise util.Abort(_("can't use --local outside a repository"))
1481 raise util.Abort(_("can't use --local outside a repository"))
1482 paths = [repo.join('hgrc')]
1482 paths = [repo.join('hgrc')]
1483 elif opts.get('global'):
1483 elif opts.get('global'):
1484 paths = scmutil.systemrcpath()
1484 paths = scmutil.systemrcpath()
1485 else:
1485 else:
1486 paths = scmutil.userrcpath()
1486 paths = scmutil.userrcpath()
1487
1487
1488 for f in paths:
1488 for f in paths:
1489 if os.path.exists(f):
1489 if os.path.exists(f):
1490 break
1490 break
1491 else:
1491 else:
1492 f = paths[0]
1492 f = paths[0]
1493 fp = open(f, "w")
1493 fp = open(f, "w")
1494 fp.write(
1494 fp.write(
1495 '# example config (see "hg help config" for more info)\n'
1495 '# example config (see "hg help config" for more info)\n'
1496 '\n'
1496 '\n'
1497 '[ui]\n'
1497 '[ui]\n'
1498 '# name and email, e.g.\n'
1498 '# name and email, e.g.\n'
1499 '# username = Jane Doe <jdoe@example.com>\n'
1499 '# username = Jane Doe <jdoe@example.com>\n'
1500 'username =\n'
1500 'username =\n'
1501 '\n'
1501 '\n'
1502 '[extensions]\n'
1502 '[extensions]\n'
1503 '# uncomment these lines to enable some popular extensions\n'
1503 '# uncomment these lines to enable some popular extensions\n'
1504 '# (see "hg help extensions" for more info)\n'
1504 '# (see "hg help extensions" for more info)\n'
1505 '# pager =\n'
1505 '# pager =\n'
1506 '# progress =\n'
1506 '# progress =\n'
1507 '# color =\n')
1507 '# color =\n')
1508 fp.close()
1508 fp.close()
1509
1509
1510 editor = ui.geteditor()
1510 editor = ui.geteditor()
1511 util.system("%s \"%s\"" % (editor, f),
1511 util.system("%s \"%s\"" % (editor, f),
1512 onerr=util.Abort, errprefix=_("edit failed"),
1512 onerr=util.Abort, errprefix=_("edit failed"),
1513 out=ui.fout)
1513 out=ui.fout)
1514 return
1514 return
1515
1515
1516 for f in scmutil.rcpath():
1516 for f in scmutil.rcpath():
1517 ui.debug('read config from: %s\n' % f)
1517 ui.debug('read config from: %s\n' % f)
1518 untrusted = bool(opts.get('untrusted'))
1518 untrusted = bool(opts.get('untrusted'))
1519 if values:
1519 if values:
1520 sections = [v for v in values if '.' not in v]
1520 sections = [v for v in values if '.' not in v]
1521 items = [v for v in values if '.' in v]
1521 items = [v for v in values if '.' in v]
1522 if len(items) > 1 or items and sections:
1522 if len(items) > 1 or items and sections:
1523 raise util.Abort(_('only one config item permitted'))
1523 raise util.Abort(_('only one config item permitted'))
1524 for section, name, value in ui.walkconfig(untrusted=untrusted):
1524 for section, name, value in ui.walkconfig(untrusted=untrusted):
1525 value = str(value).replace('\n', '\\n')
1525 value = str(value).replace('\n', '\\n')
1526 sectname = section + '.' + name
1526 sectname = section + '.' + name
1527 if values:
1527 if values:
1528 for v in values:
1528 for v in values:
1529 if v == section:
1529 if v == section:
1530 ui.debug('%s: ' %
1530 ui.debug('%s: ' %
1531 ui.configsource(section, name, untrusted))
1531 ui.configsource(section, name, untrusted))
1532 ui.write('%s=%s\n' % (sectname, value))
1532 ui.write('%s=%s\n' % (sectname, value))
1533 elif v == sectname:
1533 elif v == sectname:
1534 ui.debug('%s: ' %
1534 ui.debug('%s: ' %
1535 ui.configsource(section, name, untrusted))
1535 ui.configsource(section, name, untrusted))
1536 ui.write(value, '\n')
1536 ui.write(value, '\n')
1537 else:
1537 else:
1538 ui.debug('%s: ' %
1538 ui.debug('%s: ' %
1539 ui.configsource(section, name, untrusted))
1539 ui.configsource(section, name, untrusted))
1540 ui.write('%s=%s\n' % (sectname, value))
1540 ui.write('%s=%s\n' % (sectname, value))
1541
1541
1542 @command('copy|cp',
1542 @command('copy|cp',
1543 [('A', 'after', None, _('record a copy that has already occurred')),
1543 [('A', 'after', None, _('record a copy that has already occurred')),
1544 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1544 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1545 ] + walkopts + dryrunopts,
1545 ] + walkopts + dryrunopts,
1546 _('[OPTION]... [SOURCE]... DEST'))
1546 _('[OPTION]... [SOURCE]... DEST'))
1547 def copy(ui, repo, *pats, **opts):
1547 def copy(ui, repo, *pats, **opts):
1548 """mark files as copied for the next commit
1548 """mark files as copied for the next commit
1549
1549
1550 Mark dest as having copies of source files. If dest is a
1550 Mark dest as having copies of source files. If dest is a
1551 directory, copies are put in that directory. If dest is a file,
1551 directory, copies are put in that directory. If dest is a file,
1552 the source must be a single file.
1552 the source must be a single file.
1553
1553
1554 By default, this command copies the contents of files as they
1554 By default, this command copies the contents of files as they
1555 exist in the working directory. If invoked with -A/--after, the
1555 exist in the working directory. If invoked with -A/--after, the
1556 operation is recorded, but no copying is performed.
1556 operation is recorded, but no copying is performed.
1557
1557
1558 This command takes effect with the next commit. To undo a copy
1558 This command takes effect with the next commit. To undo a copy
1559 before that, see :hg:`revert`.
1559 before that, see :hg:`revert`.
1560
1560
1561 Returns 0 on success, 1 if errors are encountered.
1561 Returns 0 on success, 1 if errors are encountered.
1562 """
1562 """
1563 wlock = repo.wlock(False)
1563 wlock = repo.wlock(False)
1564 try:
1564 try:
1565 return cmdutil.copy(ui, repo, pats, opts)
1565 return cmdutil.copy(ui, repo, pats, opts)
1566 finally:
1566 finally:
1567 wlock.release()
1567 wlock.release()
1568
1568
1569 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1569 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1570 def debugancestor(ui, repo, *args):
1570 def debugancestor(ui, repo, *args):
1571 """find the ancestor revision of two revisions in a given index"""
1571 """find the ancestor revision of two revisions in a given index"""
1572 if len(args) == 3:
1572 if len(args) == 3:
1573 index, rev1, rev2 = args
1573 index, rev1, rev2 = args
1574 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1574 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1575 lookup = r.lookup
1575 lookup = r.lookup
1576 elif len(args) == 2:
1576 elif len(args) == 2:
1577 if not repo:
1577 if not repo:
1578 raise util.Abort(_("there is no Mercurial repository here "
1578 raise util.Abort(_("there is no Mercurial repository here "
1579 "(.hg not found)"))
1579 "(.hg not found)"))
1580 rev1, rev2 = args
1580 rev1, rev2 = args
1581 r = repo.changelog
1581 r = repo.changelog
1582 lookup = repo.lookup
1582 lookup = repo.lookup
1583 else:
1583 else:
1584 raise util.Abort(_('either two or three arguments required'))
1584 raise util.Abort(_('either two or three arguments required'))
1585 a = r.ancestor(lookup(rev1), lookup(rev2))
1585 a = r.ancestor(lookup(rev1), lookup(rev2))
1586 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1586 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1587
1587
1588 @command('debugbuilddag',
1588 @command('debugbuilddag',
1589 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1589 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1590 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1590 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1591 ('n', 'new-file', None, _('add new file at each rev'))],
1591 ('n', 'new-file', None, _('add new file at each rev'))],
1592 _('[OPTION]... [TEXT]'))
1592 _('[OPTION]... [TEXT]'))
1593 def debugbuilddag(ui, repo, text=None,
1593 def debugbuilddag(ui, repo, text=None,
1594 mergeable_file=False,
1594 mergeable_file=False,
1595 overwritten_file=False,
1595 overwritten_file=False,
1596 new_file=False):
1596 new_file=False):
1597 """builds a repo with a given DAG from scratch in the current empty repo
1597 """builds a repo with a given DAG from scratch in the current empty repo
1598
1598
1599 The description of the DAG is read from stdin if not given on the
1599 The description of the DAG is read from stdin if not given on the
1600 command line.
1600 command line.
1601
1601
1602 Elements:
1602 Elements:
1603
1603
1604 - "+n" is a linear run of n nodes based on the current default parent
1604 - "+n" is a linear run of n nodes based on the current default parent
1605 - "." is a single node based on the current default parent
1605 - "." is a single node based on the current default parent
1606 - "$" resets the default parent to null (implied at the start);
1606 - "$" resets the default parent to null (implied at the start);
1607 otherwise the default parent is always the last node created
1607 otherwise the default parent is always the last node created
1608 - "<p" sets the default parent to the backref p
1608 - "<p" sets the default parent to the backref p
1609 - "*p" is a fork at parent p, which is a backref
1609 - "*p" is a fork at parent p, which is a backref
1610 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1610 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1611 - "/p2" is a merge of the preceding node and p2
1611 - "/p2" is a merge of the preceding node and p2
1612 - ":tag" defines a local tag for the preceding node
1612 - ":tag" defines a local tag for the preceding node
1613 - "@branch" sets the named branch for subsequent nodes
1613 - "@branch" sets the named branch for subsequent nodes
1614 - "#...\\n" is a comment up to the end of the line
1614 - "#...\\n" is a comment up to the end of the line
1615
1615
1616 Whitespace between the above elements is ignored.
1616 Whitespace between the above elements is ignored.
1617
1617
1618 A backref is either
1618 A backref is either
1619
1619
1620 - a number n, which references the node curr-n, where curr is the current
1620 - a number n, which references the node curr-n, where curr is the current
1621 node, or
1621 node, or
1622 - the name of a local tag you placed earlier using ":tag", or
1622 - the name of a local tag you placed earlier using ":tag", or
1623 - empty to denote the default parent.
1623 - empty to denote the default parent.
1624
1624
1625 All string valued-elements are either strictly alphanumeric, or must
1625 All string valued-elements are either strictly alphanumeric, or must
1626 be enclosed in double quotes ("..."), with "\\" as escape character.
1626 be enclosed in double quotes ("..."), with "\\" as escape character.
1627 """
1627 """
1628
1628
1629 if text is None:
1629 if text is None:
1630 ui.status(_("reading DAG from stdin\n"))
1630 ui.status(_("reading DAG from stdin\n"))
1631 text = ui.fin.read()
1631 text = ui.fin.read()
1632
1632
1633 cl = repo.changelog
1633 cl = repo.changelog
1634 if len(cl) > 0:
1634 if len(cl) > 0:
1635 raise util.Abort(_('repository is not empty'))
1635 raise util.Abort(_('repository is not empty'))
1636
1636
1637 # determine number of revs in DAG
1637 # determine number of revs in DAG
1638 total = 0
1638 total = 0
1639 for type, data in dagparser.parsedag(text):
1639 for type, data in dagparser.parsedag(text):
1640 if type == 'n':
1640 if type == 'n':
1641 total += 1
1641 total += 1
1642
1642
1643 if mergeable_file:
1643 if mergeable_file:
1644 linesperrev = 2
1644 linesperrev = 2
1645 # make a file with k lines per rev
1645 # make a file with k lines per rev
1646 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1646 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1647 initialmergedlines.append("")
1647 initialmergedlines.append("")
1648
1648
1649 tags = []
1649 tags = []
1650
1650
1651 lock = tr = None
1651 lock = tr = None
1652 try:
1652 try:
1653 lock = repo.lock()
1653 lock = repo.lock()
1654 tr = repo.transaction("builddag")
1654 tr = repo.transaction("builddag")
1655
1655
1656 at = -1
1656 at = -1
1657 atbranch = 'default'
1657 atbranch = 'default'
1658 nodeids = []
1658 nodeids = []
1659 id = 0
1659 id = 0
1660 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1660 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1661 for type, data in dagparser.parsedag(text):
1661 for type, data in dagparser.parsedag(text):
1662 if type == 'n':
1662 if type == 'n':
1663 ui.note(('node %s\n' % str(data)))
1663 ui.note(('node %s\n' % str(data)))
1664 id, ps = data
1664 id, ps = data
1665
1665
1666 files = []
1666 files = []
1667 fctxs = {}
1667 fctxs = {}
1668
1668
1669 p2 = None
1669 p2 = None
1670 if mergeable_file:
1670 if mergeable_file:
1671 fn = "mf"
1671 fn = "mf"
1672 p1 = repo[ps[0]]
1672 p1 = repo[ps[0]]
1673 if len(ps) > 1:
1673 if len(ps) > 1:
1674 p2 = repo[ps[1]]
1674 p2 = repo[ps[1]]
1675 pa = p1.ancestor(p2)
1675 pa = p1.ancestor(p2)
1676 base, local, other = [x[fn].data() for x in (pa, p1,
1676 base, local, other = [x[fn].data() for x in (pa, p1,
1677 p2)]
1677 p2)]
1678 m3 = simplemerge.Merge3Text(base, local, other)
1678 m3 = simplemerge.Merge3Text(base, local, other)
1679 ml = [l.strip() for l in m3.merge_lines()]
1679 ml = [l.strip() for l in m3.merge_lines()]
1680 ml.append("")
1680 ml.append("")
1681 elif at > 0:
1681 elif at > 0:
1682 ml = p1[fn].data().split("\n")
1682 ml = p1[fn].data().split("\n")
1683 else:
1683 else:
1684 ml = initialmergedlines
1684 ml = initialmergedlines
1685 ml[id * linesperrev] += " r%i" % id
1685 ml[id * linesperrev] += " r%i" % id
1686 mergedtext = "\n".join(ml)
1686 mergedtext = "\n".join(ml)
1687 files.append(fn)
1687 files.append(fn)
1688 fctxs[fn] = context.memfilectx(fn, mergedtext)
1688 fctxs[fn] = context.memfilectx(fn, mergedtext)
1689
1689
1690 if overwritten_file:
1690 if overwritten_file:
1691 fn = "of"
1691 fn = "of"
1692 files.append(fn)
1692 files.append(fn)
1693 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1693 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1694
1694
1695 if new_file:
1695 if new_file:
1696 fn = "nf%i" % id
1696 fn = "nf%i" % id
1697 files.append(fn)
1697 files.append(fn)
1698 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1698 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1699 if len(ps) > 1:
1699 if len(ps) > 1:
1700 if not p2:
1700 if not p2:
1701 p2 = repo[ps[1]]
1701 p2 = repo[ps[1]]
1702 for fn in p2:
1702 for fn in p2:
1703 if fn.startswith("nf"):
1703 if fn.startswith("nf"):
1704 files.append(fn)
1704 files.append(fn)
1705 fctxs[fn] = p2[fn]
1705 fctxs[fn] = p2[fn]
1706
1706
1707 def fctxfn(repo, cx, path):
1707 def fctxfn(repo, cx, path):
1708 return fctxs.get(path)
1708 return fctxs.get(path)
1709
1709
1710 if len(ps) == 0 or ps[0] < 0:
1710 if len(ps) == 0 or ps[0] < 0:
1711 pars = [None, None]
1711 pars = [None, None]
1712 elif len(ps) == 1:
1712 elif len(ps) == 1:
1713 pars = [nodeids[ps[0]], None]
1713 pars = [nodeids[ps[0]], None]
1714 else:
1714 else:
1715 pars = [nodeids[p] for p in ps]
1715 pars = [nodeids[p] for p in ps]
1716 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1716 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1717 date=(id, 0),
1717 date=(id, 0),
1718 user="debugbuilddag",
1718 user="debugbuilddag",
1719 extra={'branch': atbranch})
1719 extra={'branch': atbranch})
1720 nodeid = repo.commitctx(cx)
1720 nodeid = repo.commitctx(cx)
1721 nodeids.append(nodeid)
1721 nodeids.append(nodeid)
1722 at = id
1722 at = id
1723 elif type == 'l':
1723 elif type == 'l':
1724 id, name = data
1724 id, name = data
1725 ui.note(('tag %s\n' % name))
1725 ui.note(('tag %s\n' % name))
1726 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1726 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1727 elif type == 'a':
1727 elif type == 'a':
1728 ui.note(('branch %s\n' % data))
1728 ui.note(('branch %s\n' % data))
1729 atbranch = data
1729 atbranch = data
1730 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1730 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1731 tr.close()
1731 tr.close()
1732
1732
1733 if tags:
1733 if tags:
1734 repo.opener.write("localtags", "".join(tags))
1734 repo.opener.write("localtags", "".join(tags))
1735 finally:
1735 finally:
1736 ui.progress(_('building'), None)
1736 ui.progress(_('building'), None)
1737 release(tr, lock)
1737 release(tr, lock)
1738
1738
1739 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1739 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1740 def debugbundle(ui, bundlepath, all=None, **opts):
1740 def debugbundle(ui, bundlepath, all=None, **opts):
1741 """lists the contents of a bundle"""
1741 """lists the contents of a bundle"""
1742 f = hg.openpath(ui, bundlepath)
1742 f = hg.openpath(ui, bundlepath)
1743 try:
1743 try:
1744 gen = exchange.readbundle(ui, f, bundlepath)
1744 gen = exchange.readbundle(ui, f, bundlepath)
1745 if all:
1745 if all:
1746 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1746 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1747
1747
1748 def showchunks(named):
1748 def showchunks(named):
1749 ui.write("\n%s\n" % named)
1749 ui.write("\n%s\n" % named)
1750 chain = None
1750 chain = None
1751 while True:
1751 while True:
1752 chunkdata = gen.deltachunk(chain)
1752 chunkdata = gen.deltachunk(chain)
1753 if not chunkdata:
1753 if not chunkdata:
1754 break
1754 break
1755 node = chunkdata['node']
1755 node = chunkdata['node']
1756 p1 = chunkdata['p1']
1756 p1 = chunkdata['p1']
1757 p2 = chunkdata['p2']
1757 p2 = chunkdata['p2']
1758 cs = chunkdata['cs']
1758 cs = chunkdata['cs']
1759 deltabase = chunkdata['deltabase']
1759 deltabase = chunkdata['deltabase']
1760 delta = chunkdata['delta']
1760 delta = chunkdata['delta']
1761 ui.write("%s %s %s %s %s %s\n" %
1761 ui.write("%s %s %s %s %s %s\n" %
1762 (hex(node), hex(p1), hex(p2),
1762 (hex(node), hex(p1), hex(p2),
1763 hex(cs), hex(deltabase), len(delta)))
1763 hex(cs), hex(deltabase), len(delta)))
1764 chain = node
1764 chain = node
1765
1765
1766 chunkdata = gen.changelogheader()
1766 chunkdata = gen.changelogheader()
1767 showchunks("changelog")
1767 showchunks("changelog")
1768 chunkdata = gen.manifestheader()
1768 chunkdata = gen.manifestheader()
1769 showchunks("manifest")
1769 showchunks("manifest")
1770 while True:
1770 while True:
1771 chunkdata = gen.filelogheader()
1771 chunkdata = gen.filelogheader()
1772 if not chunkdata:
1772 if not chunkdata:
1773 break
1773 break
1774 fname = chunkdata['filename']
1774 fname = chunkdata['filename']
1775 showchunks(fname)
1775 showchunks(fname)
1776 else:
1776 else:
1777 chunkdata = gen.changelogheader()
1777 chunkdata = gen.changelogheader()
1778 chain = None
1778 chain = None
1779 while True:
1779 while True:
1780 chunkdata = gen.deltachunk(chain)
1780 chunkdata = gen.deltachunk(chain)
1781 if not chunkdata:
1781 if not chunkdata:
1782 break
1782 break
1783 node = chunkdata['node']
1783 node = chunkdata['node']
1784 ui.write("%s\n" % hex(node))
1784 ui.write("%s\n" % hex(node))
1785 chain = node
1785 chain = node
1786 finally:
1786 finally:
1787 f.close()
1787 f.close()
1788
1788
1789 @command('debugcheckstate', [], '')
1789 @command('debugcheckstate', [], '')
1790 def debugcheckstate(ui, repo):
1790 def debugcheckstate(ui, repo):
1791 """validate the correctness of the current dirstate"""
1791 """validate the correctness of the current dirstate"""
1792 parent1, parent2 = repo.dirstate.parents()
1792 parent1, parent2 = repo.dirstate.parents()
1793 m1 = repo[parent1].manifest()
1793 m1 = repo[parent1].manifest()
1794 m2 = repo[parent2].manifest()
1794 m2 = repo[parent2].manifest()
1795 errors = 0
1795 errors = 0
1796 for f in repo.dirstate:
1796 for f in repo.dirstate:
1797 state = repo.dirstate[f]
1797 state = repo.dirstate[f]
1798 if state in "nr" and f not in m1:
1798 if state in "nr" and f not in m1:
1799 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1799 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1800 errors += 1
1800 errors += 1
1801 if state in "a" and f in m1:
1801 if state in "a" and f in m1:
1802 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1802 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1803 errors += 1
1803 errors += 1
1804 if state in "m" and f not in m1 and f not in m2:
1804 if state in "m" and f not in m1 and f not in m2:
1805 ui.warn(_("%s in state %s, but not in either manifest\n") %
1805 ui.warn(_("%s in state %s, but not in either manifest\n") %
1806 (f, state))
1806 (f, state))
1807 errors += 1
1807 errors += 1
1808 for f in m1:
1808 for f in m1:
1809 state = repo.dirstate[f]
1809 state = repo.dirstate[f]
1810 if state not in "nrm":
1810 if state not in "nrm":
1811 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1811 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1812 errors += 1
1812 errors += 1
1813 if errors:
1813 if errors:
1814 error = _(".hg/dirstate inconsistent with current parent's manifest")
1814 error = _(".hg/dirstate inconsistent with current parent's manifest")
1815 raise util.Abort(error)
1815 raise util.Abort(error)
1816
1816
1817 @command('debugcommands', [], _('[COMMAND]'))
1817 @command('debugcommands', [], _('[COMMAND]'))
1818 def debugcommands(ui, cmd='', *args):
1818 def debugcommands(ui, cmd='', *args):
1819 """list all available commands and options"""
1819 """list all available commands and options"""
1820 for cmd, vals in sorted(table.iteritems()):
1820 for cmd, vals in sorted(table.iteritems()):
1821 cmd = cmd.split('|')[0].strip('^')
1821 cmd = cmd.split('|')[0].strip('^')
1822 opts = ', '.join([i[1] for i in vals[1]])
1822 opts = ', '.join([i[1] for i in vals[1]])
1823 ui.write('%s: %s\n' % (cmd, opts))
1823 ui.write('%s: %s\n' % (cmd, opts))
1824
1824
1825 @command('debugcomplete',
1825 @command('debugcomplete',
1826 [('o', 'options', None, _('show the command options'))],
1826 [('o', 'options', None, _('show the command options'))],
1827 _('[-o] CMD'))
1827 _('[-o] CMD'))
1828 def debugcomplete(ui, cmd='', **opts):
1828 def debugcomplete(ui, cmd='', **opts):
1829 """returns the completion list associated with the given command"""
1829 """returns the completion list associated with the given command"""
1830
1830
1831 if opts.get('options'):
1831 if opts.get('options'):
1832 options = []
1832 options = []
1833 otables = [globalopts]
1833 otables = [globalopts]
1834 if cmd:
1834 if cmd:
1835 aliases, entry = cmdutil.findcmd(cmd, table, False)
1835 aliases, entry = cmdutil.findcmd(cmd, table, False)
1836 otables.append(entry[1])
1836 otables.append(entry[1])
1837 for t in otables:
1837 for t in otables:
1838 for o in t:
1838 for o in t:
1839 if "(DEPRECATED)" in o[3]:
1839 if "(DEPRECATED)" in o[3]:
1840 continue
1840 continue
1841 if o[0]:
1841 if o[0]:
1842 options.append('-%s' % o[0])
1842 options.append('-%s' % o[0])
1843 options.append('--%s' % o[1])
1843 options.append('--%s' % o[1])
1844 ui.write("%s\n" % "\n".join(options))
1844 ui.write("%s\n" % "\n".join(options))
1845 return
1845 return
1846
1846
1847 cmdlist = cmdutil.findpossible(cmd, table)
1847 cmdlist = cmdutil.findpossible(cmd, table)
1848 if ui.verbose:
1848 if ui.verbose:
1849 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1849 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1850 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1850 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1851
1851
1852 @command('debugdag',
1852 @command('debugdag',
1853 [('t', 'tags', None, _('use tags as labels')),
1853 [('t', 'tags', None, _('use tags as labels')),
1854 ('b', 'branches', None, _('annotate with branch names')),
1854 ('b', 'branches', None, _('annotate with branch names')),
1855 ('', 'dots', None, _('use dots for runs')),
1855 ('', 'dots', None, _('use dots for runs')),
1856 ('s', 'spaces', None, _('separate elements by spaces'))],
1856 ('s', 'spaces', None, _('separate elements by spaces'))],
1857 _('[OPTION]... [FILE [REV]...]'))
1857 _('[OPTION]... [FILE [REV]...]'))
1858 def debugdag(ui, repo, file_=None, *revs, **opts):
1858 def debugdag(ui, repo, file_=None, *revs, **opts):
1859 """format the changelog or an index DAG as a concise textual description
1859 """format the changelog or an index DAG as a concise textual description
1860
1860
1861 If you pass a revlog index, the revlog's DAG is emitted. If you list
1861 If you pass a revlog index, the revlog's DAG is emitted. If you list
1862 revision numbers, they get labeled in the output as rN.
1862 revision numbers, they get labeled in the output as rN.
1863
1863
1864 Otherwise, the changelog DAG of the current repo is emitted.
1864 Otherwise, the changelog DAG of the current repo is emitted.
1865 """
1865 """
1866 spaces = opts.get('spaces')
1866 spaces = opts.get('spaces')
1867 dots = opts.get('dots')
1867 dots = opts.get('dots')
1868 if file_:
1868 if file_:
1869 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1869 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1870 revs = set((int(r) for r in revs))
1870 revs = set((int(r) for r in revs))
1871 def events():
1871 def events():
1872 for r in rlog:
1872 for r in rlog:
1873 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1873 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1874 if p != -1)))
1874 if p != -1)))
1875 if r in revs:
1875 if r in revs:
1876 yield 'l', (r, "r%i" % r)
1876 yield 'l', (r, "r%i" % r)
1877 elif repo:
1877 elif repo:
1878 cl = repo.changelog
1878 cl = repo.changelog
1879 tags = opts.get('tags')
1879 tags = opts.get('tags')
1880 branches = opts.get('branches')
1880 branches = opts.get('branches')
1881 if tags:
1881 if tags:
1882 labels = {}
1882 labels = {}
1883 for l, n in repo.tags().items():
1883 for l, n in repo.tags().items():
1884 labels.setdefault(cl.rev(n), []).append(l)
1884 labels.setdefault(cl.rev(n), []).append(l)
1885 def events():
1885 def events():
1886 b = "default"
1886 b = "default"
1887 for r in cl:
1887 for r in cl:
1888 if branches:
1888 if branches:
1889 newb = cl.read(cl.node(r))[5]['branch']
1889 newb = cl.read(cl.node(r))[5]['branch']
1890 if newb != b:
1890 if newb != b:
1891 yield 'a', newb
1891 yield 'a', newb
1892 b = newb
1892 b = newb
1893 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1893 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1894 if p != -1)))
1894 if p != -1)))
1895 if tags:
1895 if tags:
1896 ls = labels.get(r)
1896 ls = labels.get(r)
1897 if ls:
1897 if ls:
1898 for l in ls:
1898 for l in ls:
1899 yield 'l', (r, l)
1899 yield 'l', (r, l)
1900 else:
1900 else:
1901 raise util.Abort(_('need repo for changelog dag'))
1901 raise util.Abort(_('need repo for changelog dag'))
1902
1902
1903 for line in dagparser.dagtextlines(events(),
1903 for line in dagparser.dagtextlines(events(),
1904 addspaces=spaces,
1904 addspaces=spaces,
1905 wraplabels=True,
1905 wraplabels=True,
1906 wrapannotations=True,
1906 wrapannotations=True,
1907 wrapnonlinear=dots,
1907 wrapnonlinear=dots,
1908 usedots=dots,
1908 usedots=dots,
1909 maxlinewidth=70):
1909 maxlinewidth=70):
1910 ui.write(line)
1910 ui.write(line)
1911 ui.write("\n")
1911 ui.write("\n")
1912
1912
1913 @command('debugdata',
1913 @command('debugdata',
1914 [('c', 'changelog', False, _('open changelog')),
1914 [('c', 'changelog', False, _('open changelog')),
1915 ('m', 'manifest', False, _('open manifest'))],
1915 ('m', 'manifest', False, _('open manifest'))],
1916 _('-c|-m|FILE REV'))
1916 _('-c|-m|FILE REV'))
1917 def debugdata(ui, repo, file_, rev=None, **opts):
1917 def debugdata(ui, repo, file_, rev=None, **opts):
1918 """dump the contents of a data file revision"""
1918 """dump the contents of a data file revision"""
1919 if opts.get('changelog') or opts.get('manifest'):
1919 if opts.get('changelog') or opts.get('manifest'):
1920 file_, rev = None, file_
1920 file_, rev = None, file_
1921 elif rev is None:
1921 elif rev is None:
1922 raise error.CommandError('debugdata', _('invalid arguments'))
1922 raise error.CommandError('debugdata', _('invalid arguments'))
1923 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1923 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1924 try:
1924 try:
1925 ui.write(r.revision(r.lookup(rev)))
1925 ui.write(r.revision(r.lookup(rev)))
1926 except KeyError:
1926 except KeyError:
1927 raise util.Abort(_('invalid revision identifier %s') % rev)
1927 raise util.Abort(_('invalid revision identifier %s') % rev)
1928
1928
1929 @command('debugdate',
1929 @command('debugdate',
1930 [('e', 'extended', None, _('try extended date formats'))],
1930 [('e', 'extended', None, _('try extended date formats'))],
1931 _('[-e] DATE [RANGE]'))
1931 _('[-e] DATE [RANGE]'))
1932 def debugdate(ui, date, range=None, **opts):
1932 def debugdate(ui, date, range=None, **opts):
1933 """parse and display a date"""
1933 """parse and display a date"""
1934 if opts["extended"]:
1934 if opts["extended"]:
1935 d = util.parsedate(date, util.extendeddateformats)
1935 d = util.parsedate(date, util.extendeddateformats)
1936 else:
1936 else:
1937 d = util.parsedate(date)
1937 d = util.parsedate(date)
1938 ui.write(("internal: %s %s\n") % d)
1938 ui.write(("internal: %s %s\n") % d)
1939 ui.write(("standard: %s\n") % util.datestr(d))
1939 ui.write(("standard: %s\n") % util.datestr(d))
1940 if range:
1940 if range:
1941 m = util.matchdate(range)
1941 m = util.matchdate(range)
1942 ui.write(("match: %s\n") % m(d[0]))
1942 ui.write(("match: %s\n") % m(d[0]))
1943
1943
1944 @command('debugdiscovery',
1944 @command('debugdiscovery',
1945 [('', 'old', None, _('use old-style discovery')),
1945 [('', 'old', None, _('use old-style discovery')),
1946 ('', 'nonheads', None,
1946 ('', 'nonheads', None,
1947 _('use old-style discovery with non-heads included')),
1947 _('use old-style discovery with non-heads included')),
1948 ] + remoteopts,
1948 ] + remoteopts,
1949 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1949 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1950 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1950 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1951 """runs the changeset discovery protocol in isolation"""
1951 """runs the changeset discovery protocol in isolation"""
1952 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1952 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1953 opts.get('branch'))
1953 opts.get('branch'))
1954 remote = hg.peer(repo, opts, remoteurl)
1954 remote = hg.peer(repo, opts, remoteurl)
1955 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1955 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1956
1956
1957 # make sure tests are repeatable
1957 # make sure tests are repeatable
1958 random.seed(12323)
1958 random.seed(12323)
1959
1959
1960 def doit(localheads, remoteheads, remote=remote):
1960 def doit(localheads, remoteheads, remote=remote):
1961 if opts.get('old'):
1961 if opts.get('old'):
1962 if localheads:
1962 if localheads:
1963 raise util.Abort('cannot use localheads with old style '
1963 raise util.Abort('cannot use localheads with old style '
1964 'discovery')
1964 'discovery')
1965 if not util.safehasattr(remote, 'branches'):
1965 if not util.safehasattr(remote, 'branches'):
1966 # enable in-client legacy support
1966 # enable in-client legacy support
1967 remote = localrepo.locallegacypeer(remote.local())
1967 remote = localrepo.locallegacypeer(remote.local())
1968 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1968 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1969 force=True)
1969 force=True)
1970 common = set(common)
1970 common = set(common)
1971 if not opts.get('nonheads'):
1971 if not opts.get('nonheads'):
1972 ui.write(("unpruned common: %s\n") %
1972 ui.write(("unpruned common: %s\n") %
1973 " ".join(sorted(short(n) for n in common)))
1973 " ".join(sorted(short(n) for n in common)))
1974 dag = dagutil.revlogdag(repo.changelog)
1974 dag = dagutil.revlogdag(repo.changelog)
1975 all = dag.ancestorset(dag.internalizeall(common))
1975 all = dag.ancestorset(dag.internalizeall(common))
1976 common = dag.externalizeall(dag.headsetofconnecteds(all))
1976 common = dag.externalizeall(dag.headsetofconnecteds(all))
1977 else:
1977 else:
1978 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1978 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1979 common = set(common)
1979 common = set(common)
1980 rheads = set(hds)
1980 rheads = set(hds)
1981 lheads = set(repo.heads())
1981 lheads = set(repo.heads())
1982 ui.write(("common heads: %s\n") %
1982 ui.write(("common heads: %s\n") %
1983 " ".join(sorted(short(n) for n in common)))
1983 " ".join(sorted(short(n) for n in common)))
1984 if lheads <= common:
1984 if lheads <= common:
1985 ui.write(("local is subset\n"))
1985 ui.write(("local is subset\n"))
1986 elif rheads <= common:
1986 elif rheads <= common:
1987 ui.write(("remote is subset\n"))
1987 ui.write(("remote is subset\n"))
1988
1988
1989 serverlogs = opts.get('serverlog')
1989 serverlogs = opts.get('serverlog')
1990 if serverlogs:
1990 if serverlogs:
1991 for filename in serverlogs:
1991 for filename in serverlogs:
1992 logfile = open(filename, 'r')
1992 logfile = open(filename, 'r')
1993 try:
1993 try:
1994 line = logfile.readline()
1994 line = logfile.readline()
1995 while line:
1995 while line:
1996 parts = line.strip().split(';')
1996 parts = line.strip().split(';')
1997 op = parts[1]
1997 op = parts[1]
1998 if op == 'cg':
1998 if op == 'cg':
1999 pass
1999 pass
2000 elif op == 'cgss':
2000 elif op == 'cgss':
2001 doit(parts[2].split(' '), parts[3].split(' '))
2001 doit(parts[2].split(' '), parts[3].split(' '))
2002 elif op == 'unb':
2002 elif op == 'unb':
2003 doit(parts[3].split(' '), parts[2].split(' '))
2003 doit(parts[3].split(' '), parts[2].split(' '))
2004 line = logfile.readline()
2004 line = logfile.readline()
2005 finally:
2005 finally:
2006 logfile.close()
2006 logfile.close()
2007
2007
2008 else:
2008 else:
2009 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2009 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2010 opts.get('remote_head'))
2010 opts.get('remote_head'))
2011 localrevs = opts.get('local_head')
2011 localrevs = opts.get('local_head')
2012 doit(localrevs, remoterevs)
2012 doit(localrevs, remoterevs)
2013
2013
2014 @command('debugfileset',
2014 @command('debugfileset',
2015 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2015 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2016 _('[-r REV] FILESPEC'))
2016 _('[-r REV] FILESPEC'))
2017 def debugfileset(ui, repo, expr, **opts):
2017 def debugfileset(ui, repo, expr, **opts):
2018 '''parse and apply a fileset specification'''
2018 '''parse and apply a fileset specification'''
2019 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2019 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2020 if ui.verbose:
2020 if ui.verbose:
2021 tree = fileset.parse(expr)[0]
2021 tree = fileset.parse(expr)[0]
2022 ui.note(tree, "\n")
2022 ui.note(tree, "\n")
2023
2023
2024 for f in ctx.getfileset(expr):
2024 for f in ctx.getfileset(expr):
2025 ui.write("%s\n" % f)
2025 ui.write("%s\n" % f)
2026
2026
2027 @command('debugfsinfo', [], _('[PATH]'))
2027 @command('debugfsinfo', [], _('[PATH]'))
2028 def debugfsinfo(ui, path="."):
2028 def debugfsinfo(ui, path="."):
2029 """show information detected about current filesystem"""
2029 """show information detected about current filesystem"""
2030 util.writefile('.debugfsinfo', '')
2030 util.writefile('.debugfsinfo', '')
2031 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2031 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2032 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2032 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2033 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2033 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2034 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2034 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2035 and 'yes' or 'no'))
2035 and 'yes' or 'no'))
2036 os.unlink('.debugfsinfo')
2036 os.unlink('.debugfsinfo')
2037
2037
2038 @command('debuggetbundle',
2038 @command('debuggetbundle',
2039 [('H', 'head', [], _('id of head node'), _('ID')),
2039 [('H', 'head', [], _('id of head node'), _('ID')),
2040 ('C', 'common', [], _('id of common node'), _('ID')),
2040 ('C', 'common', [], _('id of common node'), _('ID')),
2041 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2041 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2042 _('REPO FILE [-H|-C ID]...'))
2042 _('REPO FILE [-H|-C ID]...'))
2043 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2043 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2044 """retrieves a bundle from a repo
2044 """retrieves a bundle from a repo
2045
2045
2046 Every ID must be a full-length hex node id string. Saves the bundle to the
2046 Every ID must be a full-length hex node id string. Saves the bundle to the
2047 given file.
2047 given file.
2048 """
2048 """
2049 repo = hg.peer(ui, opts, repopath)
2049 repo = hg.peer(ui, opts, repopath)
2050 if not repo.capable('getbundle'):
2050 if not repo.capable('getbundle'):
2051 raise util.Abort("getbundle() not supported by target repository")
2051 raise util.Abort("getbundle() not supported by target repository")
2052 args = {}
2052 args = {}
2053 if common:
2053 if common:
2054 args['common'] = [bin(s) for s in common]
2054 args['common'] = [bin(s) for s in common]
2055 if head:
2055 if head:
2056 args['heads'] = [bin(s) for s in head]
2056 args['heads'] = [bin(s) for s in head]
2057 # TODO: get desired bundlecaps from command line.
2057 # TODO: get desired bundlecaps from command line.
2058 args['bundlecaps'] = None
2058 args['bundlecaps'] = None
2059 bundle = repo.getbundle('debug', **args)
2059 bundle = repo.getbundle('debug', **args)
2060
2060
2061 bundletype = opts.get('type', 'bzip2').lower()
2061 bundletype = opts.get('type', 'bzip2').lower()
2062 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2062 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2063 bundletype = btypes.get(bundletype)
2063 bundletype = btypes.get(bundletype)
2064 if bundletype not in changegroup.bundletypes:
2064 if bundletype not in changegroup.bundletypes:
2065 raise util.Abort(_('unknown bundle type specified with --type'))
2065 raise util.Abort(_('unknown bundle type specified with --type'))
2066 changegroup.writebundle(bundle, bundlepath, bundletype)
2066 changegroup.writebundle(bundle, bundlepath, bundletype)
2067
2067
2068 @command('debugignore', [], '')
2068 @command('debugignore', [], '')
2069 def debugignore(ui, repo, *values, **opts):
2069 def debugignore(ui, repo, *values, **opts):
2070 """display the combined ignore pattern"""
2070 """display the combined ignore pattern"""
2071 ignore = repo.dirstate._ignore
2071 ignore = repo.dirstate._ignore
2072 includepat = getattr(ignore, 'includepat', None)
2072 includepat = getattr(ignore, 'includepat', None)
2073 if includepat is not None:
2073 if includepat is not None:
2074 ui.write("%s\n" % includepat)
2074 ui.write("%s\n" % includepat)
2075 else:
2075 else:
2076 raise util.Abort(_("no ignore patterns found"))
2076 raise util.Abort(_("no ignore patterns found"))
2077
2077
2078 @command('debugindex',
2078 @command('debugindex',
2079 [('c', 'changelog', False, _('open changelog')),
2079 [('c', 'changelog', False, _('open changelog')),
2080 ('m', 'manifest', False, _('open manifest')),
2080 ('m', 'manifest', False, _('open manifest')),
2081 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2081 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2082 _('[-f FORMAT] -c|-m|FILE'))
2082 _('[-f FORMAT] -c|-m|FILE'))
2083 def debugindex(ui, repo, file_=None, **opts):
2083 def debugindex(ui, repo, file_=None, **opts):
2084 """dump the contents of an index file"""
2084 """dump the contents of an index file"""
2085 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2085 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2086 format = opts.get('format', 0)
2086 format = opts.get('format', 0)
2087 if format not in (0, 1):
2087 if format not in (0, 1):
2088 raise util.Abort(_("unknown format %d") % format)
2088 raise util.Abort(_("unknown format %d") % format)
2089
2089
2090 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2090 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2091 if generaldelta:
2091 if generaldelta:
2092 basehdr = ' delta'
2092 basehdr = ' delta'
2093 else:
2093 else:
2094 basehdr = ' base'
2094 basehdr = ' base'
2095
2095
2096 if format == 0:
2096 if format == 0:
2097 ui.write(" rev offset length " + basehdr + " linkrev"
2097 ui.write(" rev offset length " + basehdr + " linkrev"
2098 " nodeid p1 p2\n")
2098 " nodeid p1 p2\n")
2099 elif format == 1:
2099 elif format == 1:
2100 ui.write(" rev flag offset length"
2100 ui.write(" rev flag offset length"
2101 " size " + basehdr + " link p1 p2"
2101 " size " + basehdr + " link p1 p2"
2102 " nodeid\n")
2102 " nodeid\n")
2103
2103
2104 for i in r:
2104 for i in r:
2105 node = r.node(i)
2105 node = r.node(i)
2106 if generaldelta:
2106 if generaldelta:
2107 base = r.deltaparent(i)
2107 base = r.deltaparent(i)
2108 else:
2108 else:
2109 base = r.chainbase(i)
2109 base = r.chainbase(i)
2110 if format == 0:
2110 if format == 0:
2111 try:
2111 try:
2112 pp = r.parents(node)
2112 pp = r.parents(node)
2113 except Exception:
2113 except Exception:
2114 pp = [nullid, nullid]
2114 pp = [nullid, nullid]
2115 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2115 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2116 i, r.start(i), r.length(i), base, r.linkrev(i),
2116 i, r.start(i), r.length(i), base, r.linkrev(i),
2117 short(node), short(pp[0]), short(pp[1])))
2117 short(node), short(pp[0]), short(pp[1])))
2118 elif format == 1:
2118 elif format == 1:
2119 pr = r.parentrevs(i)
2119 pr = r.parentrevs(i)
2120 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2120 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2121 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2121 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2122 base, r.linkrev(i), pr[0], pr[1], short(node)))
2122 base, r.linkrev(i), pr[0], pr[1], short(node)))
2123
2123
2124 @command('debugindexdot', [], _('FILE'))
2124 @command('debugindexdot', [], _('FILE'))
2125 def debugindexdot(ui, repo, file_):
2125 def debugindexdot(ui, repo, file_):
2126 """dump an index DAG as a graphviz dot file"""
2126 """dump an index DAG as a graphviz dot file"""
2127 r = None
2127 r = None
2128 if repo:
2128 if repo:
2129 filelog = repo.file(file_)
2129 filelog = repo.file(file_)
2130 if len(filelog):
2130 if len(filelog):
2131 r = filelog
2131 r = filelog
2132 if not r:
2132 if not r:
2133 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2133 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2134 ui.write(("digraph G {\n"))
2134 ui.write(("digraph G {\n"))
2135 for i in r:
2135 for i in r:
2136 node = r.node(i)
2136 node = r.node(i)
2137 pp = r.parents(node)
2137 pp = r.parents(node)
2138 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2138 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2139 if pp[1] != nullid:
2139 if pp[1] != nullid:
2140 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2140 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2141 ui.write("}\n")
2141 ui.write("}\n")
2142
2142
2143 @command('debuginstall', [], '')
2143 @command('debuginstall', [], '')
2144 def debuginstall(ui):
2144 def debuginstall(ui):
2145 '''test Mercurial installation
2145 '''test Mercurial installation
2146
2146
2147 Returns 0 on success.
2147 Returns 0 on success.
2148 '''
2148 '''
2149
2149
2150 def writetemp(contents):
2150 def writetemp(contents):
2151 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2151 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2152 f = os.fdopen(fd, "wb")
2152 f = os.fdopen(fd, "wb")
2153 f.write(contents)
2153 f.write(contents)
2154 f.close()
2154 f.close()
2155 return name
2155 return name
2156
2156
2157 problems = 0
2157 problems = 0
2158
2158
2159 # encoding
2159 # encoding
2160 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2160 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2161 try:
2161 try:
2162 encoding.fromlocal("test")
2162 encoding.fromlocal("test")
2163 except util.Abort, inst:
2163 except util.Abort, inst:
2164 ui.write(" %s\n" % inst)
2164 ui.write(" %s\n" % inst)
2165 ui.write(_(" (check that your locale is properly set)\n"))
2165 ui.write(_(" (check that your locale is properly set)\n"))
2166 problems += 1
2166 problems += 1
2167
2167
2168 # Python
2168 # Python
2169 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2169 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2170 ui.status(_("checking Python version (%s)\n")
2170 ui.status(_("checking Python version (%s)\n")
2171 % ("%s.%s.%s" % sys.version_info[:3]))
2171 % ("%s.%s.%s" % sys.version_info[:3]))
2172 ui.status(_("checking Python lib (%s)...\n")
2172 ui.status(_("checking Python lib (%s)...\n")
2173 % os.path.dirname(os.__file__))
2173 % os.path.dirname(os.__file__))
2174
2174
2175 # compiled modules
2175 # compiled modules
2176 ui.status(_("checking installed modules (%s)...\n")
2176 ui.status(_("checking installed modules (%s)...\n")
2177 % os.path.dirname(__file__))
2177 % os.path.dirname(__file__))
2178 try:
2178 try:
2179 import bdiff, mpatch, base85, osutil
2179 import bdiff, mpatch, base85, osutil
2180 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2180 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2181 except Exception, inst:
2181 except Exception, inst:
2182 ui.write(" %s\n" % inst)
2182 ui.write(" %s\n" % inst)
2183 ui.write(_(" One or more extensions could not be found"))
2183 ui.write(_(" One or more extensions could not be found"))
2184 ui.write(_(" (check that you compiled the extensions)\n"))
2184 ui.write(_(" (check that you compiled the extensions)\n"))
2185 problems += 1
2185 problems += 1
2186
2186
2187 # templates
2187 # templates
2188 import templater
2188 import templater
2189 p = templater.templatepath()
2189 p = templater.templatepath()
2190 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2190 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2191 if p:
2191 if p:
2192 m = templater.templatepath("map-cmdline.default")
2192 m = templater.templatepath("map-cmdline.default")
2193 if m:
2193 if m:
2194 # template found, check if it is working
2194 # template found, check if it is working
2195 try:
2195 try:
2196 templater.templater(m)
2196 templater.templater(m)
2197 except Exception, inst:
2197 except Exception, inst:
2198 ui.write(" %s\n" % inst)
2198 ui.write(" %s\n" % inst)
2199 p = None
2199 p = None
2200 else:
2200 else:
2201 ui.write(_(" template 'default' not found\n"))
2201 ui.write(_(" template 'default' not found\n"))
2202 p = None
2202 p = None
2203 else:
2203 else:
2204 ui.write(_(" no template directories found\n"))
2204 ui.write(_(" no template directories found\n"))
2205 if not p:
2205 if not p:
2206 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2206 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2207 problems += 1
2207 problems += 1
2208
2208
2209 # editor
2209 # editor
2210 ui.status(_("checking commit editor...\n"))
2210 ui.status(_("checking commit editor...\n"))
2211 editor = ui.geteditor()
2211 editor = ui.geteditor()
2212 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2212 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2213 if not cmdpath:
2213 if not cmdpath:
2214 if editor == 'vi':
2214 if editor == 'vi':
2215 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2215 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2216 ui.write(_(" (specify a commit editor in your configuration"
2216 ui.write(_(" (specify a commit editor in your configuration"
2217 " file)\n"))
2217 " file)\n"))
2218 else:
2218 else:
2219 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2219 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2220 ui.write(_(" (specify a commit editor in your configuration"
2220 ui.write(_(" (specify a commit editor in your configuration"
2221 " file)\n"))
2221 " file)\n"))
2222 problems += 1
2222 problems += 1
2223
2223
2224 # check username
2224 # check username
2225 ui.status(_("checking username...\n"))
2225 ui.status(_("checking username...\n"))
2226 try:
2226 try:
2227 ui.username()
2227 ui.username()
2228 except util.Abort, e:
2228 except util.Abort, e:
2229 ui.write(" %s\n" % e)
2229 ui.write(" %s\n" % e)
2230 ui.write(_(" (specify a username in your configuration file)\n"))
2230 ui.write(_(" (specify a username in your configuration file)\n"))
2231 problems += 1
2231 problems += 1
2232
2232
2233 if not problems:
2233 if not problems:
2234 ui.status(_("no problems detected\n"))
2234 ui.status(_("no problems detected\n"))
2235 else:
2235 else:
2236 ui.write(_("%s problems detected,"
2236 ui.write(_("%s problems detected,"
2237 " please check your install!\n") % problems)
2237 " please check your install!\n") % problems)
2238
2238
2239 return problems
2239 return problems
2240
2240
2241 @command('debugknown', [], _('REPO ID...'))
2241 @command('debugknown', [], _('REPO ID...'))
2242 def debugknown(ui, repopath, *ids, **opts):
2242 def debugknown(ui, repopath, *ids, **opts):
2243 """test whether node ids are known to a repo
2243 """test whether node ids are known to a repo
2244
2244
2245 Every ID must be a full-length hex node id string. Returns a list of 0s
2245 Every ID must be a full-length hex node id string. Returns a list of 0s
2246 and 1s indicating unknown/known.
2246 and 1s indicating unknown/known.
2247 """
2247 """
2248 repo = hg.peer(ui, opts, repopath)
2248 repo = hg.peer(ui, opts, repopath)
2249 if not repo.capable('known'):
2249 if not repo.capable('known'):
2250 raise util.Abort("known() not supported by target repository")
2250 raise util.Abort("known() not supported by target repository")
2251 flags = repo.known([bin(s) for s in ids])
2251 flags = repo.known([bin(s) for s in ids])
2252 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2252 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2253
2253
2254 @command('debuglabelcomplete', [], _('LABEL...'))
2254 @command('debuglabelcomplete', [], _('LABEL...'))
2255 def debuglabelcomplete(ui, repo, *args):
2255 def debuglabelcomplete(ui, repo, *args):
2256 '''complete "labels" - tags, open branch names, bookmark names'''
2256 '''complete "labels" - tags, open branch names, bookmark names'''
2257
2257
2258 labels = set()
2258 labels = set()
2259 labels.update(t[0] for t in repo.tagslist())
2259 labels.update(t[0] for t in repo.tagslist())
2260 labels.update(repo._bookmarks.keys())
2260 labels.update(repo._bookmarks.keys())
2261 labels.update(tag for (tag, heads, tip, closed)
2261 labels.update(tag for (tag, heads, tip, closed)
2262 in repo.branchmap().iterbranches() if not closed)
2262 in repo.branchmap().iterbranches() if not closed)
2263 completions = set()
2263 completions = set()
2264 if not args:
2264 if not args:
2265 args = ['']
2265 args = ['']
2266 for a in args:
2266 for a in args:
2267 completions.update(l for l in labels if l.startswith(a))
2267 completions.update(l for l in labels if l.startswith(a))
2268 ui.write('\n'.join(sorted(completions)))
2268 ui.write('\n'.join(sorted(completions)))
2269 ui.write('\n')
2269 ui.write('\n')
2270
2270
2271 @command('debugobsolete',
2271 @command('debugobsolete',
2272 [('', 'flags', 0, _('markers flag')),
2272 [('', 'flags', 0, _('markers flag')),
2273 ] + commitopts2,
2273 ] + commitopts2,
2274 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2274 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2275 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2275 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2276 """create arbitrary obsolete marker
2276 """create arbitrary obsolete marker
2277
2277
2278 With no arguments, displays the list of obsolescence markers."""
2278 With no arguments, displays the list of obsolescence markers."""
2279 def parsenodeid(s):
2279 def parsenodeid(s):
2280 try:
2280 try:
2281 # We do not use revsingle/revrange functions here to accept
2281 # We do not use revsingle/revrange functions here to accept
2282 # arbitrary node identifiers, possibly not present in the
2282 # arbitrary node identifiers, possibly not present in the
2283 # local repository.
2283 # local repository.
2284 n = bin(s)
2284 n = bin(s)
2285 if len(n) != len(nullid):
2285 if len(n) != len(nullid):
2286 raise TypeError()
2286 raise TypeError()
2287 return n
2287 return n
2288 except TypeError:
2288 except TypeError:
2289 raise util.Abort('changeset references must be full hexadecimal '
2289 raise util.Abort('changeset references must be full hexadecimal '
2290 'node identifiers')
2290 'node identifiers')
2291
2291
2292 if precursor is not None:
2292 if precursor is not None:
2293 metadata = {}
2293 metadata = {}
2294 if 'date' in opts:
2294 if 'date' in opts:
2295 metadata['date'] = opts['date']
2295 metadata['date'] = opts['date']
2296 metadata['user'] = opts['user'] or ui.username()
2296 metadata['user'] = opts['user'] or ui.username()
2297 succs = tuple(parsenodeid(succ) for succ in successors)
2297 succs = tuple(parsenodeid(succ) for succ in successors)
2298 l = repo.lock()
2298 l = repo.lock()
2299 try:
2299 try:
2300 tr = repo.transaction('debugobsolete')
2300 tr = repo.transaction('debugobsolete')
2301 try:
2301 try:
2302 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2302 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2303 opts['flags'], metadata)
2303 opts['flags'], metadata)
2304 tr.close()
2304 tr.close()
2305 finally:
2305 finally:
2306 tr.release()
2306 tr.release()
2307 finally:
2307 finally:
2308 l.release()
2308 l.release()
2309 else:
2309 else:
2310 for m in obsolete.allmarkers(repo):
2310 for m in obsolete.allmarkers(repo):
2311 cmdutil.showmarker(ui, m)
2311 cmdutil.showmarker(ui, m)
2312
2312
2313 @command('debugpathcomplete',
2313 @command('debugpathcomplete',
2314 [('f', 'full', None, _('complete an entire path')),
2314 [('f', 'full', None, _('complete an entire path')),
2315 ('n', 'normal', None, _('show only normal files')),
2315 ('n', 'normal', None, _('show only normal files')),
2316 ('a', 'added', None, _('show only added files')),
2316 ('a', 'added', None, _('show only added files')),
2317 ('r', 'removed', None, _('show only removed files'))],
2317 ('r', 'removed', None, _('show only removed files'))],
2318 _('FILESPEC...'))
2318 _('FILESPEC...'))
2319 def debugpathcomplete(ui, repo, *specs, **opts):
2319 def debugpathcomplete(ui, repo, *specs, **opts):
2320 '''complete part or all of a tracked path
2320 '''complete part or all of a tracked path
2321
2321
2322 This command supports shells that offer path name completion. It
2322 This command supports shells that offer path name completion. It
2323 currently completes only files already known to the dirstate.
2323 currently completes only files already known to the dirstate.
2324
2324
2325 Completion extends only to the next path segment unless
2325 Completion extends only to the next path segment unless
2326 --full is specified, in which case entire paths are used.'''
2326 --full is specified, in which case entire paths are used.'''
2327
2327
2328 def complete(path, acceptable):
2328 def complete(path, acceptable):
2329 dirstate = repo.dirstate
2329 dirstate = repo.dirstate
2330 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2330 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2331 rootdir = repo.root + os.sep
2331 rootdir = repo.root + os.sep
2332 if spec != repo.root and not spec.startswith(rootdir):
2332 if spec != repo.root and not spec.startswith(rootdir):
2333 return [], []
2333 return [], []
2334 if os.path.isdir(spec):
2334 if os.path.isdir(spec):
2335 spec += '/'
2335 spec += '/'
2336 spec = spec[len(rootdir):]
2336 spec = spec[len(rootdir):]
2337 fixpaths = os.sep != '/'
2337 fixpaths = os.sep != '/'
2338 if fixpaths:
2338 if fixpaths:
2339 spec = spec.replace(os.sep, '/')
2339 spec = spec.replace(os.sep, '/')
2340 speclen = len(spec)
2340 speclen = len(spec)
2341 fullpaths = opts['full']
2341 fullpaths = opts['full']
2342 files, dirs = set(), set()
2342 files, dirs = set(), set()
2343 adddir, addfile = dirs.add, files.add
2343 adddir, addfile = dirs.add, files.add
2344 for f, st in dirstate.iteritems():
2344 for f, st in dirstate.iteritems():
2345 if f.startswith(spec) and st[0] in acceptable:
2345 if f.startswith(spec) and st[0] in acceptable:
2346 if fixpaths:
2346 if fixpaths:
2347 f = f.replace('/', os.sep)
2347 f = f.replace('/', os.sep)
2348 if fullpaths:
2348 if fullpaths:
2349 addfile(f)
2349 addfile(f)
2350 continue
2350 continue
2351 s = f.find(os.sep, speclen)
2351 s = f.find(os.sep, speclen)
2352 if s >= 0:
2352 if s >= 0:
2353 adddir(f[:s])
2353 adddir(f[:s])
2354 else:
2354 else:
2355 addfile(f)
2355 addfile(f)
2356 return files, dirs
2356 return files, dirs
2357
2357
2358 acceptable = ''
2358 acceptable = ''
2359 if opts['normal']:
2359 if opts['normal']:
2360 acceptable += 'nm'
2360 acceptable += 'nm'
2361 if opts['added']:
2361 if opts['added']:
2362 acceptable += 'a'
2362 acceptable += 'a'
2363 if opts['removed']:
2363 if opts['removed']:
2364 acceptable += 'r'
2364 acceptable += 'r'
2365 cwd = repo.getcwd()
2365 cwd = repo.getcwd()
2366 if not specs:
2366 if not specs:
2367 specs = ['.']
2367 specs = ['.']
2368
2368
2369 files, dirs = set(), set()
2369 files, dirs = set(), set()
2370 for spec in specs:
2370 for spec in specs:
2371 f, d = complete(spec, acceptable or 'nmar')
2371 f, d = complete(spec, acceptable or 'nmar')
2372 files.update(f)
2372 files.update(f)
2373 dirs.update(d)
2373 dirs.update(d)
2374 files.update(dirs)
2374 files.update(dirs)
2375 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2375 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2376 ui.write('\n')
2376 ui.write('\n')
2377
2377
2378 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2378 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2379 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2379 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2380 '''access the pushkey key/value protocol
2380 '''access the pushkey key/value protocol
2381
2381
2382 With two args, list the keys in the given namespace.
2382 With two args, list the keys in the given namespace.
2383
2383
2384 With five args, set a key to new if it currently is set to old.
2384 With five args, set a key to new if it currently is set to old.
2385 Reports success or failure.
2385 Reports success or failure.
2386 '''
2386 '''
2387
2387
2388 target = hg.peer(ui, {}, repopath)
2388 target = hg.peer(ui, {}, repopath)
2389 if keyinfo:
2389 if keyinfo:
2390 key, old, new = keyinfo
2390 key, old, new = keyinfo
2391 r = target.pushkey(namespace, key, old, new)
2391 r = target.pushkey(namespace, key, old, new)
2392 ui.status(str(r) + '\n')
2392 ui.status(str(r) + '\n')
2393 return not r
2393 return not r
2394 else:
2394 else:
2395 for k, v in sorted(target.listkeys(namespace).iteritems()):
2395 for k, v in sorted(target.listkeys(namespace).iteritems()):
2396 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2396 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2397 v.encode('string-escape')))
2397 v.encode('string-escape')))
2398
2398
2399 @command('debugpvec', [], _('A B'))
2399 @command('debugpvec', [], _('A B'))
2400 def debugpvec(ui, repo, a, b=None):
2400 def debugpvec(ui, repo, a, b=None):
2401 ca = scmutil.revsingle(repo, a)
2401 ca = scmutil.revsingle(repo, a)
2402 cb = scmutil.revsingle(repo, b)
2402 cb = scmutil.revsingle(repo, b)
2403 pa = pvec.ctxpvec(ca)
2403 pa = pvec.ctxpvec(ca)
2404 pb = pvec.ctxpvec(cb)
2404 pb = pvec.ctxpvec(cb)
2405 if pa == pb:
2405 if pa == pb:
2406 rel = "="
2406 rel = "="
2407 elif pa > pb:
2407 elif pa > pb:
2408 rel = ">"
2408 rel = ">"
2409 elif pa < pb:
2409 elif pa < pb:
2410 rel = "<"
2410 rel = "<"
2411 elif pa | pb:
2411 elif pa | pb:
2412 rel = "|"
2412 rel = "|"
2413 ui.write(_("a: %s\n") % pa)
2413 ui.write(_("a: %s\n") % pa)
2414 ui.write(_("b: %s\n") % pb)
2414 ui.write(_("b: %s\n") % pb)
2415 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2415 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2416 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2416 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2417 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2417 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2418 pa.distance(pb), rel))
2418 pa.distance(pb), rel))
2419
2419
2420 @command('debugrebuilddirstate|debugrebuildstate',
2420 @command('debugrebuilddirstate|debugrebuildstate',
2421 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2421 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2422 _('[-r REV]'))
2422 _('[-r REV]'))
2423 def debugrebuilddirstate(ui, repo, rev):
2423 def debugrebuilddirstate(ui, repo, rev):
2424 """rebuild the dirstate as it would look like for the given revision
2424 """rebuild the dirstate as it would look like for the given revision
2425
2425
2426 If no revision is specified the first current parent will be used.
2426 If no revision is specified the first current parent will be used.
2427
2427
2428 The dirstate will be set to the files of the given revision.
2428 The dirstate will be set to the files of the given revision.
2429 The actual working directory content or existing dirstate
2429 The actual working directory content or existing dirstate
2430 information such as adds or removes is not considered.
2430 information such as adds or removes is not considered.
2431
2431
2432 One use of this command is to make the next :hg:`status` invocation
2432 One use of this command is to make the next :hg:`status` invocation
2433 check the actual file content.
2433 check the actual file content.
2434 """
2434 """
2435 ctx = scmutil.revsingle(repo, rev)
2435 ctx = scmutil.revsingle(repo, rev)
2436 wlock = repo.wlock()
2436 wlock = repo.wlock()
2437 try:
2437 try:
2438 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2438 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2439 finally:
2439 finally:
2440 wlock.release()
2440 wlock.release()
2441
2441
2442 @command('debugrename',
2442 @command('debugrename',
2443 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2443 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2444 _('[-r REV] FILE'))
2444 _('[-r REV] FILE'))
2445 def debugrename(ui, repo, file1, *pats, **opts):
2445 def debugrename(ui, repo, file1, *pats, **opts):
2446 """dump rename information"""
2446 """dump rename information"""
2447
2447
2448 ctx = scmutil.revsingle(repo, opts.get('rev'))
2448 ctx = scmutil.revsingle(repo, opts.get('rev'))
2449 m = scmutil.match(ctx, (file1,) + pats, opts)
2449 m = scmutil.match(ctx, (file1,) + pats, opts)
2450 for abs in ctx.walk(m):
2450 for abs in ctx.walk(m):
2451 fctx = ctx[abs]
2451 fctx = ctx[abs]
2452 o = fctx.filelog().renamed(fctx.filenode())
2452 o = fctx.filelog().renamed(fctx.filenode())
2453 rel = m.rel(abs)
2453 rel = m.rel(abs)
2454 if o:
2454 if o:
2455 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2455 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2456 else:
2456 else:
2457 ui.write(_("%s not renamed\n") % rel)
2457 ui.write(_("%s not renamed\n") % rel)
2458
2458
2459 @command('debugrevlog',
2459 @command('debugrevlog',
2460 [('c', 'changelog', False, _('open changelog')),
2460 [('c', 'changelog', False, _('open changelog')),
2461 ('m', 'manifest', False, _('open manifest')),
2461 ('m', 'manifest', False, _('open manifest')),
2462 ('d', 'dump', False, _('dump index data'))],
2462 ('d', 'dump', False, _('dump index data'))],
2463 _('-c|-m|FILE'))
2463 _('-c|-m|FILE'))
2464 def debugrevlog(ui, repo, file_=None, **opts):
2464 def debugrevlog(ui, repo, file_=None, **opts):
2465 """show data and statistics about a revlog"""
2465 """show data and statistics about a revlog"""
2466 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2466 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2467
2467
2468 if opts.get("dump"):
2468 if opts.get("dump"):
2469 numrevs = len(r)
2469 numrevs = len(r)
2470 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2470 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2471 " rawsize totalsize compression heads\n")
2471 " rawsize totalsize compression heads\n")
2472 ts = 0
2472 ts = 0
2473 heads = set()
2473 heads = set()
2474 for rev in xrange(numrevs):
2474 for rev in xrange(numrevs):
2475 dbase = r.deltaparent(rev)
2475 dbase = r.deltaparent(rev)
2476 if dbase == -1:
2476 if dbase == -1:
2477 dbase = rev
2477 dbase = rev
2478 cbase = r.chainbase(rev)
2478 cbase = r.chainbase(rev)
2479 p1, p2 = r.parentrevs(rev)
2479 p1, p2 = r.parentrevs(rev)
2480 rs = r.rawsize(rev)
2480 rs = r.rawsize(rev)
2481 ts = ts + rs
2481 ts = ts + rs
2482 heads -= set(r.parentrevs(rev))
2482 heads -= set(r.parentrevs(rev))
2483 heads.add(rev)
2483 heads.add(rev)
2484 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d %11d %5d\n" %
2484 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d %11d %5d\n" %
2485 (rev, p1, p2, r.start(rev), r.end(rev),
2485 (rev, p1, p2, r.start(rev), r.end(rev),
2486 r.start(dbase), r.start(cbase),
2486 r.start(dbase), r.start(cbase),
2487 r.start(p1), r.start(p2),
2487 r.start(p1), r.start(p2),
2488 rs, ts, ts / r.end(rev), len(heads)))
2488 rs, ts, ts / r.end(rev), len(heads)))
2489 return 0
2489 return 0
2490
2490
2491 v = r.version
2491 v = r.version
2492 format = v & 0xFFFF
2492 format = v & 0xFFFF
2493 flags = []
2493 flags = []
2494 gdelta = False
2494 gdelta = False
2495 if v & revlog.REVLOGNGINLINEDATA:
2495 if v & revlog.REVLOGNGINLINEDATA:
2496 flags.append('inline')
2496 flags.append('inline')
2497 if v & revlog.REVLOGGENERALDELTA:
2497 if v & revlog.REVLOGGENERALDELTA:
2498 gdelta = True
2498 gdelta = True
2499 flags.append('generaldelta')
2499 flags.append('generaldelta')
2500 if not flags:
2500 if not flags:
2501 flags = ['(none)']
2501 flags = ['(none)']
2502
2502
2503 nummerges = 0
2503 nummerges = 0
2504 numfull = 0
2504 numfull = 0
2505 numprev = 0
2505 numprev = 0
2506 nump1 = 0
2506 nump1 = 0
2507 nump2 = 0
2507 nump2 = 0
2508 numother = 0
2508 numother = 0
2509 nump1prev = 0
2509 nump1prev = 0
2510 nump2prev = 0
2510 nump2prev = 0
2511 chainlengths = []
2511 chainlengths = []
2512
2512
2513 datasize = [None, 0, 0L]
2513 datasize = [None, 0, 0L]
2514 fullsize = [None, 0, 0L]
2514 fullsize = [None, 0, 0L]
2515 deltasize = [None, 0, 0L]
2515 deltasize = [None, 0, 0L]
2516
2516
2517 def addsize(size, l):
2517 def addsize(size, l):
2518 if l[0] is None or size < l[0]:
2518 if l[0] is None or size < l[0]:
2519 l[0] = size
2519 l[0] = size
2520 if size > l[1]:
2520 if size > l[1]:
2521 l[1] = size
2521 l[1] = size
2522 l[2] += size
2522 l[2] += size
2523
2523
2524 numrevs = len(r)
2524 numrevs = len(r)
2525 for rev in xrange(numrevs):
2525 for rev in xrange(numrevs):
2526 p1, p2 = r.parentrevs(rev)
2526 p1, p2 = r.parentrevs(rev)
2527 delta = r.deltaparent(rev)
2527 delta = r.deltaparent(rev)
2528 if format > 0:
2528 if format > 0:
2529 addsize(r.rawsize(rev), datasize)
2529 addsize(r.rawsize(rev), datasize)
2530 if p2 != nullrev:
2530 if p2 != nullrev:
2531 nummerges += 1
2531 nummerges += 1
2532 size = r.length(rev)
2532 size = r.length(rev)
2533 if delta == nullrev:
2533 if delta == nullrev:
2534 chainlengths.append(0)
2534 chainlengths.append(0)
2535 numfull += 1
2535 numfull += 1
2536 addsize(size, fullsize)
2536 addsize(size, fullsize)
2537 else:
2537 else:
2538 chainlengths.append(chainlengths[delta] + 1)
2538 chainlengths.append(chainlengths[delta] + 1)
2539 addsize(size, deltasize)
2539 addsize(size, deltasize)
2540 if delta == rev - 1:
2540 if delta == rev - 1:
2541 numprev += 1
2541 numprev += 1
2542 if delta == p1:
2542 if delta == p1:
2543 nump1prev += 1
2543 nump1prev += 1
2544 elif delta == p2:
2544 elif delta == p2:
2545 nump2prev += 1
2545 nump2prev += 1
2546 elif delta == p1:
2546 elif delta == p1:
2547 nump1 += 1
2547 nump1 += 1
2548 elif delta == p2:
2548 elif delta == p2:
2549 nump2 += 1
2549 nump2 += 1
2550 elif delta != nullrev:
2550 elif delta != nullrev:
2551 numother += 1
2551 numother += 1
2552
2552
2553 # Adjust size min value for empty cases
2553 # Adjust size min value for empty cases
2554 for size in (datasize, fullsize, deltasize):
2554 for size in (datasize, fullsize, deltasize):
2555 if size[0] is None:
2555 if size[0] is None:
2556 size[0] = 0
2556 size[0] = 0
2557
2557
2558 numdeltas = numrevs - numfull
2558 numdeltas = numrevs - numfull
2559 numoprev = numprev - nump1prev - nump2prev
2559 numoprev = numprev - nump1prev - nump2prev
2560 totalrawsize = datasize[2]
2560 totalrawsize = datasize[2]
2561 datasize[2] /= numrevs
2561 datasize[2] /= numrevs
2562 fulltotal = fullsize[2]
2562 fulltotal = fullsize[2]
2563 fullsize[2] /= numfull
2563 fullsize[2] /= numfull
2564 deltatotal = deltasize[2]
2564 deltatotal = deltasize[2]
2565 if numrevs - numfull > 0:
2565 if numrevs - numfull > 0:
2566 deltasize[2] /= numrevs - numfull
2566 deltasize[2] /= numrevs - numfull
2567 totalsize = fulltotal + deltatotal
2567 totalsize = fulltotal + deltatotal
2568 avgchainlen = sum(chainlengths) / numrevs
2568 avgchainlen = sum(chainlengths) / numrevs
2569 compratio = totalrawsize / totalsize
2569 compratio = totalrawsize / totalsize
2570
2570
2571 basedfmtstr = '%%%dd\n'
2571 basedfmtstr = '%%%dd\n'
2572 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2572 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2573
2573
2574 def dfmtstr(max):
2574 def dfmtstr(max):
2575 return basedfmtstr % len(str(max))
2575 return basedfmtstr % len(str(max))
2576 def pcfmtstr(max, padding=0):
2576 def pcfmtstr(max, padding=0):
2577 return basepcfmtstr % (len(str(max)), ' ' * padding)
2577 return basepcfmtstr % (len(str(max)), ' ' * padding)
2578
2578
2579 def pcfmt(value, total):
2579 def pcfmt(value, total):
2580 return (value, 100 * float(value) / total)
2580 return (value, 100 * float(value) / total)
2581
2581
2582 ui.write(('format : %d\n') % format)
2582 ui.write(('format : %d\n') % format)
2583 ui.write(('flags : %s\n') % ', '.join(flags))
2583 ui.write(('flags : %s\n') % ', '.join(flags))
2584
2584
2585 ui.write('\n')
2585 ui.write('\n')
2586 fmt = pcfmtstr(totalsize)
2586 fmt = pcfmtstr(totalsize)
2587 fmt2 = dfmtstr(totalsize)
2587 fmt2 = dfmtstr(totalsize)
2588 ui.write(('revisions : ') + fmt2 % numrevs)
2588 ui.write(('revisions : ') + fmt2 % numrevs)
2589 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2589 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2590 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2590 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2591 ui.write(('revisions : ') + fmt2 % numrevs)
2591 ui.write(('revisions : ') + fmt2 % numrevs)
2592 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2592 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2593 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2593 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2594 ui.write(('revision size : ') + fmt2 % totalsize)
2594 ui.write(('revision size : ') + fmt2 % totalsize)
2595 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2595 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2596 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2596 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2597
2597
2598 ui.write('\n')
2598 ui.write('\n')
2599 fmt = dfmtstr(max(avgchainlen, compratio))
2599 fmt = dfmtstr(max(avgchainlen, compratio))
2600 ui.write(('avg chain length : ') + fmt % avgchainlen)
2600 ui.write(('avg chain length : ') + fmt % avgchainlen)
2601 ui.write(('compression ratio : ') + fmt % compratio)
2601 ui.write(('compression ratio : ') + fmt % compratio)
2602
2602
2603 if format > 0:
2603 if format > 0:
2604 ui.write('\n')
2604 ui.write('\n')
2605 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2605 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2606 % tuple(datasize))
2606 % tuple(datasize))
2607 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2607 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2608 % tuple(fullsize))
2608 % tuple(fullsize))
2609 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2609 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2610 % tuple(deltasize))
2610 % tuple(deltasize))
2611
2611
2612 if numdeltas > 0:
2612 if numdeltas > 0:
2613 ui.write('\n')
2613 ui.write('\n')
2614 fmt = pcfmtstr(numdeltas)
2614 fmt = pcfmtstr(numdeltas)
2615 fmt2 = pcfmtstr(numdeltas, 4)
2615 fmt2 = pcfmtstr(numdeltas, 4)
2616 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2616 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2617 if numprev > 0:
2617 if numprev > 0:
2618 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2618 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2619 numprev))
2619 numprev))
2620 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2620 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2621 numprev))
2621 numprev))
2622 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2622 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2623 numprev))
2623 numprev))
2624 if gdelta:
2624 if gdelta:
2625 ui.write(('deltas against p1 : ')
2625 ui.write(('deltas against p1 : ')
2626 + fmt % pcfmt(nump1, numdeltas))
2626 + fmt % pcfmt(nump1, numdeltas))
2627 ui.write(('deltas against p2 : ')
2627 ui.write(('deltas against p2 : ')
2628 + fmt % pcfmt(nump2, numdeltas))
2628 + fmt % pcfmt(nump2, numdeltas))
2629 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2629 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2630 numdeltas))
2630 numdeltas))
2631
2631
2632 @command('debugrevspec',
2632 @command('debugrevspec',
2633 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2633 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2634 ('REVSPEC'))
2634 ('REVSPEC'))
2635 def debugrevspec(ui, repo, expr, **opts):
2635 def debugrevspec(ui, repo, expr, **opts):
2636 """parse and apply a revision specification
2636 """parse and apply a revision specification
2637
2637
2638 Use --verbose to print the parsed tree before and after aliases
2638 Use --verbose to print the parsed tree before and after aliases
2639 expansion.
2639 expansion.
2640 """
2640 """
2641 if ui.verbose:
2641 if ui.verbose:
2642 tree = revset.parse(expr)[0]
2642 tree = revset.parse(expr)[0]
2643 ui.note(revset.prettyformat(tree), "\n")
2643 ui.note(revset.prettyformat(tree), "\n")
2644 newtree = revset.findaliases(ui, tree)
2644 newtree = revset.findaliases(ui, tree)
2645 if newtree != tree:
2645 if newtree != tree:
2646 ui.note(revset.prettyformat(newtree), "\n")
2646 ui.note(revset.prettyformat(newtree), "\n")
2647 if opts["optimize"]:
2647 if opts["optimize"]:
2648 weight, optimizedtree = revset.optimize(newtree, True)
2648 weight, optimizedtree = revset.optimize(newtree, True)
2649 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2649 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2650 func = revset.match(ui, expr)
2650 func = revset.match(ui, expr)
2651 for c in func(repo, revset.spanset(repo)):
2651 for c in func(repo, revset.spanset(repo)):
2652 ui.write("%s\n" % c)
2652 ui.write("%s\n" % c)
2653
2653
2654 @command('debugsetparents', [], _('REV1 [REV2]'))
2654 @command('debugsetparents', [], _('REV1 [REV2]'))
2655 def debugsetparents(ui, repo, rev1, rev2=None):
2655 def debugsetparents(ui, repo, rev1, rev2=None):
2656 """manually set the parents of the current working directory
2656 """manually set the parents of the current working directory
2657
2657
2658 This is useful for writing repository conversion tools, but should
2658 This is useful for writing repository conversion tools, but should
2659 be used with care.
2659 be used with care.
2660
2660
2661 Returns 0 on success.
2661 Returns 0 on success.
2662 """
2662 """
2663
2663
2664 r1 = scmutil.revsingle(repo, rev1).node()
2664 r1 = scmutil.revsingle(repo, rev1).node()
2665 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2665 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2666
2666
2667 wlock = repo.wlock()
2667 wlock = repo.wlock()
2668 try:
2668 try:
2669 repo.setparents(r1, r2)
2669 repo.setparents(r1, r2)
2670 finally:
2670 finally:
2671 wlock.release()
2671 wlock.release()
2672
2672
2673 @command('debugdirstate|debugstate',
2673 @command('debugdirstate|debugstate',
2674 [('', 'nodates', None, _('do not display the saved mtime')),
2674 [('', 'nodates', None, _('do not display the saved mtime')),
2675 ('', 'datesort', None, _('sort by saved mtime'))],
2675 ('', 'datesort', None, _('sort by saved mtime'))],
2676 _('[OPTION]...'))
2676 _('[OPTION]...'))
2677 def debugstate(ui, repo, nodates=None, datesort=None):
2677 def debugstate(ui, repo, nodates=None, datesort=None):
2678 """show the contents of the current dirstate"""
2678 """show the contents of the current dirstate"""
2679 timestr = ""
2679 timestr = ""
2680 showdate = not nodates
2680 showdate = not nodates
2681 if datesort:
2681 if datesort:
2682 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2682 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2683 else:
2683 else:
2684 keyfunc = None # sort by filename
2684 keyfunc = None # sort by filename
2685 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2685 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2686 if showdate:
2686 if showdate:
2687 if ent[3] == -1:
2687 if ent[3] == -1:
2688 # Pad or slice to locale representation
2688 # Pad or slice to locale representation
2689 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2689 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2690 time.localtime(0)))
2690 time.localtime(0)))
2691 timestr = 'unset'
2691 timestr = 'unset'
2692 timestr = (timestr[:locale_len] +
2692 timestr = (timestr[:locale_len] +
2693 ' ' * (locale_len - len(timestr)))
2693 ' ' * (locale_len - len(timestr)))
2694 else:
2694 else:
2695 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2695 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2696 time.localtime(ent[3]))
2696 time.localtime(ent[3]))
2697 if ent[1] & 020000:
2697 if ent[1] & 020000:
2698 mode = 'lnk'
2698 mode = 'lnk'
2699 else:
2699 else:
2700 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2700 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2701 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2701 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2702 for f in repo.dirstate.copies():
2702 for f in repo.dirstate.copies():
2703 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2703 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2704
2704
2705 @command('debugsub',
2705 @command('debugsub',
2706 [('r', 'rev', '',
2706 [('r', 'rev', '',
2707 _('revision to check'), _('REV'))],
2707 _('revision to check'), _('REV'))],
2708 _('[-r REV] [REV]'))
2708 _('[-r REV] [REV]'))
2709 def debugsub(ui, repo, rev=None):
2709 def debugsub(ui, repo, rev=None):
2710 ctx = scmutil.revsingle(repo, rev, None)
2710 ctx = scmutil.revsingle(repo, rev, None)
2711 for k, v in sorted(ctx.substate.items()):
2711 for k, v in sorted(ctx.substate.items()):
2712 ui.write(('path %s\n') % k)
2712 ui.write(('path %s\n') % k)
2713 ui.write((' source %s\n') % v[0])
2713 ui.write((' source %s\n') % v[0])
2714 ui.write((' revision %s\n') % v[1])
2714 ui.write((' revision %s\n') % v[1])
2715
2715
2716 @command('debugsuccessorssets',
2716 @command('debugsuccessorssets',
2717 [],
2717 [],
2718 _('[REV]'))
2718 _('[REV]'))
2719 def debugsuccessorssets(ui, repo, *revs):
2719 def debugsuccessorssets(ui, repo, *revs):
2720 """show set of successors for revision
2720 """show set of successors for revision
2721
2721
2722 A successors set of changeset A is a consistent group of revisions that
2722 A successors set of changeset A is a consistent group of revisions that
2723 succeed A. It contains non-obsolete changesets only.
2723 succeed A. It contains non-obsolete changesets only.
2724
2724
2725 In most cases a changeset A has a single successors set containing a single
2725 In most cases a changeset A has a single successors set containing a single
2726 successor (changeset A replaced by A').
2726 successor (changeset A replaced by A').
2727
2727
2728 A changeset that is made obsolete with no successors are called "pruned".
2728 A changeset that is made obsolete with no successors are called "pruned".
2729 Such changesets have no successors sets at all.
2729 Such changesets have no successors sets at all.
2730
2730
2731 A changeset that has been "split" will have a successors set containing
2731 A changeset that has been "split" will have a successors set containing
2732 more than one successor.
2732 more than one successor.
2733
2733
2734 A changeset that has been rewritten in multiple different ways is called
2734 A changeset that has been rewritten in multiple different ways is called
2735 "divergent". Such changesets have multiple successor sets (each of which
2735 "divergent". Such changesets have multiple successor sets (each of which
2736 may also be split, i.e. have multiple successors).
2736 may also be split, i.e. have multiple successors).
2737
2737
2738 Results are displayed as follows::
2738 Results are displayed as follows::
2739
2739
2740 <rev1>
2740 <rev1>
2741 <successors-1A>
2741 <successors-1A>
2742 <rev2>
2742 <rev2>
2743 <successors-2A>
2743 <successors-2A>
2744 <successors-2B1> <successors-2B2> <successors-2B3>
2744 <successors-2B1> <successors-2B2> <successors-2B3>
2745
2745
2746 Here rev2 has two possible (i.e. divergent) successors sets. The first
2746 Here rev2 has two possible (i.e. divergent) successors sets. The first
2747 holds one element, whereas the second holds three (i.e. the changeset has
2747 holds one element, whereas the second holds three (i.e. the changeset has
2748 been split).
2748 been split).
2749 """
2749 """
2750 # passed to successorssets caching computation from one call to another
2750 # passed to successorssets caching computation from one call to another
2751 cache = {}
2751 cache = {}
2752 ctx2str = str
2752 ctx2str = str
2753 node2str = short
2753 node2str = short
2754 if ui.debug():
2754 if ui.debug():
2755 def ctx2str(ctx):
2755 def ctx2str(ctx):
2756 return ctx.hex()
2756 return ctx.hex()
2757 node2str = hex
2757 node2str = hex
2758 for rev in scmutil.revrange(repo, revs):
2758 for rev in scmutil.revrange(repo, revs):
2759 ctx = repo[rev]
2759 ctx = repo[rev]
2760 ui.write('%s\n'% ctx2str(ctx))
2760 ui.write('%s\n'% ctx2str(ctx))
2761 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2761 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2762 if succsset:
2762 if succsset:
2763 ui.write(' ')
2763 ui.write(' ')
2764 ui.write(node2str(succsset[0]))
2764 ui.write(node2str(succsset[0]))
2765 for node in succsset[1:]:
2765 for node in succsset[1:]:
2766 ui.write(' ')
2766 ui.write(' ')
2767 ui.write(node2str(node))
2767 ui.write(node2str(node))
2768 ui.write('\n')
2768 ui.write('\n')
2769
2769
2770 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2770 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2771 def debugwalk(ui, repo, *pats, **opts):
2771 def debugwalk(ui, repo, *pats, **opts):
2772 """show how files match on given patterns"""
2772 """show how files match on given patterns"""
2773 m = scmutil.match(repo[None], pats, opts)
2773 m = scmutil.match(repo[None], pats, opts)
2774 items = list(repo.walk(m))
2774 items = list(repo.walk(m))
2775 if not items:
2775 if not items:
2776 return
2776 return
2777 f = lambda fn: fn
2777 f = lambda fn: fn
2778 if ui.configbool('ui', 'slash') and os.sep != '/':
2778 if ui.configbool('ui', 'slash') and os.sep != '/':
2779 f = lambda fn: util.normpath(fn)
2779 f = lambda fn: util.normpath(fn)
2780 fmt = 'f %%-%ds %%-%ds %%s' % (
2780 fmt = 'f %%-%ds %%-%ds %%s' % (
2781 max([len(abs) for abs in items]),
2781 max([len(abs) for abs in items]),
2782 max([len(m.rel(abs)) for abs in items]))
2782 max([len(m.rel(abs)) for abs in items]))
2783 for abs in items:
2783 for abs in items:
2784 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2784 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2785 ui.write("%s\n" % line.rstrip())
2785 ui.write("%s\n" % line.rstrip())
2786
2786
2787 @command('debugwireargs',
2787 @command('debugwireargs',
2788 [('', 'three', '', 'three'),
2788 [('', 'three', '', 'three'),
2789 ('', 'four', '', 'four'),
2789 ('', 'four', '', 'four'),
2790 ('', 'five', '', 'five'),
2790 ('', 'five', '', 'five'),
2791 ] + remoteopts,
2791 ] + remoteopts,
2792 _('REPO [OPTIONS]... [ONE [TWO]]'))
2792 _('REPO [OPTIONS]... [ONE [TWO]]'))
2793 def debugwireargs(ui, repopath, *vals, **opts):
2793 def debugwireargs(ui, repopath, *vals, **opts):
2794 repo = hg.peer(ui, opts, repopath)
2794 repo = hg.peer(ui, opts, repopath)
2795 for opt in remoteopts:
2795 for opt in remoteopts:
2796 del opts[opt[1]]
2796 del opts[opt[1]]
2797 args = {}
2797 args = {}
2798 for k, v in opts.iteritems():
2798 for k, v in opts.iteritems():
2799 if v:
2799 if v:
2800 args[k] = v
2800 args[k] = v
2801 # run twice to check that we don't mess up the stream for the next command
2801 # run twice to check that we don't mess up the stream for the next command
2802 res1 = repo.debugwireargs(*vals, **args)
2802 res1 = repo.debugwireargs(*vals, **args)
2803 res2 = repo.debugwireargs(*vals, **args)
2803 res2 = repo.debugwireargs(*vals, **args)
2804 ui.write("%s\n" % res1)
2804 ui.write("%s\n" % res1)
2805 if res1 != res2:
2805 if res1 != res2:
2806 ui.warn("%s\n" % res2)
2806 ui.warn("%s\n" % res2)
2807
2807
2808 @command('^diff',
2808 @command('^diff',
2809 [('r', 'rev', [], _('revision'), _('REV')),
2809 [('r', 'rev', [], _('revision'), _('REV')),
2810 ('c', 'change', '', _('change made by revision'), _('REV'))
2810 ('c', 'change', '', _('change made by revision'), _('REV'))
2811 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2811 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2812 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2812 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2813 def diff(ui, repo, *pats, **opts):
2813 def diff(ui, repo, *pats, **opts):
2814 """diff repository (or selected files)
2814 """diff repository (or selected files)
2815
2815
2816 Show differences between revisions for the specified files.
2816 Show differences between revisions for the specified files.
2817
2817
2818 Differences between files are shown using the unified diff format.
2818 Differences between files are shown using the unified diff format.
2819
2819
2820 .. note::
2820 .. note::
2821
2821
2822 diff may generate unexpected results for merges, as it will
2822 diff may generate unexpected results for merges, as it will
2823 default to comparing against the working directory's first
2823 default to comparing against the working directory's first
2824 parent changeset if no revisions are specified.
2824 parent changeset if no revisions are specified.
2825
2825
2826 When two revision arguments are given, then changes are shown
2826 When two revision arguments are given, then changes are shown
2827 between those revisions. If only one revision is specified then
2827 between those revisions. If only one revision is specified then
2828 that revision is compared to the working directory, and, when no
2828 that revision is compared to the working directory, and, when no
2829 revisions are specified, the working directory files are compared
2829 revisions are specified, the working directory files are compared
2830 to its parent.
2830 to its parent.
2831
2831
2832 Alternatively you can specify -c/--change with a revision to see
2832 Alternatively you can specify -c/--change with a revision to see
2833 the changes in that changeset relative to its first parent.
2833 the changes in that changeset relative to its first parent.
2834
2834
2835 Without the -a/--text option, diff will avoid generating diffs of
2835 Without the -a/--text option, diff will avoid generating diffs of
2836 files it detects as binary. With -a, diff will generate a diff
2836 files it detects as binary. With -a, diff will generate a diff
2837 anyway, probably with undesirable results.
2837 anyway, probably with undesirable results.
2838
2838
2839 Use the -g/--git option to generate diffs in the git extended diff
2839 Use the -g/--git option to generate diffs in the git extended diff
2840 format. For more information, read :hg:`help diffs`.
2840 format. For more information, read :hg:`help diffs`.
2841
2841
2842 .. container:: verbose
2842 .. container:: verbose
2843
2843
2844 Examples:
2844 Examples:
2845
2845
2846 - compare a file in the current working directory to its parent::
2846 - compare a file in the current working directory to its parent::
2847
2847
2848 hg diff foo.c
2848 hg diff foo.c
2849
2849
2850 - compare two historical versions of a directory, with rename info::
2850 - compare two historical versions of a directory, with rename info::
2851
2851
2852 hg diff --git -r 1.0:1.2 lib/
2852 hg diff --git -r 1.0:1.2 lib/
2853
2853
2854 - get change stats relative to the last change on some date::
2854 - get change stats relative to the last change on some date::
2855
2855
2856 hg diff --stat -r "date('may 2')"
2856 hg diff --stat -r "date('may 2')"
2857
2857
2858 - diff all newly-added files that contain a keyword::
2858 - diff all newly-added files that contain a keyword::
2859
2859
2860 hg diff "set:added() and grep(GNU)"
2860 hg diff "set:added() and grep(GNU)"
2861
2861
2862 - compare a revision and its parents::
2862 - compare a revision and its parents::
2863
2863
2864 hg diff -c 9353 # compare against first parent
2864 hg diff -c 9353 # compare against first parent
2865 hg diff -r 9353^:9353 # same using revset syntax
2865 hg diff -r 9353^:9353 # same using revset syntax
2866 hg diff -r 9353^2:9353 # compare against the second parent
2866 hg diff -r 9353^2:9353 # compare against the second parent
2867
2867
2868 Returns 0 on success.
2868 Returns 0 on success.
2869 """
2869 """
2870
2870
2871 revs = opts.get('rev')
2871 revs = opts.get('rev')
2872 change = opts.get('change')
2872 change = opts.get('change')
2873 stat = opts.get('stat')
2873 stat = opts.get('stat')
2874 reverse = opts.get('reverse')
2874 reverse = opts.get('reverse')
2875
2875
2876 if revs and change:
2876 if revs and change:
2877 msg = _('cannot specify --rev and --change at the same time')
2877 msg = _('cannot specify --rev and --change at the same time')
2878 raise util.Abort(msg)
2878 raise util.Abort(msg)
2879 elif change:
2879 elif change:
2880 node2 = scmutil.revsingle(repo, change, None).node()
2880 node2 = scmutil.revsingle(repo, change, None).node()
2881 node1 = repo[node2].p1().node()
2881 node1 = repo[node2].p1().node()
2882 else:
2882 else:
2883 node1, node2 = scmutil.revpair(repo, revs)
2883 node1, node2 = scmutil.revpair(repo, revs)
2884
2884
2885 if reverse:
2885 if reverse:
2886 node1, node2 = node2, node1
2886 node1, node2 = node2, node1
2887
2887
2888 diffopts = patch.diffopts(ui, opts)
2888 diffopts = patch.diffopts(ui, opts)
2889 m = scmutil.match(repo[node2], pats, opts)
2889 m = scmutil.match(repo[node2], pats, opts)
2890 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2890 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2891 listsubrepos=opts.get('subrepos'))
2891 listsubrepos=opts.get('subrepos'))
2892
2892
2893 @command('^export',
2893 @command('^export',
2894 [('o', 'output', '',
2894 [('o', 'output', '',
2895 _('print output to file with formatted name'), _('FORMAT')),
2895 _('print output to file with formatted name'), _('FORMAT')),
2896 ('', 'switch-parent', None, _('diff against the second parent')),
2896 ('', 'switch-parent', None, _('diff against the second parent')),
2897 ('r', 'rev', [], _('revisions to export'), _('REV')),
2897 ('r', 'rev', [], _('revisions to export'), _('REV')),
2898 ] + diffopts,
2898 ] + diffopts,
2899 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2899 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2900 def export(ui, repo, *changesets, **opts):
2900 def export(ui, repo, *changesets, **opts):
2901 """dump the header and diffs for one or more changesets
2901 """dump the header and diffs for one or more changesets
2902
2902
2903 Print the changeset header and diffs for one or more revisions.
2903 Print the changeset header and diffs for one or more revisions.
2904 If no revision is given, the parent of the working directory is used.
2904 If no revision is given, the parent of the working directory is used.
2905
2905
2906 The information shown in the changeset header is: author, date,
2906 The information shown in the changeset header is: author, date,
2907 branch name (if non-default), changeset hash, parent(s) and commit
2907 branch name (if non-default), changeset hash, parent(s) and commit
2908 comment.
2908 comment.
2909
2909
2910 .. note::
2910 .. note::
2911
2911
2912 export may generate unexpected diff output for merge
2912 export may generate unexpected diff output for merge
2913 changesets, as it will compare the merge changeset against its
2913 changesets, as it will compare the merge changeset against its
2914 first parent only.
2914 first parent only.
2915
2915
2916 Output may be to a file, in which case the name of the file is
2916 Output may be to a file, in which case the name of the file is
2917 given using a format string. The formatting rules are as follows:
2917 given using a format string. The formatting rules are as follows:
2918
2918
2919 :``%%``: literal "%" character
2919 :``%%``: literal "%" character
2920 :``%H``: changeset hash (40 hexadecimal digits)
2920 :``%H``: changeset hash (40 hexadecimal digits)
2921 :``%N``: number of patches being generated
2921 :``%N``: number of patches being generated
2922 :``%R``: changeset revision number
2922 :``%R``: changeset revision number
2923 :``%b``: basename of the exporting repository
2923 :``%b``: basename of the exporting repository
2924 :``%h``: short-form changeset hash (12 hexadecimal digits)
2924 :``%h``: short-form changeset hash (12 hexadecimal digits)
2925 :``%m``: first line of the commit message (only alphanumeric characters)
2925 :``%m``: first line of the commit message (only alphanumeric characters)
2926 :``%n``: zero-padded sequence number, starting at 1
2926 :``%n``: zero-padded sequence number, starting at 1
2927 :``%r``: zero-padded changeset revision number
2927 :``%r``: zero-padded changeset revision number
2928
2928
2929 Without the -a/--text option, export will avoid generating diffs
2929 Without the -a/--text option, export will avoid generating diffs
2930 of files it detects as binary. With -a, export will generate a
2930 of files it detects as binary. With -a, export will generate a
2931 diff anyway, probably with undesirable results.
2931 diff anyway, probably with undesirable results.
2932
2932
2933 Use the -g/--git option to generate diffs in the git extended diff
2933 Use the -g/--git option to generate diffs in the git extended diff
2934 format. See :hg:`help diffs` for more information.
2934 format. See :hg:`help diffs` for more information.
2935
2935
2936 With the --switch-parent option, the diff will be against the
2936 With the --switch-parent option, the diff will be against the
2937 second parent. It can be useful to review a merge.
2937 second parent. It can be useful to review a merge.
2938
2938
2939 .. container:: verbose
2939 .. container:: verbose
2940
2940
2941 Examples:
2941 Examples:
2942
2942
2943 - use export and import to transplant a bugfix to the current
2943 - use export and import to transplant a bugfix to the current
2944 branch::
2944 branch::
2945
2945
2946 hg export -r 9353 | hg import -
2946 hg export -r 9353 | hg import -
2947
2947
2948 - export all the changesets between two revisions to a file with
2948 - export all the changesets between two revisions to a file with
2949 rename information::
2949 rename information::
2950
2950
2951 hg export --git -r 123:150 > changes.txt
2951 hg export --git -r 123:150 > changes.txt
2952
2952
2953 - split outgoing changes into a series of patches with
2953 - split outgoing changes into a series of patches with
2954 descriptive names::
2954 descriptive names::
2955
2955
2956 hg export -r "outgoing()" -o "%n-%m.patch"
2956 hg export -r "outgoing()" -o "%n-%m.patch"
2957
2957
2958 Returns 0 on success.
2958 Returns 0 on success.
2959 """
2959 """
2960 changesets += tuple(opts.get('rev', []))
2960 changesets += tuple(opts.get('rev', []))
2961 if not changesets:
2961 if not changesets:
2962 changesets = ['.']
2962 changesets = ['.']
2963 revs = scmutil.revrange(repo, changesets)
2963 revs = scmutil.revrange(repo, changesets)
2964 if not revs:
2964 if not revs:
2965 raise util.Abort(_("export requires at least one changeset"))
2965 raise util.Abort(_("export requires at least one changeset"))
2966 if len(revs) > 1:
2966 if len(revs) > 1:
2967 ui.note(_('exporting patches:\n'))
2967 ui.note(_('exporting patches:\n'))
2968 else:
2968 else:
2969 ui.note(_('exporting patch:\n'))
2969 ui.note(_('exporting patch:\n'))
2970 cmdutil.export(repo, revs, template=opts.get('output'),
2970 cmdutil.export(repo, revs, template=opts.get('output'),
2971 switch_parent=opts.get('switch_parent'),
2971 switch_parent=opts.get('switch_parent'),
2972 opts=patch.diffopts(ui, opts))
2972 opts=patch.diffopts(ui, opts))
2973
2973
2974 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2974 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2975 def forget(ui, repo, *pats, **opts):
2975 def forget(ui, repo, *pats, **opts):
2976 """forget the specified files on the next commit
2976 """forget the specified files on the next commit
2977
2977
2978 Mark the specified files so they will no longer be tracked
2978 Mark the specified files so they will no longer be tracked
2979 after the next commit.
2979 after the next commit.
2980
2980
2981 This only removes files from the current branch, not from the
2981 This only removes files from the current branch, not from the
2982 entire project history, and it does not delete them from the
2982 entire project history, and it does not delete them from the
2983 working directory.
2983 working directory.
2984
2984
2985 To undo a forget before the next commit, see :hg:`add`.
2985 To undo a forget before the next commit, see :hg:`add`.
2986
2986
2987 .. container:: verbose
2987 .. container:: verbose
2988
2988
2989 Examples:
2989 Examples:
2990
2990
2991 - forget newly-added binary files::
2991 - forget newly-added binary files::
2992
2992
2993 hg forget "set:added() and binary()"
2993 hg forget "set:added() and binary()"
2994
2994
2995 - forget files that would be excluded by .hgignore::
2995 - forget files that would be excluded by .hgignore::
2996
2996
2997 hg forget "set:hgignore()"
2997 hg forget "set:hgignore()"
2998
2998
2999 Returns 0 on success.
2999 Returns 0 on success.
3000 """
3000 """
3001
3001
3002 if not pats:
3002 if not pats:
3003 raise util.Abort(_('no files specified'))
3003 raise util.Abort(_('no files specified'))
3004
3004
3005 m = scmutil.match(repo[None], pats, opts)
3005 m = scmutil.match(repo[None], pats, opts)
3006 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3006 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3007 return rejected and 1 or 0
3007 return rejected and 1 or 0
3008
3008
3009 @command(
3009 @command(
3010 'graft',
3010 'graft',
3011 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3011 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3012 ('c', 'continue', False, _('resume interrupted graft')),
3012 ('c', 'continue', False, _('resume interrupted graft')),
3013 ('e', 'edit', False, _('invoke editor on commit messages')),
3013 ('e', 'edit', False, _('invoke editor on commit messages')),
3014 ('', 'log', None, _('append graft info to log message')),
3014 ('', 'log', None, _('append graft info to log message')),
3015 ('D', 'currentdate', False,
3015 ('D', 'currentdate', False,
3016 _('record the current date as commit date')),
3016 _('record the current date as commit date')),
3017 ('U', 'currentuser', False,
3017 ('U', 'currentuser', False,
3018 _('record the current user as committer'), _('DATE'))]
3018 _('record the current user as committer'), _('DATE'))]
3019 + commitopts2 + mergetoolopts + dryrunopts,
3019 + commitopts2 + mergetoolopts + dryrunopts,
3020 _('[OPTION]... [-r] REV...'))
3020 _('[OPTION]... [-r] REV...'))
3021 def graft(ui, repo, *revs, **opts):
3021 def graft(ui, repo, *revs, **opts):
3022 '''copy changes from other branches onto the current branch
3022 '''copy changes from other branches onto the current branch
3023
3023
3024 This command uses Mercurial's merge logic to copy individual
3024 This command uses Mercurial's merge logic to copy individual
3025 changes from other branches without merging branches in the
3025 changes from other branches without merging branches in the
3026 history graph. This is sometimes known as 'backporting' or
3026 history graph. This is sometimes known as 'backporting' or
3027 'cherry-picking'. By default, graft will copy user, date, and
3027 'cherry-picking'. By default, graft will copy user, date, and
3028 description from the source changesets.
3028 description from the source changesets.
3029
3029
3030 Changesets that are ancestors of the current revision, that have
3030 Changesets that are ancestors of the current revision, that have
3031 already been grafted, or that are merges will be skipped.
3031 already been grafted, or that are merges will be skipped.
3032
3032
3033 If --log is specified, log messages will have a comment appended
3033 If --log is specified, log messages will have a comment appended
3034 of the form::
3034 of the form::
3035
3035
3036 (grafted from CHANGESETHASH)
3036 (grafted from CHANGESETHASH)
3037
3037
3038 If a graft merge results in conflicts, the graft process is
3038 If a graft merge results in conflicts, the graft process is
3039 interrupted so that the current merge can be manually resolved.
3039 interrupted so that the current merge can be manually resolved.
3040 Once all conflicts are addressed, the graft process can be
3040 Once all conflicts are addressed, the graft process can be
3041 continued with the -c/--continue option.
3041 continued with the -c/--continue option.
3042
3042
3043 .. note::
3043 .. note::
3044
3044
3045 The -c/--continue option does not reapply earlier options.
3045 The -c/--continue option does not reapply earlier options.
3046
3046
3047 .. container:: verbose
3047 .. container:: verbose
3048
3048
3049 Examples:
3049 Examples:
3050
3050
3051 - copy a single change to the stable branch and edit its description::
3051 - copy a single change to the stable branch and edit its description::
3052
3052
3053 hg update stable
3053 hg update stable
3054 hg graft --edit 9393
3054 hg graft --edit 9393
3055
3055
3056 - graft a range of changesets with one exception, updating dates::
3056 - graft a range of changesets with one exception, updating dates::
3057
3057
3058 hg graft -D "2085::2093 and not 2091"
3058 hg graft -D "2085::2093 and not 2091"
3059
3059
3060 - continue a graft after resolving conflicts::
3060 - continue a graft after resolving conflicts::
3061
3061
3062 hg graft -c
3062 hg graft -c
3063
3063
3064 - show the source of a grafted changeset::
3064 - show the source of a grafted changeset::
3065
3065
3066 hg log --debug -r .
3066 hg log --debug -r .
3067
3067
3068 Returns 0 on successful completion.
3068 Returns 0 on successful completion.
3069 '''
3069 '''
3070
3070
3071 revs = list(revs)
3071 revs = list(revs)
3072 revs.extend(opts['rev'])
3072 revs.extend(opts['rev'])
3073
3073
3074 if not opts.get('user') and opts.get('currentuser'):
3074 if not opts.get('user') and opts.get('currentuser'):
3075 opts['user'] = ui.username()
3075 opts['user'] = ui.username()
3076 if not opts.get('date') and opts.get('currentdate'):
3076 if not opts.get('date') and opts.get('currentdate'):
3077 opts['date'] = "%d %d" % util.makedate()
3077 opts['date'] = "%d %d" % util.makedate()
3078
3078
3079 editor = None
3079 editor = None
3080 if opts.get('edit'):
3080 if opts.get('edit'):
3081 editor = cmdutil.commitforceeditor
3081 editor = cmdutil.commitforceeditor
3082
3082
3083 cont = False
3083 cont = False
3084 if opts['continue']:
3084 if opts['continue']:
3085 cont = True
3085 cont = True
3086 if revs:
3086 if revs:
3087 raise util.Abort(_("can't specify --continue and revisions"))
3087 raise util.Abort(_("can't specify --continue and revisions"))
3088 # read in unfinished revisions
3088 # read in unfinished revisions
3089 try:
3089 try:
3090 nodes = repo.opener.read('graftstate').splitlines()
3090 nodes = repo.opener.read('graftstate').splitlines()
3091 revs = [repo[node].rev() for node in nodes]
3091 revs = [repo[node].rev() for node in nodes]
3092 except IOError, inst:
3092 except IOError, inst:
3093 if inst.errno != errno.ENOENT:
3093 if inst.errno != errno.ENOENT:
3094 raise
3094 raise
3095 raise util.Abort(_("no graft state found, can't continue"))
3095 raise util.Abort(_("no graft state found, can't continue"))
3096 else:
3096 else:
3097 cmdutil.checkunfinished(repo)
3097 cmdutil.checkunfinished(repo)
3098 cmdutil.bailifchanged(repo)
3098 cmdutil.bailifchanged(repo)
3099 if not revs:
3099 if not revs:
3100 raise util.Abort(_('no revisions specified'))
3100 raise util.Abort(_('no revisions specified'))
3101 revs = scmutil.revrange(repo, revs)
3101 revs = scmutil.revrange(repo, revs)
3102
3102
3103 # check for merges
3103 # check for merges
3104 for rev in repo.revs('%ld and merge()', revs):
3104 for rev in repo.revs('%ld and merge()', revs):
3105 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3105 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3106 revs.remove(rev)
3106 revs.remove(rev)
3107 if not revs:
3107 if not revs:
3108 return -1
3108 return -1
3109
3109
3110 # check for ancestors of dest branch
3110 # check for ancestors of dest branch
3111 crev = repo['.'].rev()
3111 crev = repo['.'].rev()
3112 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3112 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3113 # Cannot use x.remove(y) on smart set, this has to be a list.
3113 # Cannot use x.remove(y) on smart set, this has to be a list.
3114 # XXX make this lazy in the future
3114 # XXX make this lazy in the future
3115 revs = list(revs)
3115 revs = list(revs)
3116 # don't mutate while iterating, create a copy
3116 # don't mutate while iterating, create a copy
3117 for rev in list(revs):
3117 for rev in list(revs):
3118 if rev in ancestors:
3118 if rev in ancestors:
3119 ui.warn(_('skipping ancestor revision %s\n') % rev)
3119 ui.warn(_('skipping ancestor revision %s\n') % rev)
3120 # XXX remove on list is slow
3120 # XXX remove on list is slow
3121 revs.remove(rev)
3121 revs.remove(rev)
3122 if not revs:
3122 if not revs:
3123 return -1
3123 return -1
3124
3124
3125 # analyze revs for earlier grafts
3125 # analyze revs for earlier grafts
3126 ids = {}
3126 ids = {}
3127 for ctx in repo.set("%ld", revs):
3127 for ctx in repo.set("%ld", revs):
3128 ids[ctx.hex()] = ctx.rev()
3128 ids[ctx.hex()] = ctx.rev()
3129 n = ctx.extra().get('source')
3129 n = ctx.extra().get('source')
3130 if n:
3130 if n:
3131 ids[n] = ctx.rev()
3131 ids[n] = ctx.rev()
3132
3132
3133 # check ancestors for earlier grafts
3133 # check ancestors for earlier grafts
3134 ui.debug('scanning for duplicate grafts\n')
3134 ui.debug('scanning for duplicate grafts\n')
3135
3135
3136 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3136 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3137 ctx = repo[rev]
3137 ctx = repo[rev]
3138 n = ctx.extra().get('source')
3138 n = ctx.extra().get('source')
3139 if n in ids:
3139 if n in ids:
3140 r = repo[n].rev()
3140 r = repo[n].rev()
3141 if r in revs:
3141 if r in revs:
3142 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3142 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3143 % (r, rev))
3143 % (r, rev))
3144 revs.remove(r)
3144 revs.remove(r)
3145 elif ids[n] in revs:
3145 elif ids[n] in revs:
3146 ui.warn(_('skipping already grafted revision %s '
3146 ui.warn(_('skipping already grafted revision %s '
3147 '(%s also has origin %d)\n') % (ids[n], rev, r))
3147 '(%s also has origin %d)\n') % (ids[n], rev, r))
3148 revs.remove(ids[n])
3148 revs.remove(ids[n])
3149 elif ctx.hex() in ids:
3149 elif ctx.hex() in ids:
3150 r = ids[ctx.hex()]
3150 r = ids[ctx.hex()]
3151 ui.warn(_('skipping already grafted revision %s '
3151 ui.warn(_('skipping already grafted revision %s '
3152 '(was grafted from %d)\n') % (r, rev))
3152 '(was grafted from %d)\n') % (r, rev))
3153 revs.remove(r)
3153 revs.remove(r)
3154 if not revs:
3154 if not revs:
3155 return -1
3155 return -1
3156
3156
3157 wlock = repo.wlock()
3157 wlock = repo.wlock()
3158 try:
3158 try:
3159 current = repo['.']
3159 current = repo['.']
3160 for pos, ctx in enumerate(repo.set("%ld", revs)):
3160 for pos, ctx in enumerate(repo.set("%ld", revs)):
3161
3161
3162 ui.status(_('grafting revision %s\n') % ctx.rev())
3162 ui.status(_('grafting revision %s\n') % ctx.rev())
3163 if opts.get('dry_run'):
3163 if opts.get('dry_run'):
3164 continue
3164 continue
3165
3165
3166 source = ctx.extra().get('source')
3166 source = ctx.extra().get('source')
3167 if not source:
3167 if not source:
3168 source = ctx.hex()
3168 source = ctx.hex()
3169 extra = {'source': source}
3169 extra = {'source': source}
3170 user = ctx.user()
3170 user = ctx.user()
3171 if opts.get('user'):
3171 if opts.get('user'):
3172 user = opts['user']
3172 user = opts['user']
3173 date = ctx.date()
3173 date = ctx.date()
3174 if opts.get('date'):
3174 if opts.get('date'):
3175 date = opts['date']
3175 date = opts['date']
3176 message = ctx.description()
3176 message = ctx.description()
3177 if opts.get('log'):
3177 if opts.get('log'):
3178 message += '\n(grafted from %s)' % ctx.hex()
3178 message += '\n(grafted from %s)' % ctx.hex()
3179
3179
3180 # we don't merge the first commit when continuing
3180 # we don't merge the first commit when continuing
3181 if not cont:
3181 if not cont:
3182 # perform the graft merge with p1(rev) as 'ancestor'
3182 # perform the graft merge with p1(rev) as 'ancestor'
3183 try:
3183 try:
3184 # ui.forcemerge is an internal variable, do not document
3184 # ui.forcemerge is an internal variable, do not document
3185 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3185 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3186 'graft')
3186 'graft')
3187 stats = mergemod.update(repo, ctx.node(), True, True, False,
3187 stats = mergemod.update(repo, ctx.node(), True, True, False,
3188 ctx.p1().node())
3188 ctx.p1().node())
3189 finally:
3189 finally:
3190 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3190 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3191 # report any conflicts
3191 # report any conflicts
3192 if stats and stats[3] > 0:
3192 if stats and stats[3] > 0:
3193 # write out state for --continue
3193 # write out state for --continue
3194 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3194 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3195 repo.opener.write('graftstate', ''.join(nodelines))
3195 repo.opener.write('graftstate', ''.join(nodelines))
3196 raise util.Abort(
3196 raise util.Abort(
3197 _("unresolved conflicts, can't continue"),
3197 _("unresolved conflicts, can't continue"),
3198 hint=_('use hg resolve and hg graft --continue'))
3198 hint=_('use hg resolve and hg graft --continue'))
3199 else:
3199 else:
3200 cont = False
3200 cont = False
3201
3201
3202 # drop the second merge parent
3202 # drop the second merge parent
3203 repo.setparents(current.node(), nullid)
3203 repo.setparents(current.node(), nullid)
3204 repo.dirstate.write()
3204 repo.dirstate.write()
3205 # fix up dirstate for copies and renames
3205 # fix up dirstate for copies and renames
3206 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
3206 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
3207
3207
3208 # commit
3208 # commit
3209 node = repo.commit(text=message, user=user,
3209 node = repo.commit(text=message, user=user,
3210 date=date, extra=extra, editor=editor)
3210 date=date, extra=extra, editor=editor)
3211 if node is None:
3211 if node is None:
3212 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3212 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3213 else:
3213 else:
3214 current = repo[node]
3214 current = repo[node]
3215 finally:
3215 finally:
3216 wlock.release()
3216 wlock.release()
3217
3217
3218 # remove state when we complete successfully
3218 # remove state when we complete successfully
3219 if not opts.get('dry_run'):
3219 if not opts.get('dry_run'):
3220 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3220 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3221
3221
3222 return 0
3222 return 0
3223
3223
3224 @command('grep',
3224 @command('grep',
3225 [('0', 'print0', None, _('end fields with NUL')),
3225 [('0', 'print0', None, _('end fields with NUL')),
3226 ('', 'all', None, _('print all revisions that match')),
3226 ('', 'all', None, _('print all revisions that match')),
3227 ('a', 'text', None, _('treat all files as text')),
3227 ('a', 'text', None, _('treat all files as text')),
3228 ('f', 'follow', None,
3228 ('f', 'follow', None,
3229 _('follow changeset history,'
3229 _('follow changeset history,'
3230 ' or file history across copies and renames')),
3230 ' or file history across copies and renames')),
3231 ('i', 'ignore-case', None, _('ignore case when matching')),
3231 ('i', 'ignore-case', None, _('ignore case when matching')),
3232 ('l', 'files-with-matches', None,
3232 ('l', 'files-with-matches', None,
3233 _('print only filenames and revisions that match')),
3233 _('print only filenames and revisions that match')),
3234 ('n', 'line-number', None, _('print matching line numbers')),
3234 ('n', 'line-number', None, _('print matching line numbers')),
3235 ('r', 'rev', [],
3235 ('r', 'rev', [],
3236 _('only search files changed within revision range'), _('REV')),
3236 _('only search files changed within revision range'), _('REV')),
3237 ('u', 'user', None, _('list the author (long with -v)')),
3237 ('u', 'user', None, _('list the author (long with -v)')),
3238 ('d', 'date', None, _('list the date (short with -q)')),
3238 ('d', 'date', None, _('list the date (short with -q)')),
3239 ] + walkopts,
3239 ] + walkopts,
3240 _('[OPTION]... PATTERN [FILE]...'))
3240 _('[OPTION]... PATTERN [FILE]...'))
3241 def grep(ui, repo, pattern, *pats, **opts):
3241 def grep(ui, repo, pattern, *pats, **opts):
3242 """search for a pattern in specified files and revisions
3242 """search for a pattern in specified files and revisions
3243
3243
3244 Search revisions of files for a regular expression.
3244 Search revisions of files for a regular expression.
3245
3245
3246 This command behaves differently than Unix grep. It only accepts
3246 This command behaves differently than Unix grep. It only accepts
3247 Python/Perl regexps. It searches repository history, not the
3247 Python/Perl regexps. It searches repository history, not the
3248 working directory. It always prints the revision number in which a
3248 working directory. It always prints the revision number in which a
3249 match appears.
3249 match appears.
3250
3250
3251 By default, grep only prints output for the first revision of a
3251 By default, grep only prints output for the first revision of a
3252 file in which it finds a match. To get it to print every revision
3252 file in which it finds a match. To get it to print every revision
3253 that contains a change in match status ("-" for a match that
3253 that contains a change in match status ("-" for a match that
3254 becomes a non-match, or "+" for a non-match that becomes a match),
3254 becomes a non-match, or "+" for a non-match that becomes a match),
3255 use the --all flag.
3255 use the --all flag.
3256
3256
3257 Returns 0 if a match is found, 1 otherwise.
3257 Returns 0 if a match is found, 1 otherwise.
3258 """
3258 """
3259 reflags = re.M
3259 reflags = re.M
3260 if opts.get('ignore_case'):
3260 if opts.get('ignore_case'):
3261 reflags |= re.I
3261 reflags |= re.I
3262 try:
3262 try:
3263 regexp = util.compilere(pattern, reflags)
3263 regexp = util.compilere(pattern, reflags)
3264 except re.error, inst:
3264 except re.error, inst:
3265 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3265 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3266 return 1
3266 return 1
3267 sep, eol = ':', '\n'
3267 sep, eol = ':', '\n'
3268 if opts.get('print0'):
3268 if opts.get('print0'):
3269 sep = eol = '\0'
3269 sep = eol = '\0'
3270
3270
3271 getfile = util.lrucachefunc(repo.file)
3271 getfile = util.lrucachefunc(repo.file)
3272
3272
3273 def matchlines(body):
3273 def matchlines(body):
3274 begin = 0
3274 begin = 0
3275 linenum = 0
3275 linenum = 0
3276 while begin < len(body):
3276 while begin < len(body):
3277 match = regexp.search(body, begin)
3277 match = regexp.search(body, begin)
3278 if not match:
3278 if not match:
3279 break
3279 break
3280 mstart, mend = match.span()
3280 mstart, mend = match.span()
3281 linenum += body.count('\n', begin, mstart) + 1
3281 linenum += body.count('\n', begin, mstart) + 1
3282 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3282 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3283 begin = body.find('\n', mend) + 1 or len(body) + 1
3283 begin = body.find('\n', mend) + 1 or len(body) + 1
3284 lend = begin - 1
3284 lend = begin - 1
3285 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3285 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3286
3286
3287 class linestate(object):
3287 class linestate(object):
3288 def __init__(self, line, linenum, colstart, colend):
3288 def __init__(self, line, linenum, colstart, colend):
3289 self.line = line
3289 self.line = line
3290 self.linenum = linenum
3290 self.linenum = linenum
3291 self.colstart = colstart
3291 self.colstart = colstart
3292 self.colend = colend
3292 self.colend = colend
3293
3293
3294 def __hash__(self):
3294 def __hash__(self):
3295 return hash((self.linenum, self.line))
3295 return hash((self.linenum, self.line))
3296
3296
3297 def __eq__(self, other):
3297 def __eq__(self, other):
3298 return self.line == other.line
3298 return self.line == other.line
3299
3299
3300 def __iter__(self):
3300 def __iter__(self):
3301 yield (self.line[:self.colstart], '')
3301 yield (self.line[:self.colstart], '')
3302 yield (self.line[self.colstart:self.colend], 'grep.match')
3302 yield (self.line[self.colstart:self.colend], 'grep.match')
3303 rest = self.line[self.colend:]
3303 rest = self.line[self.colend:]
3304 while rest != '':
3304 while rest != '':
3305 match = regexp.search(rest)
3305 match = regexp.search(rest)
3306 if not match:
3306 if not match:
3307 yield (rest, '')
3307 yield (rest, '')
3308 break
3308 break
3309 mstart, mend = match.span()
3309 mstart, mend = match.span()
3310 yield (rest[:mstart], '')
3310 yield (rest[:mstart], '')
3311 yield (rest[mstart:mend], 'grep.match')
3311 yield (rest[mstart:mend], 'grep.match')
3312 rest = rest[mend:]
3312 rest = rest[mend:]
3313
3313
3314 matches = {}
3314 matches = {}
3315 copies = {}
3315 copies = {}
3316 def grepbody(fn, rev, body):
3316 def grepbody(fn, rev, body):
3317 matches[rev].setdefault(fn, [])
3317 matches[rev].setdefault(fn, [])
3318 m = matches[rev][fn]
3318 m = matches[rev][fn]
3319 for lnum, cstart, cend, line in matchlines(body):
3319 for lnum, cstart, cend, line in matchlines(body):
3320 s = linestate(line, lnum, cstart, cend)
3320 s = linestate(line, lnum, cstart, cend)
3321 m.append(s)
3321 m.append(s)
3322
3322
3323 def difflinestates(a, b):
3323 def difflinestates(a, b):
3324 sm = difflib.SequenceMatcher(None, a, b)
3324 sm = difflib.SequenceMatcher(None, a, b)
3325 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3325 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3326 if tag == 'insert':
3326 if tag == 'insert':
3327 for i in xrange(blo, bhi):
3327 for i in xrange(blo, bhi):
3328 yield ('+', b[i])
3328 yield ('+', b[i])
3329 elif tag == 'delete':
3329 elif tag == 'delete':
3330 for i in xrange(alo, ahi):
3330 for i in xrange(alo, ahi):
3331 yield ('-', a[i])
3331 yield ('-', a[i])
3332 elif tag == 'replace':
3332 elif tag == 'replace':
3333 for i in xrange(alo, ahi):
3333 for i in xrange(alo, ahi):
3334 yield ('-', a[i])
3334 yield ('-', a[i])
3335 for i in xrange(blo, bhi):
3335 for i in xrange(blo, bhi):
3336 yield ('+', b[i])
3336 yield ('+', b[i])
3337
3337
3338 def display(fn, ctx, pstates, states):
3338 def display(fn, ctx, pstates, states):
3339 rev = ctx.rev()
3339 rev = ctx.rev()
3340 datefunc = ui.quiet and util.shortdate or util.datestr
3340 datefunc = ui.quiet and util.shortdate or util.datestr
3341 found = False
3341 found = False
3342 @util.cachefunc
3342 @util.cachefunc
3343 def binary():
3343 def binary():
3344 flog = getfile(fn)
3344 flog = getfile(fn)
3345 return util.binary(flog.read(ctx.filenode(fn)))
3345 return util.binary(flog.read(ctx.filenode(fn)))
3346
3346
3347 if opts.get('all'):
3347 if opts.get('all'):
3348 iter = difflinestates(pstates, states)
3348 iter = difflinestates(pstates, states)
3349 else:
3349 else:
3350 iter = [('', l) for l in states]
3350 iter = [('', l) for l in states]
3351 for change, l in iter:
3351 for change, l in iter:
3352 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3352 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3353
3353
3354 if opts.get('line_number'):
3354 if opts.get('line_number'):
3355 cols.append((str(l.linenum), 'grep.linenumber'))
3355 cols.append((str(l.linenum), 'grep.linenumber'))
3356 if opts.get('all'):
3356 if opts.get('all'):
3357 cols.append((change, 'grep.change'))
3357 cols.append((change, 'grep.change'))
3358 if opts.get('user'):
3358 if opts.get('user'):
3359 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3359 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3360 if opts.get('date'):
3360 if opts.get('date'):
3361 cols.append((datefunc(ctx.date()), 'grep.date'))
3361 cols.append((datefunc(ctx.date()), 'grep.date'))
3362 for col, label in cols[:-1]:
3362 for col, label in cols[:-1]:
3363 ui.write(col, label=label)
3363 ui.write(col, label=label)
3364 ui.write(sep, label='grep.sep')
3364 ui.write(sep, label='grep.sep')
3365 ui.write(cols[-1][0], label=cols[-1][1])
3365 ui.write(cols[-1][0], label=cols[-1][1])
3366 if not opts.get('files_with_matches'):
3366 if not opts.get('files_with_matches'):
3367 ui.write(sep, label='grep.sep')
3367 ui.write(sep, label='grep.sep')
3368 if not opts.get('text') and binary():
3368 if not opts.get('text') and binary():
3369 ui.write(" Binary file matches")
3369 ui.write(" Binary file matches")
3370 else:
3370 else:
3371 for s, label in l:
3371 for s, label in l:
3372 ui.write(s, label=label)
3372 ui.write(s, label=label)
3373 ui.write(eol)
3373 ui.write(eol)
3374 found = True
3374 found = True
3375 if opts.get('files_with_matches'):
3375 if opts.get('files_with_matches'):
3376 break
3376 break
3377 return found
3377 return found
3378
3378
3379 skip = {}
3379 skip = {}
3380 revfiles = {}
3380 revfiles = {}
3381 matchfn = scmutil.match(repo[None], pats, opts)
3381 matchfn = scmutil.match(repo[None], pats, opts)
3382 found = False
3382 found = False
3383 follow = opts.get('follow')
3383 follow = opts.get('follow')
3384
3384
3385 def prep(ctx, fns):
3385 def prep(ctx, fns):
3386 rev = ctx.rev()
3386 rev = ctx.rev()
3387 pctx = ctx.p1()
3387 pctx = ctx.p1()
3388 parent = pctx.rev()
3388 parent = pctx.rev()
3389 matches.setdefault(rev, {})
3389 matches.setdefault(rev, {})
3390 matches.setdefault(parent, {})
3390 matches.setdefault(parent, {})
3391 files = revfiles.setdefault(rev, [])
3391 files = revfiles.setdefault(rev, [])
3392 for fn in fns:
3392 for fn in fns:
3393 flog = getfile(fn)
3393 flog = getfile(fn)
3394 try:
3394 try:
3395 fnode = ctx.filenode(fn)
3395 fnode = ctx.filenode(fn)
3396 except error.LookupError:
3396 except error.LookupError:
3397 continue
3397 continue
3398
3398
3399 copied = flog.renamed(fnode)
3399 copied = flog.renamed(fnode)
3400 copy = follow and copied and copied[0]
3400 copy = follow and copied and copied[0]
3401 if copy:
3401 if copy:
3402 copies.setdefault(rev, {})[fn] = copy
3402 copies.setdefault(rev, {})[fn] = copy
3403 if fn in skip:
3403 if fn in skip:
3404 if copy:
3404 if copy:
3405 skip[copy] = True
3405 skip[copy] = True
3406 continue
3406 continue
3407 files.append(fn)
3407 files.append(fn)
3408
3408
3409 if fn not in matches[rev]:
3409 if fn not in matches[rev]:
3410 grepbody(fn, rev, flog.read(fnode))
3410 grepbody(fn, rev, flog.read(fnode))
3411
3411
3412 pfn = copy or fn
3412 pfn = copy or fn
3413 if pfn not in matches[parent]:
3413 if pfn not in matches[parent]:
3414 try:
3414 try:
3415 fnode = pctx.filenode(pfn)
3415 fnode = pctx.filenode(pfn)
3416 grepbody(pfn, parent, flog.read(fnode))
3416 grepbody(pfn, parent, flog.read(fnode))
3417 except error.LookupError:
3417 except error.LookupError:
3418 pass
3418 pass
3419
3419
3420 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3420 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3421 rev = ctx.rev()
3421 rev = ctx.rev()
3422 parent = ctx.p1().rev()
3422 parent = ctx.p1().rev()
3423 for fn in sorted(revfiles.get(rev, [])):
3423 for fn in sorted(revfiles.get(rev, [])):
3424 states = matches[rev][fn]
3424 states = matches[rev][fn]
3425 copy = copies.get(rev, {}).get(fn)
3425 copy = copies.get(rev, {}).get(fn)
3426 if fn in skip:
3426 if fn in skip:
3427 if copy:
3427 if copy:
3428 skip[copy] = True
3428 skip[copy] = True
3429 continue
3429 continue
3430 pstates = matches.get(parent, {}).get(copy or fn, [])
3430 pstates = matches.get(parent, {}).get(copy or fn, [])
3431 if pstates or states:
3431 if pstates or states:
3432 r = display(fn, ctx, pstates, states)
3432 r = display(fn, ctx, pstates, states)
3433 found = found or r
3433 found = found or r
3434 if r and not opts.get('all'):
3434 if r and not opts.get('all'):
3435 skip[fn] = True
3435 skip[fn] = True
3436 if copy:
3436 if copy:
3437 skip[copy] = True
3437 skip[copy] = True
3438 del matches[rev]
3438 del matches[rev]
3439 del revfiles[rev]
3439 del revfiles[rev]
3440
3440
3441 return not found
3441 return not found
3442
3442
3443 @command('heads',
3443 @command('heads',
3444 [('r', 'rev', '',
3444 [('r', 'rev', '',
3445 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3445 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3446 ('t', 'topo', False, _('show topological heads only')),
3446 ('t', 'topo', False, _('show topological heads only')),
3447 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3447 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3448 ('c', 'closed', False, _('show normal and closed branch heads')),
3448 ('c', 'closed', False, _('show normal and closed branch heads')),
3449 ] + templateopts,
3449 ] + templateopts,
3450 _('[-ct] [-r STARTREV] [REV]...'))
3450 _('[-ct] [-r STARTREV] [REV]...'))
3451 def heads(ui, repo, *branchrevs, **opts):
3451 def heads(ui, repo, *branchrevs, **opts):
3452 """show branch heads
3452 """show branch heads
3453
3453
3454 With no arguments, show all open branch heads in the repository.
3454 With no arguments, show all open branch heads in the repository.
3455 Branch heads are changesets that have no descendants on the
3455 Branch heads are changesets that have no descendants on the
3456 same branch. They are where development generally takes place and
3456 same branch. They are where development generally takes place and
3457 are the usual targets for update and merge operations.
3457 are the usual targets for update and merge operations.
3458
3458
3459 If one or more REVs are given, only open branch heads on the
3459 If one or more REVs are given, only open branch heads on the
3460 branches associated with the specified changesets are shown. This
3460 branches associated with the specified changesets are shown. This
3461 means that you can use :hg:`heads .` to see the heads on the
3461 means that you can use :hg:`heads .` to see the heads on the
3462 currently checked-out branch.
3462 currently checked-out branch.
3463
3463
3464 If -c/--closed is specified, also show branch heads marked closed
3464 If -c/--closed is specified, also show branch heads marked closed
3465 (see :hg:`commit --close-branch`).
3465 (see :hg:`commit --close-branch`).
3466
3466
3467 If STARTREV is specified, only those heads that are descendants of
3467 If STARTREV is specified, only those heads that are descendants of
3468 STARTREV will be displayed.
3468 STARTREV will be displayed.
3469
3469
3470 If -t/--topo is specified, named branch mechanics will be ignored and only
3470 If -t/--topo is specified, named branch mechanics will be ignored and only
3471 topological heads (changesets with no children) will be shown.
3471 topological heads (changesets with no children) will be shown.
3472
3472
3473 Returns 0 if matching heads are found, 1 if not.
3473 Returns 0 if matching heads are found, 1 if not.
3474 """
3474 """
3475
3475
3476 start = None
3476 start = None
3477 if 'rev' in opts:
3477 if 'rev' in opts:
3478 start = scmutil.revsingle(repo, opts['rev'], None).node()
3478 start = scmutil.revsingle(repo, opts['rev'], None).node()
3479
3479
3480 if opts.get('topo'):
3480 if opts.get('topo'):
3481 heads = [repo[h] for h in repo.heads(start)]
3481 heads = [repo[h] for h in repo.heads(start)]
3482 else:
3482 else:
3483 heads = []
3483 heads = []
3484 for branch in repo.branchmap():
3484 for branch in repo.branchmap():
3485 heads += repo.branchheads(branch, start, opts.get('closed'))
3485 heads += repo.branchheads(branch, start, opts.get('closed'))
3486 heads = [repo[h] for h in heads]
3486 heads = [repo[h] for h in heads]
3487
3487
3488 if branchrevs:
3488 if branchrevs:
3489 branches = set(repo[br].branch() for br in branchrevs)
3489 branches = set(repo[br].branch() for br in branchrevs)
3490 heads = [h for h in heads if h.branch() in branches]
3490 heads = [h for h in heads if h.branch() in branches]
3491
3491
3492 if opts.get('active') and branchrevs:
3492 if opts.get('active') and branchrevs:
3493 dagheads = repo.heads(start)
3493 dagheads = repo.heads(start)
3494 heads = [h for h in heads if h.node() in dagheads]
3494 heads = [h for h in heads if h.node() in dagheads]
3495
3495
3496 if branchrevs:
3496 if branchrevs:
3497 haveheads = set(h.branch() for h in heads)
3497 haveheads = set(h.branch() for h in heads)
3498 if branches - haveheads:
3498 if branches - haveheads:
3499 headless = ', '.join(b for b in branches - haveheads)
3499 headless = ', '.join(b for b in branches - haveheads)
3500 msg = _('no open branch heads found on branches %s')
3500 msg = _('no open branch heads found on branches %s')
3501 if opts.get('rev'):
3501 if opts.get('rev'):
3502 msg += _(' (started at %s)') % opts['rev']
3502 msg += _(' (started at %s)') % opts['rev']
3503 ui.warn((msg + '\n') % headless)
3503 ui.warn((msg + '\n') % headless)
3504
3504
3505 if not heads:
3505 if not heads:
3506 return 1
3506 return 1
3507
3507
3508 heads = sorted(heads, key=lambda x: -x.rev())
3508 heads = sorted(heads, key=lambda x: -x.rev())
3509 displayer = cmdutil.show_changeset(ui, repo, opts)
3509 displayer = cmdutil.show_changeset(ui, repo, opts)
3510 for ctx in heads:
3510 for ctx in heads:
3511 displayer.show(ctx)
3511 displayer.show(ctx)
3512 displayer.close()
3512 displayer.close()
3513
3513
3514 @command('help',
3514 @command('help',
3515 [('e', 'extension', None, _('show only help for extensions')),
3515 [('e', 'extension', None, _('show only help for extensions')),
3516 ('c', 'command', None, _('show only help for commands')),
3516 ('c', 'command', None, _('show only help for commands')),
3517 ('k', 'keyword', '', _('show topics matching keyword')),
3517 ('k', 'keyword', '', _('show topics matching keyword')),
3518 ],
3518 ],
3519 _('[-ec] [TOPIC]'))
3519 _('[-ec] [TOPIC]'))
3520 def help_(ui, name=None, **opts):
3520 def help_(ui, name=None, **opts):
3521 """show help for a given topic or a help overview
3521 """show help for a given topic or a help overview
3522
3522
3523 With no arguments, print a list of commands with short help messages.
3523 With no arguments, print a list of commands with short help messages.
3524
3524
3525 Given a topic, extension, or command name, print help for that
3525 Given a topic, extension, or command name, print help for that
3526 topic.
3526 topic.
3527
3527
3528 Returns 0 if successful.
3528 Returns 0 if successful.
3529 """
3529 """
3530
3530
3531 textwidth = min(ui.termwidth(), 80) - 2
3531 textwidth = min(ui.termwidth(), 80) - 2
3532
3532
3533 keep = ui.verbose and ['verbose'] or []
3533 keep = ui.verbose and ['verbose'] or []
3534 text = help.help_(ui, name, **opts)
3534 text = help.help_(ui, name, **opts)
3535
3535
3536 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3536 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3537 if 'verbose' in pruned:
3537 if 'verbose' in pruned:
3538 keep.append('omitted')
3538 keep.append('omitted')
3539 else:
3539 else:
3540 keep.append('notomitted')
3540 keep.append('notomitted')
3541 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3541 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3542 ui.write(formatted)
3542 ui.write(formatted)
3543
3543
3544
3544
3545 @command('identify|id',
3545 @command('identify|id',
3546 [('r', 'rev', '',
3546 [('r', 'rev', '',
3547 _('identify the specified revision'), _('REV')),
3547 _('identify the specified revision'), _('REV')),
3548 ('n', 'num', None, _('show local revision number')),
3548 ('n', 'num', None, _('show local revision number')),
3549 ('i', 'id', None, _('show global revision id')),
3549 ('i', 'id', None, _('show global revision id')),
3550 ('b', 'branch', None, _('show branch')),
3550 ('b', 'branch', None, _('show branch')),
3551 ('t', 'tags', None, _('show tags')),
3551 ('t', 'tags', None, _('show tags')),
3552 ('B', 'bookmarks', None, _('show bookmarks')),
3552 ('B', 'bookmarks', None, _('show bookmarks')),
3553 ] + remoteopts,
3553 ] + remoteopts,
3554 _('[-nibtB] [-r REV] [SOURCE]'))
3554 _('[-nibtB] [-r REV] [SOURCE]'))
3555 def identify(ui, repo, source=None, rev=None,
3555 def identify(ui, repo, source=None, rev=None,
3556 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3556 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3557 """identify the working copy or specified revision
3557 """identify the working copy or specified revision
3558
3558
3559 Print a summary identifying the repository state at REV using one or
3559 Print a summary identifying the repository state at REV using one or
3560 two parent hash identifiers, followed by a "+" if the working
3560 two parent hash identifiers, followed by a "+" if the working
3561 directory has uncommitted changes, the branch name (if not default),
3561 directory has uncommitted changes, the branch name (if not default),
3562 a list of tags, and a list of bookmarks.
3562 a list of tags, and a list of bookmarks.
3563
3563
3564 When REV is not given, print a summary of the current state of the
3564 When REV is not given, print a summary of the current state of the
3565 repository.
3565 repository.
3566
3566
3567 Specifying a path to a repository root or Mercurial bundle will
3567 Specifying a path to a repository root or Mercurial bundle will
3568 cause lookup to operate on that repository/bundle.
3568 cause lookup to operate on that repository/bundle.
3569
3569
3570 .. container:: verbose
3570 .. container:: verbose
3571
3571
3572 Examples:
3572 Examples:
3573
3573
3574 - generate a build identifier for the working directory::
3574 - generate a build identifier for the working directory::
3575
3575
3576 hg id --id > build-id.dat
3576 hg id --id > build-id.dat
3577
3577
3578 - find the revision corresponding to a tag::
3578 - find the revision corresponding to a tag::
3579
3579
3580 hg id -n -r 1.3
3580 hg id -n -r 1.3
3581
3581
3582 - check the most recent revision of a remote repository::
3582 - check the most recent revision of a remote repository::
3583
3583
3584 hg id -r tip http://selenic.com/hg/
3584 hg id -r tip http://selenic.com/hg/
3585
3585
3586 Returns 0 if successful.
3586 Returns 0 if successful.
3587 """
3587 """
3588
3588
3589 if not repo and not source:
3589 if not repo and not source:
3590 raise util.Abort(_("there is no Mercurial repository here "
3590 raise util.Abort(_("there is no Mercurial repository here "
3591 "(.hg not found)"))
3591 "(.hg not found)"))
3592
3592
3593 hexfunc = ui.debugflag and hex or short
3593 hexfunc = ui.debugflag and hex or short
3594 default = not (num or id or branch or tags or bookmarks)
3594 default = not (num or id or branch or tags or bookmarks)
3595 output = []
3595 output = []
3596 revs = []
3596 revs = []
3597
3597
3598 if source:
3598 if source:
3599 source, branches = hg.parseurl(ui.expandpath(source))
3599 source, branches = hg.parseurl(ui.expandpath(source))
3600 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3600 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3601 repo = peer.local()
3601 repo = peer.local()
3602 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3602 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3603
3603
3604 if not repo:
3604 if not repo:
3605 if num or branch or tags:
3605 if num or branch or tags:
3606 raise util.Abort(
3606 raise util.Abort(
3607 _("can't query remote revision number, branch, or tags"))
3607 _("can't query remote revision number, branch, or tags"))
3608 if not rev and revs:
3608 if not rev and revs:
3609 rev = revs[0]
3609 rev = revs[0]
3610 if not rev:
3610 if not rev:
3611 rev = "tip"
3611 rev = "tip"
3612
3612
3613 remoterev = peer.lookup(rev)
3613 remoterev = peer.lookup(rev)
3614 if default or id:
3614 if default or id:
3615 output = [hexfunc(remoterev)]
3615 output = [hexfunc(remoterev)]
3616
3616
3617 def getbms():
3617 def getbms():
3618 bms = []
3618 bms = []
3619
3619
3620 if 'bookmarks' in peer.listkeys('namespaces'):
3620 if 'bookmarks' in peer.listkeys('namespaces'):
3621 hexremoterev = hex(remoterev)
3621 hexremoterev = hex(remoterev)
3622 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3622 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3623 if bmr == hexremoterev]
3623 if bmr == hexremoterev]
3624
3624
3625 return sorted(bms)
3625 return sorted(bms)
3626
3626
3627 if bookmarks:
3627 if bookmarks:
3628 output.extend(getbms())
3628 output.extend(getbms())
3629 elif default and not ui.quiet:
3629 elif default and not ui.quiet:
3630 # multiple bookmarks for a single parent separated by '/'
3630 # multiple bookmarks for a single parent separated by '/'
3631 bm = '/'.join(getbms())
3631 bm = '/'.join(getbms())
3632 if bm:
3632 if bm:
3633 output.append(bm)
3633 output.append(bm)
3634 else:
3634 else:
3635 if not rev:
3635 if not rev:
3636 ctx = repo[None]
3636 ctx = repo[None]
3637 parents = ctx.parents()
3637 parents = ctx.parents()
3638 changed = ""
3638 changed = ""
3639 if default or id or num:
3639 if default or id or num:
3640 if (util.any(repo.status())
3640 if (util.any(repo.status())
3641 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3641 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3642 changed = '+'
3642 changed = '+'
3643 if default or id:
3643 if default or id:
3644 output = ["%s%s" %
3644 output = ["%s%s" %
3645 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3645 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3646 if num:
3646 if num:
3647 output.append("%s%s" %
3647 output.append("%s%s" %
3648 ('+'.join([str(p.rev()) for p in parents]), changed))
3648 ('+'.join([str(p.rev()) for p in parents]), changed))
3649 else:
3649 else:
3650 ctx = scmutil.revsingle(repo, rev)
3650 ctx = scmutil.revsingle(repo, rev)
3651 if default or id:
3651 if default or id:
3652 output = [hexfunc(ctx.node())]
3652 output = [hexfunc(ctx.node())]
3653 if num:
3653 if num:
3654 output.append(str(ctx.rev()))
3654 output.append(str(ctx.rev()))
3655
3655
3656 if default and not ui.quiet:
3656 if default and not ui.quiet:
3657 b = ctx.branch()
3657 b = ctx.branch()
3658 if b != 'default':
3658 if b != 'default':
3659 output.append("(%s)" % b)
3659 output.append("(%s)" % b)
3660
3660
3661 # multiple tags for a single parent separated by '/'
3661 # multiple tags for a single parent separated by '/'
3662 t = '/'.join(ctx.tags())
3662 t = '/'.join(ctx.tags())
3663 if t:
3663 if t:
3664 output.append(t)
3664 output.append(t)
3665
3665
3666 # multiple bookmarks for a single parent separated by '/'
3666 # multiple bookmarks for a single parent separated by '/'
3667 bm = '/'.join(ctx.bookmarks())
3667 bm = '/'.join(ctx.bookmarks())
3668 if bm:
3668 if bm:
3669 output.append(bm)
3669 output.append(bm)
3670 else:
3670 else:
3671 if branch:
3671 if branch:
3672 output.append(ctx.branch())
3672 output.append(ctx.branch())
3673
3673
3674 if tags:
3674 if tags:
3675 output.extend(ctx.tags())
3675 output.extend(ctx.tags())
3676
3676
3677 if bookmarks:
3677 if bookmarks:
3678 output.extend(ctx.bookmarks())
3678 output.extend(ctx.bookmarks())
3679
3679
3680 ui.write("%s\n" % ' '.join(output))
3680 ui.write("%s\n" % ' '.join(output))
3681
3681
3682 @command('import|patch',
3682 @command('import|patch',
3683 [('p', 'strip', 1,
3683 [('p', 'strip', 1,
3684 _('directory strip option for patch. This has the same '
3684 _('directory strip option for patch. This has the same '
3685 'meaning as the corresponding patch option'), _('NUM')),
3685 'meaning as the corresponding patch option'), _('NUM')),
3686 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3686 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3687 ('e', 'edit', False, _('invoke editor on commit messages')),
3687 ('e', 'edit', False, _('invoke editor on commit messages')),
3688 ('f', 'force', None,
3688 ('f', 'force', None,
3689 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3689 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3690 ('', 'no-commit', None,
3690 ('', 'no-commit', None,
3691 _("don't commit, just update the working directory")),
3691 _("don't commit, just update the working directory")),
3692 ('', 'bypass', None,
3692 ('', 'bypass', None,
3693 _("apply patch without touching the working directory")),
3693 _("apply patch without touching the working directory")),
3694 ('', 'exact', None,
3694 ('', 'exact', None,
3695 _('apply patch to the nodes from which it was generated')),
3695 _('apply patch to the nodes from which it was generated')),
3696 ('', 'import-branch', None,
3696 ('', 'import-branch', None,
3697 _('use any branch information in patch (implied by --exact)'))] +
3697 _('use any branch information in patch (implied by --exact)'))] +
3698 commitopts + commitopts2 + similarityopts,
3698 commitopts + commitopts2 + similarityopts,
3699 _('[OPTION]... PATCH...'))
3699 _('[OPTION]... PATCH...'))
3700 def import_(ui, repo, patch1=None, *patches, **opts):
3700 def import_(ui, repo, patch1=None, *patches, **opts):
3701 """import an ordered set of patches
3701 """import an ordered set of patches
3702
3702
3703 Import a list of patches and commit them individually (unless
3703 Import a list of patches and commit them individually (unless
3704 --no-commit is specified).
3704 --no-commit is specified).
3705
3705
3706 Because import first applies changes to the working directory,
3706 Because import first applies changes to the working directory,
3707 import will abort if there are outstanding changes.
3707 import will abort if there are outstanding changes.
3708
3708
3709 You can import a patch straight from a mail message. Even patches
3709 You can import a patch straight from a mail message. Even patches
3710 as attachments work (to use the body part, it must have type
3710 as attachments work (to use the body part, it must have type
3711 text/plain or text/x-patch). From and Subject headers of email
3711 text/plain or text/x-patch). From and Subject headers of email
3712 message are used as default committer and commit message. All
3712 message are used as default committer and commit message. All
3713 text/plain body parts before first diff are added to commit
3713 text/plain body parts before first diff are added to commit
3714 message.
3714 message.
3715
3715
3716 If the imported patch was generated by :hg:`export`, user and
3716 If the imported patch was generated by :hg:`export`, user and
3717 description from patch override values from message headers and
3717 description from patch override values from message headers and
3718 body. Values given on command line with -m/--message and -u/--user
3718 body. Values given on command line with -m/--message and -u/--user
3719 override these.
3719 override these.
3720
3720
3721 If --exact is specified, import will set the working directory to
3721 If --exact is specified, import will set the working directory to
3722 the parent of each patch before applying it, and will abort if the
3722 the parent of each patch before applying it, and will abort if the
3723 resulting changeset has a different ID than the one recorded in
3723 resulting changeset has a different ID than the one recorded in
3724 the patch. This may happen due to character set problems or other
3724 the patch. This may happen due to character set problems or other
3725 deficiencies in the text patch format.
3725 deficiencies in the text patch format.
3726
3726
3727 Use --bypass to apply and commit patches directly to the
3727 Use --bypass to apply and commit patches directly to the
3728 repository, not touching the working directory. Without --exact,
3728 repository, not touching the working directory. Without --exact,
3729 patches will be applied on top of the working directory parent
3729 patches will be applied on top of the working directory parent
3730 revision.
3730 revision.
3731
3731
3732 With -s/--similarity, hg will attempt to discover renames and
3732 With -s/--similarity, hg will attempt to discover renames and
3733 copies in the patch in the same way as :hg:`addremove`.
3733 copies in the patch in the same way as :hg:`addremove`.
3734
3734
3735 To read a patch from standard input, use "-" as the patch name. If
3735 To read a patch from standard input, use "-" as the patch name. If
3736 a URL is specified, the patch will be downloaded from it.
3736 a URL is specified, the patch will be downloaded from it.
3737 See :hg:`help dates` for a list of formats valid for -d/--date.
3737 See :hg:`help dates` for a list of formats valid for -d/--date.
3738
3738
3739 .. container:: verbose
3739 .. container:: verbose
3740
3740
3741 Examples:
3741 Examples:
3742
3742
3743 - import a traditional patch from a website and detect renames::
3743 - import a traditional patch from a website and detect renames::
3744
3744
3745 hg import -s 80 http://example.com/bugfix.patch
3745 hg import -s 80 http://example.com/bugfix.patch
3746
3746
3747 - import a changeset from an hgweb server::
3747 - import a changeset from an hgweb server::
3748
3748
3749 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3749 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3750
3750
3751 - import all the patches in an Unix-style mbox::
3751 - import all the patches in an Unix-style mbox::
3752
3752
3753 hg import incoming-patches.mbox
3753 hg import incoming-patches.mbox
3754
3754
3755 - attempt to exactly restore an exported changeset (not always
3755 - attempt to exactly restore an exported changeset (not always
3756 possible)::
3756 possible)::
3757
3757
3758 hg import --exact proposed-fix.patch
3758 hg import --exact proposed-fix.patch
3759
3759
3760 Returns 0 on success.
3760 Returns 0 on success.
3761 """
3761 """
3762
3762
3763 if not patch1:
3763 if not patch1:
3764 raise util.Abort(_('need at least one patch to import'))
3764 raise util.Abort(_('need at least one patch to import'))
3765
3765
3766 patches = (patch1,) + patches
3766 patches = (patch1,) + patches
3767
3767
3768 date = opts.get('date')
3768 date = opts.get('date')
3769 if date:
3769 if date:
3770 opts['date'] = util.parsedate(date)
3770 opts['date'] = util.parsedate(date)
3771
3771
3772 update = not opts.get('bypass')
3772 update = not opts.get('bypass')
3773 if not update and opts.get('no_commit'):
3773 if not update and opts.get('no_commit'):
3774 raise util.Abort(_('cannot use --no-commit with --bypass'))
3774 raise util.Abort(_('cannot use --no-commit with --bypass'))
3775 try:
3775 try:
3776 sim = float(opts.get('similarity') or 0)
3776 sim = float(opts.get('similarity') or 0)
3777 except ValueError:
3777 except ValueError:
3778 raise util.Abort(_('similarity must be a number'))
3778 raise util.Abort(_('similarity must be a number'))
3779 if sim < 0 or sim > 100:
3779 if sim < 0 or sim > 100:
3780 raise util.Abort(_('similarity must be between 0 and 100'))
3780 raise util.Abort(_('similarity must be between 0 and 100'))
3781 if sim and not update:
3781 if sim and not update:
3782 raise util.Abort(_('cannot use --similarity with --bypass'))
3782 raise util.Abort(_('cannot use --similarity with --bypass'))
3783
3783
3784 if update:
3784 if update:
3785 cmdutil.checkunfinished(repo)
3785 cmdutil.checkunfinished(repo)
3786 if (opts.get('exact') or not opts.get('force')) and update:
3786 if (opts.get('exact') or not opts.get('force')) and update:
3787 cmdutil.bailifchanged(repo)
3787 cmdutil.bailifchanged(repo)
3788
3788
3789 base = opts["base"]
3789 base = opts["base"]
3790 wlock = lock = tr = None
3790 wlock = lock = tr = None
3791 msgs = []
3791 msgs = []
3792
3792
3793
3793
3794 try:
3794 try:
3795 try:
3795 try:
3796 wlock = repo.wlock()
3796 wlock = repo.wlock()
3797 if not opts.get('no_commit'):
3797 if not opts.get('no_commit'):
3798 lock = repo.lock()
3798 lock = repo.lock()
3799 tr = repo.transaction('import')
3799 tr = repo.transaction('import')
3800 parents = repo.parents()
3800 parents = repo.parents()
3801 for patchurl in patches:
3801 for patchurl in patches:
3802 if patchurl == '-':
3802 if patchurl == '-':
3803 ui.status(_('applying patch from stdin\n'))
3803 ui.status(_('applying patch from stdin\n'))
3804 patchfile = ui.fin
3804 patchfile = ui.fin
3805 patchurl = 'stdin' # for error message
3805 patchurl = 'stdin' # for error message
3806 else:
3806 else:
3807 patchurl = os.path.join(base, patchurl)
3807 patchurl = os.path.join(base, patchurl)
3808 ui.status(_('applying %s\n') % patchurl)
3808 ui.status(_('applying %s\n') % patchurl)
3809 patchfile = hg.openpath(ui, patchurl)
3809 patchfile = hg.openpath(ui, patchurl)
3810
3810
3811 haspatch = False
3811 haspatch = False
3812 for hunk in patch.split(patchfile):
3812 for hunk in patch.split(patchfile):
3813 (msg, node) = cmdutil.tryimportone(ui, repo, hunk, parents,
3813 (msg, node) = cmdutil.tryimportone(ui, repo, hunk, parents,
3814 opts, msgs, hg.clean)
3814 opts, msgs, hg.clean)
3815 if msg:
3815 if msg:
3816 haspatch = True
3816 haspatch = True
3817 ui.note(msg + '\n')
3817 ui.note(msg + '\n')
3818 if update or opts.get('exact'):
3818 if update or opts.get('exact'):
3819 parents = repo.parents()
3819 parents = repo.parents()
3820 else:
3820 else:
3821 parents = [repo[node]]
3821 parents = [repo[node]]
3822
3822
3823 if not haspatch:
3823 if not haspatch:
3824 raise util.Abort(_('%s: no diffs found') % patchurl)
3824 raise util.Abort(_('%s: no diffs found') % patchurl)
3825
3825
3826 if tr:
3826 if tr:
3827 tr.close()
3827 tr.close()
3828 if msgs:
3828 if msgs:
3829 repo.savecommitmessage('\n* * *\n'.join(msgs))
3829 repo.savecommitmessage('\n* * *\n'.join(msgs))
3830 except: # re-raises
3830 except: # re-raises
3831 # wlock.release() indirectly calls dirstate.write(): since
3831 # wlock.release() indirectly calls dirstate.write(): since
3832 # we're crashing, we do not want to change the working dir
3832 # we're crashing, we do not want to change the working dir
3833 # parent after all, so make sure it writes nothing
3833 # parent after all, so make sure it writes nothing
3834 repo.dirstate.invalidate()
3834 repo.dirstate.invalidate()
3835 raise
3835 raise
3836 finally:
3836 finally:
3837 if tr:
3837 if tr:
3838 tr.release()
3838 tr.release()
3839 release(lock, wlock)
3839 release(lock, wlock)
3840
3840
3841 @command('incoming|in',
3841 @command('incoming|in',
3842 [('f', 'force', None,
3842 [('f', 'force', None,
3843 _('run even if remote repository is unrelated')),
3843 _('run even if remote repository is unrelated')),
3844 ('n', 'newest-first', None, _('show newest record first')),
3844 ('n', 'newest-first', None, _('show newest record first')),
3845 ('', 'bundle', '',
3845 ('', 'bundle', '',
3846 _('file to store the bundles into'), _('FILE')),
3846 _('file to store the bundles into'), _('FILE')),
3847 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3847 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3848 ('B', 'bookmarks', False, _("compare bookmarks")),
3848 ('B', 'bookmarks', False, _("compare bookmarks")),
3849 ('b', 'branch', [],
3849 ('b', 'branch', [],
3850 _('a specific branch you would like to pull'), _('BRANCH')),
3850 _('a specific branch you would like to pull'), _('BRANCH')),
3851 ] + logopts + remoteopts + subrepoopts,
3851 ] + logopts + remoteopts + subrepoopts,
3852 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3852 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3853 def incoming(ui, repo, source="default", **opts):
3853 def incoming(ui, repo, source="default", **opts):
3854 """show new changesets found in source
3854 """show new changesets found in source
3855
3855
3856 Show new changesets found in the specified path/URL or the default
3856 Show new changesets found in the specified path/URL or the default
3857 pull location. These are the changesets that would have been pulled
3857 pull location. These are the changesets that would have been pulled
3858 if a pull at the time you issued this command.
3858 if a pull at the time you issued this command.
3859
3859
3860 For remote repository, using --bundle avoids downloading the
3860 For remote repository, using --bundle avoids downloading the
3861 changesets twice if the incoming is followed by a pull.
3861 changesets twice if the incoming is followed by a pull.
3862
3862
3863 See pull for valid source format details.
3863 See pull for valid source format details.
3864
3864
3865 .. container:: verbose
3865 .. container:: verbose
3866
3866
3867 Examples:
3867 Examples:
3868
3868
3869 - show incoming changes with patches and full description::
3869 - show incoming changes with patches and full description::
3870
3870
3871 hg incoming -vp
3871 hg incoming -vp
3872
3872
3873 - show incoming changes excluding merges, store a bundle::
3873 - show incoming changes excluding merges, store a bundle::
3874
3874
3875 hg in -vpM --bundle incoming.hg
3875 hg in -vpM --bundle incoming.hg
3876 hg pull incoming.hg
3876 hg pull incoming.hg
3877
3877
3878 - briefly list changes inside a bundle::
3878 - briefly list changes inside a bundle::
3879
3879
3880 hg in changes.hg -T "{desc|firstline}\\n"
3880 hg in changes.hg -T "{desc|firstline}\\n"
3881
3881
3882 Returns 0 if there are incoming changes, 1 otherwise.
3882 Returns 0 if there are incoming changes, 1 otherwise.
3883 """
3883 """
3884 if opts.get('graph'):
3884 if opts.get('graph'):
3885 cmdutil.checkunsupportedgraphflags([], opts)
3885 cmdutil.checkunsupportedgraphflags([], opts)
3886 def display(other, chlist, displayer):
3886 def display(other, chlist, displayer):
3887 revdag = cmdutil.graphrevs(other, chlist, opts)
3887 revdag = cmdutil.graphrevs(other, chlist, opts)
3888 showparents = [ctx.node() for ctx in repo[None].parents()]
3888 showparents = [ctx.node() for ctx in repo[None].parents()]
3889 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3889 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3890 graphmod.asciiedges)
3890 graphmod.asciiedges)
3891
3891
3892 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3892 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3893 return 0
3893 return 0
3894
3894
3895 if opts.get('bundle') and opts.get('subrepos'):
3895 if opts.get('bundle') and opts.get('subrepos'):
3896 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3896 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3897
3897
3898 if opts.get('bookmarks'):
3898 if opts.get('bookmarks'):
3899 source, branches = hg.parseurl(ui.expandpath(source),
3899 source, branches = hg.parseurl(ui.expandpath(source),
3900 opts.get('branch'))
3900 opts.get('branch'))
3901 other = hg.peer(repo, opts, source)
3901 other = hg.peer(repo, opts, source)
3902 if 'bookmarks' not in other.listkeys('namespaces'):
3902 if 'bookmarks' not in other.listkeys('namespaces'):
3903 ui.warn(_("remote doesn't support bookmarks\n"))
3903 ui.warn(_("remote doesn't support bookmarks\n"))
3904 return 0
3904 return 0
3905 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3905 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3906 return bookmarks.diff(ui, repo, other)
3906 return bookmarks.diff(ui, repo, other)
3907
3907
3908 repo._subtoppath = ui.expandpath(source)
3908 repo._subtoppath = ui.expandpath(source)
3909 try:
3909 try:
3910 return hg.incoming(ui, repo, source, opts)
3910 return hg.incoming(ui, repo, source, opts)
3911 finally:
3911 finally:
3912 del repo._subtoppath
3912 del repo._subtoppath
3913
3913
3914
3914
3915 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3915 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3916 def init(ui, dest=".", **opts):
3916 def init(ui, dest=".", **opts):
3917 """create a new repository in the given directory
3917 """create a new repository in the given directory
3918
3918
3919 Initialize a new repository in the given directory. If the given
3919 Initialize a new repository in the given directory. If the given
3920 directory does not exist, it will be created.
3920 directory does not exist, it will be created.
3921
3921
3922 If no directory is given, the current directory is used.
3922 If no directory is given, the current directory is used.
3923
3923
3924 It is possible to specify an ``ssh://`` URL as the destination.
3924 It is possible to specify an ``ssh://`` URL as the destination.
3925 See :hg:`help urls` for more information.
3925 See :hg:`help urls` for more information.
3926
3926
3927 Returns 0 on success.
3927 Returns 0 on success.
3928 """
3928 """
3929 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3929 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3930
3930
3931 @command('locate',
3931 @command('locate',
3932 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3932 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3933 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3933 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3934 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3934 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3935 ] + walkopts,
3935 ] + walkopts,
3936 _('[OPTION]... [PATTERN]...'))
3936 _('[OPTION]... [PATTERN]...'))
3937 def locate(ui, repo, *pats, **opts):
3937 def locate(ui, repo, *pats, **opts):
3938 """locate files matching specific patterns
3938 """locate files matching specific patterns
3939
3939
3940 Print files under Mercurial control in the working directory whose
3940 Print files under Mercurial control in the working directory whose
3941 names match the given patterns.
3941 names match the given patterns.
3942
3942
3943 By default, this command searches all directories in the working
3943 By default, this command searches all directories in the working
3944 directory. To search just the current directory and its
3944 directory. To search just the current directory and its
3945 subdirectories, use "--include .".
3945 subdirectories, use "--include .".
3946
3946
3947 If no patterns are given to match, this command prints the names
3947 If no patterns are given to match, this command prints the names
3948 of all files under Mercurial control in the working directory.
3948 of all files under Mercurial control in the working directory.
3949
3949
3950 If you want to feed the output of this command into the "xargs"
3950 If you want to feed the output of this command into the "xargs"
3951 command, use the -0 option to both this command and "xargs". This
3951 command, use the -0 option to both this command and "xargs". This
3952 will avoid the problem of "xargs" treating single filenames that
3952 will avoid the problem of "xargs" treating single filenames that
3953 contain whitespace as multiple filenames.
3953 contain whitespace as multiple filenames.
3954
3954
3955 Returns 0 if a match is found, 1 otherwise.
3955 Returns 0 if a match is found, 1 otherwise.
3956 """
3956 """
3957 end = opts.get('print0') and '\0' or '\n'
3957 end = opts.get('print0') and '\0' or '\n'
3958 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3958 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3959
3959
3960 ret = 1
3960 ret = 1
3961 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3961 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3962 m.bad = lambda x, y: False
3962 m.bad = lambda x, y: False
3963 for abs in repo[rev].walk(m):
3963 for abs in repo[rev].walk(m):
3964 if not rev and abs not in repo.dirstate:
3964 if not rev and abs not in repo.dirstate:
3965 continue
3965 continue
3966 if opts.get('fullpath'):
3966 if opts.get('fullpath'):
3967 ui.write(repo.wjoin(abs), end)
3967 ui.write(repo.wjoin(abs), end)
3968 else:
3968 else:
3969 ui.write(((pats and m.rel(abs)) or abs), end)
3969 ui.write(((pats and m.rel(abs)) or abs), end)
3970 ret = 0
3970 ret = 0
3971
3971
3972 return ret
3972 return ret
3973
3973
3974 @command('^log|history',
3974 @command('^log|history',
3975 [('f', 'follow', None,
3975 [('f', 'follow', None,
3976 _('follow changeset history, or file history across copies and renames')),
3976 _('follow changeset history, or file history across copies and renames')),
3977 ('', 'follow-first', None,
3977 ('', 'follow-first', None,
3978 _('only follow the first parent of merge changesets (DEPRECATED)')),
3978 _('only follow the first parent of merge changesets (DEPRECATED)')),
3979 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3979 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3980 ('C', 'copies', None, _('show copied files')),
3980 ('C', 'copies', None, _('show copied files')),
3981 ('k', 'keyword', [],
3981 ('k', 'keyword', [],
3982 _('do case-insensitive search for a given text'), _('TEXT')),
3982 _('do case-insensitive search for a given text'), _('TEXT')),
3983 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3983 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3984 ('', 'removed', None, _('include revisions where files were removed')),
3984 ('', 'removed', None, _('include revisions where files were removed')),
3985 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3985 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3986 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3986 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3987 ('', 'only-branch', [],
3987 ('', 'only-branch', [],
3988 _('show only changesets within the given named branch (DEPRECATED)'),
3988 _('show only changesets within the given named branch (DEPRECATED)'),
3989 _('BRANCH')),
3989 _('BRANCH')),
3990 ('b', 'branch', [],
3990 ('b', 'branch', [],
3991 _('show changesets within the given named branch'), _('BRANCH')),
3991 _('show changesets within the given named branch'), _('BRANCH')),
3992 ('P', 'prune', [],
3992 ('P', 'prune', [],
3993 _('do not display revision or any of its ancestors'), _('REV')),
3993 _('do not display revision or any of its ancestors'), _('REV')),
3994 ] + logopts + walkopts,
3994 ] + logopts + walkopts,
3995 _('[OPTION]... [FILE]'))
3995 _('[OPTION]... [FILE]'))
3996 def log(ui, repo, *pats, **opts):
3996 def log(ui, repo, *pats, **opts):
3997 """show revision history of entire repository or files
3997 """show revision history of entire repository or files
3998
3998
3999 Print the revision history of the specified files or the entire
3999 Print the revision history of the specified files or the entire
4000 project.
4000 project.
4001
4001
4002 If no revision range is specified, the default is ``tip:0`` unless
4002 If no revision range is specified, the default is ``tip:0`` unless
4003 --follow is set, in which case the working directory parent is
4003 --follow is set, in which case the working directory parent is
4004 used as the starting revision.
4004 used as the starting revision.
4005
4005
4006 File history is shown without following rename or copy history of
4006 File history is shown without following rename or copy history of
4007 files. Use -f/--follow with a filename to follow history across
4007 files. Use -f/--follow with a filename to follow history across
4008 renames and copies. --follow without a filename will only show
4008 renames and copies. --follow without a filename will only show
4009 ancestors or descendants of the starting revision.
4009 ancestors or descendants of the starting revision.
4010
4010
4011 By default this command prints revision number and changeset id,
4011 By default this command prints revision number and changeset id,
4012 tags, non-trivial parents, user, date and time, and a summary for
4012 tags, non-trivial parents, user, date and time, and a summary for
4013 each commit. When the -v/--verbose switch is used, the list of
4013 each commit. When the -v/--verbose switch is used, the list of
4014 changed files and full commit message are shown.
4014 changed files and full commit message are shown.
4015
4015
4016 With --graph the revisions are shown as an ASCII art DAG with the most
4016 With --graph the revisions are shown as an ASCII art DAG with the most
4017 recent changeset at the top.
4017 recent changeset at the top.
4018 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4018 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4019 and '+' represents a fork where the changeset from the lines below is a
4019 and '+' represents a fork where the changeset from the lines below is a
4020 parent of the 'o' merge on the same line.
4020 parent of the 'o' merge on the same line.
4021
4021
4022 .. note::
4022 .. note::
4023
4023
4024 log -p/--patch may generate unexpected diff output for merge
4024 log -p/--patch may generate unexpected diff output for merge
4025 changesets, as it will only compare the merge changeset against
4025 changesets, as it will only compare the merge changeset against
4026 its first parent. Also, only files different from BOTH parents
4026 its first parent. Also, only files different from BOTH parents
4027 will appear in files:.
4027 will appear in files:.
4028
4028
4029 .. note::
4029 .. note::
4030
4030
4031 for performance reasons, log FILE may omit duplicate changes
4031 for performance reasons, log FILE may omit duplicate changes
4032 made on branches and will not show deletions. To see all
4032 made on branches and will not show deletions. To see all
4033 changes including duplicates and deletions, use the --removed
4033 changes including duplicates and deletions, use the --removed
4034 switch.
4034 switch.
4035
4035
4036 .. container:: verbose
4036 .. container:: verbose
4037
4037
4038 Some examples:
4038 Some examples:
4039
4039
4040 - changesets with full descriptions and file lists::
4040 - changesets with full descriptions and file lists::
4041
4041
4042 hg log -v
4042 hg log -v
4043
4043
4044 - changesets ancestral to the working directory::
4044 - changesets ancestral to the working directory::
4045
4045
4046 hg log -f
4046 hg log -f
4047
4047
4048 - last 10 commits on the current branch::
4048 - last 10 commits on the current branch::
4049
4049
4050 hg log -l 10 -b .
4050 hg log -l 10 -b .
4051
4051
4052 - changesets showing all modifications of a file, including removals::
4052 - changesets showing all modifications of a file, including removals::
4053
4053
4054 hg log --removed file.c
4054 hg log --removed file.c
4055
4055
4056 - all changesets that touch a directory, with diffs, excluding merges::
4056 - all changesets that touch a directory, with diffs, excluding merges::
4057
4057
4058 hg log -Mp lib/
4058 hg log -Mp lib/
4059
4059
4060 - all revision numbers that match a keyword::
4060 - all revision numbers that match a keyword::
4061
4061
4062 hg log -k bug --template "{rev}\\n"
4062 hg log -k bug --template "{rev}\\n"
4063
4063
4064 - check if a given changeset is included is a tagged release::
4064 - check if a given changeset is included is a tagged release::
4065
4065
4066 hg log -r "a21ccf and ancestor(1.9)"
4066 hg log -r "a21ccf and ancestor(1.9)"
4067
4067
4068 - find all changesets by some user in a date range::
4068 - find all changesets by some user in a date range::
4069
4069
4070 hg log -k alice -d "may 2008 to jul 2008"
4070 hg log -k alice -d "may 2008 to jul 2008"
4071
4071
4072 - summary of all changesets after the last tag::
4072 - summary of all changesets after the last tag::
4073
4073
4074 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4074 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4075
4075
4076 See :hg:`help dates` for a list of formats valid for -d/--date.
4076 See :hg:`help dates` for a list of formats valid for -d/--date.
4077
4077
4078 See :hg:`help revisions` and :hg:`help revsets` for more about
4078 See :hg:`help revisions` and :hg:`help revsets` for more about
4079 specifying revisions.
4079 specifying revisions.
4080
4080
4081 See :hg:`help templates` for more about pre-packaged styles and
4081 See :hg:`help templates` for more about pre-packaged styles and
4082 specifying custom templates.
4082 specifying custom templates.
4083
4083
4084 Returns 0 on success.
4084 Returns 0 on success.
4085 """
4085 """
4086 if opts.get('graph'):
4086 if opts.get('graph'):
4087 return cmdutil.graphlog(ui, repo, *pats, **opts)
4087 return cmdutil.graphlog(ui, repo, *pats, **opts)
4088
4088
4089 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4089 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4090 limit = cmdutil.loglimit(opts)
4090 limit = cmdutil.loglimit(opts)
4091 count = 0
4091 count = 0
4092
4092
4093 getrenamed = None
4093 getrenamed = None
4094 if opts.get('copies'):
4094 if opts.get('copies'):
4095 endrev = None
4095 endrev = None
4096 if opts.get('rev'):
4096 if opts.get('rev'):
4097 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4097 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4098 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4098 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4099
4099
4100 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4100 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4101 for rev in revs:
4101 for rev in revs:
4102 if count == limit:
4102 if count == limit:
4103 break
4103 break
4104 ctx = repo[rev]
4104 ctx = repo[rev]
4105 copies = None
4105 copies = None
4106 if getrenamed is not None and rev:
4106 if getrenamed is not None and rev:
4107 copies = []
4107 copies = []
4108 for fn in ctx.files():
4108 for fn in ctx.files():
4109 rename = getrenamed(fn, rev)
4109 rename = getrenamed(fn, rev)
4110 if rename:
4110 if rename:
4111 copies.append((fn, rename[0]))
4111 copies.append((fn, rename[0]))
4112 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4112 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4113 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4113 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4114 if displayer.flush(rev):
4114 if displayer.flush(rev):
4115 count += 1
4115 count += 1
4116
4116
4117 displayer.close()
4117 displayer.close()
4118
4118
4119 @command('manifest',
4119 @command('manifest',
4120 [('r', 'rev', '', _('revision to display'), _('REV')),
4120 [('r', 'rev', '', _('revision to display'), _('REV')),
4121 ('', 'all', False, _("list files from all revisions"))],
4121 ('', 'all', False, _("list files from all revisions"))],
4122 _('[-r REV]'))
4122 _('[-r REV]'))
4123 def manifest(ui, repo, node=None, rev=None, **opts):
4123 def manifest(ui, repo, node=None, rev=None, **opts):
4124 """output the current or given revision of the project manifest
4124 """output the current or given revision of the project manifest
4125
4125
4126 Print a list of version controlled files for the given revision.
4126 Print a list of version controlled files for the given revision.
4127 If no revision is given, the first parent of the working directory
4127 If no revision is given, the first parent of the working directory
4128 is used, or the null revision if no revision is checked out.
4128 is used, or the null revision if no revision is checked out.
4129
4129
4130 With -v, print file permissions, symlink and executable bits.
4130 With -v, print file permissions, symlink and executable bits.
4131 With --debug, print file revision hashes.
4131 With --debug, print file revision hashes.
4132
4132
4133 If option --all is specified, the list of all files from all revisions
4133 If option --all is specified, the list of all files from all revisions
4134 is printed. This includes deleted and renamed files.
4134 is printed. This includes deleted and renamed files.
4135
4135
4136 Returns 0 on success.
4136 Returns 0 on success.
4137 """
4137 """
4138
4138
4139 fm = ui.formatter('manifest', opts)
4139 fm = ui.formatter('manifest', opts)
4140
4140
4141 if opts.get('all'):
4141 if opts.get('all'):
4142 if rev or node:
4142 if rev or node:
4143 raise util.Abort(_("can't specify a revision with --all"))
4143 raise util.Abort(_("can't specify a revision with --all"))
4144
4144
4145 res = []
4145 res = []
4146 prefix = "data/"
4146 prefix = "data/"
4147 suffix = ".i"
4147 suffix = ".i"
4148 plen = len(prefix)
4148 plen = len(prefix)
4149 slen = len(suffix)
4149 slen = len(suffix)
4150 lock = repo.lock()
4150 lock = repo.lock()
4151 try:
4151 try:
4152 for fn, b, size in repo.store.datafiles():
4152 for fn, b, size in repo.store.datafiles():
4153 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4153 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4154 res.append(fn[plen:-slen])
4154 res.append(fn[plen:-slen])
4155 finally:
4155 finally:
4156 lock.release()
4156 lock.release()
4157 for f in res:
4157 for f in res:
4158 fm.startitem()
4158 fm.startitem()
4159 fm.write("path", '%s\n', f)
4159 fm.write("path", '%s\n', f)
4160 fm.end()
4160 fm.end()
4161 return
4161 return
4162
4162
4163 if rev and node:
4163 if rev and node:
4164 raise util.Abort(_("please specify just one revision"))
4164 raise util.Abort(_("please specify just one revision"))
4165
4165
4166 if not node:
4166 if not node:
4167 node = rev
4167 node = rev
4168
4168
4169 char = {'l': '@', 'x': '*', '': ''}
4169 char = {'l': '@', 'x': '*', '': ''}
4170 mode = {'l': '644', 'x': '755', '': '644'}
4170 mode = {'l': '644', 'x': '755', '': '644'}
4171 ctx = scmutil.revsingle(repo, node)
4171 ctx = scmutil.revsingle(repo, node)
4172 mf = ctx.manifest()
4172 mf = ctx.manifest()
4173 for f in ctx:
4173 for f in ctx:
4174 fm.startitem()
4174 fm.startitem()
4175 fl = ctx[f].flags()
4175 fl = ctx[f].flags()
4176 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4176 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4177 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4177 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4178 fm.write('path', '%s\n', f)
4178 fm.write('path', '%s\n', f)
4179 fm.end()
4179 fm.end()
4180
4180
4181 @command('^merge',
4181 @command('^merge',
4182 [('f', 'force', None,
4182 [('f', 'force', None,
4183 _('force a merge including outstanding changes (DEPRECATED)')),
4183 _('force a merge including outstanding changes (DEPRECATED)')),
4184 ('r', 'rev', '', _('revision to merge'), _('REV')),
4184 ('r', 'rev', '', _('revision to merge'), _('REV')),
4185 ('P', 'preview', None,
4185 ('P', 'preview', None,
4186 _('review revisions to merge (no merge is performed)'))
4186 _('review revisions to merge (no merge is performed)'))
4187 ] + mergetoolopts,
4187 ] + mergetoolopts,
4188 _('[-P] [-f] [[-r] REV]'))
4188 _('[-P] [-f] [[-r] REV]'))
4189 def merge(ui, repo, node=None, **opts):
4189 def merge(ui, repo, node=None, **opts):
4190 """merge working directory with another revision
4190 """merge working directory with another revision
4191
4191
4192 The current working directory is updated with all changes made in
4192 The current working directory is updated with all changes made in
4193 the requested revision since the last common predecessor revision.
4193 the requested revision since the last common predecessor revision.
4194
4194
4195 Files that changed between either parent are marked as changed for
4195 Files that changed between either parent are marked as changed for
4196 the next commit and a commit must be performed before any further
4196 the next commit and a commit must be performed before any further
4197 updates to the repository are allowed. The next commit will have
4197 updates to the repository are allowed. The next commit will have
4198 two parents.
4198 two parents.
4199
4199
4200 ``--tool`` can be used to specify the merge tool used for file
4200 ``--tool`` can be used to specify the merge tool used for file
4201 merges. It overrides the HGMERGE environment variable and your
4201 merges. It overrides the HGMERGE environment variable and your
4202 configuration files. See :hg:`help merge-tools` for options.
4202 configuration files. See :hg:`help merge-tools` for options.
4203
4203
4204 If no revision is specified, the working directory's parent is a
4204 If no revision is specified, the working directory's parent is a
4205 head revision, and the current branch contains exactly one other
4205 head revision, and the current branch contains exactly one other
4206 head, the other head is merged with by default. Otherwise, an
4206 head, the other head is merged with by default. Otherwise, an
4207 explicit revision with which to merge with must be provided.
4207 explicit revision with which to merge with must be provided.
4208
4208
4209 :hg:`resolve` must be used to resolve unresolved files.
4209 :hg:`resolve` must be used to resolve unresolved files.
4210
4210
4211 To undo an uncommitted merge, use :hg:`update --clean .` which
4211 To undo an uncommitted merge, use :hg:`update --clean .` which
4212 will check out a clean copy of the original merge parent, losing
4212 will check out a clean copy of the original merge parent, losing
4213 all changes.
4213 all changes.
4214
4214
4215 Returns 0 on success, 1 if there are unresolved files.
4215 Returns 0 on success, 1 if there are unresolved files.
4216 """
4216 """
4217
4217
4218 if opts.get('rev') and node:
4218 if opts.get('rev') and node:
4219 raise util.Abort(_("please specify just one revision"))
4219 raise util.Abort(_("please specify just one revision"))
4220 if not node:
4220 if not node:
4221 node = opts.get('rev')
4221 node = opts.get('rev')
4222
4222
4223 if node:
4223 if node:
4224 node = scmutil.revsingle(repo, node).node()
4224 node = scmutil.revsingle(repo, node).node()
4225
4225
4226 if not node and repo._bookmarkcurrent:
4226 if not node and repo._bookmarkcurrent:
4227 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4227 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4228 curhead = repo[repo._bookmarkcurrent].node()
4228 curhead = repo[repo._bookmarkcurrent].node()
4229 if len(bmheads) == 2:
4229 if len(bmheads) == 2:
4230 if curhead == bmheads[0]:
4230 if curhead == bmheads[0]:
4231 node = bmheads[1]
4231 node = bmheads[1]
4232 else:
4232 else:
4233 node = bmheads[0]
4233 node = bmheads[0]
4234 elif len(bmheads) > 2:
4234 elif len(bmheads) > 2:
4235 raise util.Abort(_("multiple matching bookmarks to merge - "
4235 raise util.Abort(_("multiple matching bookmarks to merge - "
4236 "please merge with an explicit rev or bookmark"),
4236 "please merge with an explicit rev or bookmark"),
4237 hint=_("run 'hg heads' to see all heads"))
4237 hint=_("run 'hg heads' to see all heads"))
4238 elif len(bmheads) <= 1:
4238 elif len(bmheads) <= 1:
4239 raise util.Abort(_("no matching bookmark to merge - "
4239 raise util.Abort(_("no matching bookmark to merge - "
4240 "please merge with an explicit rev or bookmark"),
4240 "please merge with an explicit rev or bookmark"),
4241 hint=_("run 'hg heads' to see all heads"))
4241 hint=_("run 'hg heads' to see all heads"))
4242
4242
4243 if not node and not repo._bookmarkcurrent:
4243 if not node and not repo._bookmarkcurrent:
4244 branch = repo[None].branch()
4244 branch = repo[None].branch()
4245 bheads = repo.branchheads(branch)
4245 bheads = repo.branchheads(branch)
4246 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4246 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4247
4247
4248 if len(nbhs) > 2:
4248 if len(nbhs) > 2:
4249 raise util.Abort(_("branch '%s' has %d heads - "
4249 raise util.Abort(_("branch '%s' has %d heads - "
4250 "please merge with an explicit rev")
4250 "please merge with an explicit rev")
4251 % (branch, len(bheads)),
4251 % (branch, len(bheads)),
4252 hint=_("run 'hg heads .' to see heads"))
4252 hint=_("run 'hg heads .' to see heads"))
4253
4253
4254 parent = repo.dirstate.p1()
4254 parent = repo.dirstate.p1()
4255 if len(nbhs) <= 1:
4255 if len(nbhs) <= 1:
4256 if len(bheads) > 1:
4256 if len(bheads) > 1:
4257 raise util.Abort(_("heads are bookmarked - "
4257 raise util.Abort(_("heads are bookmarked - "
4258 "please merge with an explicit rev"),
4258 "please merge with an explicit rev"),
4259 hint=_("run 'hg heads' to see all heads"))
4259 hint=_("run 'hg heads' to see all heads"))
4260 if len(repo.heads()) > 1:
4260 if len(repo.heads()) > 1:
4261 raise util.Abort(_("branch '%s' has one head - "
4261 raise util.Abort(_("branch '%s' has one head - "
4262 "please merge with an explicit rev")
4262 "please merge with an explicit rev")
4263 % branch,
4263 % branch,
4264 hint=_("run 'hg heads' to see all heads"))
4264 hint=_("run 'hg heads' to see all heads"))
4265 msg, hint = _('nothing to merge'), None
4265 msg, hint = _('nothing to merge'), None
4266 if parent != repo.lookup(branch):
4266 if parent != repo.lookup(branch):
4267 hint = _("use 'hg update' instead")
4267 hint = _("use 'hg update' instead")
4268 raise util.Abort(msg, hint=hint)
4268 raise util.Abort(msg, hint=hint)
4269
4269
4270 if parent not in bheads:
4270 if parent not in bheads:
4271 raise util.Abort(_('working directory not at a head revision'),
4271 raise util.Abort(_('working directory not at a head revision'),
4272 hint=_("use 'hg update' or merge with an "
4272 hint=_("use 'hg update' or merge with an "
4273 "explicit revision"))
4273 "explicit revision"))
4274 if parent == nbhs[0]:
4274 if parent == nbhs[0]:
4275 node = nbhs[-1]
4275 node = nbhs[-1]
4276 else:
4276 else:
4277 node = nbhs[0]
4277 node = nbhs[0]
4278
4278
4279 if opts.get('preview'):
4279 if opts.get('preview'):
4280 # find nodes that are ancestors of p2 but not of p1
4280 # find nodes that are ancestors of p2 but not of p1
4281 p1 = repo.lookup('.')
4281 p1 = repo.lookup('.')
4282 p2 = repo.lookup(node)
4282 p2 = repo.lookup(node)
4283 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4283 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4284
4284
4285 displayer = cmdutil.show_changeset(ui, repo, opts)
4285 displayer = cmdutil.show_changeset(ui, repo, opts)
4286 for node in nodes:
4286 for node in nodes:
4287 displayer.show(repo[node])
4287 displayer.show(repo[node])
4288 displayer.close()
4288 displayer.close()
4289 return 0
4289 return 0
4290
4290
4291 try:
4291 try:
4292 # ui.forcemerge is an internal variable, do not document
4292 # ui.forcemerge is an internal variable, do not document
4293 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4293 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4294 return hg.merge(repo, node, force=opts.get('force'))
4294 return hg.merge(repo, node, force=opts.get('force'))
4295 finally:
4295 finally:
4296 ui.setconfig('ui', 'forcemerge', '', 'merge')
4296 ui.setconfig('ui', 'forcemerge', '', 'merge')
4297
4297
4298 @command('outgoing|out',
4298 @command('outgoing|out',
4299 [('f', 'force', None, _('run even when the destination is unrelated')),
4299 [('f', 'force', None, _('run even when the destination is unrelated')),
4300 ('r', 'rev', [],
4300 ('r', 'rev', [],
4301 _('a changeset intended to be included in the destination'), _('REV')),
4301 _('a changeset intended to be included in the destination'), _('REV')),
4302 ('n', 'newest-first', None, _('show newest record first')),
4302 ('n', 'newest-first', None, _('show newest record first')),
4303 ('B', 'bookmarks', False, _('compare bookmarks')),
4303 ('B', 'bookmarks', False, _('compare bookmarks')),
4304 ('b', 'branch', [], _('a specific branch you would like to push'),
4304 ('b', 'branch', [], _('a specific branch you would like to push'),
4305 _('BRANCH')),
4305 _('BRANCH')),
4306 ] + logopts + remoteopts + subrepoopts,
4306 ] + logopts + remoteopts + subrepoopts,
4307 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4307 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4308 def outgoing(ui, repo, dest=None, **opts):
4308 def outgoing(ui, repo, dest=None, **opts):
4309 """show changesets not found in the destination
4309 """show changesets not found in the destination
4310
4310
4311 Show changesets not found in the specified destination repository
4311 Show changesets not found in the specified destination repository
4312 or the default push location. These are the changesets that would
4312 or the default push location. These are the changesets that would
4313 be pushed if a push was requested.
4313 be pushed if a push was requested.
4314
4314
4315 See pull for details of valid destination formats.
4315 See pull for details of valid destination formats.
4316
4316
4317 Returns 0 if there are outgoing changes, 1 otherwise.
4317 Returns 0 if there are outgoing changes, 1 otherwise.
4318 """
4318 """
4319 if opts.get('graph'):
4319 if opts.get('graph'):
4320 cmdutil.checkunsupportedgraphflags([], opts)
4320 cmdutil.checkunsupportedgraphflags([], opts)
4321 o, other = hg._outgoing(ui, repo, dest, opts)
4321 o, other = hg._outgoing(ui, repo, dest, opts)
4322 if not o:
4322 if not o:
4323 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4323 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4324 return
4324 return
4325
4325
4326 revdag = cmdutil.graphrevs(repo, o, opts)
4326 revdag = cmdutil.graphrevs(repo, o, opts)
4327 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4327 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4328 showparents = [ctx.node() for ctx in repo[None].parents()]
4328 showparents = [ctx.node() for ctx in repo[None].parents()]
4329 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4329 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4330 graphmod.asciiedges)
4330 graphmod.asciiedges)
4331 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4331 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4332 return 0
4332 return 0
4333
4333
4334 if opts.get('bookmarks'):
4334 if opts.get('bookmarks'):
4335 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4335 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4336 dest, branches = hg.parseurl(dest, opts.get('branch'))
4336 dest, branches = hg.parseurl(dest, opts.get('branch'))
4337 other = hg.peer(repo, opts, dest)
4337 other = hg.peer(repo, opts, dest)
4338 if 'bookmarks' not in other.listkeys('namespaces'):
4338 if 'bookmarks' not in other.listkeys('namespaces'):
4339 ui.warn(_("remote doesn't support bookmarks\n"))
4339 ui.warn(_("remote doesn't support bookmarks\n"))
4340 return 0
4340 return 0
4341 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4341 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4342 return bookmarks.diff(ui, other, repo)
4342 return bookmarks.diff(ui, other, repo)
4343
4343
4344 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4344 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4345 try:
4345 try:
4346 return hg.outgoing(ui, repo, dest, opts)
4346 return hg.outgoing(ui, repo, dest, opts)
4347 finally:
4347 finally:
4348 del repo._subtoppath
4348 del repo._subtoppath
4349
4349
4350 @command('parents',
4350 @command('parents',
4351 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4351 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4352 ] + templateopts,
4352 ] + templateopts,
4353 _('[-r REV] [FILE]'))
4353 _('[-r REV] [FILE]'))
4354 def parents(ui, repo, file_=None, **opts):
4354 def parents(ui, repo, file_=None, **opts):
4355 """show the parents of the working directory or revision
4355 """show the parents of the working directory or revision
4356
4356
4357 Print the working directory's parent revisions. If a revision is
4357 Print the working directory's parent revisions. If a revision is
4358 given via -r/--rev, the parent of that revision will be printed.
4358 given via -r/--rev, the parent of that revision will be printed.
4359 If a file argument is given, the revision in which the file was
4359 If a file argument is given, the revision in which the file was
4360 last changed (before the working directory revision or the
4360 last changed (before the working directory revision or the
4361 argument to --rev if given) is printed.
4361 argument to --rev if given) is printed.
4362
4362
4363 Returns 0 on success.
4363 Returns 0 on success.
4364 """
4364 """
4365
4365
4366 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4366 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4367
4367
4368 if file_:
4368 if file_:
4369 m = scmutil.match(ctx, (file_,), opts)
4369 m = scmutil.match(ctx, (file_,), opts)
4370 if m.anypats() or len(m.files()) != 1:
4370 if m.anypats() or len(m.files()) != 1:
4371 raise util.Abort(_('can only specify an explicit filename'))
4371 raise util.Abort(_('can only specify an explicit filename'))
4372 file_ = m.files()[0]
4372 file_ = m.files()[0]
4373 filenodes = []
4373 filenodes = []
4374 for cp in ctx.parents():
4374 for cp in ctx.parents():
4375 if not cp:
4375 if not cp:
4376 continue
4376 continue
4377 try:
4377 try:
4378 filenodes.append(cp.filenode(file_))
4378 filenodes.append(cp.filenode(file_))
4379 except error.LookupError:
4379 except error.LookupError:
4380 pass
4380 pass
4381 if not filenodes:
4381 if not filenodes:
4382 raise util.Abort(_("'%s' not found in manifest!") % file_)
4382 raise util.Abort(_("'%s' not found in manifest!") % file_)
4383 p = []
4383 p = []
4384 for fn in filenodes:
4384 for fn in filenodes:
4385 fctx = repo.filectx(file_, fileid=fn)
4385 fctx = repo.filectx(file_, fileid=fn)
4386 p.append(fctx.node())
4386 p.append(fctx.node())
4387 else:
4387 else:
4388 p = [cp.node() for cp in ctx.parents()]
4388 p = [cp.node() for cp in ctx.parents()]
4389
4389
4390 displayer = cmdutil.show_changeset(ui, repo, opts)
4390 displayer = cmdutil.show_changeset(ui, repo, opts)
4391 for n in p:
4391 for n in p:
4392 if n != nullid:
4392 if n != nullid:
4393 displayer.show(repo[n])
4393 displayer.show(repo[n])
4394 displayer.close()
4394 displayer.close()
4395
4395
4396 @command('paths', [], _('[NAME]'))
4396 @command('paths', [], _('[NAME]'))
4397 def paths(ui, repo, search=None):
4397 def paths(ui, repo, search=None):
4398 """show aliases for remote repositories
4398 """show aliases for remote repositories
4399
4399
4400 Show definition of symbolic path name NAME. If no name is given,
4400 Show definition of symbolic path name NAME. If no name is given,
4401 show definition of all available names.
4401 show definition of all available names.
4402
4402
4403 Option -q/--quiet suppresses all output when searching for NAME
4403 Option -q/--quiet suppresses all output when searching for NAME
4404 and shows only the path names when listing all definitions.
4404 and shows only the path names when listing all definitions.
4405
4405
4406 Path names are defined in the [paths] section of your
4406 Path names are defined in the [paths] section of your
4407 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4407 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4408 repository, ``.hg/hgrc`` is used, too.
4408 repository, ``.hg/hgrc`` is used, too.
4409
4409
4410 The path names ``default`` and ``default-push`` have a special
4410 The path names ``default`` and ``default-push`` have a special
4411 meaning. When performing a push or pull operation, they are used
4411 meaning. When performing a push or pull operation, they are used
4412 as fallbacks if no location is specified on the command-line.
4412 as fallbacks if no location is specified on the command-line.
4413 When ``default-push`` is set, it will be used for push and
4413 When ``default-push`` is set, it will be used for push and
4414 ``default`` will be used for pull; otherwise ``default`` is used
4414 ``default`` will be used for pull; otherwise ``default`` is used
4415 as the fallback for both. When cloning a repository, the clone
4415 as the fallback for both. When cloning a repository, the clone
4416 source is written as ``default`` in ``.hg/hgrc``. Note that
4416 source is written as ``default`` in ``.hg/hgrc``. Note that
4417 ``default`` and ``default-push`` apply to all inbound (e.g.
4417 ``default`` and ``default-push`` apply to all inbound (e.g.
4418 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4418 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4419 :hg:`bundle`) operations.
4419 :hg:`bundle`) operations.
4420
4420
4421 See :hg:`help urls` for more information.
4421 See :hg:`help urls` for more information.
4422
4422
4423 Returns 0 on success.
4423 Returns 0 on success.
4424 """
4424 """
4425 if search:
4425 if search:
4426 for name, path in ui.configitems("paths"):
4426 for name, path in ui.configitems("paths"):
4427 if name == search:
4427 if name == search:
4428 ui.status("%s\n" % util.hidepassword(path))
4428 ui.status("%s\n" % util.hidepassword(path))
4429 return
4429 return
4430 if not ui.quiet:
4430 if not ui.quiet:
4431 ui.warn(_("not found!\n"))
4431 ui.warn(_("not found!\n"))
4432 return 1
4432 return 1
4433 else:
4433 else:
4434 for name, path in ui.configitems("paths"):
4434 for name, path in ui.configitems("paths"):
4435 if ui.quiet:
4435 if ui.quiet:
4436 ui.write("%s\n" % name)
4436 ui.write("%s\n" % name)
4437 else:
4437 else:
4438 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4438 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4439
4439
4440 @command('phase',
4440 @command('phase',
4441 [('p', 'public', False, _('set changeset phase to public')),
4441 [('p', 'public', False, _('set changeset phase to public')),
4442 ('d', 'draft', False, _('set changeset phase to draft')),
4442 ('d', 'draft', False, _('set changeset phase to draft')),
4443 ('s', 'secret', False, _('set changeset phase to secret')),
4443 ('s', 'secret', False, _('set changeset phase to secret')),
4444 ('f', 'force', False, _('allow to move boundary backward')),
4444 ('f', 'force', False, _('allow to move boundary backward')),
4445 ('r', 'rev', [], _('target revision'), _('REV')),
4445 ('r', 'rev', [], _('target revision'), _('REV')),
4446 ],
4446 ],
4447 _('[-p|-d|-s] [-f] [-r] REV...'))
4447 _('[-p|-d|-s] [-f] [-r] REV...'))
4448 def phase(ui, repo, *revs, **opts):
4448 def phase(ui, repo, *revs, **opts):
4449 """set or show the current phase name
4449 """set or show the current phase name
4450
4450
4451 With no argument, show the phase name of specified revisions.
4451 With no argument, show the phase name of specified revisions.
4452
4452
4453 With one of -p/--public, -d/--draft or -s/--secret, change the
4453 With one of -p/--public, -d/--draft or -s/--secret, change the
4454 phase value of the specified revisions.
4454 phase value of the specified revisions.
4455
4455
4456 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4456 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4457 lower phase to an higher phase. Phases are ordered as follows::
4457 lower phase to an higher phase. Phases are ordered as follows::
4458
4458
4459 public < draft < secret
4459 public < draft < secret
4460
4460
4461 Returns 0 on success, 1 if no phases were changed or some could not
4461 Returns 0 on success, 1 if no phases were changed or some could not
4462 be changed.
4462 be changed.
4463 """
4463 """
4464 # search for a unique phase argument
4464 # search for a unique phase argument
4465 targetphase = None
4465 targetphase = None
4466 for idx, name in enumerate(phases.phasenames):
4466 for idx, name in enumerate(phases.phasenames):
4467 if opts[name]:
4467 if opts[name]:
4468 if targetphase is not None:
4468 if targetphase is not None:
4469 raise util.Abort(_('only one phase can be specified'))
4469 raise util.Abort(_('only one phase can be specified'))
4470 targetphase = idx
4470 targetphase = idx
4471
4471
4472 # look for specified revision
4472 # look for specified revision
4473 revs = list(revs)
4473 revs = list(revs)
4474 revs.extend(opts['rev'])
4474 revs.extend(opts['rev'])
4475 if not revs:
4475 if not revs:
4476 raise util.Abort(_('no revisions specified'))
4476 raise util.Abort(_('no revisions specified'))
4477
4477
4478 revs = scmutil.revrange(repo, revs)
4478 revs = scmutil.revrange(repo, revs)
4479
4479
4480 lock = None
4480 lock = None
4481 ret = 0
4481 ret = 0
4482 if targetphase is None:
4482 if targetphase is None:
4483 # display
4483 # display
4484 for r in revs:
4484 for r in revs:
4485 ctx = repo[r]
4485 ctx = repo[r]
4486 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4486 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4487 else:
4487 else:
4488 lock = repo.lock()
4488 lock = repo.lock()
4489 try:
4489 try:
4490 # set phase
4490 # set phase
4491 if not revs:
4491 if not revs:
4492 raise util.Abort(_('empty revision set'))
4492 raise util.Abort(_('empty revision set'))
4493 nodes = [repo[r].node() for r in revs]
4493 nodes = [repo[r].node() for r in revs]
4494 olddata = repo._phasecache.getphaserevs(repo)[:]
4494 olddata = repo._phasecache.getphaserevs(repo)[:]
4495 phases.advanceboundary(repo, targetphase, nodes)
4495 phases.advanceboundary(repo, targetphase, nodes)
4496 if opts['force']:
4496 if opts['force']:
4497 phases.retractboundary(repo, targetphase, nodes)
4497 phases.retractboundary(repo, targetphase, nodes)
4498 finally:
4498 finally:
4499 lock.release()
4499 lock.release()
4500 # moving revision from public to draft may hide them
4500 # moving revision from public to draft may hide them
4501 # We have to check result on an unfiltered repository
4501 # We have to check result on an unfiltered repository
4502 unfi = repo.unfiltered()
4502 unfi = repo.unfiltered()
4503 newdata = repo._phasecache.getphaserevs(unfi)
4503 newdata = repo._phasecache.getphaserevs(unfi)
4504 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4504 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4505 cl = unfi.changelog
4505 cl = unfi.changelog
4506 rejected = [n for n in nodes
4506 rejected = [n for n in nodes
4507 if newdata[cl.rev(n)] < targetphase]
4507 if newdata[cl.rev(n)] < targetphase]
4508 if rejected:
4508 if rejected:
4509 ui.warn(_('cannot move %i changesets to a higher '
4509 ui.warn(_('cannot move %i changesets to a higher '
4510 'phase, use --force\n') % len(rejected))
4510 'phase, use --force\n') % len(rejected))
4511 ret = 1
4511 ret = 1
4512 if changes:
4512 if changes:
4513 msg = _('phase changed for %i changesets\n') % changes
4513 msg = _('phase changed for %i changesets\n') % changes
4514 if ret:
4514 if ret:
4515 ui.status(msg)
4515 ui.status(msg)
4516 else:
4516 else:
4517 ui.note(msg)
4517 ui.note(msg)
4518 else:
4518 else:
4519 ui.warn(_('no phases changed\n'))
4519 ui.warn(_('no phases changed\n'))
4520 ret = 1
4520 ret = 1
4521 return ret
4521 return ret
4522
4522
4523 def postincoming(ui, repo, modheads, optupdate, checkout):
4523 def postincoming(ui, repo, modheads, optupdate, checkout):
4524 if modheads == 0:
4524 if modheads == 0:
4525 return
4525 return
4526 if optupdate:
4526 if optupdate:
4527 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4527 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4528 try:
4528 try:
4529 ret = hg.update(repo, checkout)
4529 ret = hg.update(repo, checkout)
4530 except util.Abort, inst:
4530 except util.Abort, inst:
4531 ui.warn(_("not updating: %s\n") % str(inst))
4531 ui.warn(_("not updating: %s\n") % str(inst))
4532 if inst.hint:
4532 if inst.hint:
4533 ui.warn(_("(%s)\n") % inst.hint)
4533 ui.warn(_("(%s)\n") % inst.hint)
4534 return 0
4534 return 0
4535 if not ret and not checkout:
4535 if not ret and not checkout:
4536 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4536 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4537 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4537 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4538 return ret
4538 return ret
4539 if modheads > 1:
4539 if modheads > 1:
4540 currentbranchheads = len(repo.branchheads())
4540 currentbranchheads = len(repo.branchheads())
4541 if currentbranchheads == modheads:
4541 if currentbranchheads == modheads:
4542 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4542 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4543 elif currentbranchheads > 1:
4543 elif currentbranchheads > 1:
4544 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4544 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4545 "merge)\n"))
4545 "merge)\n"))
4546 else:
4546 else:
4547 ui.status(_("(run 'hg heads' to see heads)\n"))
4547 ui.status(_("(run 'hg heads' to see heads)\n"))
4548 else:
4548 else:
4549 ui.status(_("(run 'hg update' to get a working copy)\n"))
4549 ui.status(_("(run 'hg update' to get a working copy)\n"))
4550
4550
4551 @command('^pull',
4551 @command('^pull',
4552 [('u', 'update', None,
4552 [('u', 'update', None,
4553 _('update to new branch head if changesets were pulled')),
4553 _('update to new branch head if changesets were pulled')),
4554 ('f', 'force', None, _('run even when remote repository is unrelated')),
4554 ('f', 'force', None, _('run even when remote repository is unrelated')),
4555 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4555 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4556 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4556 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4557 ('b', 'branch', [], _('a specific branch you would like to pull'),
4557 ('b', 'branch', [], _('a specific branch you would like to pull'),
4558 _('BRANCH')),
4558 _('BRANCH')),
4559 ] + remoteopts,
4559 ] + remoteopts,
4560 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4560 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4561 def pull(ui, repo, source="default", **opts):
4561 def pull(ui, repo, source="default", **opts):
4562 """pull changes from the specified source
4562 """pull changes from the specified source
4563
4563
4564 Pull changes from a remote repository to a local one.
4564 Pull changes from a remote repository to a local one.
4565
4565
4566 This finds all changes from the repository at the specified path
4566 This finds all changes from the repository at the specified path
4567 or URL and adds them to a local repository (the current one unless
4567 or URL and adds them to a local repository (the current one unless
4568 -R is specified). By default, this does not update the copy of the
4568 -R is specified). By default, this does not update the copy of the
4569 project in the working directory.
4569 project in the working directory.
4570
4570
4571 Use :hg:`incoming` if you want to see what would have been added
4571 Use :hg:`incoming` if you want to see what would have been added
4572 by a pull at the time you issued this command. If you then decide
4572 by a pull at the time you issued this command. If you then decide
4573 to add those changes to the repository, you should use :hg:`pull
4573 to add those changes to the repository, you should use :hg:`pull
4574 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4574 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4575
4575
4576 If SOURCE is omitted, the 'default' path will be used.
4576 If SOURCE is omitted, the 'default' path will be used.
4577 See :hg:`help urls` for more information.
4577 See :hg:`help urls` for more information.
4578
4578
4579 Returns 0 on success, 1 if an update had unresolved files.
4579 Returns 0 on success, 1 if an update had unresolved files.
4580 """
4580 """
4581 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4581 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4582 other = hg.peer(repo, opts, source)
4582 other = hg.peer(repo, opts, source)
4583 try:
4583 try:
4584 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4584 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4585 revs, checkout = hg.addbranchrevs(repo, other, branches,
4585 revs, checkout = hg.addbranchrevs(repo, other, branches,
4586 opts.get('rev'))
4586 opts.get('rev'))
4587
4587
4588 remotebookmarks = other.listkeys('bookmarks')
4588 remotebookmarks = other.listkeys('bookmarks')
4589
4589
4590 if opts.get('bookmark'):
4590 if opts.get('bookmark'):
4591 if not revs:
4591 if not revs:
4592 revs = []
4592 revs = []
4593 for b in opts['bookmark']:
4593 for b in opts['bookmark']:
4594 if b not in remotebookmarks:
4594 if b not in remotebookmarks:
4595 raise util.Abort(_('remote bookmark %s not found!') % b)
4595 raise util.Abort(_('remote bookmark %s not found!') % b)
4596 revs.append(remotebookmarks[b])
4596 revs.append(remotebookmarks[b])
4597
4597
4598 if revs:
4598 if revs:
4599 try:
4599 try:
4600 revs = [other.lookup(rev) for rev in revs]
4600 revs = [other.lookup(rev) for rev in revs]
4601 except error.CapabilityError:
4601 except error.CapabilityError:
4602 err = _("other repository doesn't support revision lookup, "
4602 err = _("other repository doesn't support revision lookup, "
4603 "so a rev cannot be specified.")
4603 "so a rev cannot be specified.")
4604 raise util.Abort(err)
4604 raise util.Abort(err)
4605
4605
4606 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4606 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4607 bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
4607 bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
4608 if checkout:
4608 if checkout:
4609 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4609 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4610 repo._subtoppath = source
4610 repo._subtoppath = source
4611 try:
4611 try:
4612 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4612 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4613
4613
4614 finally:
4614 finally:
4615 del repo._subtoppath
4615 del repo._subtoppath
4616
4616
4617 # update specified bookmarks
4617 # update specified bookmarks
4618 if opts.get('bookmark'):
4618 if opts.get('bookmark'):
4619 marks = repo._bookmarks
4619 marks = repo._bookmarks
4620 for b in opts['bookmark']:
4620 for b in opts['bookmark']:
4621 # explicit pull overrides local bookmark if any
4621 # explicit pull overrides local bookmark if any
4622 ui.status(_("importing bookmark %s\n") % b)
4622 ui.status(_("importing bookmark %s\n") % b)
4623 marks[b] = repo[remotebookmarks[b]].node()
4623 marks[b] = repo[remotebookmarks[b]].node()
4624 marks.write()
4624 marks.write()
4625 finally:
4625 finally:
4626 other.close()
4626 other.close()
4627 return ret
4627 return ret
4628
4628
4629 @command('^push',
4629 @command('^push',
4630 [('f', 'force', None, _('force push')),
4630 [('f', 'force', None, _('force push')),
4631 ('r', 'rev', [],
4631 ('r', 'rev', [],
4632 _('a changeset intended to be included in the destination'),
4632 _('a changeset intended to be included in the destination'),
4633 _('REV')),
4633 _('REV')),
4634 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4634 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4635 ('b', 'branch', [],
4635 ('b', 'branch', [],
4636 _('a specific branch you would like to push'), _('BRANCH')),
4636 _('a specific branch you would like to push'), _('BRANCH')),
4637 ('', 'new-branch', False, _('allow pushing a new branch')),
4637 ('', 'new-branch', False, _('allow pushing a new branch')),
4638 ] + remoteopts,
4638 ] + remoteopts,
4639 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4639 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4640 def push(ui, repo, dest=None, **opts):
4640 def push(ui, repo, dest=None, **opts):
4641 """push changes to the specified destination
4641 """push changes to the specified destination
4642
4642
4643 Push changesets from the local repository to the specified
4643 Push changesets from the local repository to the specified
4644 destination.
4644 destination.
4645
4645
4646 This operation is symmetrical to pull: it is identical to a pull
4646 This operation is symmetrical to pull: it is identical to a pull
4647 in the destination repository from the current one.
4647 in the destination repository from the current one.
4648
4648
4649 By default, push will not allow creation of new heads at the
4649 By default, push will not allow creation of new heads at the
4650 destination, since multiple heads would make it unclear which head
4650 destination, since multiple heads would make it unclear which head
4651 to use. In this situation, it is recommended to pull and merge
4651 to use. In this situation, it is recommended to pull and merge
4652 before pushing.
4652 before pushing.
4653
4653
4654 Use --new-branch if you want to allow push to create a new named
4654 Use --new-branch if you want to allow push to create a new named
4655 branch that is not present at the destination. This allows you to
4655 branch that is not present at the destination. This allows you to
4656 only create a new branch without forcing other changes.
4656 only create a new branch without forcing other changes.
4657
4657
4658 .. note::
4658 .. note::
4659
4659
4660 Extra care should be taken with the -f/--force option,
4660 Extra care should be taken with the -f/--force option,
4661 which will push all new heads on all branches, an action which will
4661 which will push all new heads on all branches, an action which will
4662 almost always cause confusion for collaborators.
4662 almost always cause confusion for collaborators.
4663
4663
4664 If -r/--rev is used, the specified revision and all its ancestors
4664 If -r/--rev is used, the specified revision and all its ancestors
4665 will be pushed to the remote repository.
4665 will be pushed to the remote repository.
4666
4666
4667 If -B/--bookmark is used, the specified bookmarked revision, its
4667 If -B/--bookmark is used, the specified bookmarked revision, its
4668 ancestors, and the bookmark will be pushed to the remote
4668 ancestors, and the bookmark will be pushed to the remote
4669 repository.
4669 repository.
4670
4670
4671 Please see :hg:`help urls` for important details about ``ssh://``
4671 Please see :hg:`help urls` for important details about ``ssh://``
4672 URLs. If DESTINATION is omitted, a default path will be used.
4672 URLs. If DESTINATION is omitted, a default path will be used.
4673
4673
4674 Returns 0 if push was successful, 1 if nothing to push.
4674 Returns 0 if push was successful, 1 if nothing to push.
4675 """
4675 """
4676
4676
4677 if opts.get('bookmark'):
4677 if opts.get('bookmark'):
4678 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4678 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4679 for b in opts['bookmark']:
4679 for b in opts['bookmark']:
4680 # translate -B options to -r so changesets get pushed
4680 # translate -B options to -r so changesets get pushed
4681 if b in repo._bookmarks:
4681 if b in repo._bookmarks:
4682 opts.setdefault('rev', []).append(b)
4682 opts.setdefault('rev', []).append(b)
4683 else:
4683 else:
4684 # if we try to push a deleted bookmark, translate it to null
4684 # if we try to push a deleted bookmark, translate it to null
4685 # this lets simultaneous -r, -b options continue working
4685 # this lets simultaneous -r, -b options continue working
4686 opts.setdefault('rev', []).append("null")
4686 opts.setdefault('rev', []).append("null")
4687
4687
4688 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4688 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4689 dest, branches = hg.parseurl(dest, opts.get('branch'))
4689 dest, branches = hg.parseurl(dest, opts.get('branch'))
4690 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4690 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4691 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4691 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4692 try:
4692 try:
4693 other = hg.peer(repo, opts, dest)
4693 other = hg.peer(repo, opts, dest)
4694 except error.RepoError:
4694 except error.RepoError:
4695 if dest == "default-push":
4695 if dest == "default-push":
4696 raise util.Abort(_("default repository not configured!"),
4696 raise util.Abort(_("default repository not configured!"),
4697 hint=_('see the "path" section in "hg help config"'))
4697 hint=_('see the "path" section in "hg help config"'))
4698 else:
4698 else:
4699 raise
4699 raise
4700
4700
4701 if revs:
4701 if revs:
4702 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4702 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4703
4703
4704 repo._subtoppath = dest
4704 repo._subtoppath = dest
4705 try:
4705 try:
4706 # push subrepos depth-first for coherent ordering
4706 # push subrepos depth-first for coherent ordering
4707 c = repo['']
4707 c = repo['']
4708 subs = c.substate # only repos that are committed
4708 subs = c.substate # only repos that are committed
4709 for s in sorted(subs):
4709 for s in sorted(subs):
4710 result = c.sub(s).push(opts)
4710 result = c.sub(s).push(opts)
4711 if result == 0:
4711 if result == 0:
4712 return not result
4712 return not result
4713 finally:
4713 finally:
4714 del repo._subtoppath
4714 del repo._subtoppath
4715 result = repo.push(other, opts.get('force'), revs=revs,
4715 result = repo.push(other, opts.get('force'), revs=revs,
4716 newbranch=opts.get('new_branch'))
4716 newbranch=opts.get('new_branch'))
4717
4717
4718 result = not result
4718 result = not result
4719
4719
4720 if opts.get('bookmark'):
4720 if opts.get('bookmark'):
4721 bresult = bookmarks.pushtoremote(ui, repo, other, opts['bookmark'])
4721 bresult = bookmarks.pushtoremote(ui, repo, other, opts['bookmark'])
4722 if bresult == 2:
4722 if bresult == 2:
4723 return 2
4723 return 2
4724 if not result and bresult:
4724 if not result and bresult:
4725 result = 2
4725 result = 2
4726
4726
4727 return result
4727 return result
4728
4728
4729 @command('recover', [])
4729 @command('recover', [])
4730 def recover(ui, repo):
4730 def recover(ui, repo):
4731 """roll back an interrupted transaction
4731 """roll back an interrupted transaction
4732
4732
4733 Recover from an interrupted commit or pull.
4733 Recover from an interrupted commit or pull.
4734
4734
4735 This command tries to fix the repository status after an
4735 This command tries to fix the repository status after an
4736 interrupted operation. It should only be necessary when Mercurial
4736 interrupted operation. It should only be necessary when Mercurial
4737 suggests it.
4737 suggests it.
4738
4738
4739 Returns 0 if successful, 1 if nothing to recover or verify fails.
4739 Returns 0 if successful, 1 if nothing to recover or verify fails.
4740 """
4740 """
4741 if repo.recover():
4741 if repo.recover():
4742 return hg.verify(repo)
4742 return hg.verify(repo)
4743 return 1
4743 return 1
4744
4744
4745 @command('^remove|rm',
4745 @command('^remove|rm',
4746 [('A', 'after', None, _('record delete for missing files')),
4746 [('A', 'after', None, _('record delete for missing files')),
4747 ('f', 'force', None,
4747 ('f', 'force', None,
4748 _('remove (and delete) file even if added or modified')),
4748 _('remove (and delete) file even if added or modified')),
4749 ] + walkopts,
4749 ] + walkopts,
4750 _('[OPTION]... FILE...'))
4750 _('[OPTION]... FILE...'))
4751 def remove(ui, repo, *pats, **opts):
4751 def remove(ui, repo, *pats, **opts):
4752 """remove the specified files on the next commit
4752 """remove the specified files on the next commit
4753
4753
4754 Schedule the indicated files for removal from the current branch.
4754 Schedule the indicated files for removal from the current branch.
4755
4755
4756 This command schedules the files to be removed at the next commit.
4756 This command schedules the files to be removed at the next commit.
4757 To undo a remove before that, see :hg:`revert`. To undo added
4757 To undo a remove before that, see :hg:`revert`. To undo added
4758 files, see :hg:`forget`.
4758 files, see :hg:`forget`.
4759
4759
4760 .. container:: verbose
4760 .. container:: verbose
4761
4761
4762 -A/--after can be used to remove only files that have already
4762 -A/--after can be used to remove only files that have already
4763 been deleted, -f/--force can be used to force deletion, and -Af
4763 been deleted, -f/--force can be used to force deletion, and -Af
4764 can be used to remove files from the next revision without
4764 can be used to remove files from the next revision without
4765 deleting them from the working directory.
4765 deleting them from the working directory.
4766
4766
4767 The following table details the behavior of remove for different
4767 The following table details the behavior of remove for different
4768 file states (columns) and option combinations (rows). The file
4768 file states (columns) and option combinations (rows). The file
4769 states are Added [A], Clean [C], Modified [M] and Missing [!]
4769 states are Added [A], Clean [C], Modified [M] and Missing [!]
4770 (as reported by :hg:`status`). The actions are Warn, Remove
4770 (as reported by :hg:`status`). The actions are Warn, Remove
4771 (from branch) and Delete (from disk):
4771 (from branch) and Delete (from disk):
4772
4772
4773 ========= == == == ==
4773 ========= == == == ==
4774 opt/state A C M !
4774 opt/state A C M !
4775 ========= == == == ==
4775 ========= == == == ==
4776 none W RD W R
4776 none W RD W R
4777 -f R RD RD R
4777 -f R RD RD R
4778 -A W W W R
4778 -A W W W R
4779 -Af R R R R
4779 -Af R R R R
4780 ========= == == == ==
4780 ========= == == == ==
4781
4781
4782 Note that remove never deletes files in Added [A] state from the
4782 Note that remove never deletes files in Added [A] state from the
4783 working directory, not even if option --force is specified.
4783 working directory, not even if option --force is specified.
4784
4784
4785 Returns 0 on success, 1 if any warnings encountered.
4785 Returns 0 on success, 1 if any warnings encountered.
4786 """
4786 """
4787
4787
4788 ret = 0
4788 ret = 0
4789 after, force = opts.get('after'), opts.get('force')
4789 after, force = opts.get('after'), opts.get('force')
4790 if not pats and not after:
4790 if not pats and not after:
4791 raise util.Abort(_('no files specified'))
4791 raise util.Abort(_('no files specified'))
4792
4792
4793 m = scmutil.match(repo[None], pats, opts)
4793 m = scmutil.match(repo[None], pats, opts)
4794 s = repo.status(match=m, clean=True)
4794 s = repo.status(match=m, clean=True)
4795 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4795 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4796
4796
4797 # warn about failure to delete explicit files/dirs
4797 # warn about failure to delete explicit files/dirs
4798 wctx = repo[None]
4798 wctx = repo[None]
4799 for f in m.files():
4799 for f in m.files():
4800 if f in repo.dirstate or f in wctx.dirs():
4800 if f in repo.dirstate or f in wctx.dirs():
4801 continue
4801 continue
4802 if os.path.exists(m.rel(f)):
4802 if os.path.exists(m.rel(f)):
4803 if os.path.isdir(m.rel(f)):
4803 if os.path.isdir(m.rel(f)):
4804 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4804 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4805 else:
4805 else:
4806 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4806 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4807 # missing files will generate a warning elsewhere
4807 # missing files will generate a warning elsewhere
4808 ret = 1
4808 ret = 1
4809
4809
4810 if force:
4810 if force:
4811 list = modified + deleted + clean + added
4811 list = modified + deleted + clean + added
4812 elif after:
4812 elif after:
4813 list = deleted
4813 list = deleted
4814 for f in modified + added + clean:
4814 for f in modified + added + clean:
4815 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4815 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4816 ret = 1
4816 ret = 1
4817 else:
4817 else:
4818 list = deleted + clean
4818 list = deleted + clean
4819 for f in modified:
4819 for f in modified:
4820 ui.warn(_('not removing %s: file is modified (use -f'
4820 ui.warn(_('not removing %s: file is modified (use -f'
4821 ' to force removal)\n') % m.rel(f))
4821 ' to force removal)\n') % m.rel(f))
4822 ret = 1
4822 ret = 1
4823 for f in added:
4823 for f in added:
4824 ui.warn(_('not removing %s: file has been marked for add'
4824 ui.warn(_('not removing %s: file has been marked for add'
4825 ' (use forget to undo)\n') % m.rel(f))
4825 ' (use forget to undo)\n') % m.rel(f))
4826 ret = 1
4826 ret = 1
4827
4827
4828 for f in sorted(list):
4828 for f in sorted(list):
4829 if ui.verbose or not m.exact(f):
4829 if ui.verbose or not m.exact(f):
4830 ui.status(_('removing %s\n') % m.rel(f))
4830 ui.status(_('removing %s\n') % m.rel(f))
4831
4831
4832 wlock = repo.wlock()
4832 wlock = repo.wlock()
4833 try:
4833 try:
4834 if not after:
4834 if not after:
4835 for f in list:
4835 for f in list:
4836 if f in added:
4836 if f in added:
4837 continue # we never unlink added files on remove
4837 continue # we never unlink added files on remove
4838 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4838 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4839 repo[None].forget(list)
4839 repo[None].forget(list)
4840 finally:
4840 finally:
4841 wlock.release()
4841 wlock.release()
4842
4842
4843 return ret
4843 return ret
4844
4844
4845 @command('rename|move|mv',
4845 @command('rename|move|mv',
4846 [('A', 'after', None, _('record a rename that has already occurred')),
4846 [('A', 'after', None, _('record a rename that has already occurred')),
4847 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4847 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4848 ] + walkopts + dryrunopts,
4848 ] + walkopts + dryrunopts,
4849 _('[OPTION]... SOURCE... DEST'))
4849 _('[OPTION]... SOURCE... DEST'))
4850 def rename(ui, repo, *pats, **opts):
4850 def rename(ui, repo, *pats, **opts):
4851 """rename files; equivalent of copy + remove
4851 """rename files; equivalent of copy + remove
4852
4852
4853 Mark dest as copies of sources; mark sources for deletion. If dest
4853 Mark dest as copies of sources; mark sources for deletion. If dest
4854 is a directory, copies are put in that directory. If dest is a
4854 is a directory, copies are put in that directory. If dest is a
4855 file, there can only be one source.
4855 file, there can only be one source.
4856
4856
4857 By default, this command copies the contents of files as they
4857 By default, this command copies the contents of files as they
4858 exist in the working directory. If invoked with -A/--after, the
4858 exist in the working directory. If invoked with -A/--after, the
4859 operation is recorded, but no copying is performed.
4859 operation is recorded, but no copying is performed.
4860
4860
4861 This command takes effect at the next commit. To undo a rename
4861 This command takes effect at the next commit. To undo a rename
4862 before that, see :hg:`revert`.
4862 before that, see :hg:`revert`.
4863
4863
4864 Returns 0 on success, 1 if errors are encountered.
4864 Returns 0 on success, 1 if errors are encountered.
4865 """
4865 """
4866 wlock = repo.wlock(False)
4866 wlock = repo.wlock(False)
4867 try:
4867 try:
4868 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4868 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4869 finally:
4869 finally:
4870 wlock.release()
4870 wlock.release()
4871
4871
4872 @command('resolve',
4872 @command('resolve',
4873 [('a', 'all', None, _('select all unresolved files')),
4873 [('a', 'all', None, _('select all unresolved files')),
4874 ('l', 'list', None, _('list state of files needing merge')),
4874 ('l', 'list', None, _('list state of files needing merge')),
4875 ('m', 'mark', None, _('mark files as resolved')),
4875 ('m', 'mark', None, _('mark files as resolved')),
4876 ('u', 'unmark', None, _('mark files as unresolved')),
4876 ('u', 'unmark', None, _('mark files as unresolved')),
4877 ('n', 'no-status', None, _('hide status prefix'))]
4877 ('n', 'no-status', None, _('hide status prefix'))]
4878 + mergetoolopts + walkopts,
4878 + mergetoolopts + walkopts,
4879 _('[OPTION]... [FILE]...'))
4879 _('[OPTION]... [FILE]...'))
4880 def resolve(ui, repo, *pats, **opts):
4880 def resolve(ui, repo, *pats, **opts):
4881 """redo merges or set/view the merge status of files
4881 """redo merges or set/view the merge status of files
4882
4882
4883 Merges with unresolved conflicts are often the result of
4883 Merges with unresolved conflicts are often the result of
4884 non-interactive merging using the ``internal:merge`` configuration
4884 non-interactive merging using the ``internal:merge`` configuration
4885 setting, or a command-line merge tool like ``diff3``. The resolve
4885 setting, or a command-line merge tool like ``diff3``. The resolve
4886 command is used to manage the files involved in a merge, after
4886 command is used to manage the files involved in a merge, after
4887 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4887 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4888 working directory must have two parents). See :hg:`help
4888 working directory must have two parents). See :hg:`help
4889 merge-tools` for information on configuring merge tools.
4889 merge-tools` for information on configuring merge tools.
4890
4890
4891 The resolve command can be used in the following ways:
4891 The resolve command can be used in the following ways:
4892
4892
4893 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4893 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4894 files, discarding any previous merge attempts. Re-merging is not
4894 files, discarding any previous merge attempts. Re-merging is not
4895 performed for files already marked as resolved. Use ``--all/-a``
4895 performed for files already marked as resolved. Use ``--all/-a``
4896 to select all unresolved files. ``--tool`` can be used to specify
4896 to select all unresolved files. ``--tool`` can be used to specify
4897 the merge tool used for the given files. It overrides the HGMERGE
4897 the merge tool used for the given files. It overrides the HGMERGE
4898 environment variable and your configuration files. Previous file
4898 environment variable and your configuration files. Previous file
4899 contents are saved with a ``.orig`` suffix.
4899 contents are saved with a ``.orig`` suffix.
4900
4900
4901 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4901 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4902 (e.g. after having manually fixed-up the files). The default is
4902 (e.g. after having manually fixed-up the files). The default is
4903 to mark all unresolved files.
4903 to mark all unresolved files.
4904
4904
4905 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4905 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4906 default is to mark all resolved files.
4906 default is to mark all resolved files.
4907
4907
4908 - :hg:`resolve -l`: list files which had or still have conflicts.
4908 - :hg:`resolve -l`: list files which had or still have conflicts.
4909 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4909 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4910
4910
4911 Note that Mercurial will not let you commit files with unresolved
4911 Note that Mercurial will not let you commit files with unresolved
4912 merge conflicts. You must use :hg:`resolve -m ...` before you can
4912 merge conflicts. You must use :hg:`resolve -m ...` before you can
4913 commit after a conflicting merge.
4913 commit after a conflicting merge.
4914
4914
4915 Returns 0 on success, 1 if any files fail a resolve attempt.
4915 Returns 0 on success, 1 if any files fail a resolve attempt.
4916 """
4916 """
4917
4917
4918 all, mark, unmark, show, nostatus = \
4918 all, mark, unmark, show, nostatus = \
4919 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4919 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4920
4920
4921 if (show and (mark or unmark)) or (mark and unmark):
4921 if (show and (mark or unmark)) or (mark and unmark):
4922 raise util.Abort(_("too many options specified"))
4922 raise util.Abort(_("too many options specified"))
4923 if pats and all:
4923 if pats and all:
4924 raise util.Abort(_("can't specify --all and patterns"))
4924 raise util.Abort(_("can't specify --all and patterns"))
4925 if not (all or pats or show or mark or unmark):
4925 if not (all or pats or show or mark or unmark):
4926 raise util.Abort(_('no files or directories specified; '
4926 raise util.Abort(_('no files or directories specified; '
4927 'use --all to remerge all files'))
4927 'use --all to remerge all files'))
4928
4928
4929 ms = mergemod.mergestate(repo)
4929 ms = mergemod.mergestate(repo)
4930 m = scmutil.match(repo[None], pats, opts)
4930 m = scmutil.match(repo[None], pats, opts)
4931 ret = 0
4931 ret = 0
4932
4932
4933 for f in ms:
4933 for f in ms:
4934 if m(f):
4934 if m(f):
4935 if show:
4935 if show:
4936 if nostatus:
4936 if nostatus:
4937 ui.write("%s\n" % f)
4937 ui.write("%s\n" % f)
4938 else:
4938 else:
4939 ui.write("%s %s\n" % (ms[f].upper(), f),
4939 ui.write("%s %s\n" % (ms[f].upper(), f),
4940 label='resolve.' +
4940 label='resolve.' +
4941 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4941 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4942 elif mark:
4942 elif mark:
4943 ms.mark(f, "r")
4943 ms.mark(f, "r")
4944 elif unmark:
4944 elif unmark:
4945 ms.mark(f, "u")
4945 ms.mark(f, "u")
4946 else:
4946 else:
4947 wctx = repo[None]
4947 wctx = repo[None]
4948
4948
4949 # backup pre-resolve (merge uses .orig for its own purposes)
4949 # backup pre-resolve (merge uses .orig for its own purposes)
4950 a = repo.wjoin(f)
4950 a = repo.wjoin(f)
4951 util.copyfile(a, a + ".resolve")
4951 util.copyfile(a, a + ".resolve")
4952
4952
4953 try:
4953 try:
4954 # resolve file
4954 # resolve file
4955 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4955 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4956 'resolve')
4956 'resolve')
4957 if ms.resolve(f, wctx):
4957 if ms.resolve(f, wctx):
4958 ret = 1
4958 ret = 1
4959 finally:
4959 finally:
4960 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4960 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4961 ms.commit()
4961 ms.commit()
4962
4962
4963 # replace filemerge's .orig file with our resolve file
4963 # replace filemerge's .orig file with our resolve file
4964 util.rename(a + ".resolve", a + ".orig")
4964 util.rename(a + ".resolve", a + ".orig")
4965
4965
4966 ms.commit()
4966 ms.commit()
4967 return ret
4967 return ret
4968
4968
4969 @command('revert',
4969 @command('revert',
4970 [('a', 'all', None, _('revert all changes when no arguments given')),
4970 [('a', 'all', None, _('revert all changes when no arguments given')),
4971 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4971 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4972 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4972 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4973 ('C', 'no-backup', None, _('do not save backup copies of files')),
4973 ('C', 'no-backup', None, _('do not save backup copies of files')),
4974 ] + walkopts + dryrunopts,
4974 ] + walkopts + dryrunopts,
4975 _('[OPTION]... [-r REV] [NAME]...'))
4975 _('[OPTION]... [-r REV] [NAME]...'))
4976 def revert(ui, repo, *pats, **opts):
4976 def revert(ui, repo, *pats, **opts):
4977 """restore files to their checkout state
4977 """restore files to their checkout state
4978
4978
4979 .. note::
4979 .. note::
4980
4980
4981 To check out earlier revisions, you should use :hg:`update REV`.
4981 To check out earlier revisions, you should use :hg:`update REV`.
4982 To cancel an uncommitted merge (and lose your changes),
4982 To cancel an uncommitted merge (and lose your changes),
4983 use :hg:`update --clean .`.
4983 use :hg:`update --clean .`.
4984
4984
4985 With no revision specified, revert the specified files or directories
4985 With no revision specified, revert the specified files or directories
4986 to the contents they had in the parent of the working directory.
4986 to the contents they had in the parent of the working directory.
4987 This restores the contents of files to an unmodified
4987 This restores the contents of files to an unmodified
4988 state and unschedules adds, removes, copies, and renames. If the
4988 state and unschedules adds, removes, copies, and renames. If the
4989 working directory has two parents, you must explicitly specify a
4989 working directory has two parents, you must explicitly specify a
4990 revision.
4990 revision.
4991
4991
4992 Using the -r/--rev or -d/--date options, revert the given files or
4992 Using the -r/--rev or -d/--date options, revert the given files or
4993 directories to their states as of a specific revision. Because
4993 directories to their states as of a specific revision. Because
4994 revert does not change the working directory parents, this will
4994 revert does not change the working directory parents, this will
4995 cause these files to appear modified. This can be helpful to "back
4995 cause these files to appear modified. This can be helpful to "back
4996 out" some or all of an earlier change. See :hg:`backout` for a
4996 out" some or all of an earlier change. See :hg:`backout` for a
4997 related method.
4997 related method.
4998
4998
4999 Modified files are saved with a .orig suffix before reverting.
4999 Modified files are saved with a .orig suffix before reverting.
5000 To disable these backups, use --no-backup.
5000 To disable these backups, use --no-backup.
5001
5001
5002 See :hg:`help dates` for a list of formats valid for -d/--date.
5002 See :hg:`help dates` for a list of formats valid for -d/--date.
5003
5003
5004 Returns 0 on success.
5004 Returns 0 on success.
5005 """
5005 """
5006
5006
5007 if opts.get("date"):
5007 if opts.get("date"):
5008 if opts.get("rev"):
5008 if opts.get("rev"):
5009 raise util.Abort(_("you can't specify a revision and a date"))
5009 raise util.Abort(_("you can't specify a revision and a date"))
5010 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5010 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5011
5011
5012 parent, p2 = repo.dirstate.parents()
5012 parent, p2 = repo.dirstate.parents()
5013 if not opts.get('rev') and p2 != nullid:
5013 if not opts.get('rev') and p2 != nullid:
5014 # revert after merge is a trap for new users (issue2915)
5014 # revert after merge is a trap for new users (issue2915)
5015 raise util.Abort(_('uncommitted merge with no revision specified'),
5015 raise util.Abort(_('uncommitted merge with no revision specified'),
5016 hint=_('use "hg update" or see "hg help revert"'))
5016 hint=_('use "hg update" or see "hg help revert"'))
5017
5017
5018 ctx = scmutil.revsingle(repo, opts.get('rev'))
5018 ctx = scmutil.revsingle(repo, opts.get('rev'))
5019
5019
5020 if not pats and not opts.get('all'):
5020 if not pats and not opts.get('all'):
5021 msg = _("no files or directories specified")
5021 msg = _("no files or directories specified")
5022 if p2 != nullid:
5022 if p2 != nullid:
5023 hint = _("uncommitted merge, use --all to discard all changes,"
5023 hint = _("uncommitted merge, use --all to discard all changes,"
5024 " or 'hg update -C .' to abort the merge")
5024 " or 'hg update -C .' to abort the merge")
5025 raise util.Abort(msg, hint=hint)
5025 raise util.Abort(msg, hint=hint)
5026 dirty = util.any(repo.status())
5026 dirty = util.any(repo.status())
5027 node = ctx.node()
5027 node = ctx.node()
5028 if node != parent:
5028 if node != parent:
5029 if dirty:
5029 if dirty:
5030 hint = _("uncommitted changes, use --all to discard all"
5030 hint = _("uncommitted changes, use --all to discard all"
5031 " changes, or 'hg update %s' to update") % ctx.rev()
5031 " changes, or 'hg update %s' to update") % ctx.rev()
5032 else:
5032 else:
5033 hint = _("use --all to revert all files,"
5033 hint = _("use --all to revert all files,"
5034 " or 'hg update %s' to update") % ctx.rev()
5034 " or 'hg update %s' to update") % ctx.rev()
5035 elif dirty:
5035 elif dirty:
5036 hint = _("uncommitted changes, use --all to discard all changes")
5036 hint = _("uncommitted changes, use --all to discard all changes")
5037 else:
5037 else:
5038 hint = _("use --all to revert all files")
5038 hint = _("use --all to revert all files")
5039 raise util.Abort(msg, hint=hint)
5039 raise util.Abort(msg, hint=hint)
5040
5040
5041 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5041 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5042
5042
5043 @command('rollback', dryrunopts +
5043 @command('rollback', dryrunopts +
5044 [('f', 'force', False, _('ignore safety measures'))])
5044 [('f', 'force', False, _('ignore safety measures'))])
5045 def rollback(ui, repo, **opts):
5045 def rollback(ui, repo, **opts):
5046 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5046 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5047
5047
5048 Please use :hg:`commit --amend` instead of rollback to correct
5048 Please use :hg:`commit --amend` instead of rollback to correct
5049 mistakes in the last commit.
5049 mistakes in the last commit.
5050
5050
5051 This command should be used with care. There is only one level of
5051 This command should be used with care. There is only one level of
5052 rollback, and there is no way to undo a rollback. It will also
5052 rollback, and there is no way to undo a rollback. It will also
5053 restore the dirstate at the time of the last transaction, losing
5053 restore the dirstate at the time of the last transaction, losing
5054 any dirstate changes since that time. This command does not alter
5054 any dirstate changes since that time. This command does not alter
5055 the working directory.
5055 the working directory.
5056
5056
5057 Transactions are used to encapsulate the effects of all commands
5057 Transactions are used to encapsulate the effects of all commands
5058 that create new changesets or propagate existing changesets into a
5058 that create new changesets or propagate existing changesets into a
5059 repository.
5059 repository.
5060
5060
5061 .. container:: verbose
5061 .. container:: verbose
5062
5062
5063 For example, the following commands are transactional, and their
5063 For example, the following commands are transactional, and their
5064 effects can be rolled back:
5064 effects can be rolled back:
5065
5065
5066 - commit
5066 - commit
5067 - import
5067 - import
5068 - pull
5068 - pull
5069 - push (with this repository as the destination)
5069 - push (with this repository as the destination)
5070 - unbundle
5070 - unbundle
5071
5071
5072 To avoid permanent data loss, rollback will refuse to rollback a
5072 To avoid permanent data loss, rollback will refuse to rollback a
5073 commit transaction if it isn't checked out. Use --force to
5073 commit transaction if it isn't checked out. Use --force to
5074 override this protection.
5074 override this protection.
5075
5075
5076 This command is not intended for use on public repositories. Once
5076 This command is not intended for use on public repositories. Once
5077 changes are visible for pull by other users, rolling a transaction
5077 changes are visible for pull by other users, rolling a transaction
5078 back locally is ineffective (someone else may already have pulled
5078 back locally is ineffective (someone else may already have pulled
5079 the changes). Furthermore, a race is possible with readers of the
5079 the changes). Furthermore, a race is possible with readers of the
5080 repository; for example an in-progress pull from the repository
5080 repository; for example an in-progress pull from the repository
5081 may fail if a rollback is performed.
5081 may fail if a rollback is performed.
5082
5082
5083 Returns 0 on success, 1 if no rollback data is available.
5083 Returns 0 on success, 1 if no rollback data is available.
5084 """
5084 """
5085 return repo.rollback(dryrun=opts.get('dry_run'),
5085 return repo.rollback(dryrun=opts.get('dry_run'),
5086 force=opts.get('force'))
5086 force=opts.get('force'))
5087
5087
5088 @command('root', [])
5088 @command('root', [])
5089 def root(ui, repo):
5089 def root(ui, repo):
5090 """print the root (top) of the current working directory
5090 """print the root (top) of the current working directory
5091
5091
5092 Print the root directory of the current repository.
5092 Print the root directory of the current repository.
5093
5093
5094 Returns 0 on success.
5094 Returns 0 on success.
5095 """
5095 """
5096 ui.write(repo.root + "\n")
5096 ui.write(repo.root + "\n")
5097
5097
5098 @command('^serve',
5098 @command('^serve',
5099 [('A', 'accesslog', '', _('name of access log file to write to'),
5099 [('A', 'accesslog', '', _('name of access log file to write to'),
5100 _('FILE')),
5100 _('FILE')),
5101 ('d', 'daemon', None, _('run server in background')),
5101 ('d', 'daemon', None, _('run server in background')),
5102 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5102 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5103 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5103 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5104 # use string type, then we can check if something was passed
5104 # use string type, then we can check if something was passed
5105 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5105 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5106 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5106 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5107 _('ADDR')),
5107 _('ADDR')),
5108 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5108 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5109 _('PREFIX')),
5109 _('PREFIX')),
5110 ('n', 'name', '',
5110 ('n', 'name', '',
5111 _('name to show in web pages (default: working directory)'), _('NAME')),
5111 _('name to show in web pages (default: working directory)'), _('NAME')),
5112 ('', 'web-conf', '',
5112 ('', 'web-conf', '',
5113 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5113 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5114 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5114 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5115 _('FILE')),
5115 _('FILE')),
5116 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5116 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5117 ('', 'stdio', None, _('for remote clients')),
5117 ('', 'stdio', None, _('for remote clients')),
5118 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5118 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5119 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5119 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5120 ('', 'style', '', _('template style to use'), _('STYLE')),
5120 ('', 'style', '', _('template style to use'), _('STYLE')),
5121 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5121 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5122 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5122 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5123 _('[OPTION]...'))
5123 _('[OPTION]...'))
5124 def serve(ui, repo, **opts):
5124 def serve(ui, repo, **opts):
5125 """start stand-alone webserver
5125 """start stand-alone webserver
5126
5126
5127 Start a local HTTP repository browser and pull server. You can use
5127 Start a local HTTP repository browser and pull server. You can use
5128 this for ad-hoc sharing and browsing of repositories. It is
5128 this for ad-hoc sharing and browsing of repositories. It is
5129 recommended to use a real web server to serve a repository for
5129 recommended to use a real web server to serve a repository for
5130 longer periods of time.
5130 longer periods of time.
5131
5131
5132 Please note that the server does not implement access control.
5132 Please note that the server does not implement access control.
5133 This means that, by default, anybody can read from the server and
5133 This means that, by default, anybody can read from the server and
5134 nobody can write to it by default. Set the ``web.allow_push``
5134 nobody can write to it by default. Set the ``web.allow_push``
5135 option to ``*`` to allow everybody to push to the server. You
5135 option to ``*`` to allow everybody to push to the server. You
5136 should use a real web server if you need to authenticate users.
5136 should use a real web server if you need to authenticate users.
5137
5137
5138 By default, the server logs accesses to stdout and errors to
5138 By default, the server logs accesses to stdout and errors to
5139 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5139 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5140 files.
5140 files.
5141
5141
5142 To have the server choose a free port number to listen on, specify
5142 To have the server choose a free port number to listen on, specify
5143 a port number of 0; in this case, the server will print the port
5143 a port number of 0; in this case, the server will print the port
5144 number it uses.
5144 number it uses.
5145
5145
5146 Returns 0 on success.
5146 Returns 0 on success.
5147 """
5147 """
5148
5148
5149 if opts["stdio"] and opts["cmdserver"]:
5149 if opts["stdio"] and opts["cmdserver"]:
5150 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5150 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5151
5151
5152 def checkrepo():
5152 def checkrepo():
5153 if repo is None:
5153 if repo is None:
5154 raise error.RepoError(_("there is no Mercurial repository here"
5154 raise error.RepoError(_("there is no Mercurial repository here"
5155 " (.hg not found)"))
5155 " (.hg not found)"))
5156
5156
5157 if opts["stdio"]:
5157 if opts["stdio"]:
5158 checkrepo()
5158 checkrepo()
5159 s = sshserver.sshserver(ui, repo)
5159 s = sshserver.sshserver(ui, repo)
5160 s.serve_forever()
5160 s.serve_forever()
5161
5161
5162 if opts["cmdserver"]:
5162 if opts["cmdserver"]:
5163 s = commandserver.server(ui, repo, opts["cmdserver"])
5163 s = commandserver.server(ui, repo, opts["cmdserver"])
5164 return s.serve()
5164 return s.serve()
5165
5165
5166 # this way we can check if something was given in the command-line
5166 # this way we can check if something was given in the command-line
5167 if opts.get('port'):
5167 if opts.get('port'):
5168 opts['port'] = util.getport(opts.get('port'))
5168 opts['port'] = util.getport(opts.get('port'))
5169
5169
5170 baseui = repo and repo.baseui or ui
5170 baseui = repo and repo.baseui or ui
5171 optlist = ("name templates style address port prefix ipv6"
5171 optlist = ("name templates style address port prefix ipv6"
5172 " accesslog errorlog certificate encoding")
5172 " accesslog errorlog certificate encoding")
5173 for o in optlist.split():
5173 for o in optlist.split():
5174 val = opts.get(o, '')
5174 val = opts.get(o, '')
5175 if val in (None, ''): # should check against default options instead
5175 if val in (None, ''): # should check against default options instead
5176 continue
5176 continue
5177 baseui.setconfig("web", o, val, 'serve')
5177 baseui.setconfig("web", o, val, 'serve')
5178 if repo and repo.ui != baseui:
5178 if repo and repo.ui != baseui:
5179 repo.ui.setconfig("web", o, val, 'serve')
5179 repo.ui.setconfig("web", o, val, 'serve')
5180
5180
5181 o = opts.get('web_conf') or opts.get('webdir_conf')
5181 o = opts.get('web_conf') or opts.get('webdir_conf')
5182 if not o:
5182 if not o:
5183 if not repo:
5183 if not repo:
5184 raise error.RepoError(_("there is no Mercurial repository"
5184 raise error.RepoError(_("there is no Mercurial repository"
5185 " here (.hg not found)"))
5185 " here (.hg not found)"))
5186 o = repo
5186 o = repo
5187
5187
5188 app = hgweb.hgweb(o, baseui=baseui)
5188 app = hgweb.hgweb(o, baseui=baseui)
5189 service = httpservice(ui, app, opts)
5189 service = httpservice(ui, app, opts)
5190 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5190 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5191
5191
5192 class httpservice(object):
5192 class httpservice(object):
5193 def __init__(self, ui, app, opts):
5193 def __init__(self, ui, app, opts):
5194 self.ui = ui
5194 self.ui = ui
5195 self.app = app
5195 self.app = app
5196 self.opts = opts
5196 self.opts = opts
5197
5197
5198 def init(self):
5198 def init(self):
5199 util.setsignalhandler()
5199 util.setsignalhandler()
5200 self.httpd = hgweb_server.create_server(self.ui, self.app)
5200 self.httpd = hgweb_server.create_server(self.ui, self.app)
5201
5201
5202 if self.opts['port'] and not self.ui.verbose:
5202 if self.opts['port'] and not self.ui.verbose:
5203 return
5203 return
5204
5204
5205 if self.httpd.prefix:
5205 if self.httpd.prefix:
5206 prefix = self.httpd.prefix.strip('/') + '/'
5206 prefix = self.httpd.prefix.strip('/') + '/'
5207 else:
5207 else:
5208 prefix = ''
5208 prefix = ''
5209
5209
5210 port = ':%d' % self.httpd.port
5210 port = ':%d' % self.httpd.port
5211 if port == ':80':
5211 if port == ':80':
5212 port = ''
5212 port = ''
5213
5213
5214 bindaddr = self.httpd.addr
5214 bindaddr = self.httpd.addr
5215 if bindaddr == '0.0.0.0':
5215 if bindaddr == '0.0.0.0':
5216 bindaddr = '*'
5216 bindaddr = '*'
5217 elif ':' in bindaddr: # IPv6
5217 elif ':' in bindaddr: # IPv6
5218 bindaddr = '[%s]' % bindaddr
5218 bindaddr = '[%s]' % bindaddr
5219
5219
5220 fqaddr = self.httpd.fqaddr
5220 fqaddr = self.httpd.fqaddr
5221 if ':' in fqaddr:
5221 if ':' in fqaddr:
5222 fqaddr = '[%s]' % fqaddr
5222 fqaddr = '[%s]' % fqaddr
5223 if self.opts['port']:
5223 if self.opts['port']:
5224 write = self.ui.status
5224 write = self.ui.status
5225 else:
5225 else:
5226 write = self.ui.write
5226 write = self.ui.write
5227 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5227 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5228 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5228 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5229
5229
5230 def run(self):
5230 def run(self):
5231 self.httpd.serve_forever()
5231 self.httpd.serve_forever()
5232
5232
5233
5233
5234 @command('^status|st',
5234 @command('^status|st',
5235 [('A', 'all', None, _('show status of all files')),
5235 [('A', 'all', None, _('show status of all files')),
5236 ('m', 'modified', None, _('show only modified files')),
5236 ('m', 'modified', None, _('show only modified files')),
5237 ('a', 'added', None, _('show only added files')),
5237 ('a', 'added', None, _('show only added files')),
5238 ('r', 'removed', None, _('show only removed files')),
5238 ('r', 'removed', None, _('show only removed files')),
5239 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5239 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5240 ('c', 'clean', None, _('show only files without changes')),
5240 ('c', 'clean', None, _('show only files without changes')),
5241 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5241 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5242 ('i', 'ignored', None, _('show only ignored files')),
5242 ('i', 'ignored', None, _('show only ignored files')),
5243 ('n', 'no-status', None, _('hide status prefix')),
5243 ('n', 'no-status', None, _('hide status prefix')),
5244 ('C', 'copies', None, _('show source of copied files')),
5244 ('C', 'copies', None, _('show source of copied files')),
5245 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5245 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5246 ('', 'rev', [], _('show difference from revision'), _('REV')),
5246 ('', 'rev', [], _('show difference from revision'), _('REV')),
5247 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5247 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5248 ] + walkopts + subrepoopts,
5248 ] + walkopts + subrepoopts,
5249 _('[OPTION]... [FILE]...'))
5249 _('[OPTION]... [FILE]...'))
5250 def status(ui, repo, *pats, **opts):
5250 def status(ui, repo, *pats, **opts):
5251 """show changed files in the working directory
5251 """show changed files in the working directory
5252
5252
5253 Show status of files in the repository. If names are given, only
5253 Show status of files in the repository. If names are given, only
5254 files that match are shown. Files that are clean or ignored or
5254 files that match are shown. Files that are clean or ignored or
5255 the source of a copy/move operation, are not listed unless
5255 the source of a copy/move operation, are not listed unless
5256 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5256 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5257 Unless options described with "show only ..." are given, the
5257 Unless options described with "show only ..." are given, the
5258 options -mardu are used.
5258 options -mardu are used.
5259
5259
5260 Option -q/--quiet hides untracked (unknown and ignored) files
5260 Option -q/--quiet hides untracked (unknown and ignored) files
5261 unless explicitly requested with -u/--unknown or -i/--ignored.
5261 unless explicitly requested with -u/--unknown or -i/--ignored.
5262
5262
5263 .. note::
5263 .. note::
5264
5264
5265 status may appear to disagree with diff if permissions have
5265 status may appear to disagree with diff if permissions have
5266 changed or a merge has occurred. The standard diff format does
5266 changed or a merge has occurred. The standard diff format does
5267 not report permission changes and diff only reports changes
5267 not report permission changes and diff only reports changes
5268 relative to one merge parent.
5268 relative to one merge parent.
5269
5269
5270 If one revision is given, it is used as the base revision.
5270 If one revision is given, it is used as the base revision.
5271 If two revisions are given, the differences between them are
5271 If two revisions are given, the differences between them are
5272 shown. The --change option can also be used as a shortcut to list
5272 shown. The --change option can also be used as a shortcut to list
5273 the changed files of a revision from its first parent.
5273 the changed files of a revision from its first parent.
5274
5274
5275 The codes used to show the status of files are::
5275 The codes used to show the status of files are::
5276
5276
5277 M = modified
5277 M = modified
5278 A = added
5278 A = added
5279 R = removed
5279 R = removed
5280 C = clean
5280 C = clean
5281 ! = missing (deleted by non-hg command, but still tracked)
5281 ! = missing (deleted by non-hg command, but still tracked)
5282 ? = not tracked
5282 ? = not tracked
5283 I = ignored
5283 I = ignored
5284 = origin of the previous file (with --copies)
5284 = origin of the previous file (with --copies)
5285
5285
5286 .. container:: verbose
5286 .. container:: verbose
5287
5287
5288 Examples:
5288 Examples:
5289
5289
5290 - show changes in the working directory relative to a
5290 - show changes in the working directory relative to a
5291 changeset::
5291 changeset::
5292
5292
5293 hg status --rev 9353
5293 hg status --rev 9353
5294
5294
5295 - show all changes including copies in an existing changeset::
5295 - show all changes including copies in an existing changeset::
5296
5296
5297 hg status --copies --change 9353
5297 hg status --copies --change 9353
5298
5298
5299 - get a NUL separated list of added files, suitable for xargs::
5299 - get a NUL separated list of added files, suitable for xargs::
5300
5300
5301 hg status -an0
5301 hg status -an0
5302
5302
5303 Returns 0 on success.
5303 Returns 0 on success.
5304 """
5304 """
5305
5305
5306 revs = opts.get('rev')
5306 revs = opts.get('rev')
5307 change = opts.get('change')
5307 change = opts.get('change')
5308
5308
5309 if revs and change:
5309 if revs and change:
5310 msg = _('cannot specify --rev and --change at the same time')
5310 msg = _('cannot specify --rev and --change at the same time')
5311 raise util.Abort(msg)
5311 raise util.Abort(msg)
5312 elif change:
5312 elif change:
5313 node2 = scmutil.revsingle(repo, change, None).node()
5313 node2 = scmutil.revsingle(repo, change, None).node()
5314 node1 = repo[node2].p1().node()
5314 node1 = repo[node2].p1().node()
5315 else:
5315 else:
5316 node1, node2 = scmutil.revpair(repo, revs)
5316 node1, node2 = scmutil.revpair(repo, revs)
5317
5317
5318 cwd = (pats and repo.getcwd()) or ''
5318 cwd = (pats and repo.getcwd()) or ''
5319 end = opts.get('print0') and '\0' or '\n'
5319 end = opts.get('print0') and '\0' or '\n'
5320 copy = {}
5320 copy = {}
5321 states = 'modified added removed deleted unknown ignored clean'.split()
5321 states = 'modified added removed deleted unknown ignored clean'.split()
5322 show = [k for k in states if opts.get(k)]
5322 show = [k for k in states if opts.get(k)]
5323 if opts.get('all'):
5323 if opts.get('all'):
5324 show += ui.quiet and (states[:4] + ['clean']) or states
5324 show += ui.quiet and (states[:4] + ['clean']) or states
5325 if not show:
5325 if not show:
5326 show = ui.quiet and states[:4] or states[:5]
5326 show = ui.quiet and states[:4] or states[:5]
5327
5327
5328 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5328 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5329 'ignored' in show, 'clean' in show, 'unknown' in show,
5329 'ignored' in show, 'clean' in show, 'unknown' in show,
5330 opts.get('subrepos'))
5330 opts.get('subrepos'))
5331 changestates = zip(states, 'MAR!?IC', stat)
5331 changestates = zip(states, 'MAR!?IC', stat)
5332
5332
5333 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5333 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5334 copy = copies.pathcopies(repo[node1], repo[node2])
5334 copy = copies.pathcopies(repo[node1], repo[node2])
5335
5335
5336 fm = ui.formatter('status', opts)
5336 fm = ui.formatter('status', opts)
5337 fmt = '%s' + end
5337 fmt = '%s' + end
5338 showchar = not opts.get('no_status')
5338 showchar = not opts.get('no_status')
5339
5339
5340 for state, char, files in changestates:
5340 for state, char, files in changestates:
5341 if state in show:
5341 if state in show:
5342 label = 'status.' + state
5342 label = 'status.' + state
5343 for f in files:
5343 for f in files:
5344 fm.startitem()
5344 fm.startitem()
5345 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5345 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5346 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5346 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5347 if f in copy:
5347 if f in copy:
5348 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5348 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5349 label='status.copied')
5349 label='status.copied')
5350 fm.end()
5350 fm.end()
5351
5351
5352 @command('^summary|sum',
5352 @command('^summary|sum',
5353 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5353 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5354 def summary(ui, repo, **opts):
5354 def summary(ui, repo, **opts):
5355 """summarize working directory state
5355 """summarize working directory state
5356
5356
5357 This generates a brief summary of the working directory state,
5357 This generates a brief summary of the working directory state,
5358 including parents, branch, commit status, and available updates.
5358 including parents, branch, commit status, and available updates.
5359
5359
5360 With the --remote option, this will check the default paths for
5360 With the --remote option, this will check the default paths for
5361 incoming and outgoing changes. This can be time-consuming.
5361 incoming and outgoing changes. This can be time-consuming.
5362
5362
5363 Returns 0 on success.
5363 Returns 0 on success.
5364 """
5364 """
5365
5365
5366 ctx = repo[None]
5366 ctx = repo[None]
5367 parents = ctx.parents()
5367 parents = ctx.parents()
5368 pnode = parents[0].node()
5368 pnode = parents[0].node()
5369 marks = []
5369 marks = []
5370
5370
5371 for p in parents:
5371 for p in parents:
5372 # label with log.changeset (instead of log.parent) since this
5372 # label with log.changeset (instead of log.parent) since this
5373 # shows a working directory parent *changeset*:
5373 # shows a working directory parent *changeset*:
5374 # i18n: column positioning for "hg summary"
5374 # i18n: column positioning for "hg summary"
5375 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5375 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5376 label='log.changeset changeset.%s' % p.phasestr())
5376 label='log.changeset changeset.%s' % p.phasestr())
5377 ui.write(' '.join(p.tags()), label='log.tag')
5377 ui.write(' '.join(p.tags()), label='log.tag')
5378 if p.bookmarks():
5378 if p.bookmarks():
5379 marks.extend(p.bookmarks())
5379 marks.extend(p.bookmarks())
5380 if p.rev() == -1:
5380 if p.rev() == -1:
5381 if not len(repo):
5381 if not len(repo):
5382 ui.write(_(' (empty repository)'))
5382 ui.write(_(' (empty repository)'))
5383 else:
5383 else:
5384 ui.write(_(' (no revision checked out)'))
5384 ui.write(_(' (no revision checked out)'))
5385 ui.write('\n')
5385 ui.write('\n')
5386 if p.description():
5386 if p.description():
5387 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5387 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5388 label='log.summary')
5388 label='log.summary')
5389
5389
5390 branch = ctx.branch()
5390 branch = ctx.branch()
5391 bheads = repo.branchheads(branch)
5391 bheads = repo.branchheads(branch)
5392 # i18n: column positioning for "hg summary"
5392 # i18n: column positioning for "hg summary"
5393 m = _('branch: %s\n') % branch
5393 m = _('branch: %s\n') % branch
5394 if branch != 'default':
5394 if branch != 'default':
5395 ui.write(m, label='log.branch')
5395 ui.write(m, label='log.branch')
5396 else:
5396 else:
5397 ui.status(m, label='log.branch')
5397 ui.status(m, label='log.branch')
5398
5398
5399 if marks:
5399 if marks:
5400 current = repo._bookmarkcurrent
5400 current = repo._bookmarkcurrent
5401 # i18n: column positioning for "hg summary"
5401 # i18n: column positioning for "hg summary"
5402 ui.write(_('bookmarks:'), label='log.bookmark')
5402 ui.write(_('bookmarks:'), label='log.bookmark')
5403 if current is not None:
5403 if current is not None:
5404 if current in marks:
5404 if current in marks:
5405 ui.write(' *' + current, label='bookmarks.current')
5405 ui.write(' *' + current, label='bookmarks.current')
5406 marks.remove(current)
5406 marks.remove(current)
5407 else:
5407 else:
5408 ui.write(' [%s]' % current, label='bookmarks.current')
5408 ui.write(' [%s]' % current, label='bookmarks.current')
5409 for m in marks:
5409 for m in marks:
5410 ui.write(' ' + m, label='log.bookmark')
5410 ui.write(' ' + m, label='log.bookmark')
5411 ui.write('\n', label='log.bookmark')
5411 ui.write('\n', label='log.bookmark')
5412
5412
5413 st = list(repo.status(unknown=True))[:6]
5413 st = list(repo.status(unknown=True))[:6]
5414
5414
5415 c = repo.dirstate.copies()
5415 c = repo.dirstate.copies()
5416 copied, renamed = [], []
5416 copied, renamed = [], []
5417 for d, s in c.iteritems():
5417 for d, s in c.iteritems():
5418 if s in st[2]:
5418 if s in st[2]:
5419 st[2].remove(s)
5419 st[2].remove(s)
5420 renamed.append(d)
5420 renamed.append(d)
5421 else:
5421 else:
5422 copied.append(d)
5422 copied.append(d)
5423 if d in st[1]:
5423 if d in st[1]:
5424 st[1].remove(d)
5424 st[1].remove(d)
5425 st.insert(3, renamed)
5425 st.insert(3, renamed)
5426 st.insert(4, copied)
5426 st.insert(4, copied)
5427
5427
5428 ms = mergemod.mergestate(repo)
5428 ms = mergemod.mergestate(repo)
5429 st.append([f for f in ms if ms[f] == 'u'])
5429 st.append([f for f in ms if ms[f] == 'u'])
5430
5430
5431 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5431 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5432 st.append(subs)
5432 st.append(subs)
5433
5433
5434 labels = [ui.label(_('%d modified'), 'status.modified'),
5434 labels = [ui.label(_('%d modified'), 'status.modified'),
5435 ui.label(_('%d added'), 'status.added'),
5435 ui.label(_('%d added'), 'status.added'),
5436 ui.label(_('%d removed'), 'status.removed'),
5436 ui.label(_('%d removed'), 'status.removed'),
5437 ui.label(_('%d renamed'), 'status.copied'),
5437 ui.label(_('%d renamed'), 'status.copied'),
5438 ui.label(_('%d copied'), 'status.copied'),
5438 ui.label(_('%d copied'), 'status.copied'),
5439 ui.label(_('%d deleted'), 'status.deleted'),
5439 ui.label(_('%d deleted'), 'status.deleted'),
5440 ui.label(_('%d unknown'), 'status.unknown'),
5440 ui.label(_('%d unknown'), 'status.unknown'),
5441 ui.label(_('%d ignored'), 'status.ignored'),
5441 ui.label(_('%d ignored'), 'status.ignored'),
5442 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5442 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5443 ui.label(_('%d subrepos'), 'status.modified')]
5443 ui.label(_('%d subrepos'), 'status.modified')]
5444 t = []
5444 t = []
5445 for s, l in zip(st, labels):
5445 for s, l in zip(st, labels):
5446 if s:
5446 if s:
5447 t.append(l % len(s))
5447 t.append(l % len(s))
5448
5448
5449 t = ', '.join(t)
5449 t = ', '.join(t)
5450 cleanworkdir = False
5450 cleanworkdir = False
5451
5451
5452 if repo.vfs.exists('updatestate'):
5452 if repo.vfs.exists('updatestate'):
5453 t += _(' (interrupted update)')
5453 t += _(' (interrupted update)')
5454 elif len(parents) > 1:
5454 elif len(parents) > 1:
5455 t += _(' (merge)')
5455 t += _(' (merge)')
5456 elif branch != parents[0].branch():
5456 elif branch != parents[0].branch():
5457 t += _(' (new branch)')
5457 t += _(' (new branch)')
5458 elif (parents[0].closesbranch() and
5458 elif (parents[0].closesbranch() and
5459 pnode in repo.branchheads(branch, closed=True)):
5459 pnode in repo.branchheads(branch, closed=True)):
5460 t += _(' (head closed)')
5460 t += _(' (head closed)')
5461 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5461 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5462 t += _(' (clean)')
5462 t += _(' (clean)')
5463 cleanworkdir = True
5463 cleanworkdir = True
5464 elif pnode not in bheads:
5464 elif pnode not in bheads:
5465 t += _(' (new branch head)')
5465 t += _(' (new branch head)')
5466
5466
5467 if cleanworkdir:
5467 if cleanworkdir:
5468 # i18n: column positioning for "hg summary"
5468 # i18n: column positioning for "hg summary"
5469 ui.status(_('commit: %s\n') % t.strip())
5469 ui.status(_('commit: %s\n') % t.strip())
5470 else:
5470 else:
5471 # i18n: column positioning for "hg summary"
5471 # i18n: column positioning for "hg summary"
5472 ui.write(_('commit: %s\n') % t.strip())
5472 ui.write(_('commit: %s\n') % t.strip())
5473
5473
5474 # all ancestors of branch heads - all ancestors of parent = new csets
5474 # all ancestors of branch heads - all ancestors of parent = new csets
5475 new = len(repo.changelog.findmissing([ctx.node() for ctx in parents],
5475 new = len(repo.changelog.findmissing([ctx.node() for ctx in parents],
5476 bheads))
5476 bheads))
5477
5477
5478 if new == 0:
5478 if new == 0:
5479 # i18n: column positioning for "hg summary"
5479 # i18n: column positioning for "hg summary"
5480 ui.status(_('update: (current)\n'))
5480 ui.status(_('update: (current)\n'))
5481 elif pnode not in bheads:
5481 elif pnode not in bheads:
5482 # i18n: column positioning for "hg summary"
5482 # i18n: column positioning for "hg summary"
5483 ui.write(_('update: %d new changesets (update)\n') % new)
5483 ui.write(_('update: %d new changesets (update)\n') % new)
5484 else:
5484 else:
5485 # i18n: column positioning for "hg summary"
5485 # i18n: column positioning for "hg summary"
5486 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5486 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5487 (new, len(bheads)))
5487 (new, len(bheads)))
5488
5488
5489 cmdutil.summaryhooks(ui, repo)
5489 cmdutil.summaryhooks(ui, repo)
5490
5490
5491 if opts.get('remote'):
5491 if opts.get('remote'):
5492 needsincoming, needsoutgoing = True, True
5492 needsincoming, needsoutgoing = True, True
5493 else:
5493 else:
5494 needsincoming, needsoutgoing = False, False
5494 needsincoming, needsoutgoing = False, False
5495 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5495 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5496 if i:
5496 if i:
5497 needsincoming = True
5497 needsincoming = True
5498 if o:
5498 if o:
5499 needsoutgoing = True
5499 needsoutgoing = True
5500 if not needsincoming and not needsoutgoing:
5500 if not needsincoming and not needsoutgoing:
5501 return
5501 return
5502
5502
5503 def getincoming():
5503 def getincoming():
5504 source, branches = hg.parseurl(ui.expandpath('default'))
5504 source, branches = hg.parseurl(ui.expandpath('default'))
5505 sbranch = branches[0]
5505 sbranch = branches[0]
5506 try:
5506 try:
5507 other = hg.peer(repo, {}, source)
5507 other = hg.peer(repo, {}, source)
5508 except error.RepoError:
5508 except error.RepoError:
5509 if opts.get('remote'):
5509 if opts.get('remote'):
5510 raise
5510 raise
5511 return source, sbranch, None, None, None
5511 return source, sbranch, None, None, None
5512 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5512 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5513 if revs:
5513 if revs:
5514 revs = [other.lookup(rev) for rev in revs]
5514 revs = [other.lookup(rev) for rev in revs]
5515 ui.debug('comparing with %s\n' % util.hidepassword(source))
5515 ui.debug('comparing with %s\n' % util.hidepassword(source))
5516 repo.ui.pushbuffer()
5516 repo.ui.pushbuffer()
5517 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5517 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5518 repo.ui.popbuffer()
5518 repo.ui.popbuffer()
5519 return source, sbranch, other, commoninc, commoninc[1]
5519 return source, sbranch, other, commoninc, commoninc[1]
5520
5520
5521 if needsincoming:
5521 if needsincoming:
5522 source, sbranch, sother, commoninc, incoming = getincoming()
5522 source, sbranch, sother, commoninc, incoming = getincoming()
5523 else:
5523 else:
5524 source = sbranch = sother = commoninc = incoming = None
5524 source = sbranch = sother = commoninc = incoming = None
5525
5525
5526 def getoutgoing():
5526 def getoutgoing():
5527 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5527 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5528 dbranch = branches[0]
5528 dbranch = branches[0]
5529 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5529 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5530 if source != dest:
5530 if source != dest:
5531 try:
5531 try:
5532 dother = hg.peer(repo, {}, dest)
5532 dother = hg.peer(repo, {}, dest)
5533 except error.RepoError:
5533 except error.RepoError:
5534 if opts.get('remote'):
5534 if opts.get('remote'):
5535 raise
5535 raise
5536 return dest, dbranch, None, None
5536 return dest, dbranch, None, None
5537 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5537 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5538 elif sother is None:
5538 elif sother is None:
5539 # there is no explicit destination peer, but source one is invalid
5539 # there is no explicit destination peer, but source one is invalid
5540 return dest, dbranch, None, None
5540 return dest, dbranch, None, None
5541 else:
5541 else:
5542 dother = sother
5542 dother = sother
5543 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5543 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5544 common = None
5544 common = None
5545 else:
5545 else:
5546 common = commoninc
5546 common = commoninc
5547 if revs:
5547 if revs:
5548 revs = [repo.lookup(rev) for rev in revs]
5548 revs = [repo.lookup(rev) for rev in revs]
5549 repo.ui.pushbuffer()
5549 repo.ui.pushbuffer()
5550 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5550 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5551 commoninc=common)
5551 commoninc=common)
5552 repo.ui.popbuffer()
5552 repo.ui.popbuffer()
5553 return dest, dbranch, dother, outgoing
5553 return dest, dbranch, dother, outgoing
5554
5554
5555 if needsoutgoing:
5555 if needsoutgoing:
5556 dest, dbranch, dother, outgoing = getoutgoing()
5556 dest, dbranch, dother, outgoing = getoutgoing()
5557 else:
5557 else:
5558 dest = dbranch = dother = outgoing = None
5558 dest = dbranch = dother = outgoing = None
5559
5559
5560 if opts.get('remote'):
5560 if opts.get('remote'):
5561 t = []
5561 t = []
5562 if incoming:
5562 if incoming:
5563 t.append(_('1 or more incoming'))
5563 t.append(_('1 or more incoming'))
5564 o = outgoing.missing
5564 o = outgoing.missing
5565 if o:
5565 if o:
5566 t.append(_('%d outgoing') % len(o))
5566 t.append(_('%d outgoing') % len(o))
5567 other = dother or sother
5567 other = dother or sother
5568 if 'bookmarks' in other.listkeys('namespaces'):
5568 if 'bookmarks' in other.listkeys('namespaces'):
5569 lmarks = repo.listkeys('bookmarks')
5569 lmarks = repo.listkeys('bookmarks')
5570 rmarks = other.listkeys('bookmarks')
5570 rmarks = other.listkeys('bookmarks')
5571 diff = set(rmarks) - set(lmarks)
5571 diff = set(rmarks) - set(lmarks)
5572 if len(diff) > 0:
5572 if len(diff) > 0:
5573 t.append(_('%d incoming bookmarks') % len(diff))
5573 t.append(_('%d incoming bookmarks') % len(diff))
5574 diff = set(lmarks) - set(rmarks)
5574 diff = set(lmarks) - set(rmarks)
5575 if len(diff) > 0:
5575 if len(diff) > 0:
5576 t.append(_('%d outgoing bookmarks') % len(diff))
5576 t.append(_('%d outgoing bookmarks') % len(diff))
5577
5577
5578 if t:
5578 if t:
5579 # i18n: column positioning for "hg summary"
5579 # i18n: column positioning for "hg summary"
5580 ui.write(_('remote: %s\n') % (', '.join(t)))
5580 ui.write(_('remote: %s\n') % (', '.join(t)))
5581 else:
5581 else:
5582 # i18n: column positioning for "hg summary"
5582 # i18n: column positioning for "hg summary"
5583 ui.status(_('remote: (synced)\n'))
5583 ui.status(_('remote: (synced)\n'))
5584
5584
5585 cmdutil.summaryremotehooks(ui, repo, opts,
5585 cmdutil.summaryremotehooks(ui, repo, opts,
5586 ((source, sbranch, sother, commoninc),
5586 ((source, sbranch, sother, commoninc),
5587 (dest, dbranch, dother, outgoing)))
5587 (dest, dbranch, dother, outgoing)))
5588
5588
5589 @command('tag',
5589 @command('tag',
5590 [('f', 'force', None, _('force tag')),
5590 [('f', 'force', None, _('force tag')),
5591 ('l', 'local', None, _('make the tag local')),
5591 ('l', 'local', None, _('make the tag local')),
5592 ('r', 'rev', '', _('revision to tag'), _('REV')),
5592 ('r', 'rev', '', _('revision to tag'), _('REV')),
5593 ('', 'remove', None, _('remove a tag')),
5593 ('', 'remove', None, _('remove a tag')),
5594 # -l/--local is already there, commitopts cannot be used
5594 # -l/--local is already there, commitopts cannot be used
5595 ('e', 'edit', None, _('edit commit message')),
5595 ('e', 'edit', None, _('edit commit message')),
5596 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5596 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5597 ] + commitopts2,
5597 ] + commitopts2,
5598 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5598 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5599 def tag(ui, repo, name1, *names, **opts):
5599 def tag(ui, repo, name1, *names, **opts):
5600 """add one or more tags for the current or given revision
5600 """add one or more tags for the current or given revision
5601
5601
5602 Name a particular revision using <name>.
5602 Name a particular revision using <name>.
5603
5603
5604 Tags are used to name particular revisions of the repository and are
5604 Tags are used to name particular revisions of the repository and are
5605 very useful to compare different revisions, to go back to significant
5605 very useful to compare different revisions, to go back to significant
5606 earlier versions or to mark branch points as releases, etc. Changing
5606 earlier versions or to mark branch points as releases, etc. Changing
5607 an existing tag is normally disallowed; use -f/--force to override.
5607 an existing tag is normally disallowed; use -f/--force to override.
5608
5608
5609 If no revision is given, the parent of the working directory is
5609 If no revision is given, the parent of the working directory is
5610 used.
5610 used.
5611
5611
5612 To facilitate version control, distribution, and merging of tags,
5612 To facilitate version control, distribution, and merging of tags,
5613 they are stored as a file named ".hgtags" which is managed similarly
5613 they are stored as a file named ".hgtags" which is managed similarly
5614 to other project files and can be hand-edited if necessary. This
5614 to other project files and can be hand-edited if necessary. This
5615 also means that tagging creates a new commit. The file
5615 also means that tagging creates a new commit. The file
5616 ".hg/localtags" is used for local tags (not shared among
5616 ".hg/localtags" is used for local tags (not shared among
5617 repositories).
5617 repositories).
5618
5618
5619 Tag commits are usually made at the head of a branch. If the parent
5619 Tag commits are usually made at the head of a branch. If the parent
5620 of the working directory is not a branch head, :hg:`tag` aborts; use
5620 of the working directory is not a branch head, :hg:`tag` aborts; use
5621 -f/--force to force the tag commit to be based on a non-head
5621 -f/--force to force the tag commit to be based on a non-head
5622 changeset.
5622 changeset.
5623
5623
5624 See :hg:`help dates` for a list of formats valid for -d/--date.
5624 See :hg:`help dates` for a list of formats valid for -d/--date.
5625
5625
5626 Since tag names have priority over branch names during revision
5626 Since tag names have priority over branch names during revision
5627 lookup, using an existing branch name as a tag name is discouraged.
5627 lookup, using an existing branch name as a tag name is discouraged.
5628
5628
5629 Returns 0 on success.
5629 Returns 0 on success.
5630 """
5630 """
5631 wlock = lock = None
5631 wlock = lock = None
5632 try:
5632 try:
5633 wlock = repo.wlock()
5633 wlock = repo.wlock()
5634 lock = repo.lock()
5634 lock = repo.lock()
5635 rev_ = "."
5635 rev_ = "."
5636 names = [t.strip() for t in (name1,) + names]
5636 names = [t.strip() for t in (name1,) + names]
5637 if len(names) != len(set(names)):
5637 if len(names) != len(set(names)):
5638 raise util.Abort(_('tag names must be unique'))
5638 raise util.Abort(_('tag names must be unique'))
5639 for n in names:
5639 for n in names:
5640 scmutil.checknewlabel(repo, n, 'tag')
5640 scmutil.checknewlabel(repo, n, 'tag')
5641 if not n:
5641 if not n:
5642 raise util.Abort(_('tag names cannot consist entirely of '
5642 raise util.Abort(_('tag names cannot consist entirely of '
5643 'whitespace'))
5643 'whitespace'))
5644 if opts.get('rev') and opts.get('remove'):
5644 if opts.get('rev') and opts.get('remove'):
5645 raise util.Abort(_("--rev and --remove are incompatible"))
5645 raise util.Abort(_("--rev and --remove are incompatible"))
5646 if opts.get('rev'):
5646 if opts.get('rev'):
5647 rev_ = opts['rev']
5647 rev_ = opts['rev']
5648 message = opts.get('message')
5648 message = opts.get('message')
5649 if opts.get('remove'):
5649 if opts.get('remove'):
5650 expectedtype = opts.get('local') and 'local' or 'global'
5650 expectedtype = opts.get('local') and 'local' or 'global'
5651 for n in names:
5651 for n in names:
5652 if not repo.tagtype(n):
5652 if not repo.tagtype(n):
5653 raise util.Abort(_("tag '%s' does not exist") % n)
5653 raise util.Abort(_("tag '%s' does not exist") % n)
5654 if repo.tagtype(n) != expectedtype:
5654 if repo.tagtype(n) != expectedtype:
5655 if expectedtype == 'global':
5655 if expectedtype == 'global':
5656 raise util.Abort(_("tag '%s' is not a global tag") % n)
5656 raise util.Abort(_("tag '%s' is not a global tag") % n)
5657 else:
5657 else:
5658 raise util.Abort(_("tag '%s' is not a local tag") % n)
5658 raise util.Abort(_("tag '%s' is not a local tag") % n)
5659 rev_ = nullid
5659 rev_ = nullid
5660 if not message:
5660 if not message:
5661 # we don't translate commit messages
5661 # we don't translate commit messages
5662 message = 'Removed tag %s' % ', '.join(names)
5662 message = 'Removed tag %s' % ', '.join(names)
5663 elif not opts.get('force'):
5663 elif not opts.get('force'):
5664 for n in names:
5664 for n in names:
5665 if n in repo.tags():
5665 if n in repo.tags():
5666 raise util.Abort(_("tag '%s' already exists "
5666 raise util.Abort(_("tag '%s' already exists "
5667 "(use -f to force)") % n)
5667 "(use -f to force)") % n)
5668 if not opts.get('local'):
5668 if not opts.get('local'):
5669 p1, p2 = repo.dirstate.parents()
5669 p1, p2 = repo.dirstate.parents()
5670 if p2 != nullid:
5670 if p2 != nullid:
5671 raise util.Abort(_('uncommitted merge'))
5671 raise util.Abort(_('uncommitted merge'))
5672 bheads = repo.branchheads()
5672 bheads = repo.branchheads()
5673 if not opts.get('force') and bheads and p1 not in bheads:
5673 if not opts.get('force') and bheads and p1 not in bheads:
5674 raise util.Abort(_('not at a branch head (use -f to force)'))
5674 raise util.Abort(_('not at a branch head (use -f to force)'))
5675 r = scmutil.revsingle(repo, rev_).node()
5675 r = scmutil.revsingle(repo, rev_).node()
5676
5676
5677 if not message:
5677 if not message:
5678 # we don't translate commit messages
5678 # we don't translate commit messages
5679 message = ('Added tag %s for changeset %s' %
5679 message = ('Added tag %s for changeset %s' %
5680 (', '.join(names), short(r)))
5680 (', '.join(names), short(r)))
5681
5681
5682 date = opts.get('date')
5682 date = opts.get('date')
5683 if date:
5683 if date:
5684 date = util.parsedate(date)
5684 date = util.parsedate(date)
5685
5685
5686 if opts.get('edit'):
5686 if opts.get('edit'):
5687 message = ui.edit(message, ui.username())
5687 def editor(repo, ctx, subs):
5688 repo.savecommitmessage(message)
5688 return ui.edit(ctx.description() + "\n", ctx.user())
5689 else:
5690 editor = False
5689
5691
5690 # don't allow tagging the null rev
5692 # don't allow tagging the null rev
5691 if (not opts.get('remove') and
5693 if (not opts.get('remove') and
5692 scmutil.revsingle(repo, rev_).rev() == nullrev):
5694 scmutil.revsingle(repo, rev_).rev() == nullrev):
5693 raise util.Abort(_("cannot tag null revision"))
5695 raise util.Abort(_("cannot tag null revision"))
5694
5696
5695 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5697 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
5698 editor=editor)
5696 finally:
5699 finally:
5697 release(lock, wlock)
5700 release(lock, wlock)
5698
5701
5699 @command('tags', [], '')
5702 @command('tags', [], '')
5700 def tags(ui, repo, **opts):
5703 def tags(ui, repo, **opts):
5701 """list repository tags
5704 """list repository tags
5702
5705
5703 This lists both regular and local tags. When the -v/--verbose
5706 This lists both regular and local tags. When the -v/--verbose
5704 switch is used, a third column "local" is printed for local tags.
5707 switch is used, a third column "local" is printed for local tags.
5705
5708
5706 Returns 0 on success.
5709 Returns 0 on success.
5707 """
5710 """
5708
5711
5709 fm = ui.formatter('tags', opts)
5712 fm = ui.formatter('tags', opts)
5710 hexfunc = ui.debugflag and hex or short
5713 hexfunc = ui.debugflag and hex or short
5711 tagtype = ""
5714 tagtype = ""
5712
5715
5713 for t, n in reversed(repo.tagslist()):
5716 for t, n in reversed(repo.tagslist()):
5714 hn = hexfunc(n)
5717 hn = hexfunc(n)
5715 label = 'tags.normal'
5718 label = 'tags.normal'
5716 tagtype = ''
5719 tagtype = ''
5717 if repo.tagtype(t) == 'local':
5720 if repo.tagtype(t) == 'local':
5718 label = 'tags.local'
5721 label = 'tags.local'
5719 tagtype = 'local'
5722 tagtype = 'local'
5720
5723
5721 fm.startitem()
5724 fm.startitem()
5722 fm.write('tag', '%s', t, label=label)
5725 fm.write('tag', '%s', t, label=label)
5723 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5726 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5724 fm.condwrite(not ui.quiet, 'rev id', fmt,
5727 fm.condwrite(not ui.quiet, 'rev id', fmt,
5725 repo.changelog.rev(n), hn, label=label)
5728 repo.changelog.rev(n), hn, label=label)
5726 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5729 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5727 tagtype, label=label)
5730 tagtype, label=label)
5728 fm.plain('\n')
5731 fm.plain('\n')
5729 fm.end()
5732 fm.end()
5730
5733
5731 @command('tip',
5734 @command('tip',
5732 [('p', 'patch', None, _('show patch')),
5735 [('p', 'patch', None, _('show patch')),
5733 ('g', 'git', None, _('use git extended diff format')),
5736 ('g', 'git', None, _('use git extended diff format')),
5734 ] + templateopts,
5737 ] + templateopts,
5735 _('[-p] [-g]'))
5738 _('[-p] [-g]'))
5736 def tip(ui, repo, **opts):
5739 def tip(ui, repo, **opts):
5737 """show the tip revision (DEPRECATED)
5740 """show the tip revision (DEPRECATED)
5738
5741
5739 The tip revision (usually just called the tip) is the changeset
5742 The tip revision (usually just called the tip) is the changeset
5740 most recently added to the repository (and therefore the most
5743 most recently added to the repository (and therefore the most
5741 recently changed head).
5744 recently changed head).
5742
5745
5743 If you have just made a commit, that commit will be the tip. If
5746 If you have just made a commit, that commit will be the tip. If
5744 you have just pulled changes from another repository, the tip of
5747 you have just pulled changes from another repository, the tip of
5745 that repository becomes the current tip. The "tip" tag is special
5748 that repository becomes the current tip. The "tip" tag is special
5746 and cannot be renamed or assigned to a different changeset.
5749 and cannot be renamed or assigned to a different changeset.
5747
5750
5748 This command is deprecated, please use :hg:`heads` instead.
5751 This command is deprecated, please use :hg:`heads` instead.
5749
5752
5750 Returns 0 on success.
5753 Returns 0 on success.
5751 """
5754 """
5752 displayer = cmdutil.show_changeset(ui, repo, opts)
5755 displayer = cmdutil.show_changeset(ui, repo, opts)
5753 displayer.show(repo['tip'])
5756 displayer.show(repo['tip'])
5754 displayer.close()
5757 displayer.close()
5755
5758
5756 @command('unbundle',
5759 @command('unbundle',
5757 [('u', 'update', None,
5760 [('u', 'update', None,
5758 _('update to new branch head if changesets were unbundled'))],
5761 _('update to new branch head if changesets were unbundled'))],
5759 _('[-u] FILE...'))
5762 _('[-u] FILE...'))
5760 def unbundle(ui, repo, fname1, *fnames, **opts):
5763 def unbundle(ui, repo, fname1, *fnames, **opts):
5761 """apply one or more changegroup files
5764 """apply one or more changegroup files
5762
5765
5763 Apply one or more compressed changegroup files generated by the
5766 Apply one or more compressed changegroup files generated by the
5764 bundle command.
5767 bundle command.
5765
5768
5766 Returns 0 on success, 1 if an update has unresolved files.
5769 Returns 0 on success, 1 if an update has unresolved files.
5767 """
5770 """
5768 fnames = (fname1,) + fnames
5771 fnames = (fname1,) + fnames
5769
5772
5770 lock = repo.lock()
5773 lock = repo.lock()
5771 wc = repo['.']
5774 wc = repo['.']
5772 try:
5775 try:
5773 for fname in fnames:
5776 for fname in fnames:
5774 f = hg.openpath(ui, fname)
5777 f = hg.openpath(ui, fname)
5775 gen = exchange.readbundle(ui, f, fname)
5778 gen = exchange.readbundle(ui, f, fname)
5776 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
5779 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
5777 'bundle:' + fname)
5780 'bundle:' + fname)
5778 finally:
5781 finally:
5779 lock.release()
5782 lock.release()
5780 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5783 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5781 return postincoming(ui, repo, modheads, opts.get('update'), None)
5784 return postincoming(ui, repo, modheads, opts.get('update'), None)
5782
5785
5783 @command('^update|up|checkout|co',
5786 @command('^update|up|checkout|co',
5784 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5787 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5785 ('c', 'check', None,
5788 ('c', 'check', None,
5786 _('update across branches if no uncommitted changes')),
5789 _('update across branches if no uncommitted changes')),
5787 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5790 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5788 ('r', 'rev', '', _('revision'), _('REV'))],
5791 ('r', 'rev', '', _('revision'), _('REV'))],
5789 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5792 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5790 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5793 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5791 """update working directory (or switch revisions)
5794 """update working directory (or switch revisions)
5792
5795
5793 Update the repository's working directory to the specified
5796 Update the repository's working directory to the specified
5794 changeset. If no changeset is specified, update to the tip of the
5797 changeset. If no changeset is specified, update to the tip of the
5795 current named branch and move the current bookmark (see :hg:`help
5798 current named branch and move the current bookmark (see :hg:`help
5796 bookmarks`).
5799 bookmarks`).
5797
5800
5798 Update sets the working directory's parent revision to the specified
5801 Update sets the working directory's parent revision to the specified
5799 changeset (see :hg:`help parents`).
5802 changeset (see :hg:`help parents`).
5800
5803
5801 If the changeset is not a descendant or ancestor of the working
5804 If the changeset is not a descendant or ancestor of the working
5802 directory's parent, the update is aborted. With the -c/--check
5805 directory's parent, the update is aborted. With the -c/--check
5803 option, the working directory is checked for uncommitted changes; if
5806 option, the working directory is checked for uncommitted changes; if
5804 none are found, the working directory is updated to the specified
5807 none are found, the working directory is updated to the specified
5805 changeset.
5808 changeset.
5806
5809
5807 .. container:: verbose
5810 .. container:: verbose
5808
5811
5809 The following rules apply when the working directory contains
5812 The following rules apply when the working directory contains
5810 uncommitted changes:
5813 uncommitted changes:
5811
5814
5812 1. If neither -c/--check nor -C/--clean is specified, and if
5815 1. If neither -c/--check nor -C/--clean is specified, and if
5813 the requested changeset is an ancestor or descendant of
5816 the requested changeset is an ancestor or descendant of
5814 the working directory's parent, the uncommitted changes
5817 the working directory's parent, the uncommitted changes
5815 are merged into the requested changeset and the merged
5818 are merged into the requested changeset and the merged
5816 result is left uncommitted. If the requested changeset is
5819 result is left uncommitted. If the requested changeset is
5817 not an ancestor or descendant (that is, it is on another
5820 not an ancestor or descendant (that is, it is on another
5818 branch), the update is aborted and the uncommitted changes
5821 branch), the update is aborted and the uncommitted changes
5819 are preserved.
5822 are preserved.
5820
5823
5821 2. With the -c/--check option, the update is aborted and the
5824 2. With the -c/--check option, the update is aborted and the
5822 uncommitted changes are preserved.
5825 uncommitted changes are preserved.
5823
5826
5824 3. With the -C/--clean option, uncommitted changes are discarded and
5827 3. With the -C/--clean option, uncommitted changes are discarded and
5825 the working directory is updated to the requested changeset.
5828 the working directory is updated to the requested changeset.
5826
5829
5827 To cancel an uncommitted merge (and lose your changes), use
5830 To cancel an uncommitted merge (and lose your changes), use
5828 :hg:`update --clean .`.
5831 :hg:`update --clean .`.
5829
5832
5830 Use null as the changeset to remove the working directory (like
5833 Use null as the changeset to remove the working directory (like
5831 :hg:`clone -U`).
5834 :hg:`clone -U`).
5832
5835
5833 If you want to revert just one file to an older revision, use
5836 If you want to revert just one file to an older revision, use
5834 :hg:`revert [-r REV] NAME`.
5837 :hg:`revert [-r REV] NAME`.
5835
5838
5836 See :hg:`help dates` for a list of formats valid for -d/--date.
5839 See :hg:`help dates` for a list of formats valid for -d/--date.
5837
5840
5838 Returns 0 on success, 1 if there are unresolved files.
5841 Returns 0 on success, 1 if there are unresolved files.
5839 """
5842 """
5840 if rev and node:
5843 if rev and node:
5841 raise util.Abort(_("please specify just one revision"))
5844 raise util.Abort(_("please specify just one revision"))
5842
5845
5843 if rev is None or rev == '':
5846 if rev is None or rev == '':
5844 rev = node
5847 rev = node
5845
5848
5846 cmdutil.clearunfinished(repo)
5849 cmdutil.clearunfinished(repo)
5847
5850
5848 # with no argument, we also move the current bookmark, if any
5851 # with no argument, we also move the current bookmark, if any
5849 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
5852 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
5850
5853
5851 # if we defined a bookmark, we have to remember the original bookmark name
5854 # if we defined a bookmark, we have to remember the original bookmark name
5852 brev = rev
5855 brev = rev
5853 rev = scmutil.revsingle(repo, rev, rev).rev()
5856 rev = scmutil.revsingle(repo, rev, rev).rev()
5854
5857
5855 if check and clean:
5858 if check and clean:
5856 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5859 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5857
5860
5858 if date:
5861 if date:
5859 if rev is not None:
5862 if rev is not None:
5860 raise util.Abort(_("you can't specify a revision and a date"))
5863 raise util.Abort(_("you can't specify a revision and a date"))
5861 rev = cmdutil.finddate(ui, repo, date)
5864 rev = cmdutil.finddate(ui, repo, date)
5862
5865
5863 if check:
5866 if check:
5864 c = repo[None]
5867 c = repo[None]
5865 if c.dirty(merge=False, branch=False, missing=True):
5868 if c.dirty(merge=False, branch=False, missing=True):
5866 raise util.Abort(_("uncommitted changes"))
5869 raise util.Abort(_("uncommitted changes"))
5867 if rev is None:
5870 if rev is None:
5868 rev = repo[repo[None].branch()].rev()
5871 rev = repo[repo[None].branch()].rev()
5869 mergemod._checkunknown(repo, repo[None], repo[rev])
5872 mergemod._checkunknown(repo, repo[None], repo[rev])
5870
5873
5871 if clean:
5874 if clean:
5872 ret = hg.clean(repo, rev)
5875 ret = hg.clean(repo, rev)
5873 else:
5876 else:
5874 ret = hg.update(repo, rev)
5877 ret = hg.update(repo, rev)
5875
5878
5876 if not ret and movemarkfrom:
5879 if not ret and movemarkfrom:
5877 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5880 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5878 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5881 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5879 elif brev in repo._bookmarks:
5882 elif brev in repo._bookmarks:
5880 bookmarks.setcurrent(repo, brev)
5883 bookmarks.setcurrent(repo, brev)
5881 elif brev:
5884 elif brev:
5882 bookmarks.unsetcurrent(repo)
5885 bookmarks.unsetcurrent(repo)
5883
5886
5884 return ret
5887 return ret
5885
5888
5886 @command('verify', [])
5889 @command('verify', [])
5887 def verify(ui, repo):
5890 def verify(ui, repo):
5888 """verify the integrity of the repository
5891 """verify the integrity of the repository
5889
5892
5890 Verify the integrity of the current repository.
5893 Verify the integrity of the current repository.
5891
5894
5892 This will perform an extensive check of the repository's
5895 This will perform an extensive check of the repository's
5893 integrity, validating the hashes and checksums of each entry in
5896 integrity, validating the hashes and checksums of each entry in
5894 the changelog, manifest, and tracked files, as well as the
5897 the changelog, manifest, and tracked files, as well as the
5895 integrity of their crosslinks and indices.
5898 integrity of their crosslinks and indices.
5896
5899
5897 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5900 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5898 for more information about recovery from corruption of the
5901 for more information about recovery from corruption of the
5899 repository.
5902 repository.
5900
5903
5901 Returns 0 on success, 1 if errors are encountered.
5904 Returns 0 on success, 1 if errors are encountered.
5902 """
5905 """
5903 return hg.verify(repo)
5906 return hg.verify(repo)
5904
5907
5905 @command('version', [])
5908 @command('version', [])
5906 def version_(ui):
5909 def version_(ui):
5907 """output version and copyright information"""
5910 """output version and copyright information"""
5908 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5911 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5909 % util.version())
5912 % util.version())
5910 ui.status(_(
5913 ui.status(_(
5911 "(see http://mercurial.selenic.com for more information)\n"
5914 "(see http://mercurial.selenic.com for more information)\n"
5912 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
5915 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
5913 "This is free software; see the source for copying conditions. "
5916 "This is free software; see the source for copying conditions. "
5914 "There is NO\nwarranty; "
5917 "There is NO\nwarranty; "
5915 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5918 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5916 ))
5919 ))
5917
5920
5918 norepo = ("clone init version help debugcommands debugcomplete"
5921 norepo = ("clone init version help debugcommands debugcomplete"
5919 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5922 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5920 " debugknown debuggetbundle debugbundle")
5923 " debugknown debuggetbundle debugbundle")
5921 optionalrepo = ("identify paths serve config showconfig debugancestor debugdag"
5924 optionalrepo = ("identify paths serve config showconfig debugancestor debugdag"
5922 " debugdata debugindex debugindexdot debugrevlog")
5925 " debugdata debugindex debugindexdot debugrevlog")
5923 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5926 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5924 " remove resolve status debugwalk")
5927 " remove resolve status debugwalk")
@@ -1,1910 +1,1912 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import urllib
9 import urllib
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
10 import peer, changegroup, subrepo, pushkey, obsolete, repoview
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
12 import lock as lockmod
12 import lock as lockmod
13 import transaction, store, encoding, exchange, bundle2
13 import transaction, store, encoding, exchange, bundle2
14 import scmutil, util, extensions, hook, error, revset
14 import scmutil, util, extensions, hook, error, revset
15 import match as matchmod
15 import match as matchmod
16 import merge as mergemod
16 import merge as mergemod
17 import tags as tagsmod
17 import tags as tagsmod
18 from lock import release
18 from lock import release
19 import weakref, errno, os, time, inspect
19 import weakref, errno, os, time, inspect
20 import branchmap, pathutil
20 import branchmap, pathutil
21 propertycache = util.propertycache
21 propertycache = util.propertycache
22 filecache = scmutil.filecache
22 filecache = scmutil.filecache
23
23
24 class repofilecache(filecache):
24 class repofilecache(filecache):
25 """All filecache usage on repo are done for logic that should be unfiltered
25 """All filecache usage on repo are done for logic that should be unfiltered
26 """
26 """
27
27
28 def __get__(self, repo, type=None):
28 def __get__(self, repo, type=None):
29 return super(repofilecache, self).__get__(repo.unfiltered(), type)
29 return super(repofilecache, self).__get__(repo.unfiltered(), type)
30 def __set__(self, repo, value):
30 def __set__(self, repo, value):
31 return super(repofilecache, self).__set__(repo.unfiltered(), value)
31 return super(repofilecache, self).__set__(repo.unfiltered(), value)
32 def __delete__(self, repo):
32 def __delete__(self, repo):
33 return super(repofilecache, self).__delete__(repo.unfiltered())
33 return super(repofilecache, self).__delete__(repo.unfiltered())
34
34
35 class storecache(repofilecache):
35 class storecache(repofilecache):
36 """filecache for files in the store"""
36 """filecache for files in the store"""
37 def join(self, obj, fname):
37 def join(self, obj, fname):
38 return obj.sjoin(fname)
38 return obj.sjoin(fname)
39
39
40 class unfilteredpropertycache(propertycache):
40 class unfilteredpropertycache(propertycache):
41 """propertycache that apply to unfiltered repo only"""
41 """propertycache that apply to unfiltered repo only"""
42
42
43 def __get__(self, repo, type=None):
43 def __get__(self, repo, type=None):
44 unfi = repo.unfiltered()
44 unfi = repo.unfiltered()
45 if unfi is repo:
45 if unfi is repo:
46 return super(unfilteredpropertycache, self).__get__(unfi)
46 return super(unfilteredpropertycache, self).__get__(unfi)
47 return getattr(unfi, self.name)
47 return getattr(unfi, self.name)
48
48
49 class filteredpropertycache(propertycache):
49 class filteredpropertycache(propertycache):
50 """propertycache that must take filtering in account"""
50 """propertycache that must take filtering in account"""
51
51
52 def cachevalue(self, obj, value):
52 def cachevalue(self, obj, value):
53 object.__setattr__(obj, self.name, value)
53 object.__setattr__(obj, self.name, value)
54
54
55
55
56 def hasunfilteredcache(repo, name):
56 def hasunfilteredcache(repo, name):
57 """check if a repo has an unfilteredpropertycache value for <name>"""
57 """check if a repo has an unfilteredpropertycache value for <name>"""
58 return name in vars(repo.unfiltered())
58 return name in vars(repo.unfiltered())
59
59
60 def unfilteredmethod(orig):
60 def unfilteredmethod(orig):
61 """decorate method that always need to be run on unfiltered version"""
61 """decorate method that always need to be run on unfiltered version"""
62 def wrapper(repo, *args, **kwargs):
62 def wrapper(repo, *args, **kwargs):
63 return orig(repo.unfiltered(), *args, **kwargs)
63 return orig(repo.unfiltered(), *args, **kwargs)
64 return wrapper
64 return wrapper
65
65
66 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
66 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
67 'unbundle'))
67 'unbundle'))
68 legacycaps = moderncaps.union(set(['changegroupsubset']))
68 legacycaps = moderncaps.union(set(['changegroupsubset']))
69
69
70 class localpeer(peer.peerrepository):
70 class localpeer(peer.peerrepository):
71 '''peer for a local repo; reflects only the most recent API'''
71 '''peer for a local repo; reflects only the most recent API'''
72
72
73 def __init__(self, repo, caps=moderncaps):
73 def __init__(self, repo, caps=moderncaps):
74 peer.peerrepository.__init__(self)
74 peer.peerrepository.__init__(self)
75 self._repo = repo.filtered('served')
75 self._repo = repo.filtered('served')
76 self.ui = repo.ui
76 self.ui = repo.ui
77 self._caps = repo._restrictcapabilities(caps)
77 self._caps = repo._restrictcapabilities(caps)
78 self.requirements = repo.requirements
78 self.requirements = repo.requirements
79 self.supportedformats = repo.supportedformats
79 self.supportedformats = repo.supportedformats
80
80
81 def close(self):
81 def close(self):
82 self._repo.close()
82 self._repo.close()
83
83
84 def _capabilities(self):
84 def _capabilities(self):
85 return self._caps
85 return self._caps
86
86
87 def local(self):
87 def local(self):
88 return self._repo
88 return self._repo
89
89
90 def canpush(self):
90 def canpush(self):
91 return True
91 return True
92
92
93 def url(self):
93 def url(self):
94 return self._repo.url()
94 return self._repo.url()
95
95
96 def lookup(self, key):
96 def lookup(self, key):
97 return self._repo.lookup(key)
97 return self._repo.lookup(key)
98
98
99 def branchmap(self):
99 def branchmap(self):
100 return self._repo.branchmap()
100 return self._repo.branchmap()
101
101
102 def heads(self):
102 def heads(self):
103 return self._repo.heads()
103 return self._repo.heads()
104
104
105 def known(self, nodes):
105 def known(self, nodes):
106 return self._repo.known(nodes)
106 return self._repo.known(nodes)
107
107
108 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
108 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
109 format='HG10', **kwargs):
109 format='HG10', **kwargs):
110 cg = exchange.getbundle(self._repo, source, heads=heads,
110 cg = exchange.getbundle(self._repo, source, heads=heads,
111 common=common, bundlecaps=bundlecaps, **kwargs)
111 common=common, bundlecaps=bundlecaps, **kwargs)
112 if bundlecaps is not None and 'HG2X' in bundlecaps:
112 if bundlecaps is not None and 'HG2X' in bundlecaps:
113 # When requesting a bundle2, getbundle returns a stream to make the
113 # When requesting a bundle2, getbundle returns a stream to make the
114 # wire level function happier. We need to build a proper object
114 # wire level function happier. We need to build a proper object
115 # from it in local peer.
115 # from it in local peer.
116 cg = bundle2.unbundle20(self.ui, cg)
116 cg = bundle2.unbundle20(self.ui, cg)
117 return cg
117 return cg
118
118
119 # TODO We might want to move the next two calls into legacypeer and add
119 # TODO We might want to move the next two calls into legacypeer and add
120 # unbundle instead.
120 # unbundle instead.
121
121
122 def unbundle(self, cg, heads, url):
122 def unbundle(self, cg, heads, url):
123 """apply a bundle on a repo
123 """apply a bundle on a repo
124
124
125 This function handles the repo locking itself."""
125 This function handles the repo locking itself."""
126 try:
126 try:
127 cg = exchange.readbundle(self.ui, cg, None)
127 cg = exchange.readbundle(self.ui, cg, None)
128 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
128 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
129 if util.safehasattr(ret, 'getchunks'):
129 if util.safehasattr(ret, 'getchunks'):
130 # This is a bundle20 object, turn it into an unbundler.
130 # This is a bundle20 object, turn it into an unbundler.
131 # This little dance should be dropped eventually when the API
131 # This little dance should be dropped eventually when the API
132 # is finally improved.
132 # is finally improved.
133 stream = util.chunkbuffer(ret.getchunks())
133 stream = util.chunkbuffer(ret.getchunks())
134 ret = bundle2.unbundle20(self.ui, stream)
134 ret = bundle2.unbundle20(self.ui, stream)
135 return ret
135 return ret
136 except error.PushRaced, exc:
136 except error.PushRaced, exc:
137 raise error.ResponseError(_('push failed:'), str(exc))
137 raise error.ResponseError(_('push failed:'), str(exc))
138
138
139 def lock(self):
139 def lock(self):
140 return self._repo.lock()
140 return self._repo.lock()
141
141
142 def addchangegroup(self, cg, source, url):
142 def addchangegroup(self, cg, source, url):
143 return changegroup.addchangegroup(self._repo, cg, source, url)
143 return changegroup.addchangegroup(self._repo, cg, source, url)
144
144
145 def pushkey(self, namespace, key, old, new):
145 def pushkey(self, namespace, key, old, new):
146 return self._repo.pushkey(namespace, key, old, new)
146 return self._repo.pushkey(namespace, key, old, new)
147
147
148 def listkeys(self, namespace):
148 def listkeys(self, namespace):
149 return self._repo.listkeys(namespace)
149 return self._repo.listkeys(namespace)
150
150
151 def debugwireargs(self, one, two, three=None, four=None, five=None):
151 def debugwireargs(self, one, two, three=None, four=None, five=None):
152 '''used to test argument passing over the wire'''
152 '''used to test argument passing over the wire'''
153 return "%s %s %s %s %s" % (one, two, three, four, five)
153 return "%s %s %s %s %s" % (one, two, three, four, five)
154
154
155 class locallegacypeer(localpeer):
155 class locallegacypeer(localpeer):
156 '''peer extension which implements legacy methods too; used for tests with
156 '''peer extension which implements legacy methods too; used for tests with
157 restricted capabilities'''
157 restricted capabilities'''
158
158
159 def __init__(self, repo):
159 def __init__(self, repo):
160 localpeer.__init__(self, repo, caps=legacycaps)
160 localpeer.__init__(self, repo, caps=legacycaps)
161
161
162 def branches(self, nodes):
162 def branches(self, nodes):
163 return self._repo.branches(nodes)
163 return self._repo.branches(nodes)
164
164
165 def between(self, pairs):
165 def between(self, pairs):
166 return self._repo.between(pairs)
166 return self._repo.between(pairs)
167
167
168 def changegroup(self, basenodes, source):
168 def changegroup(self, basenodes, source):
169 return changegroup.changegroup(self._repo, basenodes, source)
169 return changegroup.changegroup(self._repo, basenodes, source)
170
170
171 def changegroupsubset(self, bases, heads, source):
171 def changegroupsubset(self, bases, heads, source):
172 return changegroup.changegroupsubset(self._repo, bases, heads, source)
172 return changegroup.changegroupsubset(self._repo, bases, heads, source)
173
173
174 class localrepository(object):
174 class localrepository(object):
175
175
176 supportedformats = set(('revlogv1', 'generaldelta'))
176 supportedformats = set(('revlogv1', 'generaldelta'))
177 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
177 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
178 'dotencode'))
178 'dotencode'))
179 openerreqs = set(('revlogv1', 'generaldelta'))
179 openerreqs = set(('revlogv1', 'generaldelta'))
180 requirements = ['revlogv1']
180 requirements = ['revlogv1']
181 filtername = None
181 filtername = None
182
182
183 bundle2caps = {'HG2X': ()}
183 bundle2caps = {'HG2X': ()}
184
184
185 # a list of (ui, featureset) functions.
185 # a list of (ui, featureset) functions.
186 # only functions defined in module of enabled extensions are invoked
186 # only functions defined in module of enabled extensions are invoked
187 featuresetupfuncs = set()
187 featuresetupfuncs = set()
188
188
189 def _baserequirements(self, create):
189 def _baserequirements(self, create):
190 return self.requirements[:]
190 return self.requirements[:]
191
191
192 def __init__(self, baseui, path=None, create=False):
192 def __init__(self, baseui, path=None, create=False):
193 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
193 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
194 self.wopener = self.wvfs
194 self.wopener = self.wvfs
195 self.root = self.wvfs.base
195 self.root = self.wvfs.base
196 self.path = self.wvfs.join(".hg")
196 self.path = self.wvfs.join(".hg")
197 self.origroot = path
197 self.origroot = path
198 self.auditor = pathutil.pathauditor(self.root, self._checknested)
198 self.auditor = pathutil.pathauditor(self.root, self._checknested)
199 self.vfs = scmutil.vfs(self.path)
199 self.vfs = scmutil.vfs(self.path)
200 self.opener = self.vfs
200 self.opener = self.vfs
201 self.baseui = baseui
201 self.baseui = baseui
202 self.ui = baseui.copy()
202 self.ui = baseui.copy()
203 self.ui.copy = baseui.copy # prevent copying repo configuration
203 self.ui.copy = baseui.copy # prevent copying repo configuration
204 # A list of callback to shape the phase if no data were found.
204 # A list of callback to shape the phase if no data were found.
205 # Callback are in the form: func(repo, roots) --> processed root.
205 # Callback are in the form: func(repo, roots) --> processed root.
206 # This list it to be filled by extension during repo setup
206 # This list it to be filled by extension during repo setup
207 self._phasedefaults = []
207 self._phasedefaults = []
208 try:
208 try:
209 self.ui.readconfig(self.join("hgrc"), self.root)
209 self.ui.readconfig(self.join("hgrc"), self.root)
210 extensions.loadall(self.ui)
210 extensions.loadall(self.ui)
211 except IOError:
211 except IOError:
212 pass
212 pass
213
213
214 if self.featuresetupfuncs:
214 if self.featuresetupfuncs:
215 self.supported = set(self._basesupported) # use private copy
215 self.supported = set(self._basesupported) # use private copy
216 extmods = set(m.__name__ for n, m
216 extmods = set(m.__name__ for n, m
217 in extensions.extensions(self.ui))
217 in extensions.extensions(self.ui))
218 for setupfunc in self.featuresetupfuncs:
218 for setupfunc in self.featuresetupfuncs:
219 if setupfunc.__module__ in extmods:
219 if setupfunc.__module__ in extmods:
220 setupfunc(self.ui, self.supported)
220 setupfunc(self.ui, self.supported)
221 else:
221 else:
222 self.supported = self._basesupported
222 self.supported = self._basesupported
223
223
224 if not self.vfs.isdir():
224 if not self.vfs.isdir():
225 if create:
225 if create:
226 if not self.wvfs.exists():
226 if not self.wvfs.exists():
227 self.wvfs.makedirs()
227 self.wvfs.makedirs()
228 self.vfs.makedir(notindexed=True)
228 self.vfs.makedir(notindexed=True)
229 requirements = self._baserequirements(create)
229 requirements = self._baserequirements(create)
230 if self.ui.configbool('format', 'usestore', True):
230 if self.ui.configbool('format', 'usestore', True):
231 self.vfs.mkdir("store")
231 self.vfs.mkdir("store")
232 requirements.append("store")
232 requirements.append("store")
233 if self.ui.configbool('format', 'usefncache', True):
233 if self.ui.configbool('format', 'usefncache', True):
234 requirements.append("fncache")
234 requirements.append("fncache")
235 if self.ui.configbool('format', 'dotencode', True):
235 if self.ui.configbool('format', 'dotencode', True):
236 requirements.append('dotencode')
236 requirements.append('dotencode')
237 # create an invalid changelog
237 # create an invalid changelog
238 self.vfs.append(
238 self.vfs.append(
239 "00changelog.i",
239 "00changelog.i",
240 '\0\0\0\2' # represents revlogv2
240 '\0\0\0\2' # represents revlogv2
241 ' dummy changelog to prevent using the old repo layout'
241 ' dummy changelog to prevent using the old repo layout'
242 )
242 )
243 if self.ui.configbool('format', 'generaldelta', False):
243 if self.ui.configbool('format', 'generaldelta', False):
244 requirements.append("generaldelta")
244 requirements.append("generaldelta")
245 requirements = set(requirements)
245 requirements = set(requirements)
246 else:
246 else:
247 raise error.RepoError(_("repository %s not found") % path)
247 raise error.RepoError(_("repository %s not found") % path)
248 elif create:
248 elif create:
249 raise error.RepoError(_("repository %s already exists") % path)
249 raise error.RepoError(_("repository %s already exists") % path)
250 else:
250 else:
251 try:
251 try:
252 requirements = scmutil.readrequires(self.vfs, self.supported)
252 requirements = scmutil.readrequires(self.vfs, self.supported)
253 except IOError, inst:
253 except IOError, inst:
254 if inst.errno != errno.ENOENT:
254 if inst.errno != errno.ENOENT:
255 raise
255 raise
256 requirements = set()
256 requirements = set()
257
257
258 self.sharedpath = self.path
258 self.sharedpath = self.path
259 try:
259 try:
260 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
260 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
261 realpath=True)
261 realpath=True)
262 s = vfs.base
262 s = vfs.base
263 if not vfs.exists():
263 if not vfs.exists():
264 raise error.RepoError(
264 raise error.RepoError(
265 _('.hg/sharedpath points to nonexistent directory %s') % s)
265 _('.hg/sharedpath points to nonexistent directory %s') % s)
266 self.sharedpath = s
266 self.sharedpath = s
267 except IOError, inst:
267 except IOError, inst:
268 if inst.errno != errno.ENOENT:
268 if inst.errno != errno.ENOENT:
269 raise
269 raise
270
270
271 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
271 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
272 self.spath = self.store.path
272 self.spath = self.store.path
273 self.svfs = self.store.vfs
273 self.svfs = self.store.vfs
274 self.sopener = self.svfs
274 self.sopener = self.svfs
275 self.sjoin = self.store.join
275 self.sjoin = self.store.join
276 self.vfs.createmode = self.store.createmode
276 self.vfs.createmode = self.store.createmode
277 self._applyrequirements(requirements)
277 self._applyrequirements(requirements)
278 if create:
278 if create:
279 self._writerequirements()
279 self._writerequirements()
280
280
281
281
282 self._branchcaches = {}
282 self._branchcaches = {}
283 self.filterpats = {}
283 self.filterpats = {}
284 self._datafilters = {}
284 self._datafilters = {}
285 self._transref = self._lockref = self._wlockref = None
285 self._transref = self._lockref = self._wlockref = None
286
286
287 # A cache for various files under .hg/ that tracks file changes,
287 # A cache for various files under .hg/ that tracks file changes,
288 # (used by the filecache decorator)
288 # (used by the filecache decorator)
289 #
289 #
290 # Maps a property name to its util.filecacheentry
290 # Maps a property name to its util.filecacheentry
291 self._filecache = {}
291 self._filecache = {}
292
292
293 # hold sets of revision to be filtered
293 # hold sets of revision to be filtered
294 # should be cleared when something might have changed the filter value:
294 # should be cleared when something might have changed the filter value:
295 # - new changesets,
295 # - new changesets,
296 # - phase change,
296 # - phase change,
297 # - new obsolescence marker,
297 # - new obsolescence marker,
298 # - working directory parent change,
298 # - working directory parent change,
299 # - bookmark changes
299 # - bookmark changes
300 self.filteredrevcache = {}
300 self.filteredrevcache = {}
301
301
302 def close(self):
302 def close(self):
303 pass
303 pass
304
304
305 def _restrictcapabilities(self, caps):
305 def _restrictcapabilities(self, caps):
306 # bundle2 is not ready for prime time, drop it unless explicitly
306 # bundle2 is not ready for prime time, drop it unless explicitly
307 # required by the tests (or some brave tester)
307 # required by the tests (or some brave tester)
308 if self.ui.configbool('experimental', 'bundle2-exp', False):
308 if self.ui.configbool('experimental', 'bundle2-exp', False):
309 caps = set(caps)
309 caps = set(caps)
310 capsblob = bundle2.encodecaps(self.bundle2caps)
310 capsblob = bundle2.encodecaps(self.bundle2caps)
311 caps.add('bundle2-exp=' + urllib.quote(capsblob))
311 caps.add('bundle2-exp=' + urllib.quote(capsblob))
312 return caps
312 return caps
313
313
314 def _applyrequirements(self, requirements):
314 def _applyrequirements(self, requirements):
315 self.requirements = requirements
315 self.requirements = requirements
316 self.sopener.options = dict((r, 1) for r in requirements
316 self.sopener.options = dict((r, 1) for r in requirements
317 if r in self.openerreqs)
317 if r in self.openerreqs)
318 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
318 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
319 if chunkcachesize is not None:
319 if chunkcachesize is not None:
320 self.sopener.options['chunkcachesize'] = chunkcachesize
320 self.sopener.options['chunkcachesize'] = chunkcachesize
321
321
322 def _writerequirements(self):
322 def _writerequirements(self):
323 reqfile = self.opener("requires", "w")
323 reqfile = self.opener("requires", "w")
324 for r in sorted(self.requirements):
324 for r in sorted(self.requirements):
325 reqfile.write("%s\n" % r)
325 reqfile.write("%s\n" % r)
326 reqfile.close()
326 reqfile.close()
327
327
328 def _checknested(self, path):
328 def _checknested(self, path):
329 """Determine if path is a legal nested repository."""
329 """Determine if path is a legal nested repository."""
330 if not path.startswith(self.root):
330 if not path.startswith(self.root):
331 return False
331 return False
332 subpath = path[len(self.root) + 1:]
332 subpath = path[len(self.root) + 1:]
333 normsubpath = util.pconvert(subpath)
333 normsubpath = util.pconvert(subpath)
334
334
335 # XXX: Checking against the current working copy is wrong in
335 # XXX: Checking against the current working copy is wrong in
336 # the sense that it can reject things like
336 # the sense that it can reject things like
337 #
337 #
338 # $ hg cat -r 10 sub/x.txt
338 # $ hg cat -r 10 sub/x.txt
339 #
339 #
340 # if sub/ is no longer a subrepository in the working copy
340 # if sub/ is no longer a subrepository in the working copy
341 # parent revision.
341 # parent revision.
342 #
342 #
343 # However, it can of course also allow things that would have
343 # However, it can of course also allow things that would have
344 # been rejected before, such as the above cat command if sub/
344 # been rejected before, such as the above cat command if sub/
345 # is a subrepository now, but was a normal directory before.
345 # is a subrepository now, but was a normal directory before.
346 # The old path auditor would have rejected by mistake since it
346 # The old path auditor would have rejected by mistake since it
347 # panics when it sees sub/.hg/.
347 # panics when it sees sub/.hg/.
348 #
348 #
349 # All in all, checking against the working copy seems sensible
349 # All in all, checking against the working copy seems sensible
350 # since we want to prevent access to nested repositories on
350 # since we want to prevent access to nested repositories on
351 # the filesystem *now*.
351 # the filesystem *now*.
352 ctx = self[None]
352 ctx = self[None]
353 parts = util.splitpath(subpath)
353 parts = util.splitpath(subpath)
354 while parts:
354 while parts:
355 prefix = '/'.join(parts)
355 prefix = '/'.join(parts)
356 if prefix in ctx.substate:
356 if prefix in ctx.substate:
357 if prefix == normsubpath:
357 if prefix == normsubpath:
358 return True
358 return True
359 else:
359 else:
360 sub = ctx.sub(prefix)
360 sub = ctx.sub(prefix)
361 return sub.checknested(subpath[len(prefix) + 1:])
361 return sub.checknested(subpath[len(prefix) + 1:])
362 else:
362 else:
363 parts.pop()
363 parts.pop()
364 return False
364 return False
365
365
366 def peer(self):
366 def peer(self):
367 return localpeer(self) # not cached to avoid reference cycle
367 return localpeer(self) # not cached to avoid reference cycle
368
368
369 def unfiltered(self):
369 def unfiltered(self):
370 """Return unfiltered version of the repository
370 """Return unfiltered version of the repository
371
371
372 Intended to be overwritten by filtered repo."""
372 Intended to be overwritten by filtered repo."""
373 return self
373 return self
374
374
375 def filtered(self, name):
375 def filtered(self, name):
376 """Return a filtered version of a repository"""
376 """Return a filtered version of a repository"""
377 # build a new class with the mixin and the current class
377 # build a new class with the mixin and the current class
378 # (possibly subclass of the repo)
378 # (possibly subclass of the repo)
379 class proxycls(repoview.repoview, self.unfiltered().__class__):
379 class proxycls(repoview.repoview, self.unfiltered().__class__):
380 pass
380 pass
381 return proxycls(self, name)
381 return proxycls(self, name)
382
382
383 @repofilecache('bookmarks')
383 @repofilecache('bookmarks')
384 def _bookmarks(self):
384 def _bookmarks(self):
385 return bookmarks.bmstore(self)
385 return bookmarks.bmstore(self)
386
386
387 @repofilecache('bookmarks.current')
387 @repofilecache('bookmarks.current')
388 def _bookmarkcurrent(self):
388 def _bookmarkcurrent(self):
389 return bookmarks.readcurrent(self)
389 return bookmarks.readcurrent(self)
390
390
391 def bookmarkheads(self, bookmark):
391 def bookmarkheads(self, bookmark):
392 name = bookmark.split('@', 1)[0]
392 name = bookmark.split('@', 1)[0]
393 heads = []
393 heads = []
394 for mark, n in self._bookmarks.iteritems():
394 for mark, n in self._bookmarks.iteritems():
395 if mark.split('@', 1)[0] == name:
395 if mark.split('@', 1)[0] == name:
396 heads.append(n)
396 heads.append(n)
397 return heads
397 return heads
398
398
399 @storecache('phaseroots')
399 @storecache('phaseroots')
400 def _phasecache(self):
400 def _phasecache(self):
401 return phases.phasecache(self, self._phasedefaults)
401 return phases.phasecache(self, self._phasedefaults)
402
402
403 @storecache('obsstore')
403 @storecache('obsstore')
404 def obsstore(self):
404 def obsstore(self):
405 store = obsolete.obsstore(self.sopener)
405 store = obsolete.obsstore(self.sopener)
406 if store and not obsolete._enabled:
406 if store and not obsolete._enabled:
407 # message is rare enough to not be translated
407 # message is rare enough to not be translated
408 msg = 'obsolete feature not enabled but %i markers found!\n'
408 msg = 'obsolete feature not enabled but %i markers found!\n'
409 self.ui.warn(msg % len(list(store)))
409 self.ui.warn(msg % len(list(store)))
410 return store
410 return store
411
411
412 @storecache('00changelog.i')
412 @storecache('00changelog.i')
413 def changelog(self):
413 def changelog(self):
414 c = changelog.changelog(self.sopener)
414 c = changelog.changelog(self.sopener)
415 if 'HG_PENDING' in os.environ:
415 if 'HG_PENDING' in os.environ:
416 p = os.environ['HG_PENDING']
416 p = os.environ['HG_PENDING']
417 if p.startswith(self.root):
417 if p.startswith(self.root):
418 c.readpending('00changelog.i.a')
418 c.readpending('00changelog.i.a')
419 return c
419 return c
420
420
421 @storecache('00manifest.i')
421 @storecache('00manifest.i')
422 def manifest(self):
422 def manifest(self):
423 return manifest.manifest(self.sopener)
423 return manifest.manifest(self.sopener)
424
424
425 @repofilecache('dirstate')
425 @repofilecache('dirstate')
426 def dirstate(self):
426 def dirstate(self):
427 warned = [0]
427 warned = [0]
428 def validate(node):
428 def validate(node):
429 try:
429 try:
430 self.changelog.rev(node)
430 self.changelog.rev(node)
431 return node
431 return node
432 except error.LookupError:
432 except error.LookupError:
433 if not warned[0]:
433 if not warned[0]:
434 warned[0] = True
434 warned[0] = True
435 self.ui.warn(_("warning: ignoring unknown"
435 self.ui.warn(_("warning: ignoring unknown"
436 " working parent %s!\n") % short(node))
436 " working parent %s!\n") % short(node))
437 return nullid
437 return nullid
438
438
439 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
439 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
440
440
441 def __getitem__(self, changeid):
441 def __getitem__(self, changeid):
442 if changeid is None:
442 if changeid is None:
443 return context.workingctx(self)
443 return context.workingctx(self)
444 return context.changectx(self, changeid)
444 return context.changectx(self, changeid)
445
445
446 def __contains__(self, changeid):
446 def __contains__(self, changeid):
447 try:
447 try:
448 return bool(self.lookup(changeid))
448 return bool(self.lookup(changeid))
449 except error.RepoLookupError:
449 except error.RepoLookupError:
450 return False
450 return False
451
451
452 def __nonzero__(self):
452 def __nonzero__(self):
453 return True
453 return True
454
454
455 def __len__(self):
455 def __len__(self):
456 return len(self.changelog)
456 return len(self.changelog)
457
457
458 def __iter__(self):
458 def __iter__(self):
459 return iter(self.changelog)
459 return iter(self.changelog)
460
460
461 def revs(self, expr, *args):
461 def revs(self, expr, *args):
462 '''Return a list of revisions matching the given revset'''
462 '''Return a list of revisions matching the given revset'''
463 expr = revset.formatspec(expr, *args)
463 expr = revset.formatspec(expr, *args)
464 m = revset.match(None, expr)
464 m = revset.match(None, expr)
465 return m(self, revset.spanset(self))
465 return m(self, revset.spanset(self))
466
466
467 def set(self, expr, *args):
467 def set(self, expr, *args):
468 '''
468 '''
469 Yield a context for each matching revision, after doing arg
469 Yield a context for each matching revision, after doing arg
470 replacement via revset.formatspec
470 replacement via revset.formatspec
471 '''
471 '''
472 for r in self.revs(expr, *args):
472 for r in self.revs(expr, *args):
473 yield self[r]
473 yield self[r]
474
474
475 def url(self):
475 def url(self):
476 return 'file:' + self.root
476 return 'file:' + self.root
477
477
478 def hook(self, name, throw=False, **args):
478 def hook(self, name, throw=False, **args):
479 return hook.hook(self.ui, self, name, throw, **args)
479 return hook.hook(self.ui, self, name, throw, **args)
480
480
481 @unfilteredmethod
481 @unfilteredmethod
482 def _tag(self, names, node, message, local, user, date, extra={}):
482 def _tag(self, names, node, message, local, user, date, extra={},
483 editor=False):
483 if isinstance(names, str):
484 if isinstance(names, str):
484 names = (names,)
485 names = (names,)
485
486
486 branches = self.branchmap()
487 branches = self.branchmap()
487 for name in names:
488 for name in names:
488 self.hook('pretag', throw=True, node=hex(node), tag=name,
489 self.hook('pretag', throw=True, node=hex(node), tag=name,
489 local=local)
490 local=local)
490 if name in branches:
491 if name in branches:
491 self.ui.warn(_("warning: tag %s conflicts with existing"
492 self.ui.warn(_("warning: tag %s conflicts with existing"
492 " branch name\n") % name)
493 " branch name\n") % name)
493
494
494 def writetags(fp, names, munge, prevtags):
495 def writetags(fp, names, munge, prevtags):
495 fp.seek(0, 2)
496 fp.seek(0, 2)
496 if prevtags and prevtags[-1] != '\n':
497 if prevtags and prevtags[-1] != '\n':
497 fp.write('\n')
498 fp.write('\n')
498 for name in names:
499 for name in names:
499 m = munge and munge(name) or name
500 m = munge and munge(name) or name
500 if (self._tagscache.tagtypes and
501 if (self._tagscache.tagtypes and
501 name in self._tagscache.tagtypes):
502 name in self._tagscache.tagtypes):
502 old = self.tags().get(name, nullid)
503 old = self.tags().get(name, nullid)
503 fp.write('%s %s\n' % (hex(old), m))
504 fp.write('%s %s\n' % (hex(old), m))
504 fp.write('%s %s\n' % (hex(node), m))
505 fp.write('%s %s\n' % (hex(node), m))
505 fp.close()
506 fp.close()
506
507
507 prevtags = ''
508 prevtags = ''
508 if local:
509 if local:
509 try:
510 try:
510 fp = self.opener('localtags', 'r+')
511 fp = self.opener('localtags', 'r+')
511 except IOError:
512 except IOError:
512 fp = self.opener('localtags', 'a')
513 fp = self.opener('localtags', 'a')
513 else:
514 else:
514 prevtags = fp.read()
515 prevtags = fp.read()
515
516
516 # local tags are stored in the current charset
517 # local tags are stored in the current charset
517 writetags(fp, names, None, prevtags)
518 writetags(fp, names, None, prevtags)
518 for name in names:
519 for name in names:
519 self.hook('tag', node=hex(node), tag=name, local=local)
520 self.hook('tag', node=hex(node), tag=name, local=local)
520 return
521 return
521
522
522 try:
523 try:
523 fp = self.wfile('.hgtags', 'rb+')
524 fp = self.wfile('.hgtags', 'rb+')
524 except IOError, e:
525 except IOError, e:
525 if e.errno != errno.ENOENT:
526 if e.errno != errno.ENOENT:
526 raise
527 raise
527 fp = self.wfile('.hgtags', 'ab')
528 fp = self.wfile('.hgtags', 'ab')
528 else:
529 else:
529 prevtags = fp.read()
530 prevtags = fp.read()
530
531
531 # committed tags are stored in UTF-8
532 # committed tags are stored in UTF-8
532 writetags(fp, names, encoding.fromlocal, prevtags)
533 writetags(fp, names, encoding.fromlocal, prevtags)
533
534
534 fp.close()
535 fp.close()
535
536
536 self.invalidatecaches()
537 self.invalidatecaches()
537
538
538 if '.hgtags' not in self.dirstate:
539 if '.hgtags' not in self.dirstate:
539 self[None].add(['.hgtags'])
540 self[None].add(['.hgtags'])
540
541
541 m = matchmod.exact(self.root, '', ['.hgtags'])
542 m = matchmod.exact(self.root, '', ['.hgtags'])
542 tagnode = self.commit(message, user, date, extra=extra, match=m)
543 tagnode = self.commit(message, user, date, extra=extra, match=m,
544 editor=editor)
543
545
544 for name in names:
546 for name in names:
545 self.hook('tag', node=hex(node), tag=name, local=local)
547 self.hook('tag', node=hex(node), tag=name, local=local)
546
548
547 return tagnode
549 return tagnode
548
550
549 def tag(self, names, node, message, local, user, date):
551 def tag(self, names, node, message, local, user, date, editor=False):
550 '''tag a revision with one or more symbolic names.
552 '''tag a revision with one or more symbolic names.
551
553
552 names is a list of strings or, when adding a single tag, names may be a
554 names is a list of strings or, when adding a single tag, names may be a
553 string.
555 string.
554
556
555 if local is True, the tags are stored in a per-repository file.
557 if local is True, the tags are stored in a per-repository file.
556 otherwise, they are stored in the .hgtags file, and a new
558 otherwise, they are stored in the .hgtags file, and a new
557 changeset is committed with the change.
559 changeset is committed with the change.
558
560
559 keyword arguments:
561 keyword arguments:
560
562
561 local: whether to store tags in non-version-controlled file
563 local: whether to store tags in non-version-controlled file
562 (default False)
564 (default False)
563
565
564 message: commit message to use if committing
566 message: commit message to use if committing
565
567
566 user: name of user to use if committing
568 user: name of user to use if committing
567
569
568 date: date tuple to use if committing'''
570 date: date tuple to use if committing'''
569
571
570 if not local:
572 if not local:
571 for x in self.status()[:5]:
573 for x in self.status()[:5]:
572 if '.hgtags' in x:
574 if '.hgtags' in x:
573 raise util.Abort(_('working copy of .hgtags is changed '
575 raise util.Abort(_('working copy of .hgtags is changed '
574 '(please commit .hgtags manually)'))
576 '(please commit .hgtags manually)'))
575
577
576 self.tags() # instantiate the cache
578 self.tags() # instantiate the cache
577 self._tag(names, node, message, local, user, date)
579 self._tag(names, node, message, local, user, date, editor=editor)
578
580
579 @filteredpropertycache
581 @filteredpropertycache
580 def _tagscache(self):
582 def _tagscache(self):
581 '''Returns a tagscache object that contains various tags related
583 '''Returns a tagscache object that contains various tags related
582 caches.'''
584 caches.'''
583
585
584 # This simplifies its cache management by having one decorated
586 # This simplifies its cache management by having one decorated
585 # function (this one) and the rest simply fetch things from it.
587 # function (this one) and the rest simply fetch things from it.
586 class tagscache(object):
588 class tagscache(object):
587 def __init__(self):
589 def __init__(self):
588 # These two define the set of tags for this repository. tags
590 # These two define the set of tags for this repository. tags
589 # maps tag name to node; tagtypes maps tag name to 'global' or
591 # maps tag name to node; tagtypes maps tag name to 'global' or
590 # 'local'. (Global tags are defined by .hgtags across all
592 # 'local'. (Global tags are defined by .hgtags across all
591 # heads, and local tags are defined in .hg/localtags.)
593 # heads, and local tags are defined in .hg/localtags.)
592 # They constitute the in-memory cache of tags.
594 # They constitute the in-memory cache of tags.
593 self.tags = self.tagtypes = None
595 self.tags = self.tagtypes = None
594
596
595 self.nodetagscache = self.tagslist = None
597 self.nodetagscache = self.tagslist = None
596
598
597 cache = tagscache()
599 cache = tagscache()
598 cache.tags, cache.tagtypes = self._findtags()
600 cache.tags, cache.tagtypes = self._findtags()
599
601
600 return cache
602 return cache
601
603
602 def tags(self):
604 def tags(self):
603 '''return a mapping of tag to node'''
605 '''return a mapping of tag to node'''
604 t = {}
606 t = {}
605 if self.changelog.filteredrevs:
607 if self.changelog.filteredrevs:
606 tags, tt = self._findtags()
608 tags, tt = self._findtags()
607 else:
609 else:
608 tags = self._tagscache.tags
610 tags = self._tagscache.tags
609 for k, v in tags.iteritems():
611 for k, v in tags.iteritems():
610 try:
612 try:
611 # ignore tags to unknown nodes
613 # ignore tags to unknown nodes
612 self.changelog.rev(v)
614 self.changelog.rev(v)
613 t[k] = v
615 t[k] = v
614 except (error.LookupError, ValueError):
616 except (error.LookupError, ValueError):
615 pass
617 pass
616 return t
618 return t
617
619
618 def _findtags(self):
620 def _findtags(self):
619 '''Do the hard work of finding tags. Return a pair of dicts
621 '''Do the hard work of finding tags. Return a pair of dicts
620 (tags, tagtypes) where tags maps tag name to node, and tagtypes
622 (tags, tagtypes) where tags maps tag name to node, and tagtypes
621 maps tag name to a string like \'global\' or \'local\'.
623 maps tag name to a string like \'global\' or \'local\'.
622 Subclasses or extensions are free to add their own tags, but
624 Subclasses or extensions are free to add their own tags, but
623 should be aware that the returned dicts will be retained for the
625 should be aware that the returned dicts will be retained for the
624 duration of the localrepo object.'''
626 duration of the localrepo object.'''
625
627
626 # XXX what tagtype should subclasses/extensions use? Currently
628 # XXX what tagtype should subclasses/extensions use? Currently
627 # mq and bookmarks add tags, but do not set the tagtype at all.
629 # mq and bookmarks add tags, but do not set the tagtype at all.
628 # Should each extension invent its own tag type? Should there
630 # Should each extension invent its own tag type? Should there
629 # be one tagtype for all such "virtual" tags? Or is the status
631 # be one tagtype for all such "virtual" tags? Or is the status
630 # quo fine?
632 # quo fine?
631
633
632 alltags = {} # map tag name to (node, hist)
634 alltags = {} # map tag name to (node, hist)
633 tagtypes = {}
635 tagtypes = {}
634
636
635 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
637 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
636 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
638 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
637
639
638 # Build the return dicts. Have to re-encode tag names because
640 # Build the return dicts. Have to re-encode tag names because
639 # the tags module always uses UTF-8 (in order not to lose info
641 # the tags module always uses UTF-8 (in order not to lose info
640 # writing to the cache), but the rest of Mercurial wants them in
642 # writing to the cache), but the rest of Mercurial wants them in
641 # local encoding.
643 # local encoding.
642 tags = {}
644 tags = {}
643 for (name, (node, hist)) in alltags.iteritems():
645 for (name, (node, hist)) in alltags.iteritems():
644 if node != nullid:
646 if node != nullid:
645 tags[encoding.tolocal(name)] = node
647 tags[encoding.tolocal(name)] = node
646 tags['tip'] = self.changelog.tip()
648 tags['tip'] = self.changelog.tip()
647 tagtypes = dict([(encoding.tolocal(name), value)
649 tagtypes = dict([(encoding.tolocal(name), value)
648 for (name, value) in tagtypes.iteritems()])
650 for (name, value) in tagtypes.iteritems()])
649 return (tags, tagtypes)
651 return (tags, tagtypes)
650
652
651 def tagtype(self, tagname):
653 def tagtype(self, tagname):
652 '''
654 '''
653 return the type of the given tag. result can be:
655 return the type of the given tag. result can be:
654
656
655 'local' : a local tag
657 'local' : a local tag
656 'global' : a global tag
658 'global' : a global tag
657 None : tag does not exist
659 None : tag does not exist
658 '''
660 '''
659
661
660 return self._tagscache.tagtypes.get(tagname)
662 return self._tagscache.tagtypes.get(tagname)
661
663
662 def tagslist(self):
664 def tagslist(self):
663 '''return a list of tags ordered by revision'''
665 '''return a list of tags ordered by revision'''
664 if not self._tagscache.tagslist:
666 if not self._tagscache.tagslist:
665 l = []
667 l = []
666 for t, n in self.tags().iteritems():
668 for t, n in self.tags().iteritems():
667 r = self.changelog.rev(n)
669 r = self.changelog.rev(n)
668 l.append((r, t, n))
670 l.append((r, t, n))
669 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
671 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
670
672
671 return self._tagscache.tagslist
673 return self._tagscache.tagslist
672
674
673 def nodetags(self, node):
675 def nodetags(self, node):
674 '''return the tags associated with a node'''
676 '''return the tags associated with a node'''
675 if not self._tagscache.nodetagscache:
677 if not self._tagscache.nodetagscache:
676 nodetagscache = {}
678 nodetagscache = {}
677 for t, n in self._tagscache.tags.iteritems():
679 for t, n in self._tagscache.tags.iteritems():
678 nodetagscache.setdefault(n, []).append(t)
680 nodetagscache.setdefault(n, []).append(t)
679 for tags in nodetagscache.itervalues():
681 for tags in nodetagscache.itervalues():
680 tags.sort()
682 tags.sort()
681 self._tagscache.nodetagscache = nodetagscache
683 self._tagscache.nodetagscache = nodetagscache
682 return self._tagscache.nodetagscache.get(node, [])
684 return self._tagscache.nodetagscache.get(node, [])
683
685
684 def nodebookmarks(self, node):
686 def nodebookmarks(self, node):
685 marks = []
687 marks = []
686 for bookmark, n in self._bookmarks.iteritems():
688 for bookmark, n in self._bookmarks.iteritems():
687 if n == node:
689 if n == node:
688 marks.append(bookmark)
690 marks.append(bookmark)
689 return sorted(marks)
691 return sorted(marks)
690
692
691 def branchmap(self):
693 def branchmap(self):
692 '''returns a dictionary {branch: [branchheads]} with branchheads
694 '''returns a dictionary {branch: [branchheads]} with branchheads
693 ordered by increasing revision number'''
695 ordered by increasing revision number'''
694 branchmap.updatecache(self)
696 branchmap.updatecache(self)
695 return self._branchcaches[self.filtername]
697 return self._branchcaches[self.filtername]
696
698
697 def branchtip(self, branch):
699 def branchtip(self, branch):
698 '''return the tip node for a given branch'''
700 '''return the tip node for a given branch'''
699 try:
701 try:
700 return self.branchmap().branchtip(branch)
702 return self.branchmap().branchtip(branch)
701 except KeyError:
703 except KeyError:
702 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
704 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
703
705
704 def lookup(self, key):
706 def lookup(self, key):
705 return self[key].node()
707 return self[key].node()
706
708
707 def lookupbranch(self, key, remote=None):
709 def lookupbranch(self, key, remote=None):
708 repo = remote or self
710 repo = remote or self
709 if key in repo.branchmap():
711 if key in repo.branchmap():
710 return key
712 return key
711
713
712 repo = (remote and remote.local()) and remote or self
714 repo = (remote and remote.local()) and remote or self
713 return repo[key].branch()
715 return repo[key].branch()
714
716
715 def known(self, nodes):
717 def known(self, nodes):
716 nm = self.changelog.nodemap
718 nm = self.changelog.nodemap
717 pc = self._phasecache
719 pc = self._phasecache
718 result = []
720 result = []
719 for n in nodes:
721 for n in nodes:
720 r = nm.get(n)
722 r = nm.get(n)
721 resp = not (r is None or pc.phase(self, r) >= phases.secret)
723 resp = not (r is None or pc.phase(self, r) >= phases.secret)
722 result.append(resp)
724 result.append(resp)
723 return result
725 return result
724
726
725 def local(self):
727 def local(self):
726 return self
728 return self
727
729
728 def cancopy(self):
730 def cancopy(self):
729 # so statichttprepo's override of local() works
731 # so statichttprepo's override of local() works
730 if not self.local():
732 if not self.local():
731 return False
733 return False
732 if not self.ui.configbool('phases', 'publish', True):
734 if not self.ui.configbool('phases', 'publish', True):
733 return True
735 return True
734 # if publishing we can't copy if there is filtered content
736 # if publishing we can't copy if there is filtered content
735 return not self.filtered('visible').changelog.filteredrevs
737 return not self.filtered('visible').changelog.filteredrevs
736
738
737 def join(self, f):
739 def join(self, f):
738 return os.path.join(self.path, f)
740 return os.path.join(self.path, f)
739
741
740 def wjoin(self, f):
742 def wjoin(self, f):
741 return os.path.join(self.root, f)
743 return os.path.join(self.root, f)
742
744
743 def file(self, f):
745 def file(self, f):
744 if f[0] == '/':
746 if f[0] == '/':
745 f = f[1:]
747 f = f[1:]
746 return filelog.filelog(self.sopener, f)
748 return filelog.filelog(self.sopener, f)
747
749
748 def changectx(self, changeid):
750 def changectx(self, changeid):
749 return self[changeid]
751 return self[changeid]
750
752
751 def parents(self, changeid=None):
753 def parents(self, changeid=None):
752 '''get list of changectxs for parents of changeid'''
754 '''get list of changectxs for parents of changeid'''
753 return self[changeid].parents()
755 return self[changeid].parents()
754
756
755 def setparents(self, p1, p2=nullid):
757 def setparents(self, p1, p2=nullid):
756 copies = self.dirstate.setparents(p1, p2)
758 copies = self.dirstate.setparents(p1, p2)
757 pctx = self[p1]
759 pctx = self[p1]
758 if copies:
760 if copies:
759 # Adjust copy records, the dirstate cannot do it, it
761 # Adjust copy records, the dirstate cannot do it, it
760 # requires access to parents manifests. Preserve them
762 # requires access to parents manifests. Preserve them
761 # only for entries added to first parent.
763 # only for entries added to first parent.
762 for f in copies:
764 for f in copies:
763 if f not in pctx and copies[f] in pctx:
765 if f not in pctx and copies[f] in pctx:
764 self.dirstate.copy(copies[f], f)
766 self.dirstate.copy(copies[f], f)
765 if p2 == nullid:
767 if p2 == nullid:
766 for f, s in sorted(self.dirstate.copies().items()):
768 for f, s in sorted(self.dirstate.copies().items()):
767 if f not in pctx and s not in pctx:
769 if f not in pctx and s not in pctx:
768 self.dirstate.copy(None, f)
770 self.dirstate.copy(None, f)
769
771
770 def filectx(self, path, changeid=None, fileid=None):
772 def filectx(self, path, changeid=None, fileid=None):
771 """changeid can be a changeset revision, node, or tag.
773 """changeid can be a changeset revision, node, or tag.
772 fileid can be a file revision or node."""
774 fileid can be a file revision or node."""
773 return context.filectx(self, path, changeid, fileid)
775 return context.filectx(self, path, changeid, fileid)
774
776
775 def getcwd(self):
777 def getcwd(self):
776 return self.dirstate.getcwd()
778 return self.dirstate.getcwd()
777
779
778 def pathto(self, f, cwd=None):
780 def pathto(self, f, cwd=None):
779 return self.dirstate.pathto(f, cwd)
781 return self.dirstate.pathto(f, cwd)
780
782
781 def wfile(self, f, mode='r'):
783 def wfile(self, f, mode='r'):
782 return self.wopener(f, mode)
784 return self.wopener(f, mode)
783
785
784 def _link(self, f):
786 def _link(self, f):
785 return self.wvfs.islink(f)
787 return self.wvfs.islink(f)
786
788
787 def _loadfilter(self, filter):
789 def _loadfilter(self, filter):
788 if filter not in self.filterpats:
790 if filter not in self.filterpats:
789 l = []
791 l = []
790 for pat, cmd in self.ui.configitems(filter):
792 for pat, cmd in self.ui.configitems(filter):
791 if cmd == '!':
793 if cmd == '!':
792 continue
794 continue
793 mf = matchmod.match(self.root, '', [pat])
795 mf = matchmod.match(self.root, '', [pat])
794 fn = None
796 fn = None
795 params = cmd
797 params = cmd
796 for name, filterfn in self._datafilters.iteritems():
798 for name, filterfn in self._datafilters.iteritems():
797 if cmd.startswith(name):
799 if cmd.startswith(name):
798 fn = filterfn
800 fn = filterfn
799 params = cmd[len(name):].lstrip()
801 params = cmd[len(name):].lstrip()
800 break
802 break
801 if not fn:
803 if not fn:
802 fn = lambda s, c, **kwargs: util.filter(s, c)
804 fn = lambda s, c, **kwargs: util.filter(s, c)
803 # Wrap old filters not supporting keyword arguments
805 # Wrap old filters not supporting keyword arguments
804 if not inspect.getargspec(fn)[2]:
806 if not inspect.getargspec(fn)[2]:
805 oldfn = fn
807 oldfn = fn
806 fn = lambda s, c, **kwargs: oldfn(s, c)
808 fn = lambda s, c, **kwargs: oldfn(s, c)
807 l.append((mf, fn, params))
809 l.append((mf, fn, params))
808 self.filterpats[filter] = l
810 self.filterpats[filter] = l
809 return self.filterpats[filter]
811 return self.filterpats[filter]
810
812
811 def _filter(self, filterpats, filename, data):
813 def _filter(self, filterpats, filename, data):
812 for mf, fn, cmd in filterpats:
814 for mf, fn, cmd in filterpats:
813 if mf(filename):
815 if mf(filename):
814 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
816 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
815 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
817 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
816 break
818 break
817
819
818 return data
820 return data
819
821
820 @unfilteredpropertycache
822 @unfilteredpropertycache
821 def _encodefilterpats(self):
823 def _encodefilterpats(self):
822 return self._loadfilter('encode')
824 return self._loadfilter('encode')
823
825
824 @unfilteredpropertycache
826 @unfilteredpropertycache
825 def _decodefilterpats(self):
827 def _decodefilterpats(self):
826 return self._loadfilter('decode')
828 return self._loadfilter('decode')
827
829
828 def adddatafilter(self, name, filter):
830 def adddatafilter(self, name, filter):
829 self._datafilters[name] = filter
831 self._datafilters[name] = filter
830
832
831 def wread(self, filename):
833 def wread(self, filename):
832 if self._link(filename):
834 if self._link(filename):
833 data = self.wvfs.readlink(filename)
835 data = self.wvfs.readlink(filename)
834 else:
836 else:
835 data = self.wopener.read(filename)
837 data = self.wopener.read(filename)
836 return self._filter(self._encodefilterpats, filename, data)
838 return self._filter(self._encodefilterpats, filename, data)
837
839
838 def wwrite(self, filename, data, flags):
840 def wwrite(self, filename, data, flags):
839 data = self._filter(self._decodefilterpats, filename, data)
841 data = self._filter(self._decodefilterpats, filename, data)
840 if 'l' in flags:
842 if 'l' in flags:
841 self.wopener.symlink(data, filename)
843 self.wopener.symlink(data, filename)
842 else:
844 else:
843 self.wopener.write(filename, data)
845 self.wopener.write(filename, data)
844 if 'x' in flags:
846 if 'x' in flags:
845 self.wvfs.setflags(filename, False, True)
847 self.wvfs.setflags(filename, False, True)
846
848
847 def wwritedata(self, filename, data):
849 def wwritedata(self, filename, data):
848 return self._filter(self._decodefilterpats, filename, data)
850 return self._filter(self._decodefilterpats, filename, data)
849
851
850 def transaction(self, desc, report=None):
852 def transaction(self, desc, report=None):
851 tr = self._transref and self._transref() or None
853 tr = self._transref and self._transref() or None
852 if tr and tr.running():
854 if tr and tr.running():
853 return tr.nest()
855 return tr.nest()
854
856
855 # abort here if the journal already exists
857 # abort here if the journal already exists
856 if self.svfs.exists("journal"):
858 if self.svfs.exists("journal"):
857 raise error.RepoError(
859 raise error.RepoError(
858 _("abandoned transaction found - run hg recover"))
860 _("abandoned transaction found - run hg recover"))
859
861
860 def onclose():
862 def onclose():
861 self.store.write(tr)
863 self.store.write(tr)
862
864
863 self._writejournal(desc)
865 self._writejournal(desc)
864 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
866 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
865 rp = report and report or self.ui.warn
867 rp = report and report or self.ui.warn
866 tr = transaction.transaction(rp, self.sopener,
868 tr = transaction.transaction(rp, self.sopener,
867 "journal",
869 "journal",
868 aftertrans(renames),
870 aftertrans(renames),
869 self.store.createmode,
871 self.store.createmode,
870 onclose)
872 onclose)
871 self._transref = weakref.ref(tr)
873 self._transref = weakref.ref(tr)
872 return tr
874 return tr
873
875
874 def _journalfiles(self):
876 def _journalfiles(self):
875 return ((self.svfs, 'journal'),
877 return ((self.svfs, 'journal'),
876 (self.vfs, 'journal.dirstate'),
878 (self.vfs, 'journal.dirstate'),
877 (self.vfs, 'journal.branch'),
879 (self.vfs, 'journal.branch'),
878 (self.vfs, 'journal.desc'),
880 (self.vfs, 'journal.desc'),
879 (self.vfs, 'journal.bookmarks'),
881 (self.vfs, 'journal.bookmarks'),
880 (self.svfs, 'journal.phaseroots'))
882 (self.svfs, 'journal.phaseroots'))
881
883
882 def undofiles(self):
884 def undofiles(self):
883 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
885 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
884
886
885 def _writejournal(self, desc):
887 def _writejournal(self, desc):
886 self.opener.write("journal.dirstate",
888 self.opener.write("journal.dirstate",
887 self.opener.tryread("dirstate"))
889 self.opener.tryread("dirstate"))
888 self.opener.write("journal.branch",
890 self.opener.write("journal.branch",
889 encoding.fromlocal(self.dirstate.branch()))
891 encoding.fromlocal(self.dirstate.branch()))
890 self.opener.write("journal.desc",
892 self.opener.write("journal.desc",
891 "%d\n%s\n" % (len(self), desc))
893 "%d\n%s\n" % (len(self), desc))
892 self.opener.write("journal.bookmarks",
894 self.opener.write("journal.bookmarks",
893 self.opener.tryread("bookmarks"))
895 self.opener.tryread("bookmarks"))
894 self.sopener.write("journal.phaseroots",
896 self.sopener.write("journal.phaseroots",
895 self.sopener.tryread("phaseroots"))
897 self.sopener.tryread("phaseroots"))
896
898
897 def recover(self):
899 def recover(self):
898 lock = self.lock()
900 lock = self.lock()
899 try:
901 try:
900 if self.svfs.exists("journal"):
902 if self.svfs.exists("journal"):
901 self.ui.status(_("rolling back interrupted transaction\n"))
903 self.ui.status(_("rolling back interrupted transaction\n"))
902 transaction.rollback(self.sopener, "journal",
904 transaction.rollback(self.sopener, "journal",
903 self.ui.warn)
905 self.ui.warn)
904 self.invalidate()
906 self.invalidate()
905 return True
907 return True
906 else:
908 else:
907 self.ui.warn(_("no interrupted transaction available\n"))
909 self.ui.warn(_("no interrupted transaction available\n"))
908 return False
910 return False
909 finally:
911 finally:
910 lock.release()
912 lock.release()
911
913
912 def rollback(self, dryrun=False, force=False):
914 def rollback(self, dryrun=False, force=False):
913 wlock = lock = None
915 wlock = lock = None
914 try:
916 try:
915 wlock = self.wlock()
917 wlock = self.wlock()
916 lock = self.lock()
918 lock = self.lock()
917 if self.svfs.exists("undo"):
919 if self.svfs.exists("undo"):
918 return self._rollback(dryrun, force)
920 return self._rollback(dryrun, force)
919 else:
921 else:
920 self.ui.warn(_("no rollback information available\n"))
922 self.ui.warn(_("no rollback information available\n"))
921 return 1
923 return 1
922 finally:
924 finally:
923 release(lock, wlock)
925 release(lock, wlock)
924
926
925 @unfilteredmethod # Until we get smarter cache management
927 @unfilteredmethod # Until we get smarter cache management
926 def _rollback(self, dryrun, force):
928 def _rollback(self, dryrun, force):
927 ui = self.ui
929 ui = self.ui
928 try:
930 try:
929 args = self.opener.read('undo.desc').splitlines()
931 args = self.opener.read('undo.desc').splitlines()
930 (oldlen, desc, detail) = (int(args[0]), args[1], None)
932 (oldlen, desc, detail) = (int(args[0]), args[1], None)
931 if len(args) >= 3:
933 if len(args) >= 3:
932 detail = args[2]
934 detail = args[2]
933 oldtip = oldlen - 1
935 oldtip = oldlen - 1
934
936
935 if detail and ui.verbose:
937 if detail and ui.verbose:
936 msg = (_('repository tip rolled back to revision %s'
938 msg = (_('repository tip rolled back to revision %s'
937 ' (undo %s: %s)\n')
939 ' (undo %s: %s)\n')
938 % (oldtip, desc, detail))
940 % (oldtip, desc, detail))
939 else:
941 else:
940 msg = (_('repository tip rolled back to revision %s'
942 msg = (_('repository tip rolled back to revision %s'
941 ' (undo %s)\n')
943 ' (undo %s)\n')
942 % (oldtip, desc))
944 % (oldtip, desc))
943 except IOError:
945 except IOError:
944 msg = _('rolling back unknown transaction\n')
946 msg = _('rolling back unknown transaction\n')
945 desc = None
947 desc = None
946
948
947 if not force and self['.'] != self['tip'] and desc == 'commit':
949 if not force and self['.'] != self['tip'] and desc == 'commit':
948 raise util.Abort(
950 raise util.Abort(
949 _('rollback of last commit while not checked out '
951 _('rollback of last commit while not checked out '
950 'may lose data'), hint=_('use -f to force'))
952 'may lose data'), hint=_('use -f to force'))
951
953
952 ui.status(msg)
954 ui.status(msg)
953 if dryrun:
955 if dryrun:
954 return 0
956 return 0
955
957
956 parents = self.dirstate.parents()
958 parents = self.dirstate.parents()
957 self.destroying()
959 self.destroying()
958 transaction.rollback(self.sopener, 'undo', ui.warn)
960 transaction.rollback(self.sopener, 'undo', ui.warn)
959 if self.vfs.exists('undo.bookmarks'):
961 if self.vfs.exists('undo.bookmarks'):
960 self.vfs.rename('undo.bookmarks', 'bookmarks')
962 self.vfs.rename('undo.bookmarks', 'bookmarks')
961 if self.svfs.exists('undo.phaseroots'):
963 if self.svfs.exists('undo.phaseroots'):
962 self.svfs.rename('undo.phaseroots', 'phaseroots')
964 self.svfs.rename('undo.phaseroots', 'phaseroots')
963 self.invalidate()
965 self.invalidate()
964
966
965 parentgone = (parents[0] not in self.changelog.nodemap or
967 parentgone = (parents[0] not in self.changelog.nodemap or
966 parents[1] not in self.changelog.nodemap)
968 parents[1] not in self.changelog.nodemap)
967 if parentgone:
969 if parentgone:
968 self.vfs.rename('undo.dirstate', 'dirstate')
970 self.vfs.rename('undo.dirstate', 'dirstate')
969 try:
971 try:
970 branch = self.opener.read('undo.branch')
972 branch = self.opener.read('undo.branch')
971 self.dirstate.setbranch(encoding.tolocal(branch))
973 self.dirstate.setbranch(encoding.tolocal(branch))
972 except IOError:
974 except IOError:
973 ui.warn(_('named branch could not be reset: '
975 ui.warn(_('named branch could not be reset: '
974 'current branch is still \'%s\'\n')
976 'current branch is still \'%s\'\n')
975 % self.dirstate.branch())
977 % self.dirstate.branch())
976
978
977 self.dirstate.invalidate()
979 self.dirstate.invalidate()
978 parents = tuple([p.rev() for p in self.parents()])
980 parents = tuple([p.rev() for p in self.parents()])
979 if len(parents) > 1:
981 if len(parents) > 1:
980 ui.status(_('working directory now based on '
982 ui.status(_('working directory now based on '
981 'revisions %d and %d\n') % parents)
983 'revisions %d and %d\n') % parents)
982 else:
984 else:
983 ui.status(_('working directory now based on '
985 ui.status(_('working directory now based on '
984 'revision %d\n') % parents)
986 'revision %d\n') % parents)
985 # TODO: if we know which new heads may result from this rollback, pass
987 # TODO: if we know which new heads may result from this rollback, pass
986 # them to destroy(), which will prevent the branchhead cache from being
988 # them to destroy(), which will prevent the branchhead cache from being
987 # invalidated.
989 # invalidated.
988 self.destroyed()
990 self.destroyed()
989 return 0
991 return 0
990
992
991 def invalidatecaches(self):
993 def invalidatecaches(self):
992
994
993 if '_tagscache' in vars(self):
995 if '_tagscache' in vars(self):
994 # can't use delattr on proxy
996 # can't use delattr on proxy
995 del self.__dict__['_tagscache']
997 del self.__dict__['_tagscache']
996
998
997 self.unfiltered()._branchcaches.clear()
999 self.unfiltered()._branchcaches.clear()
998 self.invalidatevolatilesets()
1000 self.invalidatevolatilesets()
999
1001
1000 def invalidatevolatilesets(self):
1002 def invalidatevolatilesets(self):
1001 self.filteredrevcache.clear()
1003 self.filteredrevcache.clear()
1002 obsolete.clearobscaches(self)
1004 obsolete.clearobscaches(self)
1003
1005
1004 def invalidatedirstate(self):
1006 def invalidatedirstate(self):
1005 '''Invalidates the dirstate, causing the next call to dirstate
1007 '''Invalidates the dirstate, causing the next call to dirstate
1006 to check if it was modified since the last time it was read,
1008 to check if it was modified since the last time it was read,
1007 rereading it if it has.
1009 rereading it if it has.
1008
1010
1009 This is different to dirstate.invalidate() that it doesn't always
1011 This is different to dirstate.invalidate() that it doesn't always
1010 rereads the dirstate. Use dirstate.invalidate() if you want to
1012 rereads the dirstate. Use dirstate.invalidate() if you want to
1011 explicitly read the dirstate again (i.e. restoring it to a previous
1013 explicitly read the dirstate again (i.e. restoring it to a previous
1012 known good state).'''
1014 known good state).'''
1013 if hasunfilteredcache(self, 'dirstate'):
1015 if hasunfilteredcache(self, 'dirstate'):
1014 for k in self.dirstate._filecache:
1016 for k in self.dirstate._filecache:
1015 try:
1017 try:
1016 delattr(self.dirstate, k)
1018 delattr(self.dirstate, k)
1017 except AttributeError:
1019 except AttributeError:
1018 pass
1020 pass
1019 delattr(self.unfiltered(), 'dirstate')
1021 delattr(self.unfiltered(), 'dirstate')
1020
1022
1021 def invalidate(self):
1023 def invalidate(self):
1022 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1024 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1023 for k in self._filecache:
1025 for k in self._filecache:
1024 # dirstate is invalidated separately in invalidatedirstate()
1026 # dirstate is invalidated separately in invalidatedirstate()
1025 if k == 'dirstate':
1027 if k == 'dirstate':
1026 continue
1028 continue
1027
1029
1028 try:
1030 try:
1029 delattr(unfiltered, k)
1031 delattr(unfiltered, k)
1030 except AttributeError:
1032 except AttributeError:
1031 pass
1033 pass
1032 self.invalidatecaches()
1034 self.invalidatecaches()
1033 self.store.invalidatecaches()
1035 self.store.invalidatecaches()
1034
1036
1035 def invalidateall(self):
1037 def invalidateall(self):
1036 '''Fully invalidates both store and non-store parts, causing the
1038 '''Fully invalidates both store and non-store parts, causing the
1037 subsequent operation to reread any outside changes.'''
1039 subsequent operation to reread any outside changes.'''
1038 # extension should hook this to invalidate its caches
1040 # extension should hook this to invalidate its caches
1039 self.invalidate()
1041 self.invalidate()
1040 self.invalidatedirstate()
1042 self.invalidatedirstate()
1041
1043
1042 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1044 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1043 try:
1045 try:
1044 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1046 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1045 except error.LockHeld, inst:
1047 except error.LockHeld, inst:
1046 if not wait:
1048 if not wait:
1047 raise
1049 raise
1048 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1050 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1049 (desc, inst.locker))
1051 (desc, inst.locker))
1050 # default to 600 seconds timeout
1052 # default to 600 seconds timeout
1051 l = lockmod.lock(vfs, lockname,
1053 l = lockmod.lock(vfs, lockname,
1052 int(self.ui.config("ui", "timeout", "600")),
1054 int(self.ui.config("ui", "timeout", "600")),
1053 releasefn, desc=desc)
1055 releasefn, desc=desc)
1054 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1056 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1055 if acquirefn:
1057 if acquirefn:
1056 acquirefn()
1058 acquirefn()
1057 return l
1059 return l
1058
1060
1059 def _afterlock(self, callback):
1061 def _afterlock(self, callback):
1060 """add a callback to the current repository lock.
1062 """add a callback to the current repository lock.
1061
1063
1062 The callback will be executed on lock release."""
1064 The callback will be executed on lock release."""
1063 l = self._lockref and self._lockref()
1065 l = self._lockref and self._lockref()
1064 if l:
1066 if l:
1065 l.postrelease.append(callback)
1067 l.postrelease.append(callback)
1066 else:
1068 else:
1067 callback()
1069 callback()
1068
1070
1069 def lock(self, wait=True):
1071 def lock(self, wait=True):
1070 '''Lock the repository store (.hg/store) and return a weak reference
1072 '''Lock the repository store (.hg/store) and return a weak reference
1071 to the lock. Use this before modifying the store (e.g. committing or
1073 to the lock. Use this before modifying the store (e.g. committing or
1072 stripping). If you are opening a transaction, get a lock as well.)'''
1074 stripping). If you are opening a transaction, get a lock as well.)'''
1073 l = self._lockref and self._lockref()
1075 l = self._lockref and self._lockref()
1074 if l is not None and l.held:
1076 if l is not None and l.held:
1075 l.lock()
1077 l.lock()
1076 return l
1078 return l
1077
1079
1078 def unlock():
1080 def unlock():
1079 if hasunfilteredcache(self, '_phasecache'):
1081 if hasunfilteredcache(self, '_phasecache'):
1080 self._phasecache.write()
1082 self._phasecache.write()
1081 for k, ce in self._filecache.items():
1083 for k, ce in self._filecache.items():
1082 if k == 'dirstate' or k not in self.__dict__:
1084 if k == 'dirstate' or k not in self.__dict__:
1083 continue
1085 continue
1084 ce.refresh()
1086 ce.refresh()
1085
1087
1086 l = self._lock(self.svfs, "lock", wait, unlock,
1088 l = self._lock(self.svfs, "lock", wait, unlock,
1087 self.invalidate, _('repository %s') % self.origroot)
1089 self.invalidate, _('repository %s') % self.origroot)
1088 self._lockref = weakref.ref(l)
1090 self._lockref = weakref.ref(l)
1089 return l
1091 return l
1090
1092
1091 def wlock(self, wait=True):
1093 def wlock(self, wait=True):
1092 '''Lock the non-store parts of the repository (everything under
1094 '''Lock the non-store parts of the repository (everything under
1093 .hg except .hg/store) and return a weak reference to the lock.
1095 .hg except .hg/store) and return a weak reference to the lock.
1094 Use this before modifying files in .hg.'''
1096 Use this before modifying files in .hg.'''
1095 l = self._wlockref and self._wlockref()
1097 l = self._wlockref and self._wlockref()
1096 if l is not None and l.held:
1098 if l is not None and l.held:
1097 l.lock()
1099 l.lock()
1098 return l
1100 return l
1099
1101
1100 def unlock():
1102 def unlock():
1101 self.dirstate.write()
1103 self.dirstate.write()
1102 self._filecache['dirstate'].refresh()
1104 self._filecache['dirstate'].refresh()
1103
1105
1104 l = self._lock(self.vfs, "wlock", wait, unlock,
1106 l = self._lock(self.vfs, "wlock", wait, unlock,
1105 self.invalidatedirstate, _('working directory of %s') %
1107 self.invalidatedirstate, _('working directory of %s') %
1106 self.origroot)
1108 self.origroot)
1107 self._wlockref = weakref.ref(l)
1109 self._wlockref = weakref.ref(l)
1108 return l
1110 return l
1109
1111
1110 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1112 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1111 """
1113 """
1112 commit an individual file as part of a larger transaction
1114 commit an individual file as part of a larger transaction
1113 """
1115 """
1114
1116
1115 fname = fctx.path()
1117 fname = fctx.path()
1116 text = fctx.data()
1118 text = fctx.data()
1117 flog = self.file(fname)
1119 flog = self.file(fname)
1118 fparent1 = manifest1.get(fname, nullid)
1120 fparent1 = manifest1.get(fname, nullid)
1119 fparent2 = fparent2o = manifest2.get(fname, nullid)
1121 fparent2 = fparent2o = manifest2.get(fname, nullid)
1120
1122
1121 meta = {}
1123 meta = {}
1122 copy = fctx.renamed()
1124 copy = fctx.renamed()
1123 if copy and copy[0] != fname:
1125 if copy and copy[0] != fname:
1124 # Mark the new revision of this file as a copy of another
1126 # Mark the new revision of this file as a copy of another
1125 # file. This copy data will effectively act as a parent
1127 # file. This copy data will effectively act as a parent
1126 # of this new revision. If this is a merge, the first
1128 # of this new revision. If this is a merge, the first
1127 # parent will be the nullid (meaning "look up the copy data")
1129 # parent will be the nullid (meaning "look up the copy data")
1128 # and the second one will be the other parent. For example:
1130 # and the second one will be the other parent. For example:
1129 #
1131 #
1130 # 0 --- 1 --- 3 rev1 changes file foo
1132 # 0 --- 1 --- 3 rev1 changes file foo
1131 # \ / rev2 renames foo to bar and changes it
1133 # \ / rev2 renames foo to bar and changes it
1132 # \- 2 -/ rev3 should have bar with all changes and
1134 # \- 2 -/ rev3 should have bar with all changes and
1133 # should record that bar descends from
1135 # should record that bar descends from
1134 # bar in rev2 and foo in rev1
1136 # bar in rev2 and foo in rev1
1135 #
1137 #
1136 # this allows this merge to succeed:
1138 # this allows this merge to succeed:
1137 #
1139 #
1138 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1140 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1139 # \ / merging rev3 and rev4 should use bar@rev2
1141 # \ / merging rev3 and rev4 should use bar@rev2
1140 # \- 2 --- 4 as the merge base
1142 # \- 2 --- 4 as the merge base
1141 #
1143 #
1142
1144
1143 cfname = copy[0]
1145 cfname = copy[0]
1144 crev = manifest1.get(cfname)
1146 crev = manifest1.get(cfname)
1145 newfparent = fparent2
1147 newfparent = fparent2
1146
1148
1147 if manifest2: # branch merge
1149 if manifest2: # branch merge
1148 if fparent2 == nullid or crev is None: # copied on remote side
1150 if fparent2 == nullid or crev is None: # copied on remote side
1149 if cfname in manifest2:
1151 if cfname in manifest2:
1150 crev = manifest2[cfname]
1152 crev = manifest2[cfname]
1151 newfparent = fparent1
1153 newfparent = fparent1
1152
1154
1153 # find source in nearest ancestor if we've lost track
1155 # find source in nearest ancestor if we've lost track
1154 if not crev:
1156 if not crev:
1155 self.ui.debug(" %s: searching for copy revision for %s\n" %
1157 self.ui.debug(" %s: searching for copy revision for %s\n" %
1156 (fname, cfname))
1158 (fname, cfname))
1157 for ancestor in self[None].ancestors():
1159 for ancestor in self[None].ancestors():
1158 if cfname in ancestor:
1160 if cfname in ancestor:
1159 crev = ancestor[cfname].filenode()
1161 crev = ancestor[cfname].filenode()
1160 break
1162 break
1161
1163
1162 if crev:
1164 if crev:
1163 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1165 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1164 meta["copy"] = cfname
1166 meta["copy"] = cfname
1165 meta["copyrev"] = hex(crev)
1167 meta["copyrev"] = hex(crev)
1166 fparent1, fparent2 = nullid, newfparent
1168 fparent1, fparent2 = nullid, newfparent
1167 else:
1169 else:
1168 self.ui.warn(_("warning: can't find ancestor for '%s' "
1170 self.ui.warn(_("warning: can't find ancestor for '%s' "
1169 "copied from '%s'!\n") % (fname, cfname))
1171 "copied from '%s'!\n") % (fname, cfname))
1170
1172
1171 elif fparent1 == nullid:
1173 elif fparent1 == nullid:
1172 fparent1, fparent2 = fparent2, nullid
1174 fparent1, fparent2 = fparent2, nullid
1173 elif fparent2 != nullid:
1175 elif fparent2 != nullid:
1174 # is one parent an ancestor of the other?
1176 # is one parent an ancestor of the other?
1175 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1177 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1176 if fparent1 in fparentancestors:
1178 if fparent1 in fparentancestors:
1177 fparent1, fparent2 = fparent2, nullid
1179 fparent1, fparent2 = fparent2, nullid
1178 elif fparent2 in fparentancestors:
1180 elif fparent2 in fparentancestors:
1179 fparent2 = nullid
1181 fparent2 = nullid
1180
1182
1181 # is the file changed?
1183 # is the file changed?
1182 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1184 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1183 changelist.append(fname)
1185 changelist.append(fname)
1184 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1186 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1185
1187
1186 # are just the flags changed during merge?
1188 # are just the flags changed during merge?
1187 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1189 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1188 changelist.append(fname)
1190 changelist.append(fname)
1189
1191
1190 return fparent1
1192 return fparent1
1191
1193
1192 @unfilteredmethod
1194 @unfilteredmethod
1193 def commit(self, text="", user=None, date=None, match=None, force=False,
1195 def commit(self, text="", user=None, date=None, match=None, force=False,
1194 editor=False, extra={}):
1196 editor=False, extra={}):
1195 """Add a new revision to current repository.
1197 """Add a new revision to current repository.
1196
1198
1197 Revision information is gathered from the working directory,
1199 Revision information is gathered from the working directory,
1198 match can be used to filter the committed files. If editor is
1200 match can be used to filter the committed files. If editor is
1199 supplied, it is called to get a commit message.
1201 supplied, it is called to get a commit message.
1200 """
1202 """
1201
1203
1202 def fail(f, msg):
1204 def fail(f, msg):
1203 raise util.Abort('%s: %s' % (f, msg))
1205 raise util.Abort('%s: %s' % (f, msg))
1204
1206
1205 if not match:
1207 if not match:
1206 match = matchmod.always(self.root, '')
1208 match = matchmod.always(self.root, '')
1207
1209
1208 if not force:
1210 if not force:
1209 vdirs = []
1211 vdirs = []
1210 match.explicitdir = vdirs.append
1212 match.explicitdir = vdirs.append
1211 match.bad = fail
1213 match.bad = fail
1212
1214
1213 wlock = self.wlock()
1215 wlock = self.wlock()
1214 try:
1216 try:
1215 wctx = self[None]
1217 wctx = self[None]
1216 merge = len(wctx.parents()) > 1
1218 merge = len(wctx.parents()) > 1
1217
1219
1218 if (not force and merge and match and
1220 if (not force and merge and match and
1219 (match.files() or match.anypats())):
1221 (match.files() or match.anypats())):
1220 raise util.Abort(_('cannot partially commit a merge '
1222 raise util.Abort(_('cannot partially commit a merge '
1221 '(do not specify files or patterns)'))
1223 '(do not specify files or patterns)'))
1222
1224
1223 changes = self.status(match=match, clean=force)
1225 changes = self.status(match=match, clean=force)
1224 if force:
1226 if force:
1225 changes[0].extend(changes[6]) # mq may commit unchanged files
1227 changes[0].extend(changes[6]) # mq may commit unchanged files
1226
1228
1227 # check subrepos
1229 # check subrepos
1228 subs = []
1230 subs = []
1229 commitsubs = set()
1231 commitsubs = set()
1230 newstate = wctx.substate.copy()
1232 newstate = wctx.substate.copy()
1231 # only manage subrepos and .hgsubstate if .hgsub is present
1233 # only manage subrepos and .hgsubstate if .hgsub is present
1232 if '.hgsub' in wctx:
1234 if '.hgsub' in wctx:
1233 # we'll decide whether to track this ourselves, thanks
1235 # we'll decide whether to track this ourselves, thanks
1234 for c in changes[:3]:
1236 for c in changes[:3]:
1235 if '.hgsubstate' in c:
1237 if '.hgsubstate' in c:
1236 c.remove('.hgsubstate')
1238 c.remove('.hgsubstate')
1237
1239
1238 # compare current state to last committed state
1240 # compare current state to last committed state
1239 # build new substate based on last committed state
1241 # build new substate based on last committed state
1240 oldstate = wctx.p1().substate
1242 oldstate = wctx.p1().substate
1241 for s in sorted(newstate.keys()):
1243 for s in sorted(newstate.keys()):
1242 if not match(s):
1244 if not match(s):
1243 # ignore working copy, use old state if present
1245 # ignore working copy, use old state if present
1244 if s in oldstate:
1246 if s in oldstate:
1245 newstate[s] = oldstate[s]
1247 newstate[s] = oldstate[s]
1246 continue
1248 continue
1247 if not force:
1249 if not force:
1248 raise util.Abort(
1250 raise util.Abort(
1249 _("commit with new subrepo %s excluded") % s)
1251 _("commit with new subrepo %s excluded") % s)
1250 if wctx.sub(s).dirty(True):
1252 if wctx.sub(s).dirty(True):
1251 if not self.ui.configbool('ui', 'commitsubrepos'):
1253 if not self.ui.configbool('ui', 'commitsubrepos'):
1252 raise util.Abort(
1254 raise util.Abort(
1253 _("uncommitted changes in subrepo %s") % s,
1255 _("uncommitted changes in subrepo %s") % s,
1254 hint=_("use --subrepos for recursive commit"))
1256 hint=_("use --subrepos for recursive commit"))
1255 subs.append(s)
1257 subs.append(s)
1256 commitsubs.add(s)
1258 commitsubs.add(s)
1257 else:
1259 else:
1258 bs = wctx.sub(s).basestate()
1260 bs = wctx.sub(s).basestate()
1259 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1261 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1260 if oldstate.get(s, (None, None, None))[1] != bs:
1262 if oldstate.get(s, (None, None, None))[1] != bs:
1261 subs.append(s)
1263 subs.append(s)
1262
1264
1263 # check for removed subrepos
1265 # check for removed subrepos
1264 for p in wctx.parents():
1266 for p in wctx.parents():
1265 r = [s for s in p.substate if s not in newstate]
1267 r = [s for s in p.substate if s not in newstate]
1266 subs += [s for s in r if match(s)]
1268 subs += [s for s in r if match(s)]
1267 if subs:
1269 if subs:
1268 if (not match('.hgsub') and
1270 if (not match('.hgsub') and
1269 '.hgsub' in (wctx.modified() + wctx.added())):
1271 '.hgsub' in (wctx.modified() + wctx.added())):
1270 raise util.Abort(
1272 raise util.Abort(
1271 _("can't commit subrepos without .hgsub"))
1273 _("can't commit subrepos without .hgsub"))
1272 changes[0].insert(0, '.hgsubstate')
1274 changes[0].insert(0, '.hgsubstate')
1273
1275
1274 elif '.hgsub' in changes[2]:
1276 elif '.hgsub' in changes[2]:
1275 # clean up .hgsubstate when .hgsub is removed
1277 # clean up .hgsubstate when .hgsub is removed
1276 if ('.hgsubstate' in wctx and
1278 if ('.hgsubstate' in wctx and
1277 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1279 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1278 changes[2].insert(0, '.hgsubstate')
1280 changes[2].insert(0, '.hgsubstate')
1279
1281
1280 # make sure all explicit patterns are matched
1282 # make sure all explicit patterns are matched
1281 if not force and match.files():
1283 if not force and match.files():
1282 matched = set(changes[0] + changes[1] + changes[2])
1284 matched = set(changes[0] + changes[1] + changes[2])
1283
1285
1284 for f in match.files():
1286 for f in match.files():
1285 f = self.dirstate.normalize(f)
1287 f = self.dirstate.normalize(f)
1286 if f == '.' or f in matched or f in wctx.substate:
1288 if f == '.' or f in matched or f in wctx.substate:
1287 continue
1289 continue
1288 if f in changes[3]: # missing
1290 if f in changes[3]: # missing
1289 fail(f, _('file not found!'))
1291 fail(f, _('file not found!'))
1290 if f in vdirs: # visited directory
1292 if f in vdirs: # visited directory
1291 d = f + '/'
1293 d = f + '/'
1292 for mf in matched:
1294 for mf in matched:
1293 if mf.startswith(d):
1295 if mf.startswith(d):
1294 break
1296 break
1295 else:
1297 else:
1296 fail(f, _("no match under directory!"))
1298 fail(f, _("no match under directory!"))
1297 elif f not in self.dirstate:
1299 elif f not in self.dirstate:
1298 fail(f, _("file not tracked!"))
1300 fail(f, _("file not tracked!"))
1299
1301
1300 cctx = context.workingctx(self, text, user, date, extra, changes)
1302 cctx = context.workingctx(self, text, user, date, extra, changes)
1301
1303
1302 if (not force and not extra.get("close") and not merge
1304 if (not force and not extra.get("close") and not merge
1303 and not cctx.files()
1305 and not cctx.files()
1304 and wctx.branch() == wctx.p1().branch()):
1306 and wctx.branch() == wctx.p1().branch()):
1305 return None
1307 return None
1306
1308
1307 if merge and cctx.deleted():
1309 if merge and cctx.deleted():
1308 raise util.Abort(_("cannot commit merge with missing files"))
1310 raise util.Abort(_("cannot commit merge with missing files"))
1309
1311
1310 ms = mergemod.mergestate(self)
1312 ms = mergemod.mergestate(self)
1311 for f in changes[0]:
1313 for f in changes[0]:
1312 if f in ms and ms[f] == 'u':
1314 if f in ms and ms[f] == 'u':
1313 raise util.Abort(_("unresolved merge conflicts "
1315 raise util.Abort(_("unresolved merge conflicts "
1314 "(see hg help resolve)"))
1316 "(see hg help resolve)"))
1315
1317
1316 if editor:
1318 if editor:
1317 cctx._text = editor(self, cctx, subs)
1319 cctx._text = editor(self, cctx, subs)
1318 edited = (text != cctx._text)
1320 edited = (text != cctx._text)
1319
1321
1320 # Save commit message in case this transaction gets rolled back
1322 # Save commit message in case this transaction gets rolled back
1321 # (e.g. by a pretxncommit hook). Leave the content alone on
1323 # (e.g. by a pretxncommit hook). Leave the content alone on
1322 # the assumption that the user will use the same editor again.
1324 # the assumption that the user will use the same editor again.
1323 msgfn = self.savecommitmessage(cctx._text)
1325 msgfn = self.savecommitmessage(cctx._text)
1324
1326
1325 # commit subs and write new state
1327 # commit subs and write new state
1326 if subs:
1328 if subs:
1327 for s in sorted(commitsubs):
1329 for s in sorted(commitsubs):
1328 sub = wctx.sub(s)
1330 sub = wctx.sub(s)
1329 self.ui.status(_('committing subrepository %s\n') %
1331 self.ui.status(_('committing subrepository %s\n') %
1330 subrepo.subrelpath(sub))
1332 subrepo.subrelpath(sub))
1331 sr = sub.commit(cctx._text, user, date)
1333 sr = sub.commit(cctx._text, user, date)
1332 newstate[s] = (newstate[s][0], sr)
1334 newstate[s] = (newstate[s][0], sr)
1333 subrepo.writestate(self, newstate)
1335 subrepo.writestate(self, newstate)
1334
1336
1335 p1, p2 = self.dirstate.parents()
1337 p1, p2 = self.dirstate.parents()
1336 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1338 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1337 try:
1339 try:
1338 self.hook("precommit", throw=True, parent1=hookp1,
1340 self.hook("precommit", throw=True, parent1=hookp1,
1339 parent2=hookp2)
1341 parent2=hookp2)
1340 ret = self.commitctx(cctx, True)
1342 ret = self.commitctx(cctx, True)
1341 except: # re-raises
1343 except: # re-raises
1342 if edited:
1344 if edited:
1343 self.ui.write(
1345 self.ui.write(
1344 _('note: commit message saved in %s\n') % msgfn)
1346 _('note: commit message saved in %s\n') % msgfn)
1345 raise
1347 raise
1346
1348
1347 # update bookmarks, dirstate and mergestate
1349 # update bookmarks, dirstate and mergestate
1348 bookmarks.update(self, [p1, p2], ret)
1350 bookmarks.update(self, [p1, p2], ret)
1349 cctx.markcommitted(ret)
1351 cctx.markcommitted(ret)
1350 ms.reset()
1352 ms.reset()
1351 finally:
1353 finally:
1352 wlock.release()
1354 wlock.release()
1353
1355
1354 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1356 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1355 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1357 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1356 self._afterlock(commithook)
1358 self._afterlock(commithook)
1357 return ret
1359 return ret
1358
1360
1359 @unfilteredmethod
1361 @unfilteredmethod
1360 def commitctx(self, ctx, error=False):
1362 def commitctx(self, ctx, error=False):
1361 """Add a new revision to current repository.
1363 """Add a new revision to current repository.
1362 Revision information is passed via the context argument.
1364 Revision information is passed via the context argument.
1363 """
1365 """
1364
1366
1365 tr = lock = None
1367 tr = lock = None
1366 removed = list(ctx.removed())
1368 removed = list(ctx.removed())
1367 p1, p2 = ctx.p1(), ctx.p2()
1369 p1, p2 = ctx.p1(), ctx.p2()
1368 user = ctx.user()
1370 user = ctx.user()
1369
1371
1370 lock = self.lock()
1372 lock = self.lock()
1371 try:
1373 try:
1372 tr = self.transaction("commit")
1374 tr = self.transaction("commit")
1373 trp = weakref.proxy(tr)
1375 trp = weakref.proxy(tr)
1374
1376
1375 if ctx.files():
1377 if ctx.files():
1376 m1 = p1.manifest().copy()
1378 m1 = p1.manifest().copy()
1377 m2 = p2.manifest()
1379 m2 = p2.manifest()
1378
1380
1379 # check in files
1381 # check in files
1380 new = {}
1382 new = {}
1381 changed = []
1383 changed = []
1382 linkrev = len(self)
1384 linkrev = len(self)
1383 for f in sorted(ctx.modified() + ctx.added()):
1385 for f in sorted(ctx.modified() + ctx.added()):
1384 self.ui.note(f + "\n")
1386 self.ui.note(f + "\n")
1385 try:
1387 try:
1386 fctx = ctx[f]
1388 fctx = ctx[f]
1387 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1389 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1388 changed)
1390 changed)
1389 m1.set(f, fctx.flags())
1391 m1.set(f, fctx.flags())
1390 except OSError, inst:
1392 except OSError, inst:
1391 self.ui.warn(_("trouble committing %s!\n") % f)
1393 self.ui.warn(_("trouble committing %s!\n") % f)
1392 raise
1394 raise
1393 except IOError, inst:
1395 except IOError, inst:
1394 errcode = getattr(inst, 'errno', errno.ENOENT)
1396 errcode = getattr(inst, 'errno', errno.ENOENT)
1395 if error or errcode and errcode != errno.ENOENT:
1397 if error or errcode and errcode != errno.ENOENT:
1396 self.ui.warn(_("trouble committing %s!\n") % f)
1398 self.ui.warn(_("trouble committing %s!\n") % f)
1397 raise
1399 raise
1398 else:
1400 else:
1399 removed.append(f)
1401 removed.append(f)
1400
1402
1401 # update manifest
1403 # update manifest
1402 m1.update(new)
1404 m1.update(new)
1403 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1405 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1404 drop = [f for f in removed if f in m1]
1406 drop = [f for f in removed if f in m1]
1405 for f in drop:
1407 for f in drop:
1406 del m1[f]
1408 del m1[f]
1407 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1409 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1408 p2.manifestnode(), (new, drop))
1410 p2.manifestnode(), (new, drop))
1409 files = changed + removed
1411 files = changed + removed
1410 else:
1412 else:
1411 mn = p1.manifestnode()
1413 mn = p1.manifestnode()
1412 files = []
1414 files = []
1413
1415
1414 # update changelog
1416 # update changelog
1415 self.changelog.delayupdate()
1417 self.changelog.delayupdate()
1416 n = self.changelog.add(mn, files, ctx.description(),
1418 n = self.changelog.add(mn, files, ctx.description(),
1417 trp, p1.node(), p2.node(),
1419 trp, p1.node(), p2.node(),
1418 user, ctx.date(), ctx.extra().copy())
1420 user, ctx.date(), ctx.extra().copy())
1419 p = lambda: self.changelog.writepending() and self.root or ""
1421 p = lambda: self.changelog.writepending() and self.root or ""
1420 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1422 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1421 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1423 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1422 parent2=xp2, pending=p)
1424 parent2=xp2, pending=p)
1423 self.changelog.finalize(trp)
1425 self.changelog.finalize(trp)
1424 # set the new commit is proper phase
1426 # set the new commit is proper phase
1425 targetphase = subrepo.newcommitphase(self.ui, ctx)
1427 targetphase = subrepo.newcommitphase(self.ui, ctx)
1426 if targetphase:
1428 if targetphase:
1427 # retract boundary do not alter parent changeset.
1429 # retract boundary do not alter parent changeset.
1428 # if a parent have higher the resulting phase will
1430 # if a parent have higher the resulting phase will
1429 # be compliant anyway
1431 # be compliant anyway
1430 #
1432 #
1431 # if minimal phase was 0 we don't need to retract anything
1433 # if minimal phase was 0 we don't need to retract anything
1432 phases.retractboundary(self, targetphase, [n])
1434 phases.retractboundary(self, targetphase, [n])
1433 tr.close()
1435 tr.close()
1434 branchmap.updatecache(self.filtered('served'))
1436 branchmap.updatecache(self.filtered('served'))
1435 return n
1437 return n
1436 finally:
1438 finally:
1437 if tr:
1439 if tr:
1438 tr.release()
1440 tr.release()
1439 lock.release()
1441 lock.release()
1440
1442
1441 @unfilteredmethod
1443 @unfilteredmethod
1442 def destroying(self):
1444 def destroying(self):
1443 '''Inform the repository that nodes are about to be destroyed.
1445 '''Inform the repository that nodes are about to be destroyed.
1444 Intended for use by strip and rollback, so there's a common
1446 Intended for use by strip and rollback, so there's a common
1445 place for anything that has to be done before destroying history.
1447 place for anything that has to be done before destroying history.
1446
1448
1447 This is mostly useful for saving state that is in memory and waiting
1449 This is mostly useful for saving state that is in memory and waiting
1448 to be flushed when the current lock is released. Because a call to
1450 to be flushed when the current lock is released. Because a call to
1449 destroyed is imminent, the repo will be invalidated causing those
1451 destroyed is imminent, the repo will be invalidated causing those
1450 changes to stay in memory (waiting for the next unlock), or vanish
1452 changes to stay in memory (waiting for the next unlock), or vanish
1451 completely.
1453 completely.
1452 '''
1454 '''
1453 # When using the same lock to commit and strip, the phasecache is left
1455 # When using the same lock to commit and strip, the phasecache is left
1454 # dirty after committing. Then when we strip, the repo is invalidated,
1456 # dirty after committing. Then when we strip, the repo is invalidated,
1455 # causing those changes to disappear.
1457 # causing those changes to disappear.
1456 if '_phasecache' in vars(self):
1458 if '_phasecache' in vars(self):
1457 self._phasecache.write()
1459 self._phasecache.write()
1458
1460
1459 @unfilteredmethod
1461 @unfilteredmethod
1460 def destroyed(self):
1462 def destroyed(self):
1461 '''Inform the repository that nodes have been destroyed.
1463 '''Inform the repository that nodes have been destroyed.
1462 Intended for use by strip and rollback, so there's a common
1464 Intended for use by strip and rollback, so there's a common
1463 place for anything that has to be done after destroying history.
1465 place for anything that has to be done after destroying history.
1464 '''
1466 '''
1465 # When one tries to:
1467 # When one tries to:
1466 # 1) destroy nodes thus calling this method (e.g. strip)
1468 # 1) destroy nodes thus calling this method (e.g. strip)
1467 # 2) use phasecache somewhere (e.g. commit)
1469 # 2) use phasecache somewhere (e.g. commit)
1468 #
1470 #
1469 # then 2) will fail because the phasecache contains nodes that were
1471 # then 2) will fail because the phasecache contains nodes that were
1470 # removed. We can either remove phasecache from the filecache,
1472 # removed. We can either remove phasecache from the filecache,
1471 # causing it to reload next time it is accessed, or simply filter
1473 # causing it to reload next time it is accessed, or simply filter
1472 # the removed nodes now and write the updated cache.
1474 # the removed nodes now and write the updated cache.
1473 self._phasecache.filterunknown(self)
1475 self._phasecache.filterunknown(self)
1474 self._phasecache.write()
1476 self._phasecache.write()
1475
1477
1476 # update the 'served' branch cache to help read only server process
1478 # update the 'served' branch cache to help read only server process
1477 # Thanks to branchcache collaboration this is done from the nearest
1479 # Thanks to branchcache collaboration this is done from the nearest
1478 # filtered subset and it is expected to be fast.
1480 # filtered subset and it is expected to be fast.
1479 branchmap.updatecache(self.filtered('served'))
1481 branchmap.updatecache(self.filtered('served'))
1480
1482
1481 # Ensure the persistent tag cache is updated. Doing it now
1483 # Ensure the persistent tag cache is updated. Doing it now
1482 # means that the tag cache only has to worry about destroyed
1484 # means that the tag cache only has to worry about destroyed
1483 # heads immediately after a strip/rollback. That in turn
1485 # heads immediately after a strip/rollback. That in turn
1484 # guarantees that "cachetip == currenttip" (comparing both rev
1486 # guarantees that "cachetip == currenttip" (comparing both rev
1485 # and node) always means no nodes have been added or destroyed.
1487 # and node) always means no nodes have been added or destroyed.
1486
1488
1487 # XXX this is suboptimal when qrefresh'ing: we strip the current
1489 # XXX this is suboptimal when qrefresh'ing: we strip the current
1488 # head, refresh the tag cache, then immediately add a new head.
1490 # head, refresh the tag cache, then immediately add a new head.
1489 # But I think doing it this way is necessary for the "instant
1491 # But I think doing it this way is necessary for the "instant
1490 # tag cache retrieval" case to work.
1492 # tag cache retrieval" case to work.
1491 self.invalidate()
1493 self.invalidate()
1492
1494
1493 def walk(self, match, node=None):
1495 def walk(self, match, node=None):
1494 '''
1496 '''
1495 walk recursively through the directory tree or a given
1497 walk recursively through the directory tree or a given
1496 changeset, finding all files matched by the match
1498 changeset, finding all files matched by the match
1497 function
1499 function
1498 '''
1500 '''
1499 return self[node].walk(match)
1501 return self[node].walk(match)
1500
1502
1501 def status(self, node1='.', node2=None, match=None,
1503 def status(self, node1='.', node2=None, match=None,
1502 ignored=False, clean=False, unknown=False,
1504 ignored=False, clean=False, unknown=False,
1503 listsubrepos=False):
1505 listsubrepos=False):
1504 """return status of files between two nodes or node and working
1506 """return status of files between two nodes or node and working
1505 directory.
1507 directory.
1506
1508
1507 If node1 is None, use the first dirstate parent instead.
1509 If node1 is None, use the first dirstate parent instead.
1508 If node2 is None, compare node1 with working directory.
1510 If node2 is None, compare node1 with working directory.
1509 """
1511 """
1510
1512
1511 def mfmatches(ctx):
1513 def mfmatches(ctx):
1512 mf = ctx.manifest().copy()
1514 mf = ctx.manifest().copy()
1513 if match.always():
1515 if match.always():
1514 return mf
1516 return mf
1515 for fn in mf.keys():
1517 for fn in mf.keys():
1516 if not match(fn):
1518 if not match(fn):
1517 del mf[fn]
1519 del mf[fn]
1518 return mf
1520 return mf
1519
1521
1520 ctx1 = self[node1]
1522 ctx1 = self[node1]
1521 ctx2 = self[node2]
1523 ctx2 = self[node2]
1522
1524
1523 working = ctx2.rev() is None
1525 working = ctx2.rev() is None
1524 parentworking = working and ctx1 == self['.']
1526 parentworking = working and ctx1 == self['.']
1525 match = match or matchmod.always(self.root, self.getcwd())
1527 match = match or matchmod.always(self.root, self.getcwd())
1526 listignored, listclean, listunknown = ignored, clean, unknown
1528 listignored, listclean, listunknown = ignored, clean, unknown
1527
1529
1528 # load earliest manifest first for caching reasons
1530 # load earliest manifest first for caching reasons
1529 if not working and ctx2.rev() < ctx1.rev():
1531 if not working and ctx2.rev() < ctx1.rev():
1530 ctx2.manifest()
1532 ctx2.manifest()
1531
1533
1532 if not parentworking:
1534 if not parentworking:
1533 def bad(f, msg):
1535 def bad(f, msg):
1534 # 'f' may be a directory pattern from 'match.files()',
1536 # 'f' may be a directory pattern from 'match.files()',
1535 # so 'f not in ctx1' is not enough
1537 # so 'f not in ctx1' is not enough
1536 if f not in ctx1 and f not in ctx1.dirs():
1538 if f not in ctx1 and f not in ctx1.dirs():
1537 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1539 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1538 match.bad = bad
1540 match.bad = bad
1539
1541
1540 if working: # we need to scan the working dir
1542 if working: # we need to scan the working dir
1541 subrepos = []
1543 subrepos = []
1542 if '.hgsub' in self.dirstate:
1544 if '.hgsub' in self.dirstate:
1543 subrepos = sorted(ctx2.substate)
1545 subrepos = sorted(ctx2.substate)
1544 s = self.dirstate.status(match, subrepos, listignored,
1546 s = self.dirstate.status(match, subrepos, listignored,
1545 listclean, listunknown)
1547 listclean, listunknown)
1546 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1548 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1547
1549
1548 # check for any possibly clean files
1550 # check for any possibly clean files
1549 if parentworking and cmp:
1551 if parentworking and cmp:
1550 fixup = []
1552 fixup = []
1551 # do a full compare of any files that might have changed
1553 # do a full compare of any files that might have changed
1552 for f in sorted(cmp):
1554 for f in sorted(cmp):
1553 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1555 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1554 or ctx1[f].cmp(ctx2[f])):
1556 or ctx1[f].cmp(ctx2[f])):
1555 modified.append(f)
1557 modified.append(f)
1556 else:
1558 else:
1557 fixup.append(f)
1559 fixup.append(f)
1558
1560
1559 # update dirstate for files that are actually clean
1561 # update dirstate for files that are actually clean
1560 if fixup:
1562 if fixup:
1561 if listclean:
1563 if listclean:
1562 clean += fixup
1564 clean += fixup
1563
1565
1564 try:
1566 try:
1565 # updating the dirstate is optional
1567 # updating the dirstate is optional
1566 # so we don't wait on the lock
1568 # so we don't wait on the lock
1567 wlock = self.wlock(False)
1569 wlock = self.wlock(False)
1568 try:
1570 try:
1569 for f in fixup:
1571 for f in fixup:
1570 self.dirstate.normal(f)
1572 self.dirstate.normal(f)
1571 finally:
1573 finally:
1572 wlock.release()
1574 wlock.release()
1573 except error.LockError:
1575 except error.LockError:
1574 pass
1576 pass
1575
1577
1576 if not parentworking:
1578 if not parentworking:
1577 mf1 = mfmatches(ctx1)
1579 mf1 = mfmatches(ctx1)
1578 if working:
1580 if working:
1579 # we are comparing working dir against non-parent
1581 # we are comparing working dir against non-parent
1580 # generate a pseudo-manifest for the working dir
1582 # generate a pseudo-manifest for the working dir
1581 mf2 = mfmatches(self['.'])
1583 mf2 = mfmatches(self['.'])
1582 for f in cmp + modified + added:
1584 for f in cmp + modified + added:
1583 mf2[f] = None
1585 mf2[f] = None
1584 mf2.set(f, ctx2.flags(f))
1586 mf2.set(f, ctx2.flags(f))
1585 for f in removed:
1587 for f in removed:
1586 if f in mf2:
1588 if f in mf2:
1587 del mf2[f]
1589 del mf2[f]
1588 else:
1590 else:
1589 # we are comparing two revisions
1591 # we are comparing two revisions
1590 deleted, unknown, ignored = [], [], []
1592 deleted, unknown, ignored = [], [], []
1591 mf2 = mfmatches(ctx2)
1593 mf2 = mfmatches(ctx2)
1592
1594
1593 modified, added, clean = [], [], []
1595 modified, added, clean = [], [], []
1594 withflags = mf1.withflags() | mf2.withflags()
1596 withflags = mf1.withflags() | mf2.withflags()
1595 for fn, mf2node in mf2.iteritems():
1597 for fn, mf2node in mf2.iteritems():
1596 if fn in mf1:
1598 if fn in mf1:
1597 if (fn not in deleted and
1599 if (fn not in deleted and
1598 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1600 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1599 (mf1[fn] != mf2node and
1601 (mf1[fn] != mf2node and
1600 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1602 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1601 modified.append(fn)
1603 modified.append(fn)
1602 elif listclean:
1604 elif listclean:
1603 clean.append(fn)
1605 clean.append(fn)
1604 del mf1[fn]
1606 del mf1[fn]
1605 elif fn not in deleted:
1607 elif fn not in deleted:
1606 added.append(fn)
1608 added.append(fn)
1607 removed = mf1.keys()
1609 removed = mf1.keys()
1608
1610
1609 if working and modified and not self.dirstate._checklink:
1611 if working and modified and not self.dirstate._checklink:
1610 # Symlink placeholders may get non-symlink-like contents
1612 # Symlink placeholders may get non-symlink-like contents
1611 # via user error or dereferencing by NFS or Samba servers,
1613 # via user error or dereferencing by NFS or Samba servers,
1612 # so we filter out any placeholders that don't look like a
1614 # so we filter out any placeholders that don't look like a
1613 # symlink
1615 # symlink
1614 sane = []
1616 sane = []
1615 for f in modified:
1617 for f in modified:
1616 if ctx2.flags(f) == 'l':
1618 if ctx2.flags(f) == 'l':
1617 d = ctx2[f].data()
1619 d = ctx2[f].data()
1618 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1620 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1619 self.ui.debug('ignoring suspect symlink placeholder'
1621 self.ui.debug('ignoring suspect symlink placeholder'
1620 ' "%s"\n' % f)
1622 ' "%s"\n' % f)
1621 continue
1623 continue
1622 sane.append(f)
1624 sane.append(f)
1623 modified = sane
1625 modified = sane
1624
1626
1625 r = modified, added, removed, deleted, unknown, ignored, clean
1627 r = modified, added, removed, deleted, unknown, ignored, clean
1626
1628
1627 if listsubrepos:
1629 if listsubrepos:
1628 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1630 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1629 if working:
1631 if working:
1630 rev2 = None
1632 rev2 = None
1631 else:
1633 else:
1632 rev2 = ctx2.substate[subpath][1]
1634 rev2 = ctx2.substate[subpath][1]
1633 try:
1635 try:
1634 submatch = matchmod.narrowmatcher(subpath, match)
1636 submatch = matchmod.narrowmatcher(subpath, match)
1635 s = sub.status(rev2, match=submatch, ignored=listignored,
1637 s = sub.status(rev2, match=submatch, ignored=listignored,
1636 clean=listclean, unknown=listunknown,
1638 clean=listclean, unknown=listunknown,
1637 listsubrepos=True)
1639 listsubrepos=True)
1638 for rfiles, sfiles in zip(r, s):
1640 for rfiles, sfiles in zip(r, s):
1639 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1641 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1640 except error.LookupError:
1642 except error.LookupError:
1641 self.ui.status(_("skipping missing subrepository: %s\n")
1643 self.ui.status(_("skipping missing subrepository: %s\n")
1642 % subpath)
1644 % subpath)
1643
1645
1644 for l in r:
1646 for l in r:
1645 l.sort()
1647 l.sort()
1646 return r
1648 return r
1647
1649
1648 def heads(self, start=None):
1650 def heads(self, start=None):
1649 heads = self.changelog.heads(start)
1651 heads = self.changelog.heads(start)
1650 # sort the output in rev descending order
1652 # sort the output in rev descending order
1651 return sorted(heads, key=self.changelog.rev, reverse=True)
1653 return sorted(heads, key=self.changelog.rev, reverse=True)
1652
1654
1653 def branchheads(self, branch=None, start=None, closed=False):
1655 def branchheads(self, branch=None, start=None, closed=False):
1654 '''return a (possibly filtered) list of heads for the given branch
1656 '''return a (possibly filtered) list of heads for the given branch
1655
1657
1656 Heads are returned in topological order, from newest to oldest.
1658 Heads are returned in topological order, from newest to oldest.
1657 If branch is None, use the dirstate branch.
1659 If branch is None, use the dirstate branch.
1658 If start is not None, return only heads reachable from start.
1660 If start is not None, return only heads reachable from start.
1659 If closed is True, return heads that are marked as closed as well.
1661 If closed is True, return heads that are marked as closed as well.
1660 '''
1662 '''
1661 if branch is None:
1663 if branch is None:
1662 branch = self[None].branch()
1664 branch = self[None].branch()
1663 branches = self.branchmap()
1665 branches = self.branchmap()
1664 if branch not in branches:
1666 if branch not in branches:
1665 return []
1667 return []
1666 # the cache returns heads ordered lowest to highest
1668 # the cache returns heads ordered lowest to highest
1667 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1669 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1668 if start is not None:
1670 if start is not None:
1669 # filter out the heads that cannot be reached from startrev
1671 # filter out the heads that cannot be reached from startrev
1670 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1672 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1671 bheads = [h for h in bheads if h in fbheads]
1673 bheads = [h for h in bheads if h in fbheads]
1672 return bheads
1674 return bheads
1673
1675
1674 def branches(self, nodes):
1676 def branches(self, nodes):
1675 if not nodes:
1677 if not nodes:
1676 nodes = [self.changelog.tip()]
1678 nodes = [self.changelog.tip()]
1677 b = []
1679 b = []
1678 for n in nodes:
1680 for n in nodes:
1679 t = n
1681 t = n
1680 while True:
1682 while True:
1681 p = self.changelog.parents(n)
1683 p = self.changelog.parents(n)
1682 if p[1] != nullid or p[0] == nullid:
1684 if p[1] != nullid or p[0] == nullid:
1683 b.append((t, n, p[0], p[1]))
1685 b.append((t, n, p[0], p[1]))
1684 break
1686 break
1685 n = p[0]
1687 n = p[0]
1686 return b
1688 return b
1687
1689
1688 def between(self, pairs):
1690 def between(self, pairs):
1689 r = []
1691 r = []
1690
1692
1691 for top, bottom in pairs:
1693 for top, bottom in pairs:
1692 n, l, i = top, [], 0
1694 n, l, i = top, [], 0
1693 f = 1
1695 f = 1
1694
1696
1695 while n != bottom and n != nullid:
1697 while n != bottom and n != nullid:
1696 p = self.changelog.parents(n)[0]
1698 p = self.changelog.parents(n)[0]
1697 if i == f:
1699 if i == f:
1698 l.append(n)
1700 l.append(n)
1699 f = f * 2
1701 f = f * 2
1700 n = p
1702 n = p
1701 i += 1
1703 i += 1
1702
1704
1703 r.append(l)
1705 r.append(l)
1704
1706
1705 return r
1707 return r
1706
1708
1707 def pull(self, remote, heads=None, force=False):
1709 def pull(self, remote, heads=None, force=False):
1708 return exchange.pull (self, remote, heads, force)
1710 return exchange.pull (self, remote, heads, force)
1709
1711
1710 def checkpush(self, pushop):
1712 def checkpush(self, pushop):
1711 """Extensions can override this function if additional checks have
1713 """Extensions can override this function if additional checks have
1712 to be performed before pushing, or call it if they override push
1714 to be performed before pushing, or call it if they override push
1713 command.
1715 command.
1714 """
1716 """
1715 pass
1717 pass
1716
1718
1717 @unfilteredpropertycache
1719 @unfilteredpropertycache
1718 def prepushoutgoinghooks(self):
1720 def prepushoutgoinghooks(self):
1719 """Return util.hooks consists of "(repo, remote, outgoing)"
1721 """Return util.hooks consists of "(repo, remote, outgoing)"
1720 functions, which are called before pushing changesets.
1722 functions, which are called before pushing changesets.
1721 """
1723 """
1722 return util.hooks()
1724 return util.hooks()
1723
1725
1724 def push(self, remote, force=False, revs=None, newbranch=False):
1726 def push(self, remote, force=False, revs=None, newbranch=False):
1725 return exchange.push(self, remote, force, revs, newbranch)
1727 return exchange.push(self, remote, force, revs, newbranch)
1726
1728
1727 def stream_in(self, remote, requirements):
1729 def stream_in(self, remote, requirements):
1728 lock = self.lock()
1730 lock = self.lock()
1729 try:
1731 try:
1730 # Save remote branchmap. We will use it later
1732 # Save remote branchmap. We will use it later
1731 # to speed up branchcache creation
1733 # to speed up branchcache creation
1732 rbranchmap = None
1734 rbranchmap = None
1733 if remote.capable("branchmap"):
1735 if remote.capable("branchmap"):
1734 rbranchmap = remote.branchmap()
1736 rbranchmap = remote.branchmap()
1735
1737
1736 fp = remote.stream_out()
1738 fp = remote.stream_out()
1737 l = fp.readline()
1739 l = fp.readline()
1738 try:
1740 try:
1739 resp = int(l)
1741 resp = int(l)
1740 except ValueError:
1742 except ValueError:
1741 raise error.ResponseError(
1743 raise error.ResponseError(
1742 _('unexpected response from remote server:'), l)
1744 _('unexpected response from remote server:'), l)
1743 if resp == 1:
1745 if resp == 1:
1744 raise util.Abort(_('operation forbidden by server'))
1746 raise util.Abort(_('operation forbidden by server'))
1745 elif resp == 2:
1747 elif resp == 2:
1746 raise util.Abort(_('locking the remote repository failed'))
1748 raise util.Abort(_('locking the remote repository failed'))
1747 elif resp != 0:
1749 elif resp != 0:
1748 raise util.Abort(_('the server sent an unknown error code'))
1750 raise util.Abort(_('the server sent an unknown error code'))
1749 self.ui.status(_('streaming all changes\n'))
1751 self.ui.status(_('streaming all changes\n'))
1750 l = fp.readline()
1752 l = fp.readline()
1751 try:
1753 try:
1752 total_files, total_bytes = map(int, l.split(' ', 1))
1754 total_files, total_bytes = map(int, l.split(' ', 1))
1753 except (ValueError, TypeError):
1755 except (ValueError, TypeError):
1754 raise error.ResponseError(
1756 raise error.ResponseError(
1755 _('unexpected response from remote server:'), l)
1757 _('unexpected response from remote server:'), l)
1756 self.ui.status(_('%d files to transfer, %s of data\n') %
1758 self.ui.status(_('%d files to transfer, %s of data\n') %
1757 (total_files, util.bytecount(total_bytes)))
1759 (total_files, util.bytecount(total_bytes)))
1758 handled_bytes = 0
1760 handled_bytes = 0
1759 self.ui.progress(_('clone'), 0, total=total_bytes)
1761 self.ui.progress(_('clone'), 0, total=total_bytes)
1760 start = time.time()
1762 start = time.time()
1761
1763
1762 tr = self.transaction(_('clone'))
1764 tr = self.transaction(_('clone'))
1763 try:
1765 try:
1764 for i in xrange(total_files):
1766 for i in xrange(total_files):
1765 # XXX doesn't support '\n' or '\r' in filenames
1767 # XXX doesn't support '\n' or '\r' in filenames
1766 l = fp.readline()
1768 l = fp.readline()
1767 try:
1769 try:
1768 name, size = l.split('\0', 1)
1770 name, size = l.split('\0', 1)
1769 size = int(size)
1771 size = int(size)
1770 except (ValueError, TypeError):
1772 except (ValueError, TypeError):
1771 raise error.ResponseError(
1773 raise error.ResponseError(
1772 _('unexpected response from remote server:'), l)
1774 _('unexpected response from remote server:'), l)
1773 if self.ui.debugflag:
1775 if self.ui.debugflag:
1774 self.ui.debug('adding %s (%s)\n' %
1776 self.ui.debug('adding %s (%s)\n' %
1775 (name, util.bytecount(size)))
1777 (name, util.bytecount(size)))
1776 # for backwards compat, name was partially encoded
1778 # for backwards compat, name was partially encoded
1777 ofp = self.sopener(store.decodedir(name), 'w')
1779 ofp = self.sopener(store.decodedir(name), 'w')
1778 for chunk in util.filechunkiter(fp, limit=size):
1780 for chunk in util.filechunkiter(fp, limit=size):
1779 handled_bytes += len(chunk)
1781 handled_bytes += len(chunk)
1780 self.ui.progress(_('clone'), handled_bytes,
1782 self.ui.progress(_('clone'), handled_bytes,
1781 total=total_bytes)
1783 total=total_bytes)
1782 ofp.write(chunk)
1784 ofp.write(chunk)
1783 ofp.close()
1785 ofp.close()
1784 tr.close()
1786 tr.close()
1785 finally:
1787 finally:
1786 tr.release()
1788 tr.release()
1787
1789
1788 # Writing straight to files circumvented the inmemory caches
1790 # Writing straight to files circumvented the inmemory caches
1789 self.invalidate()
1791 self.invalidate()
1790
1792
1791 elapsed = time.time() - start
1793 elapsed = time.time() - start
1792 if elapsed <= 0:
1794 if elapsed <= 0:
1793 elapsed = 0.001
1795 elapsed = 0.001
1794 self.ui.progress(_('clone'), None)
1796 self.ui.progress(_('clone'), None)
1795 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1797 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1796 (util.bytecount(total_bytes), elapsed,
1798 (util.bytecount(total_bytes), elapsed,
1797 util.bytecount(total_bytes / elapsed)))
1799 util.bytecount(total_bytes / elapsed)))
1798
1800
1799 # new requirements = old non-format requirements +
1801 # new requirements = old non-format requirements +
1800 # new format-related
1802 # new format-related
1801 # requirements from the streamed-in repository
1803 # requirements from the streamed-in repository
1802 requirements.update(set(self.requirements) - self.supportedformats)
1804 requirements.update(set(self.requirements) - self.supportedformats)
1803 self._applyrequirements(requirements)
1805 self._applyrequirements(requirements)
1804 self._writerequirements()
1806 self._writerequirements()
1805
1807
1806 if rbranchmap:
1808 if rbranchmap:
1807 rbheads = []
1809 rbheads = []
1808 for bheads in rbranchmap.itervalues():
1810 for bheads in rbranchmap.itervalues():
1809 rbheads.extend(bheads)
1811 rbheads.extend(bheads)
1810
1812
1811 if rbheads:
1813 if rbheads:
1812 rtiprev = max((int(self.changelog.rev(node))
1814 rtiprev = max((int(self.changelog.rev(node))
1813 for node in rbheads))
1815 for node in rbheads))
1814 cache = branchmap.branchcache(rbranchmap,
1816 cache = branchmap.branchcache(rbranchmap,
1815 self[rtiprev].node(),
1817 self[rtiprev].node(),
1816 rtiprev)
1818 rtiprev)
1817 # Try to stick it as low as possible
1819 # Try to stick it as low as possible
1818 # filter above served are unlikely to be fetch from a clone
1820 # filter above served are unlikely to be fetch from a clone
1819 for candidate in ('base', 'immutable', 'served'):
1821 for candidate in ('base', 'immutable', 'served'):
1820 rview = self.filtered(candidate)
1822 rview = self.filtered(candidate)
1821 if cache.validfor(rview):
1823 if cache.validfor(rview):
1822 self._branchcaches[candidate] = cache
1824 self._branchcaches[candidate] = cache
1823 cache.write(rview)
1825 cache.write(rview)
1824 break
1826 break
1825 self.invalidate()
1827 self.invalidate()
1826 return len(self.heads()) + 1
1828 return len(self.heads()) + 1
1827 finally:
1829 finally:
1828 lock.release()
1830 lock.release()
1829
1831
1830 def clone(self, remote, heads=[], stream=False):
1832 def clone(self, remote, heads=[], stream=False):
1831 '''clone remote repository.
1833 '''clone remote repository.
1832
1834
1833 keyword arguments:
1835 keyword arguments:
1834 heads: list of revs to clone (forces use of pull)
1836 heads: list of revs to clone (forces use of pull)
1835 stream: use streaming clone if possible'''
1837 stream: use streaming clone if possible'''
1836
1838
1837 # now, all clients that can request uncompressed clones can
1839 # now, all clients that can request uncompressed clones can
1838 # read repo formats supported by all servers that can serve
1840 # read repo formats supported by all servers that can serve
1839 # them.
1841 # them.
1840
1842
1841 # if revlog format changes, client will have to check version
1843 # if revlog format changes, client will have to check version
1842 # and format flags on "stream" capability, and use
1844 # and format flags on "stream" capability, and use
1843 # uncompressed only if compatible.
1845 # uncompressed only if compatible.
1844
1846
1845 if not stream:
1847 if not stream:
1846 # if the server explicitly prefers to stream (for fast LANs)
1848 # if the server explicitly prefers to stream (for fast LANs)
1847 stream = remote.capable('stream-preferred')
1849 stream = remote.capable('stream-preferred')
1848
1850
1849 if stream and not heads:
1851 if stream and not heads:
1850 # 'stream' means remote revlog format is revlogv1 only
1852 # 'stream' means remote revlog format is revlogv1 only
1851 if remote.capable('stream'):
1853 if remote.capable('stream'):
1852 return self.stream_in(remote, set(('revlogv1',)))
1854 return self.stream_in(remote, set(('revlogv1',)))
1853 # otherwise, 'streamreqs' contains the remote revlog format
1855 # otherwise, 'streamreqs' contains the remote revlog format
1854 streamreqs = remote.capable('streamreqs')
1856 streamreqs = remote.capable('streamreqs')
1855 if streamreqs:
1857 if streamreqs:
1856 streamreqs = set(streamreqs.split(','))
1858 streamreqs = set(streamreqs.split(','))
1857 # if we support it, stream in and adjust our requirements
1859 # if we support it, stream in and adjust our requirements
1858 if not streamreqs - self.supportedformats:
1860 if not streamreqs - self.supportedformats:
1859 return self.stream_in(remote, streamreqs)
1861 return self.stream_in(remote, streamreqs)
1860 return self.pull(remote, heads)
1862 return self.pull(remote, heads)
1861
1863
1862 def pushkey(self, namespace, key, old, new):
1864 def pushkey(self, namespace, key, old, new):
1863 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1865 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1864 old=old, new=new)
1866 old=old, new=new)
1865 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1867 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1866 ret = pushkey.push(self, namespace, key, old, new)
1868 ret = pushkey.push(self, namespace, key, old, new)
1867 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1869 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1868 ret=ret)
1870 ret=ret)
1869 return ret
1871 return ret
1870
1872
1871 def listkeys(self, namespace):
1873 def listkeys(self, namespace):
1872 self.hook('prelistkeys', throw=True, namespace=namespace)
1874 self.hook('prelistkeys', throw=True, namespace=namespace)
1873 self.ui.debug('listing keys for "%s"\n' % namespace)
1875 self.ui.debug('listing keys for "%s"\n' % namespace)
1874 values = pushkey.list(self, namespace)
1876 values = pushkey.list(self, namespace)
1875 self.hook('listkeys', namespace=namespace, values=values)
1877 self.hook('listkeys', namespace=namespace, values=values)
1876 return values
1878 return values
1877
1879
1878 def debugwireargs(self, one, two, three=None, four=None, five=None):
1880 def debugwireargs(self, one, two, three=None, four=None, five=None):
1879 '''used to test argument passing over the wire'''
1881 '''used to test argument passing over the wire'''
1880 return "%s %s %s %s %s" % (one, two, three, four, five)
1882 return "%s %s %s %s %s" % (one, two, three, four, five)
1881
1883
1882 def savecommitmessage(self, text):
1884 def savecommitmessage(self, text):
1883 fp = self.opener('last-message.txt', 'wb')
1885 fp = self.opener('last-message.txt', 'wb')
1884 try:
1886 try:
1885 fp.write(text)
1887 fp.write(text)
1886 finally:
1888 finally:
1887 fp.close()
1889 fp.close()
1888 return self.pathto(fp.name[len(self.root) + 1:])
1890 return self.pathto(fp.name[len(self.root) + 1:])
1889
1891
1890 # used to avoid circular references so destructors work
1892 # used to avoid circular references so destructors work
1891 def aftertrans(files):
1893 def aftertrans(files):
1892 renamefiles = [tuple(t) for t in files]
1894 renamefiles = [tuple(t) for t in files]
1893 def a():
1895 def a():
1894 for vfs, src, dest in renamefiles:
1896 for vfs, src, dest in renamefiles:
1895 try:
1897 try:
1896 vfs.rename(src, dest)
1898 vfs.rename(src, dest)
1897 except OSError: # journal file does not yet exist
1899 except OSError: # journal file does not yet exist
1898 pass
1900 pass
1899 return a
1901 return a
1900
1902
1901 def undoname(fn):
1903 def undoname(fn):
1902 base, name = os.path.split(fn)
1904 base, name = os.path.split(fn)
1903 assert name.startswith('journal')
1905 assert name.startswith('journal')
1904 return os.path.join(base, name.replace('journal', 'undo', 1))
1906 return os.path.join(base, name.replace('journal', 'undo', 1))
1905
1907
1906 def instance(ui, path, create):
1908 def instance(ui, path, create):
1907 return localrepository(ui, util.urllocalpath(path), create)
1909 return localrepository(ui, util.urllocalpath(path), create)
1908
1910
1909 def islocal(path):
1911 def islocal(path):
1910 return True
1912 return True
@@ -1,346 +1,374 b''
1 $ hg init test
1 $ hg init test
2 $ cd test
2 $ cd test
3
3
4 $ echo a > a
4 $ echo a > a
5 $ hg add a
5 $ hg add a
6 $ hg commit -m "test"
6 $ hg commit -m "test"
7 $ hg history
7 $ hg history
8 changeset: 0:acb14030fe0a
8 changeset: 0:acb14030fe0a
9 tag: tip
9 tag: tip
10 user: test
10 user: test
11 date: Thu Jan 01 00:00:00 1970 +0000
11 date: Thu Jan 01 00:00:00 1970 +0000
12 summary: test
12 summary: test
13
13
14
14
15 $ hg tag ' '
15 $ hg tag ' '
16 abort: tag names cannot consist entirely of whitespace
16 abort: tag names cannot consist entirely of whitespace
17 [255]
17 [255]
18
18
19 $ hg tag "bleah"
19 $ hg tag "bleah"
20 $ hg history
20 $ hg history
21 changeset: 1:d4f0d2909abc
21 changeset: 1:d4f0d2909abc
22 tag: tip
22 tag: tip
23 user: test
23 user: test
24 date: Thu Jan 01 00:00:00 1970 +0000
24 date: Thu Jan 01 00:00:00 1970 +0000
25 summary: Added tag bleah for changeset acb14030fe0a
25 summary: Added tag bleah for changeset acb14030fe0a
26
26
27 changeset: 0:acb14030fe0a
27 changeset: 0:acb14030fe0a
28 tag: bleah
28 tag: bleah
29 user: test
29 user: test
30 date: Thu Jan 01 00:00:00 1970 +0000
30 date: Thu Jan 01 00:00:00 1970 +0000
31 summary: test
31 summary: test
32
32
33
33
34 $ echo foo >> .hgtags
34 $ echo foo >> .hgtags
35 $ hg tag "bleah2"
35 $ hg tag "bleah2"
36 abort: working copy of .hgtags is changed (please commit .hgtags manually)
36 abort: working copy of .hgtags is changed (please commit .hgtags manually)
37 [255]
37 [255]
38
38
39 $ hg revert .hgtags
39 $ hg revert .hgtags
40 $ hg tag -r 0 x y z y y z
40 $ hg tag -r 0 x y z y y z
41 abort: tag names must be unique
41 abort: tag names must be unique
42 [255]
42 [255]
43 $ hg tag tap nada dot tip
43 $ hg tag tap nada dot tip
44 abort: the name 'tip' is reserved
44 abort: the name 'tip' is reserved
45 [255]
45 [255]
46 $ hg tag .
46 $ hg tag .
47 abort: the name '.' is reserved
47 abort: the name '.' is reserved
48 [255]
48 [255]
49 $ hg tag null
49 $ hg tag null
50 abort: the name 'null' is reserved
50 abort: the name 'null' is reserved
51 [255]
51 [255]
52 $ hg tag "bleah"
52 $ hg tag "bleah"
53 abort: tag 'bleah' already exists (use -f to force)
53 abort: tag 'bleah' already exists (use -f to force)
54 [255]
54 [255]
55 $ hg tag "blecch" "bleah"
55 $ hg tag "blecch" "bleah"
56 abort: tag 'bleah' already exists (use -f to force)
56 abort: tag 'bleah' already exists (use -f to force)
57 [255]
57 [255]
58
58
59 $ hg tag --remove "blecch"
59 $ hg tag --remove "blecch"
60 abort: tag 'blecch' does not exist
60 abort: tag 'blecch' does not exist
61 [255]
61 [255]
62 $ hg tag --remove "bleah" "blecch" "blough"
62 $ hg tag --remove "bleah" "blecch" "blough"
63 abort: tag 'blecch' does not exist
63 abort: tag 'blecch' does not exist
64 [255]
64 [255]
65
65
66 $ hg tag -r 0 "bleah0"
66 $ hg tag -r 0 "bleah0"
67 $ hg tag -l -r 1 "bleah1"
67 $ hg tag -l -r 1 "bleah1"
68 $ hg tag gack gawk gorp
68 $ hg tag gack gawk gorp
69 $ hg tag -f gack
69 $ hg tag -f gack
70 $ hg tag --remove gack gorp
70 $ hg tag --remove gack gorp
71
71
72 $ hg tag "bleah "
72 $ hg tag "bleah "
73 abort: tag 'bleah' already exists (use -f to force)
73 abort: tag 'bleah' already exists (use -f to force)
74 [255]
74 [255]
75 $ hg tag " bleah"
75 $ hg tag " bleah"
76 abort: tag 'bleah' already exists (use -f to force)
76 abort: tag 'bleah' already exists (use -f to force)
77 [255]
77 [255]
78 $ hg tag " bleah"
78 $ hg tag " bleah"
79 abort: tag 'bleah' already exists (use -f to force)
79 abort: tag 'bleah' already exists (use -f to force)
80 [255]
80 [255]
81 $ hg tag -r 0 " bleahbleah "
81 $ hg tag -r 0 " bleahbleah "
82 $ hg tag -r 0 " bleah bleah "
82 $ hg tag -r 0 " bleah bleah "
83
83
84 $ cat .hgtags
84 $ cat .hgtags
85 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
85 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
86 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
86 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
87 336fccc858a4eb69609a291105009e484a6b6b8d gack
87 336fccc858a4eb69609a291105009e484a6b6b8d gack
88 336fccc858a4eb69609a291105009e484a6b6b8d gawk
88 336fccc858a4eb69609a291105009e484a6b6b8d gawk
89 336fccc858a4eb69609a291105009e484a6b6b8d gorp
89 336fccc858a4eb69609a291105009e484a6b6b8d gorp
90 336fccc858a4eb69609a291105009e484a6b6b8d gack
90 336fccc858a4eb69609a291105009e484a6b6b8d gack
91 799667b6f2d9b957f73fa644a918c2df22bab58f gack
91 799667b6f2d9b957f73fa644a918c2df22bab58f gack
92 799667b6f2d9b957f73fa644a918c2df22bab58f gack
92 799667b6f2d9b957f73fa644a918c2df22bab58f gack
93 0000000000000000000000000000000000000000 gack
93 0000000000000000000000000000000000000000 gack
94 336fccc858a4eb69609a291105009e484a6b6b8d gorp
94 336fccc858a4eb69609a291105009e484a6b6b8d gorp
95 0000000000000000000000000000000000000000 gorp
95 0000000000000000000000000000000000000000 gorp
96 acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
96 acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
97 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
97 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
98
98
99 $ cat .hg/localtags
99 $ cat .hg/localtags
100 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
100 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
101
101
102 tagging on a non-head revision
102 tagging on a non-head revision
103
103
104 $ hg update 0
104 $ hg update 0
105 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
105 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 $ hg tag -l localblah
106 $ hg tag -l localblah
107 $ hg tag "foobar"
107 $ hg tag "foobar"
108 abort: not at a branch head (use -f to force)
108 abort: not at a branch head (use -f to force)
109 [255]
109 [255]
110 $ hg tag -f "foobar"
110 $ hg tag -f "foobar"
111 $ cat .hgtags
111 $ cat .hgtags
112 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
112 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
113 $ cat .hg/localtags
113 $ cat .hg/localtags
114 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
114 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
115 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
115 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
116
116
117 $ hg tag -l 'xx
117 $ hg tag -l 'xx
118 > newline'
118 > newline'
119 abort: '\n' cannot be used in a name
119 abort: '\n' cannot be used in a name
120 [255]
120 [255]
121 $ hg tag -l 'xx:xx'
121 $ hg tag -l 'xx:xx'
122 abort: ':' cannot be used in a name
122 abort: ':' cannot be used in a name
123 [255]
123 [255]
124
124
125 cloning local tags
125 cloning local tags
126
126
127 $ cd ..
127 $ cd ..
128 $ hg -R test log -r0:5
128 $ hg -R test log -r0:5
129 changeset: 0:acb14030fe0a
129 changeset: 0:acb14030fe0a
130 tag: bleah
130 tag: bleah
131 tag: bleah bleah
131 tag: bleah bleah
132 tag: bleah0
132 tag: bleah0
133 tag: bleahbleah
133 tag: bleahbleah
134 tag: foobar
134 tag: foobar
135 tag: localblah
135 tag: localblah
136 user: test
136 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
137 date: Thu Jan 01 00:00:00 1970 +0000
138 summary: test
138 summary: test
139
139
140 changeset: 1:d4f0d2909abc
140 changeset: 1:d4f0d2909abc
141 tag: bleah1
141 tag: bleah1
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:00 1970 +0000
143 date: Thu Jan 01 00:00:00 1970 +0000
144 summary: Added tag bleah for changeset acb14030fe0a
144 summary: Added tag bleah for changeset acb14030fe0a
145
145
146 changeset: 2:336fccc858a4
146 changeset: 2:336fccc858a4
147 tag: gawk
147 tag: gawk
148 user: test
148 user: test
149 date: Thu Jan 01 00:00:00 1970 +0000
149 date: Thu Jan 01 00:00:00 1970 +0000
150 summary: Added tag bleah0 for changeset acb14030fe0a
150 summary: Added tag bleah0 for changeset acb14030fe0a
151
151
152 changeset: 3:799667b6f2d9
152 changeset: 3:799667b6f2d9
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
154 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: Added tag gack, gawk, gorp for changeset 336fccc858a4
155 summary: Added tag gack, gawk, gorp for changeset 336fccc858a4
156
156
157 changeset: 4:154eeb7c0138
157 changeset: 4:154eeb7c0138
158 user: test
158 user: test
159 date: Thu Jan 01 00:00:00 1970 +0000
159 date: Thu Jan 01 00:00:00 1970 +0000
160 summary: Added tag gack for changeset 799667b6f2d9
160 summary: Added tag gack for changeset 799667b6f2d9
161
161
162 changeset: 5:b4bb47aaff09
162 changeset: 5:b4bb47aaff09
163 user: test
163 user: test
164 date: Thu Jan 01 00:00:00 1970 +0000
164 date: Thu Jan 01 00:00:00 1970 +0000
165 summary: Removed tag gack, gorp
165 summary: Removed tag gack, gorp
166
166
167 $ hg clone -q -rbleah1 test test1
167 $ hg clone -q -rbleah1 test test1
168 $ hg -R test1 parents --style=compact
168 $ hg -R test1 parents --style=compact
169 1[tip] d4f0d2909abc 1970-01-01 00:00 +0000 test
169 1[tip] d4f0d2909abc 1970-01-01 00:00 +0000 test
170 Added tag bleah for changeset acb14030fe0a
170 Added tag bleah for changeset acb14030fe0a
171
171
172 $ hg clone -q -r5 test#bleah1 test2
172 $ hg clone -q -r5 test#bleah1 test2
173 $ hg -R test2 parents --style=compact
173 $ hg -R test2 parents --style=compact
174 5[tip] b4bb47aaff09 1970-01-01 00:00 +0000 test
174 5[tip] b4bb47aaff09 1970-01-01 00:00 +0000 test
175 Removed tag gack, gorp
175 Removed tag gack, gorp
176
176
177 $ hg clone -q -U test#bleah1 test3
177 $ hg clone -q -U test#bleah1 test3
178 $ hg -R test3 parents --style=compact
178 $ hg -R test3 parents --style=compact
179
179
180 $ cd test
180 $ cd test
181
181
182 Issue601: hg tag doesn't do the right thing if .hgtags or localtags
182 Issue601: hg tag doesn't do the right thing if .hgtags or localtags
183 doesn't end with EOL
183 doesn't end with EOL
184
184
185 $ python << EOF
185 $ python << EOF
186 > f = file('.hg/localtags'); last = f.readlines()[-1][:-1]; f.close()
186 > f = file('.hg/localtags'); last = f.readlines()[-1][:-1]; f.close()
187 > f = file('.hg/localtags', 'w'); f.write(last); f.close()
187 > f = file('.hg/localtags', 'w'); f.write(last); f.close()
188 > EOF
188 > EOF
189 $ cat .hg/localtags; echo
189 $ cat .hg/localtags; echo
190 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
190 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
191 $ hg tag -l localnewline
191 $ hg tag -l localnewline
192 $ cat .hg/localtags; echo
192 $ cat .hg/localtags; echo
193 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
193 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
194 c2899151f4e76890c602a2597a650a72666681bf localnewline
194 c2899151f4e76890c602a2597a650a72666681bf localnewline
195
195
196
196
197 $ python << EOF
197 $ python << EOF
198 > f = file('.hgtags'); last = f.readlines()[-1][:-1]; f.close()
198 > f = file('.hgtags'); last = f.readlines()[-1][:-1]; f.close()
199 > f = file('.hgtags', 'w'); f.write(last); f.close()
199 > f = file('.hgtags', 'w'); f.write(last); f.close()
200 > EOF
200 > EOF
201 $ hg ci -m'broken manual edit of .hgtags'
201 $ hg ci -m'broken manual edit of .hgtags'
202 $ cat .hgtags; echo
202 $ cat .hgtags; echo
203 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
203 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
204 $ hg tag newline
204 $ hg tag newline
205 $ cat .hgtags; echo
205 $ cat .hgtags; echo
206 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
206 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
207 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
207 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
208
208
209
209
210 tag and branch using same name
210 tag and branch using same name
211
211
212 $ hg branch tag-and-branch-same-name
212 $ hg branch tag-and-branch-same-name
213 marked working directory as branch tag-and-branch-same-name
213 marked working directory as branch tag-and-branch-same-name
214 (branches are permanent and global, did you want a bookmark?)
214 (branches are permanent and global, did you want a bookmark?)
215 $ hg ci -m"discouraged"
215 $ hg ci -m"discouraged"
216 $ hg tag tag-and-branch-same-name
216 $ hg tag tag-and-branch-same-name
217 warning: tag tag-and-branch-same-name conflicts with existing branch name
217 warning: tag tag-and-branch-same-name conflicts with existing branch name
218
218
219 test custom commit messages
219 test custom commit messages
220
220
221 $ cat > editor.sh << '__EOF__'
221 $ cat > editor.sh << '__EOF__'
222 > echo "custom tag message" > "$1"
222 > echo "custom tag message" > "$1"
223 > echo "second line" >> "$1"
223 > echo "second line" >> "$1"
224 > __EOF__
224 > __EOF__
225
225
226 at first, test saving last-message.txt
226 at first, test saving last-message.txt
227
227
228 (test that editor is not invoked before transaction starting)
229
228 $ cat > .hg/hgrc << '__EOF__'
230 $ cat > .hg/hgrc << '__EOF__'
229 > [hooks]
231 > [hooks]
232 > # this failure occurs before editor invocation
230 > pretag.test-saving-lastmessage = false
233 > pretag.test-saving-lastmessage = false
231 > __EOF__
234 > __EOF__
232 $ rm -f .hg/last-message.txt
235 $ rm -f .hg/last-message.txt
233 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
236 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
234 abort: pretag.test-saving-lastmessage hook exited with status 1
237 abort: pretag.test-saving-lastmessage hook exited with status 1
235 [255]
238 [255]
236 $ cat .hg/last-message.txt
239 $ cat .hg/last-message.txt
240 cat: .hg/last-message.txt: No such file or directory
241 [1]
242
243 (test that editor is invoked and commit message is saved into
244 "last-message.txt")
245
246 $ cat >> .hg/hgrc << '__EOF__'
247 > [hooks]
248 > pretag.test-saving-lastmessage =
249 > # this failure occurs after editor invocation
250 > pretxncommit.unexpectedabort = false
251 > __EOF__
252
253 $ rm -f .hg/last-message.txt
254 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
255 transaction abort!
256 rollback completed
257 note: commit message saved in .hg/last-message.txt
258 abort: pretxncommit.unexpectedabort hook exited with status 1
259 [255]
260 $ cat .hg/last-message.txt
237 custom tag message
261 custom tag message
238 second line
262 second line
239 $ cat > .hg/hgrc << '__EOF__'
263
264 $ cat >> .hg/hgrc << '__EOF__'
240 > [hooks]
265 > [hooks]
241 > pretag.test-saving-lastmessage =
266 > pretxncommit.unexpectedabort =
242 > __EOF__
267 > __EOF__
268 $ hg status .hgtags
269 M .hgtags
270 $ hg revert --no-backup -q .hgtags
243
271
244 then, test custom commit message itself
272 then, test custom commit message itself
245
273
246 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
274 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
247 $ hg log -l1 --template "{desc}\n"
275 $ hg log -l1 --template "{desc}\n"
248 custom tag message
276 custom tag message
249 second line
277 second line
250
278
251
279
252 local tag with .hgtags modified
280 local tag with .hgtags modified
253
281
254 $ hg tag hgtags-modified
282 $ hg tag hgtags-modified
255 $ hg rollback
283 $ hg rollback
256 repository tip rolled back to revision 13 (undo commit)
284 repository tip rolled back to revision 13 (undo commit)
257 working directory now based on revision 13
285 working directory now based on revision 13
258 $ hg st
286 $ hg st
259 M .hgtags
287 M .hgtags
260 ? .hgtags.orig
288 ? .hgtags.orig
261 ? editor.sh
289 ? editor.sh
262 $ hg tag --local baz
290 $ hg tag --local baz
263 $ hg revert --no-backup .hgtags
291 $ hg revert --no-backup .hgtags
264
292
265
293
266 tagging when at named-branch-head that's not a topo-head
294 tagging when at named-branch-head that's not a topo-head
267
295
268 $ hg up default
296 $ hg up default
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 $ hg merge -t internal:local
298 $ hg merge -t internal:local
271 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
299 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
272 (branch merge, don't forget to commit)
300 (branch merge, don't forget to commit)
273 $ hg ci -m 'merge named branch'
301 $ hg ci -m 'merge named branch'
274 $ hg up 13
302 $ hg up 13
275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
303 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 $ hg tag new-topo-head
304 $ hg tag new-topo-head
277
305
278 tagging on null rev
306 tagging on null rev
279
307
280 $ hg up null
308 $ hg up null
281 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
309 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
282 $ hg tag nullrev
310 $ hg tag nullrev
283 abort: not at a branch head (use -f to force)
311 abort: not at a branch head (use -f to force)
284 [255]
312 [255]
285
313
286 $ hg init empty
314 $ hg init empty
287 $ hg tag -R empty nullrev
315 $ hg tag -R empty nullrev
288 abort: cannot tag null revision
316 abort: cannot tag null revision
289 [255]
317 [255]
290
318
291 $ hg tag -R empty -r 00000000000 -f nulltag
319 $ hg tag -R empty -r 00000000000 -f nulltag
292 abort: cannot tag null revision
320 abort: cannot tag null revision
293 [255]
321 [255]
294
322
295 $ cd ..
323 $ cd ..
296
324
297 tagging on an uncommitted merge (issue2542)
325 tagging on an uncommitted merge (issue2542)
298
326
299 $ hg init repo-tag-uncommitted-merge
327 $ hg init repo-tag-uncommitted-merge
300 $ cd repo-tag-uncommitted-merge
328 $ cd repo-tag-uncommitted-merge
301 $ echo c1 > f1
329 $ echo c1 > f1
302 $ hg ci -Am0
330 $ hg ci -Am0
303 adding f1
331 adding f1
304 $ echo c2 > f2
332 $ echo c2 > f2
305 $ hg ci -Am1
333 $ hg ci -Am1
306 adding f2
334 adding f2
307 $ hg co -q 0
335 $ hg co -q 0
308 $ hg branch b1
336 $ hg branch b1
309 marked working directory as branch b1
337 marked working directory as branch b1
310 (branches are permanent and global, did you want a bookmark?)
338 (branches are permanent and global, did you want a bookmark?)
311 $ hg ci -m2
339 $ hg ci -m2
312 $ hg up default
340 $ hg up default
313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
314 $ hg merge b1
342 $ hg merge b1
315 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 (branch merge, don't forget to commit)
344 (branch merge, don't forget to commit)
317
345
318 $ hg tag t1
346 $ hg tag t1
319 abort: uncommitted merge
347 abort: uncommitted merge
320 [255]
348 [255]
321 $ hg status
349 $ hg status
322 $ hg tag --rev 1 t2
350 $ hg tag --rev 1 t2
323 abort: uncommitted merge
351 abort: uncommitted merge
324 [255]
352 [255]
325 $ hg tag --rev 1 --local t3
353 $ hg tag --rev 1 --local t3
326 $ hg tags -v
354 $ hg tags -v
327 tip 2:2a156e8887cc
355 tip 2:2a156e8887cc
328 t3 1:c3adabd1a5f4 local
356 t3 1:c3adabd1a5f4 local
329
357
330 $ cd ..
358 $ cd ..
331
359
332 commit hook on tag used to be run without write lock - issue3344
360 commit hook on tag used to be run without write lock - issue3344
333
361
334 $ hg init repo-tag
362 $ hg init repo-tag
335 $ touch repo-tag/test
363 $ touch repo-tag/test
336 $ hg -R repo-tag commit -A -m "test"
364 $ hg -R repo-tag commit -A -m "test"
337 adding test
365 adding test
338 $ hg init repo-tag-target
366 $ hg init repo-tag-target
339 $ hg -R repo-tag --config hooks.commit="\"hg\" push \"`pwd`/repo-tag-target\"" tag tag
367 $ hg -R repo-tag --config hooks.commit="\"hg\" push \"`pwd`/repo-tag-target\"" tag tag
340 pushing to $TESTTMP/repo-tag-target (glob)
368 pushing to $TESTTMP/repo-tag-target (glob)
341 searching for changes
369 searching for changes
342 adding changesets
370 adding changesets
343 adding manifests
371 adding manifests
344 adding file changes
372 adding file changes
345 added 2 changesets with 2 changes to 2 files
373 added 2 changesets with 2 changes to 2 files
346
374
General Comments 0
You need to be logged in to leave comments. Login now