##// END OF EJS Templates
revlog: introduce isancestor method for efficiently determining node lineage...
Mads Kiilerich -
r22381:392ae5cb default
parent child Browse files
Show More
@@ -1,6117 +1,6117 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, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
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 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod
21 import dagparser, context, simplemerge, graphmod
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange
24 import phases, obsolete, exchange
25
25
26 table = {}
26 table = {}
27
27
28 command = cmdutil.command(table)
28 command = cmdutil.command(table)
29
29
30 # Space delimited list of commands that don't require local repositories.
30 # Space delimited list of commands that don't require local repositories.
31 # This should be populated by passing norepo=True into the @command decorator.
31 # This should be populated by passing norepo=True into the @command decorator.
32 norepo = ''
32 norepo = ''
33 # Space delimited list of commands that optionally require local repositories.
33 # Space delimited list of commands that optionally require local repositories.
34 # This should be populated by passing optionalrepo=True into the @command
34 # This should be populated by passing optionalrepo=True into the @command
35 # decorator.
35 # decorator.
36 optionalrepo = ''
36 optionalrepo = ''
37 # Space delimited list of commands that will examine arguments looking for
37 # Space delimited list of commands that will examine arguments looking for
38 # a repository. This should be populated by passing inferrepo=True into the
38 # a repository. This should be populated by passing inferrepo=True into the
39 # @command decorator.
39 # @command decorator.
40 inferrepo = ''
40 inferrepo = ''
41
41
42 # common command options
42 # common command options
43
43
44 globalopts = [
44 globalopts = [
45 ('R', 'repository', '',
45 ('R', 'repository', '',
46 _('repository root directory or name of overlay bundle file'),
46 _('repository root directory or name of overlay bundle file'),
47 _('REPO')),
47 _('REPO')),
48 ('', 'cwd', '',
48 ('', 'cwd', '',
49 _('change working directory'), _('DIR')),
49 _('change working directory'), _('DIR')),
50 ('y', 'noninteractive', None,
50 ('y', 'noninteractive', None,
51 _('do not prompt, automatically pick the first choice for all prompts')),
51 _('do not prompt, automatically pick the first choice for all prompts')),
52 ('q', 'quiet', None, _('suppress output')),
52 ('q', 'quiet', None, _('suppress output')),
53 ('v', 'verbose', None, _('enable additional output')),
53 ('v', 'verbose', None, _('enable additional output')),
54 ('', 'config', [],
54 ('', 'config', [],
55 _('set/override config option (use \'section.name=value\')'),
55 _('set/override config option (use \'section.name=value\')'),
56 _('CONFIG')),
56 _('CONFIG')),
57 ('', 'debug', None, _('enable debugging output')),
57 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debugger', None, _('start debugger')),
58 ('', 'debugger', None, _('start debugger')),
59 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
59 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 _('ENCODE')),
60 _('ENCODE')),
61 ('', 'encodingmode', encoding.encodingmode,
61 ('', 'encodingmode', encoding.encodingmode,
62 _('set the charset encoding mode'), _('MODE')),
62 _('set the charset encoding mode'), _('MODE')),
63 ('', 'traceback', None, _('always print a traceback on exception')),
63 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'time', None, _('time how long the command takes')),
64 ('', 'time', None, _('time how long the command takes')),
65 ('', 'profile', None, _('print command execution profile')),
65 ('', 'profile', None, _('print command execution profile')),
66 ('', 'version', None, _('output version information and exit')),
66 ('', 'version', None, _('output version information and exit')),
67 ('h', 'help', None, _('display help and exit')),
67 ('h', 'help', None, _('display help and exit')),
68 ('', 'hidden', False, _('consider hidden changesets')),
68 ('', 'hidden', False, _('consider hidden changesets')),
69 ]
69 ]
70
70
71 dryrunopts = [('n', 'dry-run', None,
71 dryrunopts = [('n', 'dry-run', None,
72 _('do not perform actions, just print output'))]
72 _('do not perform actions, just print output'))]
73
73
74 remoteopts = [
74 remoteopts = [
75 ('e', 'ssh', '',
75 ('e', 'ssh', '',
76 _('specify ssh command to use'), _('CMD')),
76 _('specify ssh command to use'), _('CMD')),
77 ('', 'remotecmd', '',
77 ('', 'remotecmd', '',
78 _('specify hg command to run on the remote side'), _('CMD')),
78 _('specify hg command to run on the remote side'), _('CMD')),
79 ('', 'insecure', None,
79 ('', 'insecure', None,
80 _('do not verify server certificate (ignoring web.cacerts config)')),
80 _('do not verify server certificate (ignoring web.cacerts config)')),
81 ]
81 ]
82
82
83 walkopts = [
83 walkopts = [
84 ('I', 'include', [],
84 ('I', 'include', [],
85 _('include names matching the given patterns'), _('PATTERN')),
85 _('include names matching the given patterns'), _('PATTERN')),
86 ('X', 'exclude', [],
86 ('X', 'exclude', [],
87 _('exclude names matching the given patterns'), _('PATTERN')),
87 _('exclude names matching the given patterns'), _('PATTERN')),
88 ]
88 ]
89
89
90 commitopts = [
90 commitopts = [
91 ('m', 'message', '',
91 ('m', 'message', '',
92 _('use text as commit message'), _('TEXT')),
92 _('use text as commit message'), _('TEXT')),
93 ('l', 'logfile', '',
93 ('l', 'logfile', '',
94 _('read commit message from file'), _('FILE')),
94 _('read commit message from file'), _('FILE')),
95 ]
95 ]
96
96
97 commitopts2 = [
97 commitopts2 = [
98 ('d', 'date', '',
98 ('d', 'date', '',
99 _('record the specified date as commit date'), _('DATE')),
99 _('record the specified date as commit date'), _('DATE')),
100 ('u', 'user', '',
100 ('u', 'user', '',
101 _('record the specified user as committer'), _('USER')),
101 _('record the specified user as committer'), _('USER')),
102 ]
102 ]
103
103
104 templateopts = [
104 templateopts = [
105 ('', 'style', '',
105 ('', 'style', '',
106 _('display using template map file (DEPRECATED)'), _('STYLE')),
106 _('display using template map file (DEPRECATED)'), _('STYLE')),
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template'), _('TEMPLATE')),
108 _('display with template'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 logopts = [
111 logopts = [
112 ('p', 'patch', None, _('show patch')),
112 ('p', 'patch', None, _('show patch')),
113 ('g', 'git', None, _('use git extended diff format')),
113 ('g', 'git', None, _('use git extended diff format')),
114 ('l', 'limit', '',
114 ('l', 'limit', '',
115 _('limit number of changes displayed'), _('NUM')),
115 _('limit number of changes displayed'), _('NUM')),
116 ('M', 'no-merges', None, _('do not show merges')),
116 ('M', 'no-merges', None, _('do not show merges')),
117 ('', 'stat', None, _('output diffstat-style summary of changes')),
117 ('', 'stat', None, _('output diffstat-style summary of changes')),
118 ('G', 'graph', None, _("show the revision DAG")),
118 ('G', 'graph', None, _("show the revision DAG")),
119 ] + templateopts
119 ] + templateopts
120
120
121 diffopts = [
121 diffopts = [
122 ('a', 'text', None, _('treat all files as text')),
122 ('a', 'text', None, _('treat all files as text')),
123 ('g', 'git', None, _('use git extended diff format')),
123 ('g', 'git', None, _('use git extended diff format')),
124 ('', 'nodates', None, _('omit dates from diff headers'))
124 ('', 'nodates', None, _('omit dates from diff headers'))
125 ]
125 ]
126
126
127 diffwsopts = [
127 diffwsopts = [
128 ('w', 'ignore-all-space', None,
128 ('w', 'ignore-all-space', None,
129 _('ignore white space when comparing lines')),
129 _('ignore white space when comparing lines')),
130 ('b', 'ignore-space-change', None,
130 ('b', 'ignore-space-change', None,
131 _('ignore changes in the amount of white space')),
131 _('ignore changes in the amount of white space')),
132 ('B', 'ignore-blank-lines', None,
132 ('B', 'ignore-blank-lines', None,
133 _('ignore changes whose lines are all blank')),
133 _('ignore changes whose lines are all blank')),
134 ]
134 ]
135
135
136 diffopts2 = [
136 diffopts2 = [
137 ('p', 'show-function', None, _('show which function each change is in')),
137 ('p', 'show-function', None, _('show which function each change is in')),
138 ('', 'reverse', None, _('produce a diff that undoes the changes')),
138 ('', 'reverse', None, _('produce a diff that undoes the changes')),
139 ] + diffwsopts + [
139 ] + diffwsopts + [
140 ('U', 'unified', '',
140 ('U', 'unified', '',
141 _('number of lines of context to show'), _('NUM')),
141 _('number of lines of context to show'), _('NUM')),
142 ('', 'stat', None, _('output diffstat-style summary of changes')),
142 ('', 'stat', None, _('output diffstat-style summary of changes')),
143 ]
143 ]
144
144
145 mergetoolopts = [
145 mergetoolopts = [
146 ('t', 'tool', '', _('specify merge tool')),
146 ('t', 'tool', '', _('specify merge tool')),
147 ]
147 ]
148
148
149 similarityopts = [
149 similarityopts = [
150 ('s', 'similarity', '',
150 ('s', 'similarity', '',
151 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
151 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
152 ]
152 ]
153
153
154 subrepoopts = [
154 subrepoopts = [
155 ('S', 'subrepos', None,
155 ('S', 'subrepos', None,
156 _('recurse into subrepositories'))
156 _('recurse into subrepositories'))
157 ]
157 ]
158
158
159 # Commands start here, listed alphabetically
159 # Commands start here, listed alphabetically
160
160
161 @command('^add',
161 @command('^add',
162 walkopts + subrepoopts + dryrunopts,
162 walkopts + subrepoopts + dryrunopts,
163 _('[OPTION]... [FILE]...'),
163 _('[OPTION]... [FILE]...'),
164 inferrepo=True)
164 inferrepo=True)
165 def add(ui, repo, *pats, **opts):
165 def add(ui, repo, *pats, **opts):
166 """add the specified files on the next commit
166 """add the specified files on the next commit
167
167
168 Schedule files to be version controlled and added to the
168 Schedule files to be version controlled and added to the
169 repository.
169 repository.
170
170
171 The files will be added to the repository at the next commit. To
171 The files will be added to the repository at the next commit. To
172 undo an add before that, see :hg:`forget`.
172 undo an add before that, see :hg:`forget`.
173
173
174 If no names are given, add all files to the repository.
174 If no names are given, add all files to the repository.
175
175
176 .. container:: verbose
176 .. container:: verbose
177
177
178 An example showing how new (unknown) files are added
178 An example showing how new (unknown) files are added
179 automatically by :hg:`add`::
179 automatically by :hg:`add`::
180
180
181 $ ls
181 $ ls
182 foo.c
182 foo.c
183 $ hg status
183 $ hg status
184 ? foo.c
184 ? foo.c
185 $ hg add
185 $ hg add
186 adding foo.c
186 adding foo.c
187 $ hg status
187 $ hg status
188 A foo.c
188 A foo.c
189
189
190 Returns 0 if all files are successfully added.
190 Returns 0 if all files are successfully added.
191 """
191 """
192
192
193 m = scmutil.match(repo[None], pats, opts)
193 m = scmutil.match(repo[None], pats, opts)
194 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
194 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
195 opts.get('subrepos'), prefix="", explicitonly=False)
195 opts.get('subrepos'), prefix="", explicitonly=False)
196 return rejected and 1 or 0
196 return rejected and 1 or 0
197
197
198 @command('addremove',
198 @command('addremove',
199 similarityopts + walkopts + dryrunopts,
199 similarityopts + walkopts + dryrunopts,
200 _('[OPTION]... [FILE]...'),
200 _('[OPTION]... [FILE]...'),
201 inferrepo=True)
201 inferrepo=True)
202 def addremove(ui, repo, *pats, **opts):
202 def addremove(ui, repo, *pats, **opts):
203 """add all new files, delete all missing files
203 """add all new files, delete all missing files
204
204
205 Add all new files and remove all missing files from the
205 Add all new files and remove all missing files from the
206 repository.
206 repository.
207
207
208 New files are ignored if they match any of the patterns in
208 New files are ignored if they match any of the patterns in
209 ``.hgignore``. As with add, these changes take effect at the next
209 ``.hgignore``. As with add, these changes take effect at the next
210 commit.
210 commit.
211
211
212 Use the -s/--similarity option to detect renamed files. This
212 Use the -s/--similarity option to detect renamed files. This
213 option takes a percentage between 0 (disabled) and 100 (files must
213 option takes a percentage between 0 (disabled) and 100 (files must
214 be identical) as its parameter. With a parameter greater than 0,
214 be identical) as its parameter. With a parameter greater than 0,
215 this compares every removed file with every added file and records
215 this compares every removed file with every added file and records
216 those similar enough as renames. Detecting renamed files this way
216 those similar enough as renames. Detecting renamed files this way
217 can be expensive. After using this option, :hg:`status -C` can be
217 can be expensive. After using this option, :hg:`status -C` can be
218 used to check which files were identified as moved or renamed. If
218 used to check which files were identified as moved or renamed. If
219 not specified, -s/--similarity defaults to 100 and only renames of
219 not specified, -s/--similarity defaults to 100 and only renames of
220 identical files are detected.
220 identical files are detected.
221
221
222 Returns 0 if all files are successfully added.
222 Returns 0 if all files are successfully added.
223 """
223 """
224 try:
224 try:
225 sim = float(opts.get('similarity') or 100)
225 sim = float(opts.get('similarity') or 100)
226 except ValueError:
226 except ValueError:
227 raise util.Abort(_('similarity must be a number'))
227 raise util.Abort(_('similarity must be a number'))
228 if sim < 0 or sim > 100:
228 if sim < 0 or sim > 100:
229 raise util.Abort(_('similarity must be between 0 and 100'))
229 raise util.Abort(_('similarity must be between 0 and 100'))
230 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
230 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
231
231
232 @command('^annotate|blame',
232 @command('^annotate|blame',
233 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
233 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
234 ('', 'follow', None,
234 ('', 'follow', None,
235 _('follow copies/renames and list the filename (DEPRECATED)')),
235 _('follow copies/renames and list the filename (DEPRECATED)')),
236 ('', 'no-follow', None, _("don't follow copies and renames")),
236 ('', 'no-follow', None, _("don't follow copies and renames")),
237 ('a', 'text', None, _('treat all files as text')),
237 ('a', 'text', None, _('treat all files as text')),
238 ('u', 'user', None, _('list the author (long with -v)')),
238 ('u', 'user', None, _('list the author (long with -v)')),
239 ('f', 'file', None, _('list the filename')),
239 ('f', 'file', None, _('list the filename')),
240 ('d', 'date', None, _('list the date (short with -q)')),
240 ('d', 'date', None, _('list the date (short with -q)')),
241 ('n', 'number', None, _('list the revision number (default)')),
241 ('n', 'number', None, _('list the revision number (default)')),
242 ('c', 'changeset', None, _('list the changeset')),
242 ('c', 'changeset', None, _('list the changeset')),
243 ('l', 'line-number', None, _('show line number at the first appearance'))
243 ('l', 'line-number', None, _('show line number at the first appearance'))
244 ] + diffwsopts + walkopts,
244 ] + diffwsopts + walkopts,
245 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
245 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
246 inferrepo=True)
246 inferrepo=True)
247 def annotate(ui, repo, *pats, **opts):
247 def annotate(ui, repo, *pats, **opts):
248 """show changeset information by line for each file
248 """show changeset information by line for each file
249
249
250 List changes in files, showing the revision id responsible for
250 List changes in files, showing the revision id responsible for
251 each line
251 each line
252
252
253 This command is useful for discovering when a change was made and
253 This command is useful for discovering when a change was made and
254 by whom.
254 by whom.
255
255
256 Without the -a/--text option, annotate will avoid processing files
256 Without the -a/--text option, annotate will avoid processing files
257 it detects as binary. With -a, annotate will annotate the file
257 it detects as binary. With -a, annotate will annotate the file
258 anyway, although the results will probably be neither useful
258 anyway, although the results will probably be neither useful
259 nor desirable.
259 nor desirable.
260
260
261 Returns 0 on success.
261 Returns 0 on success.
262 """
262 """
263 if not pats:
263 if not pats:
264 raise util.Abort(_('at least one filename or pattern is required'))
264 raise util.Abort(_('at least one filename or pattern is required'))
265
265
266 if opts.get('follow'):
266 if opts.get('follow'):
267 # --follow is deprecated and now just an alias for -f/--file
267 # --follow is deprecated and now just an alias for -f/--file
268 # to mimic the behavior of Mercurial before version 1.5
268 # to mimic the behavior of Mercurial before version 1.5
269 opts['file'] = True
269 opts['file'] = True
270
270
271 datefunc = ui.quiet and util.shortdate or util.datestr
271 datefunc = ui.quiet and util.shortdate or util.datestr
272 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
272 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
273 hexfn = ui.debugflag and hex or short
273 hexfn = ui.debugflag and hex or short
274
274
275 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
275 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
276 ('number', ' ', lambda x: str(x[0].rev())),
276 ('number', ' ', lambda x: str(x[0].rev())),
277 ('changeset', ' ', lambda x: hexfn(x[0].node())),
277 ('changeset', ' ', lambda x: hexfn(x[0].node())),
278 ('date', ' ', getdate),
278 ('date', ' ', getdate),
279 ('file', ' ', lambda x: x[0].path()),
279 ('file', ' ', lambda x: x[0].path()),
280 ('line_number', ':', lambda x: str(x[1])),
280 ('line_number', ':', lambda x: str(x[1])),
281 ]
281 ]
282
282
283 if (not opts.get('user') and not opts.get('changeset')
283 if (not opts.get('user') and not opts.get('changeset')
284 and not opts.get('date') and not opts.get('file')):
284 and not opts.get('date') and not opts.get('file')):
285 opts['number'] = True
285 opts['number'] = True
286
286
287 linenumber = opts.get('line_number') is not None
287 linenumber = opts.get('line_number') is not None
288 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
288 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
289 raise util.Abort(_('at least one of -n/-c is required for -l'))
289 raise util.Abort(_('at least one of -n/-c is required for -l'))
290
290
291 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
291 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
292 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
292 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
293
293
294 def bad(x, y):
294 def bad(x, y):
295 raise util.Abort("%s: %s" % (x, y))
295 raise util.Abort("%s: %s" % (x, y))
296
296
297 ctx = scmutil.revsingle(repo, opts.get('rev'))
297 ctx = scmutil.revsingle(repo, opts.get('rev'))
298 m = scmutil.match(ctx, pats, opts)
298 m = scmutil.match(ctx, pats, opts)
299 m.bad = bad
299 m.bad = bad
300 follow = not opts.get('no_follow')
300 follow = not opts.get('no_follow')
301 diffopts = patch.diffopts(ui, opts, section='annotate')
301 diffopts = patch.diffopts(ui, opts, section='annotate')
302 for abs in ctx.walk(m):
302 for abs in ctx.walk(m):
303 fctx = ctx[abs]
303 fctx = ctx[abs]
304 if not opts.get('text') and util.binary(fctx.data()):
304 if not opts.get('text') and util.binary(fctx.data()):
305 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
305 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
306 continue
306 continue
307
307
308 lines = fctx.annotate(follow=follow, linenumber=linenumber,
308 lines = fctx.annotate(follow=follow, linenumber=linenumber,
309 diffopts=diffopts)
309 diffopts=diffopts)
310 pieces = []
310 pieces = []
311
311
312 for f, sep in funcmap:
312 for f, sep in funcmap:
313 l = [f(n) for n, dummy in lines]
313 l = [f(n) for n, dummy in lines]
314 if l:
314 if l:
315 sized = [(x, encoding.colwidth(x)) for x in l]
315 sized = [(x, encoding.colwidth(x)) for x in l]
316 ml = max([w for x, w in sized])
316 ml = max([w for x, w in sized])
317 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
317 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
318 for x, w in sized])
318 for x, w in sized])
319
319
320 if pieces:
320 if pieces:
321 for p, l in zip(zip(*pieces), lines):
321 for p, l in zip(zip(*pieces), lines):
322 ui.write("%s: %s" % ("".join(p), l[1]))
322 ui.write("%s: %s" % ("".join(p), l[1]))
323
323
324 if lines and not lines[-1][1].endswith('\n'):
324 if lines and not lines[-1][1].endswith('\n'):
325 ui.write('\n')
325 ui.write('\n')
326
326
327 @command('archive',
327 @command('archive',
328 [('', 'no-decode', None, _('do not pass files through decoders')),
328 [('', 'no-decode', None, _('do not pass files through decoders')),
329 ('p', 'prefix', '', _('directory prefix for files in archive'),
329 ('p', 'prefix', '', _('directory prefix for files in archive'),
330 _('PREFIX')),
330 _('PREFIX')),
331 ('r', 'rev', '', _('revision to distribute'), _('REV')),
331 ('r', 'rev', '', _('revision to distribute'), _('REV')),
332 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
332 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
333 ] + subrepoopts + walkopts,
333 ] + subrepoopts + walkopts,
334 _('[OPTION]... DEST'))
334 _('[OPTION]... DEST'))
335 def archive(ui, repo, dest, **opts):
335 def archive(ui, repo, dest, **opts):
336 '''create an unversioned archive of a repository revision
336 '''create an unversioned archive of a repository revision
337
337
338 By default, the revision used is the parent of the working
338 By default, the revision used is the parent of the working
339 directory; use -r/--rev to specify a different revision.
339 directory; use -r/--rev to specify a different revision.
340
340
341 The archive type is automatically detected based on file
341 The archive type is automatically detected based on file
342 extension (or override using -t/--type).
342 extension (or override using -t/--type).
343
343
344 .. container:: verbose
344 .. container:: verbose
345
345
346 Examples:
346 Examples:
347
347
348 - create a zip file containing the 1.0 release::
348 - create a zip file containing the 1.0 release::
349
349
350 hg archive -r 1.0 project-1.0.zip
350 hg archive -r 1.0 project-1.0.zip
351
351
352 - create a tarball excluding .hg files::
352 - create a tarball excluding .hg files::
353
353
354 hg archive project.tar.gz -X ".hg*"
354 hg archive project.tar.gz -X ".hg*"
355
355
356 Valid types are:
356 Valid types are:
357
357
358 :``files``: a directory full of files (default)
358 :``files``: a directory full of files (default)
359 :``tar``: tar archive, uncompressed
359 :``tar``: tar archive, uncompressed
360 :``tbz2``: tar archive, compressed using bzip2
360 :``tbz2``: tar archive, compressed using bzip2
361 :``tgz``: tar archive, compressed using gzip
361 :``tgz``: tar archive, compressed using gzip
362 :``uzip``: zip archive, uncompressed
362 :``uzip``: zip archive, uncompressed
363 :``zip``: zip archive, compressed using deflate
363 :``zip``: zip archive, compressed using deflate
364
364
365 The exact name of the destination archive or directory is given
365 The exact name of the destination archive or directory is given
366 using a format string; see :hg:`help export` for details.
366 using a format string; see :hg:`help export` for details.
367
367
368 Each member added to an archive file has a directory prefix
368 Each member added to an archive file has a directory prefix
369 prepended. Use -p/--prefix to specify a format string for the
369 prepended. Use -p/--prefix to specify a format string for the
370 prefix. The default is the basename of the archive, with suffixes
370 prefix. The default is the basename of the archive, with suffixes
371 removed.
371 removed.
372
372
373 Returns 0 on success.
373 Returns 0 on success.
374 '''
374 '''
375
375
376 ctx = scmutil.revsingle(repo, opts.get('rev'))
376 ctx = scmutil.revsingle(repo, opts.get('rev'))
377 if not ctx:
377 if not ctx:
378 raise util.Abort(_('no working directory: please specify a revision'))
378 raise util.Abort(_('no working directory: please specify a revision'))
379 node = ctx.node()
379 node = ctx.node()
380 dest = cmdutil.makefilename(repo, dest, node)
380 dest = cmdutil.makefilename(repo, dest, node)
381 if os.path.realpath(dest) == repo.root:
381 if os.path.realpath(dest) == repo.root:
382 raise util.Abort(_('repository root cannot be destination'))
382 raise util.Abort(_('repository root cannot be destination'))
383
383
384 kind = opts.get('type') or archival.guesskind(dest) or 'files'
384 kind = opts.get('type') or archival.guesskind(dest) or 'files'
385 prefix = opts.get('prefix')
385 prefix = opts.get('prefix')
386
386
387 if dest == '-':
387 if dest == '-':
388 if kind == 'files':
388 if kind == 'files':
389 raise util.Abort(_('cannot archive plain files to stdout'))
389 raise util.Abort(_('cannot archive plain files to stdout'))
390 dest = cmdutil.makefileobj(repo, dest)
390 dest = cmdutil.makefileobj(repo, dest)
391 if not prefix:
391 if not prefix:
392 prefix = os.path.basename(repo.root) + '-%h'
392 prefix = os.path.basename(repo.root) + '-%h'
393
393
394 prefix = cmdutil.makefilename(repo, prefix, node)
394 prefix = cmdutil.makefilename(repo, prefix, node)
395 matchfn = scmutil.match(ctx, [], opts)
395 matchfn = scmutil.match(ctx, [], opts)
396 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
396 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
397 matchfn, prefix, subrepos=opts.get('subrepos'))
397 matchfn, prefix, subrepos=opts.get('subrepos'))
398
398
399 @command('backout',
399 @command('backout',
400 [('', 'merge', None, _('merge with old dirstate parent after backout')),
400 [('', 'merge', None, _('merge with old dirstate parent after backout')),
401 ('', 'parent', '',
401 ('', 'parent', '',
402 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
402 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
403 ('r', 'rev', '', _('revision to backout'), _('REV')),
403 ('r', 'rev', '', _('revision to backout'), _('REV')),
404 ('e', 'edit', False, _('invoke editor on commit messages')),
404 ('e', 'edit', False, _('invoke editor on commit messages')),
405 ] + mergetoolopts + walkopts + commitopts + commitopts2,
405 ] + mergetoolopts + walkopts + commitopts + commitopts2,
406 _('[OPTION]... [-r] REV'))
406 _('[OPTION]... [-r] REV'))
407 def backout(ui, repo, node=None, rev=None, **opts):
407 def backout(ui, repo, node=None, rev=None, **opts):
408 '''reverse effect of earlier changeset
408 '''reverse effect of earlier changeset
409
409
410 Prepare a new changeset with the effect of REV undone in the
410 Prepare a new changeset with the effect of REV undone in the
411 current working directory.
411 current working directory.
412
412
413 If REV is the parent of the working directory, then this new changeset
413 If REV is the parent of the working directory, then this new changeset
414 is committed automatically. Otherwise, hg needs to merge the
414 is committed automatically. Otherwise, hg needs to merge the
415 changes and the merged result is left uncommitted.
415 changes and the merged result is left uncommitted.
416
416
417 .. note::
417 .. note::
418
418
419 backout cannot be used to fix either an unwanted or
419 backout cannot be used to fix either an unwanted or
420 incorrect merge.
420 incorrect merge.
421
421
422 .. container:: verbose
422 .. container:: verbose
423
423
424 By default, the pending changeset will have one parent,
424 By default, the pending changeset will have one parent,
425 maintaining a linear history. With --merge, the pending
425 maintaining a linear history. With --merge, the pending
426 changeset will instead have two parents: the old parent of the
426 changeset will instead have two parents: the old parent of the
427 working directory and a new child of REV that simply undoes REV.
427 working directory and a new child of REV that simply undoes REV.
428
428
429 Before version 1.7, the behavior without --merge was equivalent
429 Before version 1.7, the behavior without --merge was equivalent
430 to specifying --merge followed by :hg:`update --clean .` to
430 to specifying --merge followed by :hg:`update --clean .` to
431 cancel the merge and leave the child of REV as a head to be
431 cancel the merge and leave the child of REV as a head to be
432 merged separately.
432 merged separately.
433
433
434 See :hg:`help dates` for a list of formats valid for -d/--date.
434 See :hg:`help dates` for a list of formats valid for -d/--date.
435
435
436 Returns 0 on success, 1 if nothing to backout or there are unresolved
436 Returns 0 on success, 1 if nothing to backout or there are unresolved
437 files.
437 files.
438 '''
438 '''
439 if rev and node:
439 if rev and node:
440 raise util.Abort(_("please specify just one revision"))
440 raise util.Abort(_("please specify just one revision"))
441
441
442 if not rev:
442 if not rev:
443 rev = node
443 rev = node
444
444
445 if not rev:
445 if not rev:
446 raise util.Abort(_("please specify a revision to backout"))
446 raise util.Abort(_("please specify a revision to backout"))
447
447
448 date = opts.get('date')
448 date = opts.get('date')
449 if date:
449 if date:
450 opts['date'] = util.parsedate(date)
450 opts['date'] = util.parsedate(date)
451
451
452 cmdutil.checkunfinished(repo)
452 cmdutil.checkunfinished(repo)
453 cmdutil.bailifchanged(repo)
453 cmdutil.bailifchanged(repo)
454 node = scmutil.revsingle(repo, rev).node()
454 node = scmutil.revsingle(repo, rev).node()
455
455
456 op1, op2 = repo.dirstate.parents()
456 op1, op2 = repo.dirstate.parents()
457 if node not in repo.changelog.commonancestorsheads(op1, node):
457 if not repo.changelog.isancestor(node, op1):
458 raise util.Abort(_('cannot backout change that is not an ancestor'))
458 raise util.Abort(_('cannot backout change that is not an ancestor'))
459
459
460 p1, p2 = repo.changelog.parents(node)
460 p1, p2 = repo.changelog.parents(node)
461 if p1 == nullid:
461 if p1 == nullid:
462 raise util.Abort(_('cannot backout a change with no parents'))
462 raise util.Abort(_('cannot backout a change with no parents'))
463 if p2 != nullid:
463 if p2 != nullid:
464 if not opts.get('parent'):
464 if not opts.get('parent'):
465 raise util.Abort(_('cannot backout a merge changeset'))
465 raise util.Abort(_('cannot backout a merge changeset'))
466 p = repo.lookup(opts['parent'])
466 p = repo.lookup(opts['parent'])
467 if p not in (p1, p2):
467 if p not in (p1, p2):
468 raise util.Abort(_('%s is not a parent of %s') %
468 raise util.Abort(_('%s is not a parent of %s') %
469 (short(p), short(node)))
469 (short(p), short(node)))
470 parent = p
470 parent = p
471 else:
471 else:
472 if opts.get('parent'):
472 if opts.get('parent'):
473 raise util.Abort(_('cannot use --parent on non-merge changeset'))
473 raise util.Abort(_('cannot use --parent on non-merge changeset'))
474 parent = p1
474 parent = p1
475
475
476 # the backout should appear on the same branch
476 # the backout should appear on the same branch
477 wlock = repo.wlock()
477 wlock = repo.wlock()
478 try:
478 try:
479 branch = repo.dirstate.branch()
479 branch = repo.dirstate.branch()
480 bheads = repo.branchheads(branch)
480 bheads = repo.branchheads(branch)
481 rctx = scmutil.revsingle(repo, hex(parent))
481 rctx = scmutil.revsingle(repo, hex(parent))
482 if not opts.get('merge') and op1 != node:
482 if not opts.get('merge') and op1 != node:
483 try:
483 try:
484 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
484 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
485 'backout')
485 'backout')
486 stats = mergemod.update(repo, parent, True, True, False,
486 stats = mergemod.update(repo, parent, True, True, False,
487 node, False)
487 node, False)
488 repo.setparents(op1, op2)
488 repo.setparents(op1, op2)
489 hg._showstats(repo, stats)
489 hg._showstats(repo, stats)
490 if stats[3]:
490 if stats[3]:
491 repo.ui.status(_("use 'hg resolve' to retry unresolved "
491 repo.ui.status(_("use 'hg resolve' to retry unresolved "
492 "file merges\n"))
492 "file merges\n"))
493 else:
493 else:
494 msg = _("changeset %s backed out, "
494 msg = _("changeset %s backed out, "
495 "don't forget to commit.\n")
495 "don't forget to commit.\n")
496 ui.status(msg % short(node))
496 ui.status(msg % short(node))
497 return stats[3] > 0
497 return stats[3] > 0
498 finally:
498 finally:
499 ui.setconfig('ui', 'forcemerge', '', '')
499 ui.setconfig('ui', 'forcemerge', '', '')
500 else:
500 else:
501 hg.clean(repo, node, show_stats=False)
501 hg.clean(repo, node, show_stats=False)
502 repo.dirstate.setbranch(branch)
502 repo.dirstate.setbranch(branch)
503 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
503 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
504
504
505
505
506 def commitfunc(ui, repo, message, match, opts):
506 def commitfunc(ui, repo, message, match, opts):
507 editform = 'backout'
507 editform = 'backout'
508 e = cmdutil.getcommiteditor(editform=editform, **opts)
508 e = cmdutil.getcommiteditor(editform=editform, **opts)
509 if not message:
509 if not message:
510 # we don't translate commit messages
510 # we don't translate commit messages
511 message = "Backed out changeset %s" % short(node)
511 message = "Backed out changeset %s" % short(node)
512 e = cmdutil.getcommiteditor(edit=True, editform=editform)
512 e = cmdutil.getcommiteditor(edit=True, editform=editform)
513 return repo.commit(message, opts.get('user'), opts.get('date'),
513 return repo.commit(message, opts.get('user'), opts.get('date'),
514 match, editor=e)
514 match, editor=e)
515 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
515 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
516 if not newnode:
516 if not newnode:
517 ui.status(_("nothing changed\n"))
517 ui.status(_("nothing changed\n"))
518 return 1
518 return 1
519 cmdutil.commitstatus(repo, newnode, branch, bheads)
519 cmdutil.commitstatus(repo, newnode, branch, bheads)
520
520
521 def nice(node):
521 def nice(node):
522 return '%d:%s' % (repo.changelog.rev(node), short(node))
522 return '%d:%s' % (repo.changelog.rev(node), short(node))
523 ui.status(_('changeset %s backs out changeset %s\n') %
523 ui.status(_('changeset %s backs out changeset %s\n') %
524 (nice(repo.changelog.tip()), nice(node)))
524 (nice(repo.changelog.tip()), nice(node)))
525 if opts.get('merge') and op1 != node:
525 if opts.get('merge') and op1 != node:
526 hg.clean(repo, op1, show_stats=False)
526 hg.clean(repo, op1, show_stats=False)
527 ui.status(_('merging with changeset %s\n')
527 ui.status(_('merging with changeset %s\n')
528 % nice(repo.changelog.tip()))
528 % nice(repo.changelog.tip()))
529 try:
529 try:
530 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
530 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
531 'backout')
531 'backout')
532 return hg.merge(repo, hex(repo.changelog.tip()))
532 return hg.merge(repo, hex(repo.changelog.tip()))
533 finally:
533 finally:
534 ui.setconfig('ui', 'forcemerge', '', '')
534 ui.setconfig('ui', 'forcemerge', '', '')
535 finally:
535 finally:
536 wlock.release()
536 wlock.release()
537 return 0
537 return 0
538
538
539 @command('bisect',
539 @command('bisect',
540 [('r', 'reset', False, _('reset bisect state')),
540 [('r', 'reset', False, _('reset bisect state')),
541 ('g', 'good', False, _('mark changeset good')),
541 ('g', 'good', False, _('mark changeset good')),
542 ('b', 'bad', False, _('mark changeset bad')),
542 ('b', 'bad', False, _('mark changeset bad')),
543 ('s', 'skip', False, _('skip testing changeset')),
543 ('s', 'skip', False, _('skip testing changeset')),
544 ('e', 'extend', False, _('extend the bisect range')),
544 ('e', 'extend', False, _('extend the bisect range')),
545 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
545 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
546 ('U', 'noupdate', False, _('do not update to target'))],
546 ('U', 'noupdate', False, _('do not update to target'))],
547 _("[-gbsr] [-U] [-c CMD] [REV]"))
547 _("[-gbsr] [-U] [-c CMD] [REV]"))
548 def bisect(ui, repo, rev=None, extra=None, command=None,
548 def bisect(ui, repo, rev=None, extra=None, command=None,
549 reset=None, good=None, bad=None, skip=None, extend=None,
549 reset=None, good=None, bad=None, skip=None, extend=None,
550 noupdate=None):
550 noupdate=None):
551 """subdivision search of changesets
551 """subdivision search of changesets
552
552
553 This command helps to find changesets which introduce problems. To
553 This command helps to find changesets which introduce problems. To
554 use, mark the earliest changeset you know exhibits the problem as
554 use, mark the earliest changeset you know exhibits the problem as
555 bad, then mark the latest changeset which is free from the problem
555 bad, then mark the latest changeset which is free from the problem
556 as good. Bisect will update your working directory to a revision
556 as good. Bisect will update your working directory to a revision
557 for testing (unless the -U/--noupdate option is specified). Once
557 for testing (unless the -U/--noupdate option is specified). Once
558 you have performed tests, mark the working directory as good or
558 you have performed tests, mark the working directory as good or
559 bad, and bisect will either update to another candidate changeset
559 bad, and bisect will either update to another candidate changeset
560 or announce that it has found the bad revision.
560 or announce that it has found the bad revision.
561
561
562 As a shortcut, you can also use the revision argument to mark a
562 As a shortcut, you can also use the revision argument to mark a
563 revision as good or bad without checking it out first.
563 revision as good or bad without checking it out first.
564
564
565 If you supply a command, it will be used for automatic bisection.
565 If you supply a command, it will be used for automatic bisection.
566 The environment variable HG_NODE will contain the ID of the
566 The environment variable HG_NODE will contain the ID of the
567 changeset being tested. The exit status of the command will be
567 changeset being tested. The exit status of the command will be
568 used to mark revisions as good or bad: status 0 means good, 125
568 used to mark revisions as good or bad: status 0 means good, 125
569 means to skip the revision, 127 (command not found) will abort the
569 means to skip the revision, 127 (command not found) will abort the
570 bisection, and any other non-zero exit status means the revision
570 bisection, and any other non-zero exit status means the revision
571 is bad.
571 is bad.
572
572
573 .. container:: verbose
573 .. container:: verbose
574
574
575 Some examples:
575 Some examples:
576
576
577 - start a bisection with known bad revision 34, and good revision 12::
577 - start a bisection with known bad revision 34, and good revision 12::
578
578
579 hg bisect --bad 34
579 hg bisect --bad 34
580 hg bisect --good 12
580 hg bisect --good 12
581
581
582 - advance the current bisection by marking current revision as good or
582 - advance the current bisection by marking current revision as good or
583 bad::
583 bad::
584
584
585 hg bisect --good
585 hg bisect --good
586 hg bisect --bad
586 hg bisect --bad
587
587
588 - mark the current revision, or a known revision, to be skipped (e.g. if
588 - mark the current revision, or a known revision, to be skipped (e.g. if
589 that revision is not usable because of another issue)::
589 that revision is not usable because of another issue)::
590
590
591 hg bisect --skip
591 hg bisect --skip
592 hg bisect --skip 23
592 hg bisect --skip 23
593
593
594 - skip all revisions that do not touch directories ``foo`` or ``bar``::
594 - skip all revisions that do not touch directories ``foo`` or ``bar``::
595
595
596 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
596 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
597
597
598 - forget the current bisection::
598 - forget the current bisection::
599
599
600 hg bisect --reset
600 hg bisect --reset
601
601
602 - use 'make && make tests' to automatically find the first broken
602 - use 'make && make tests' to automatically find the first broken
603 revision::
603 revision::
604
604
605 hg bisect --reset
605 hg bisect --reset
606 hg bisect --bad 34
606 hg bisect --bad 34
607 hg bisect --good 12
607 hg bisect --good 12
608 hg bisect --command "make && make tests"
608 hg bisect --command "make && make tests"
609
609
610 - see all changesets whose states are already known in the current
610 - see all changesets whose states are already known in the current
611 bisection::
611 bisection::
612
612
613 hg log -r "bisect(pruned)"
613 hg log -r "bisect(pruned)"
614
614
615 - see the changeset currently being bisected (especially useful
615 - see the changeset currently being bisected (especially useful
616 if running with -U/--noupdate)::
616 if running with -U/--noupdate)::
617
617
618 hg log -r "bisect(current)"
618 hg log -r "bisect(current)"
619
619
620 - see all changesets that took part in the current bisection::
620 - see all changesets that took part in the current bisection::
621
621
622 hg log -r "bisect(range)"
622 hg log -r "bisect(range)"
623
623
624 - you can even get a nice graph::
624 - you can even get a nice graph::
625
625
626 hg log --graph -r "bisect(range)"
626 hg log --graph -r "bisect(range)"
627
627
628 See :hg:`help revsets` for more about the `bisect()` keyword.
628 See :hg:`help revsets` for more about the `bisect()` keyword.
629
629
630 Returns 0 on success.
630 Returns 0 on success.
631 """
631 """
632 def extendbisectrange(nodes, good):
632 def extendbisectrange(nodes, good):
633 # bisect is incomplete when it ends on a merge node and
633 # bisect is incomplete when it ends on a merge node and
634 # one of the parent was not checked.
634 # one of the parent was not checked.
635 parents = repo[nodes[0]].parents()
635 parents = repo[nodes[0]].parents()
636 if len(parents) > 1:
636 if len(parents) > 1:
637 side = good and state['bad'] or state['good']
637 side = good and state['bad'] or state['good']
638 num = len(set(i.node() for i in parents) & set(side))
638 num = len(set(i.node() for i in parents) & set(side))
639 if num == 1:
639 if num == 1:
640 return parents[0].ancestor(parents[1])
640 return parents[0].ancestor(parents[1])
641 return None
641 return None
642
642
643 def print_result(nodes, good):
643 def print_result(nodes, good):
644 displayer = cmdutil.show_changeset(ui, repo, {})
644 displayer = cmdutil.show_changeset(ui, repo, {})
645 if len(nodes) == 1:
645 if len(nodes) == 1:
646 # narrowed it down to a single revision
646 # narrowed it down to a single revision
647 if good:
647 if good:
648 ui.write(_("The first good revision is:\n"))
648 ui.write(_("The first good revision is:\n"))
649 else:
649 else:
650 ui.write(_("The first bad revision is:\n"))
650 ui.write(_("The first bad revision is:\n"))
651 displayer.show(repo[nodes[0]])
651 displayer.show(repo[nodes[0]])
652 extendnode = extendbisectrange(nodes, good)
652 extendnode = extendbisectrange(nodes, good)
653 if extendnode is not None:
653 if extendnode is not None:
654 ui.write(_('Not all ancestors of this changeset have been'
654 ui.write(_('Not all ancestors of this changeset have been'
655 ' checked.\nUse bisect --extend to continue the '
655 ' checked.\nUse bisect --extend to continue the '
656 'bisection from\nthe common ancestor, %s.\n')
656 'bisection from\nthe common ancestor, %s.\n')
657 % extendnode)
657 % extendnode)
658 else:
658 else:
659 # multiple possible revisions
659 # multiple possible revisions
660 if good:
660 if good:
661 ui.write(_("Due to skipped revisions, the first "
661 ui.write(_("Due to skipped revisions, the first "
662 "good revision could be any of:\n"))
662 "good revision could be any of:\n"))
663 else:
663 else:
664 ui.write(_("Due to skipped revisions, the first "
664 ui.write(_("Due to skipped revisions, the first "
665 "bad revision could be any of:\n"))
665 "bad revision could be any of:\n"))
666 for n in nodes:
666 for n in nodes:
667 displayer.show(repo[n])
667 displayer.show(repo[n])
668 displayer.close()
668 displayer.close()
669
669
670 def check_state(state, interactive=True):
670 def check_state(state, interactive=True):
671 if not state['good'] or not state['bad']:
671 if not state['good'] or not state['bad']:
672 if (good or bad or skip or reset) and interactive:
672 if (good or bad or skip or reset) and interactive:
673 return
673 return
674 if not state['good']:
674 if not state['good']:
675 raise util.Abort(_('cannot bisect (no known good revisions)'))
675 raise util.Abort(_('cannot bisect (no known good revisions)'))
676 else:
676 else:
677 raise util.Abort(_('cannot bisect (no known bad revisions)'))
677 raise util.Abort(_('cannot bisect (no known bad revisions)'))
678 return True
678 return True
679
679
680 # backward compatibility
680 # backward compatibility
681 if rev in "good bad reset init".split():
681 if rev in "good bad reset init".split():
682 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
682 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
683 cmd, rev, extra = rev, extra, None
683 cmd, rev, extra = rev, extra, None
684 if cmd == "good":
684 if cmd == "good":
685 good = True
685 good = True
686 elif cmd == "bad":
686 elif cmd == "bad":
687 bad = True
687 bad = True
688 else:
688 else:
689 reset = True
689 reset = True
690 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
690 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
691 raise util.Abort(_('incompatible arguments'))
691 raise util.Abort(_('incompatible arguments'))
692
692
693 cmdutil.checkunfinished(repo)
693 cmdutil.checkunfinished(repo)
694
694
695 if reset:
695 if reset:
696 p = repo.join("bisect.state")
696 p = repo.join("bisect.state")
697 if os.path.exists(p):
697 if os.path.exists(p):
698 os.unlink(p)
698 os.unlink(p)
699 return
699 return
700
700
701 state = hbisect.load_state(repo)
701 state = hbisect.load_state(repo)
702
702
703 if command:
703 if command:
704 changesets = 1
704 changesets = 1
705 if noupdate:
705 if noupdate:
706 try:
706 try:
707 node = state['current'][0]
707 node = state['current'][0]
708 except LookupError:
708 except LookupError:
709 raise util.Abort(_('current bisect revision is unknown - '
709 raise util.Abort(_('current bisect revision is unknown - '
710 'start a new bisect to fix'))
710 'start a new bisect to fix'))
711 else:
711 else:
712 node, p2 = repo.dirstate.parents()
712 node, p2 = repo.dirstate.parents()
713 if p2 != nullid:
713 if p2 != nullid:
714 raise util.Abort(_('current bisect revision is a merge'))
714 raise util.Abort(_('current bisect revision is a merge'))
715 try:
715 try:
716 while changesets:
716 while changesets:
717 # update state
717 # update state
718 state['current'] = [node]
718 state['current'] = [node]
719 hbisect.save_state(repo, state)
719 hbisect.save_state(repo, state)
720 status = util.system(command,
720 status = util.system(command,
721 environ={'HG_NODE': hex(node)},
721 environ={'HG_NODE': hex(node)},
722 out=ui.fout)
722 out=ui.fout)
723 if status == 125:
723 if status == 125:
724 transition = "skip"
724 transition = "skip"
725 elif status == 0:
725 elif status == 0:
726 transition = "good"
726 transition = "good"
727 # status < 0 means process was killed
727 # status < 0 means process was killed
728 elif status == 127:
728 elif status == 127:
729 raise util.Abort(_("failed to execute %s") % command)
729 raise util.Abort(_("failed to execute %s") % command)
730 elif status < 0:
730 elif status < 0:
731 raise util.Abort(_("%s killed") % command)
731 raise util.Abort(_("%s killed") % command)
732 else:
732 else:
733 transition = "bad"
733 transition = "bad"
734 ctx = scmutil.revsingle(repo, rev, node)
734 ctx = scmutil.revsingle(repo, rev, node)
735 rev = None # clear for future iterations
735 rev = None # clear for future iterations
736 state[transition].append(ctx.node())
736 state[transition].append(ctx.node())
737 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
737 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
738 check_state(state, interactive=False)
738 check_state(state, interactive=False)
739 # bisect
739 # bisect
740 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
740 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
741 # update to next check
741 # update to next check
742 node = nodes[0]
742 node = nodes[0]
743 if not noupdate:
743 if not noupdate:
744 cmdutil.bailifchanged(repo)
744 cmdutil.bailifchanged(repo)
745 hg.clean(repo, node, show_stats=False)
745 hg.clean(repo, node, show_stats=False)
746 finally:
746 finally:
747 state['current'] = [node]
747 state['current'] = [node]
748 hbisect.save_state(repo, state)
748 hbisect.save_state(repo, state)
749 print_result(nodes, bgood)
749 print_result(nodes, bgood)
750 return
750 return
751
751
752 # update state
752 # update state
753
753
754 if rev:
754 if rev:
755 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
755 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
756 else:
756 else:
757 nodes = [repo.lookup('.')]
757 nodes = [repo.lookup('.')]
758
758
759 if good or bad or skip:
759 if good or bad or skip:
760 if good:
760 if good:
761 state['good'] += nodes
761 state['good'] += nodes
762 elif bad:
762 elif bad:
763 state['bad'] += nodes
763 state['bad'] += nodes
764 elif skip:
764 elif skip:
765 state['skip'] += nodes
765 state['skip'] += nodes
766 hbisect.save_state(repo, state)
766 hbisect.save_state(repo, state)
767
767
768 if not check_state(state):
768 if not check_state(state):
769 return
769 return
770
770
771 # actually bisect
771 # actually bisect
772 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
772 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
773 if extend:
773 if extend:
774 if not changesets:
774 if not changesets:
775 extendnode = extendbisectrange(nodes, good)
775 extendnode = extendbisectrange(nodes, good)
776 if extendnode is not None:
776 if extendnode is not None:
777 ui.write(_("Extending search to changeset %d:%s\n")
777 ui.write(_("Extending search to changeset %d:%s\n")
778 % (extendnode.rev(), extendnode))
778 % (extendnode.rev(), extendnode))
779 state['current'] = [extendnode.node()]
779 state['current'] = [extendnode.node()]
780 hbisect.save_state(repo, state)
780 hbisect.save_state(repo, state)
781 if noupdate:
781 if noupdate:
782 return
782 return
783 cmdutil.bailifchanged(repo)
783 cmdutil.bailifchanged(repo)
784 return hg.clean(repo, extendnode.node())
784 return hg.clean(repo, extendnode.node())
785 raise util.Abort(_("nothing to extend"))
785 raise util.Abort(_("nothing to extend"))
786
786
787 if changesets == 0:
787 if changesets == 0:
788 print_result(nodes, good)
788 print_result(nodes, good)
789 else:
789 else:
790 assert len(nodes) == 1 # only a single node can be tested next
790 assert len(nodes) == 1 # only a single node can be tested next
791 node = nodes[0]
791 node = nodes[0]
792 # compute the approximate number of remaining tests
792 # compute the approximate number of remaining tests
793 tests, size = 0, 2
793 tests, size = 0, 2
794 while size <= changesets:
794 while size <= changesets:
795 tests, size = tests + 1, size * 2
795 tests, size = tests + 1, size * 2
796 rev = repo.changelog.rev(node)
796 rev = repo.changelog.rev(node)
797 ui.write(_("Testing changeset %d:%s "
797 ui.write(_("Testing changeset %d:%s "
798 "(%d changesets remaining, ~%d tests)\n")
798 "(%d changesets remaining, ~%d tests)\n")
799 % (rev, short(node), changesets, tests))
799 % (rev, short(node), changesets, tests))
800 state['current'] = [node]
800 state['current'] = [node]
801 hbisect.save_state(repo, state)
801 hbisect.save_state(repo, state)
802 if not noupdate:
802 if not noupdate:
803 cmdutil.bailifchanged(repo)
803 cmdutil.bailifchanged(repo)
804 return hg.clean(repo, node)
804 return hg.clean(repo, node)
805
805
806 @command('bookmarks|bookmark',
806 @command('bookmarks|bookmark',
807 [('f', 'force', False, _('force')),
807 [('f', 'force', False, _('force')),
808 ('r', 'rev', '', _('revision'), _('REV')),
808 ('r', 'rev', '', _('revision'), _('REV')),
809 ('d', 'delete', False, _('delete a given bookmark')),
809 ('d', 'delete', False, _('delete a given bookmark')),
810 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
810 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
811 ('i', 'inactive', False, _('mark a bookmark inactive'))],
811 ('i', 'inactive', False, _('mark a bookmark inactive'))],
812 _('hg bookmarks [OPTIONS]... [NAME]...'))
812 _('hg bookmarks [OPTIONS]... [NAME]...'))
813 def bookmark(ui, repo, *names, **opts):
813 def bookmark(ui, repo, *names, **opts):
814 '''create a new bookmark or list existing bookmarks
814 '''create a new bookmark or list existing bookmarks
815
815
816 Bookmarks are labels on changesets to help track lines of development.
816 Bookmarks are labels on changesets to help track lines of development.
817 Bookmarks are unversioned and can be moved, renamed and deleted.
817 Bookmarks are unversioned and can be moved, renamed and deleted.
818 Deleting or moving a bookmark has no effect on the associated changesets.
818 Deleting or moving a bookmark has no effect on the associated changesets.
819
819
820 Creating or updating to a bookmark causes it to be marked as 'active'.
820 Creating or updating to a bookmark causes it to be marked as 'active'.
821 The active bookmark is indicated with a '*'.
821 The active bookmark is indicated with a '*'.
822 When a commit is made, the active bookmark will advance to the new commit.
822 When a commit is made, the active bookmark will advance to the new commit.
823 A plain :hg:`update` will also advance an active bookmark, if possible.
823 A plain :hg:`update` will also advance an active bookmark, if possible.
824 Updating away from a bookmark will cause it to be deactivated.
824 Updating away from a bookmark will cause it to be deactivated.
825
825
826 Bookmarks can be pushed and pulled between repositories (see
826 Bookmarks can be pushed and pulled between repositories (see
827 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
827 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
828 diverged, a new 'divergent bookmark' of the form 'name@path' will
828 diverged, a new 'divergent bookmark' of the form 'name@path' will
829 be created. Using :hg:'merge' will resolve the divergence.
829 be created. Using :hg:'merge' will resolve the divergence.
830
830
831 A bookmark named '@' has the special property that :hg:`clone` will
831 A bookmark named '@' has the special property that :hg:`clone` will
832 check it out by default if it exists.
832 check it out by default if it exists.
833
833
834 .. container:: verbose
834 .. container:: verbose
835
835
836 Examples:
836 Examples:
837
837
838 - create an active bookmark for a new line of development::
838 - create an active bookmark for a new line of development::
839
839
840 hg book new-feature
840 hg book new-feature
841
841
842 - create an inactive bookmark as a place marker::
842 - create an inactive bookmark as a place marker::
843
843
844 hg book -i reviewed
844 hg book -i reviewed
845
845
846 - create an inactive bookmark on another changeset::
846 - create an inactive bookmark on another changeset::
847
847
848 hg book -r .^ tested
848 hg book -r .^ tested
849
849
850 - move the '@' bookmark from another branch::
850 - move the '@' bookmark from another branch::
851
851
852 hg book -f @
852 hg book -f @
853 '''
853 '''
854 force = opts.get('force')
854 force = opts.get('force')
855 rev = opts.get('rev')
855 rev = opts.get('rev')
856 delete = opts.get('delete')
856 delete = opts.get('delete')
857 rename = opts.get('rename')
857 rename = opts.get('rename')
858 inactive = opts.get('inactive')
858 inactive = opts.get('inactive')
859
859
860 def checkformat(mark):
860 def checkformat(mark):
861 mark = mark.strip()
861 mark = mark.strip()
862 if not mark:
862 if not mark:
863 raise util.Abort(_("bookmark names cannot consist entirely of "
863 raise util.Abort(_("bookmark names cannot consist entirely of "
864 "whitespace"))
864 "whitespace"))
865 scmutil.checknewlabel(repo, mark, 'bookmark')
865 scmutil.checknewlabel(repo, mark, 'bookmark')
866 return mark
866 return mark
867
867
868 def checkconflict(repo, mark, cur, force=False, target=None):
868 def checkconflict(repo, mark, cur, force=False, target=None):
869 if mark in marks and not force:
869 if mark in marks and not force:
870 if target:
870 if target:
871 if marks[mark] == target and target == cur:
871 if marks[mark] == target and target == cur:
872 # re-activating a bookmark
872 # re-activating a bookmark
873 return
873 return
874 anc = repo.changelog.ancestors([repo[target].rev()])
874 anc = repo.changelog.ancestors([repo[target].rev()])
875 bmctx = repo[marks[mark]]
875 bmctx = repo[marks[mark]]
876 divs = [repo[b].node() for b in marks
876 divs = [repo[b].node() for b in marks
877 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
877 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
878
878
879 # allow resolving a single divergent bookmark even if moving
879 # allow resolving a single divergent bookmark even if moving
880 # the bookmark across branches when a revision is specified
880 # the bookmark across branches when a revision is specified
881 # that contains a divergent bookmark
881 # that contains a divergent bookmark
882 if bmctx.rev() not in anc and target in divs:
882 if bmctx.rev() not in anc and target in divs:
883 bookmarks.deletedivergent(repo, [target], mark)
883 bookmarks.deletedivergent(repo, [target], mark)
884 return
884 return
885
885
886 deletefrom = [b for b in divs
886 deletefrom = [b for b in divs
887 if repo[b].rev() in anc or b == target]
887 if repo[b].rev() in anc or b == target]
888 bookmarks.deletedivergent(repo, deletefrom, mark)
888 bookmarks.deletedivergent(repo, deletefrom, mark)
889 if bookmarks.validdest(repo, bmctx, repo[target]):
889 if bookmarks.validdest(repo, bmctx, repo[target]):
890 ui.status(_("moving bookmark '%s' forward from %s\n") %
890 ui.status(_("moving bookmark '%s' forward from %s\n") %
891 (mark, short(bmctx.node())))
891 (mark, short(bmctx.node())))
892 return
892 return
893 raise util.Abort(_("bookmark '%s' already exists "
893 raise util.Abort(_("bookmark '%s' already exists "
894 "(use -f to force)") % mark)
894 "(use -f to force)") % mark)
895 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
895 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
896 and not force):
896 and not force):
897 raise util.Abort(
897 raise util.Abort(
898 _("a bookmark cannot have the name of an existing branch"))
898 _("a bookmark cannot have the name of an existing branch"))
899
899
900 if delete and rename:
900 if delete and rename:
901 raise util.Abort(_("--delete and --rename are incompatible"))
901 raise util.Abort(_("--delete and --rename are incompatible"))
902 if delete and rev:
902 if delete and rev:
903 raise util.Abort(_("--rev is incompatible with --delete"))
903 raise util.Abort(_("--rev is incompatible with --delete"))
904 if rename and rev:
904 if rename and rev:
905 raise util.Abort(_("--rev is incompatible with --rename"))
905 raise util.Abort(_("--rev is incompatible with --rename"))
906 if not names and (delete or rev):
906 if not names and (delete or rev):
907 raise util.Abort(_("bookmark name required"))
907 raise util.Abort(_("bookmark name required"))
908
908
909 if delete or rename or names or inactive:
909 if delete or rename or names or inactive:
910 wlock = repo.wlock()
910 wlock = repo.wlock()
911 try:
911 try:
912 cur = repo.changectx('.').node()
912 cur = repo.changectx('.').node()
913 marks = repo._bookmarks
913 marks = repo._bookmarks
914 if delete:
914 if delete:
915 for mark in names:
915 for mark in names:
916 if mark not in marks:
916 if mark not in marks:
917 raise util.Abort(_("bookmark '%s' does not exist") %
917 raise util.Abort(_("bookmark '%s' does not exist") %
918 mark)
918 mark)
919 if mark == repo._bookmarkcurrent:
919 if mark == repo._bookmarkcurrent:
920 bookmarks.unsetcurrent(repo)
920 bookmarks.unsetcurrent(repo)
921 del marks[mark]
921 del marks[mark]
922 marks.write()
922 marks.write()
923
923
924 elif rename:
924 elif rename:
925 if not names:
925 if not names:
926 raise util.Abort(_("new bookmark name required"))
926 raise util.Abort(_("new bookmark name required"))
927 elif len(names) > 1:
927 elif len(names) > 1:
928 raise util.Abort(_("only one new bookmark name allowed"))
928 raise util.Abort(_("only one new bookmark name allowed"))
929 mark = checkformat(names[0])
929 mark = checkformat(names[0])
930 if rename not in marks:
930 if rename not in marks:
931 raise util.Abort(_("bookmark '%s' does not exist") % rename)
931 raise util.Abort(_("bookmark '%s' does not exist") % rename)
932 checkconflict(repo, mark, cur, force)
932 checkconflict(repo, mark, cur, force)
933 marks[mark] = marks[rename]
933 marks[mark] = marks[rename]
934 if repo._bookmarkcurrent == rename and not inactive:
934 if repo._bookmarkcurrent == rename and not inactive:
935 bookmarks.setcurrent(repo, mark)
935 bookmarks.setcurrent(repo, mark)
936 del marks[rename]
936 del marks[rename]
937 marks.write()
937 marks.write()
938
938
939 elif names:
939 elif names:
940 newact = None
940 newact = None
941 for mark in names:
941 for mark in names:
942 mark = checkformat(mark)
942 mark = checkformat(mark)
943 if newact is None:
943 if newact is None:
944 newact = mark
944 newact = mark
945 if inactive and mark == repo._bookmarkcurrent:
945 if inactive and mark == repo._bookmarkcurrent:
946 bookmarks.unsetcurrent(repo)
946 bookmarks.unsetcurrent(repo)
947 return
947 return
948 tgt = cur
948 tgt = cur
949 if rev:
949 if rev:
950 tgt = scmutil.revsingle(repo, rev).node()
950 tgt = scmutil.revsingle(repo, rev).node()
951 checkconflict(repo, mark, cur, force, tgt)
951 checkconflict(repo, mark, cur, force, tgt)
952 marks[mark] = tgt
952 marks[mark] = tgt
953 if not inactive and cur == marks[newact] and not rev:
953 if not inactive and cur == marks[newact] and not rev:
954 bookmarks.setcurrent(repo, newact)
954 bookmarks.setcurrent(repo, newact)
955 elif cur != tgt and newact == repo._bookmarkcurrent:
955 elif cur != tgt and newact == repo._bookmarkcurrent:
956 bookmarks.unsetcurrent(repo)
956 bookmarks.unsetcurrent(repo)
957 marks.write()
957 marks.write()
958
958
959 elif inactive:
959 elif inactive:
960 if len(marks) == 0:
960 if len(marks) == 0:
961 ui.status(_("no bookmarks set\n"))
961 ui.status(_("no bookmarks set\n"))
962 elif not repo._bookmarkcurrent:
962 elif not repo._bookmarkcurrent:
963 ui.status(_("no active bookmark\n"))
963 ui.status(_("no active bookmark\n"))
964 else:
964 else:
965 bookmarks.unsetcurrent(repo)
965 bookmarks.unsetcurrent(repo)
966 finally:
966 finally:
967 wlock.release()
967 wlock.release()
968 else: # show bookmarks
968 else: # show bookmarks
969 hexfn = ui.debugflag and hex or short
969 hexfn = ui.debugflag and hex or short
970 marks = repo._bookmarks
970 marks = repo._bookmarks
971 if len(marks) == 0:
971 if len(marks) == 0:
972 ui.status(_("no bookmarks set\n"))
972 ui.status(_("no bookmarks set\n"))
973 else:
973 else:
974 for bmark, n in sorted(marks.iteritems()):
974 for bmark, n in sorted(marks.iteritems()):
975 current = repo._bookmarkcurrent
975 current = repo._bookmarkcurrent
976 if bmark == current:
976 if bmark == current:
977 prefix, label = '*', 'bookmarks.current'
977 prefix, label = '*', 'bookmarks.current'
978 else:
978 else:
979 prefix, label = ' ', ''
979 prefix, label = ' ', ''
980
980
981 if ui.quiet:
981 if ui.quiet:
982 ui.write("%s\n" % bmark, label=label)
982 ui.write("%s\n" % bmark, label=label)
983 else:
983 else:
984 pad = " " * (25 - encoding.colwidth(bmark))
984 pad = " " * (25 - encoding.colwidth(bmark))
985 ui.write(" %s %s%s %d:%s\n" % (
985 ui.write(" %s %s%s %d:%s\n" % (
986 prefix, bmark, pad, repo.changelog.rev(n), hexfn(n)),
986 prefix, bmark, pad, repo.changelog.rev(n), hexfn(n)),
987 label=label)
987 label=label)
988
988
989 @command('branch',
989 @command('branch',
990 [('f', 'force', None,
990 [('f', 'force', None,
991 _('set branch name even if it shadows an existing branch')),
991 _('set branch name even if it shadows an existing branch')),
992 ('C', 'clean', None, _('reset branch name to parent branch name'))],
992 ('C', 'clean', None, _('reset branch name to parent branch name'))],
993 _('[-fC] [NAME]'))
993 _('[-fC] [NAME]'))
994 def branch(ui, repo, label=None, **opts):
994 def branch(ui, repo, label=None, **opts):
995 """set or show the current branch name
995 """set or show the current branch name
996
996
997 .. note::
997 .. note::
998
998
999 Branch names are permanent and global. Use :hg:`bookmark` to create a
999 Branch names are permanent and global. Use :hg:`bookmark` to create a
1000 light-weight bookmark instead. See :hg:`help glossary` for more
1000 light-weight bookmark instead. See :hg:`help glossary` for more
1001 information about named branches and bookmarks.
1001 information about named branches and bookmarks.
1002
1002
1003 With no argument, show the current branch name. With one argument,
1003 With no argument, show the current branch name. With one argument,
1004 set the working directory branch name (the branch will not exist
1004 set the working directory branch name (the branch will not exist
1005 in the repository until the next commit). Standard practice
1005 in the repository until the next commit). Standard practice
1006 recommends that primary development take place on the 'default'
1006 recommends that primary development take place on the 'default'
1007 branch.
1007 branch.
1008
1008
1009 Unless -f/--force is specified, branch will not let you set a
1009 Unless -f/--force is specified, branch will not let you set a
1010 branch name that already exists, even if it's inactive.
1010 branch name that already exists, even if it's inactive.
1011
1011
1012 Use -C/--clean to reset the working directory branch to that of
1012 Use -C/--clean to reset the working directory branch to that of
1013 the parent of the working directory, negating a previous branch
1013 the parent of the working directory, negating a previous branch
1014 change.
1014 change.
1015
1015
1016 Use the command :hg:`update` to switch to an existing branch. Use
1016 Use the command :hg:`update` to switch to an existing branch. Use
1017 :hg:`commit --close-branch` to mark this branch as closed.
1017 :hg:`commit --close-branch` to mark this branch as closed.
1018
1018
1019 Returns 0 on success.
1019 Returns 0 on success.
1020 """
1020 """
1021 if label:
1021 if label:
1022 label = label.strip()
1022 label = label.strip()
1023
1023
1024 if not opts.get('clean') and not label:
1024 if not opts.get('clean') and not label:
1025 ui.write("%s\n" % repo.dirstate.branch())
1025 ui.write("%s\n" % repo.dirstate.branch())
1026 return
1026 return
1027
1027
1028 wlock = repo.wlock()
1028 wlock = repo.wlock()
1029 try:
1029 try:
1030 if opts.get('clean'):
1030 if opts.get('clean'):
1031 label = repo[None].p1().branch()
1031 label = repo[None].p1().branch()
1032 repo.dirstate.setbranch(label)
1032 repo.dirstate.setbranch(label)
1033 ui.status(_('reset working directory to branch %s\n') % label)
1033 ui.status(_('reset working directory to branch %s\n') % label)
1034 elif label:
1034 elif label:
1035 if not opts.get('force') and label in repo.branchmap():
1035 if not opts.get('force') and label in repo.branchmap():
1036 if label not in [p.branch() for p in repo.parents()]:
1036 if label not in [p.branch() for p in repo.parents()]:
1037 raise util.Abort(_('a branch of the same name already'
1037 raise util.Abort(_('a branch of the same name already'
1038 ' exists'),
1038 ' exists'),
1039 # i18n: "it" refers to an existing branch
1039 # i18n: "it" refers to an existing branch
1040 hint=_("use 'hg update' to switch to it"))
1040 hint=_("use 'hg update' to switch to it"))
1041 scmutil.checknewlabel(repo, label, 'branch')
1041 scmutil.checknewlabel(repo, label, 'branch')
1042 repo.dirstate.setbranch(label)
1042 repo.dirstate.setbranch(label)
1043 ui.status(_('marked working directory as branch %s\n') % label)
1043 ui.status(_('marked working directory as branch %s\n') % label)
1044 ui.status(_('(branches are permanent and global, '
1044 ui.status(_('(branches are permanent and global, '
1045 'did you want a bookmark?)\n'))
1045 'did you want a bookmark?)\n'))
1046 finally:
1046 finally:
1047 wlock.release()
1047 wlock.release()
1048
1048
1049 @command('branches',
1049 @command('branches',
1050 [('a', 'active', False, _('show only branches that have unmerged heads')),
1050 [('a', 'active', False, _('show only branches that have unmerged heads')),
1051 ('c', 'closed', False, _('show normal and closed branches'))],
1051 ('c', 'closed', False, _('show normal and closed branches'))],
1052 _('[-ac]'))
1052 _('[-ac]'))
1053 def branches(ui, repo, active=False, closed=False):
1053 def branches(ui, repo, active=False, closed=False):
1054 """list repository named branches
1054 """list repository named branches
1055
1055
1056 List the repository's named branches, indicating which ones are
1056 List the repository's named branches, indicating which ones are
1057 inactive. If -c/--closed is specified, also list branches which have
1057 inactive. If -c/--closed is specified, also list branches which have
1058 been marked closed (see :hg:`commit --close-branch`).
1058 been marked closed (see :hg:`commit --close-branch`).
1059
1059
1060 If -a/--active is specified, only show active branches. A branch
1060 If -a/--active is specified, only show active branches. A branch
1061 is considered active if it contains repository heads.
1061 is considered active if it contains repository heads.
1062
1062
1063 Use the command :hg:`update` to switch to an existing branch.
1063 Use the command :hg:`update` to switch to an existing branch.
1064
1064
1065 Returns 0.
1065 Returns 0.
1066 """
1066 """
1067
1067
1068 hexfunc = ui.debugflag and hex or short
1068 hexfunc = ui.debugflag and hex or short
1069
1069
1070 allheads = set(repo.heads())
1070 allheads = set(repo.heads())
1071 branches = []
1071 branches = []
1072 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1072 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1073 isactive = not isclosed and bool(set(heads) & allheads)
1073 isactive = not isclosed and bool(set(heads) & allheads)
1074 branches.append((tag, repo[tip], isactive, not isclosed))
1074 branches.append((tag, repo[tip], isactive, not isclosed))
1075 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1075 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1076 reverse=True)
1076 reverse=True)
1077
1077
1078 for tag, ctx, isactive, isopen in branches:
1078 for tag, ctx, isactive, isopen in branches:
1079 if (not active) or isactive:
1079 if (not active) or isactive:
1080 if isactive:
1080 if isactive:
1081 label = 'branches.active'
1081 label = 'branches.active'
1082 notice = ''
1082 notice = ''
1083 elif not isopen:
1083 elif not isopen:
1084 if not closed:
1084 if not closed:
1085 continue
1085 continue
1086 label = 'branches.closed'
1086 label = 'branches.closed'
1087 notice = _(' (closed)')
1087 notice = _(' (closed)')
1088 else:
1088 else:
1089 label = 'branches.inactive'
1089 label = 'branches.inactive'
1090 notice = _(' (inactive)')
1090 notice = _(' (inactive)')
1091 if tag == repo.dirstate.branch():
1091 if tag == repo.dirstate.branch():
1092 label = 'branches.current'
1092 label = 'branches.current'
1093 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(tag))
1093 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(tag))
1094 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1094 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1095 'log.changeset changeset.%s' % ctx.phasestr())
1095 'log.changeset changeset.%s' % ctx.phasestr())
1096 labeledtag = ui.label(tag, label)
1096 labeledtag = ui.label(tag, label)
1097 if ui.quiet:
1097 if ui.quiet:
1098 ui.write("%s\n" % labeledtag)
1098 ui.write("%s\n" % labeledtag)
1099 else:
1099 else:
1100 ui.write("%s %s%s\n" % (labeledtag, rev, notice))
1100 ui.write("%s %s%s\n" % (labeledtag, rev, notice))
1101
1101
1102 @command('bundle',
1102 @command('bundle',
1103 [('f', 'force', None, _('run even when the destination is unrelated')),
1103 [('f', 'force', None, _('run even when the destination is unrelated')),
1104 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1104 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1105 _('REV')),
1105 _('REV')),
1106 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1106 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1107 _('BRANCH')),
1107 _('BRANCH')),
1108 ('', 'base', [],
1108 ('', 'base', [],
1109 _('a base changeset assumed to be available at the destination'),
1109 _('a base changeset assumed to be available at the destination'),
1110 _('REV')),
1110 _('REV')),
1111 ('a', 'all', None, _('bundle all changesets in the repository')),
1111 ('a', 'all', None, _('bundle all changesets in the repository')),
1112 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1112 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1113 ] + remoteopts,
1113 ] + remoteopts,
1114 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1114 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1115 def bundle(ui, repo, fname, dest=None, **opts):
1115 def bundle(ui, repo, fname, dest=None, **opts):
1116 """create a changegroup file
1116 """create a changegroup file
1117
1117
1118 Generate a compressed changegroup file collecting changesets not
1118 Generate a compressed changegroup file collecting changesets not
1119 known to be in another repository.
1119 known to be in another repository.
1120
1120
1121 If you omit the destination repository, then hg assumes the
1121 If you omit the destination repository, then hg assumes the
1122 destination will have all the nodes you specify with --base
1122 destination will have all the nodes you specify with --base
1123 parameters. To create a bundle containing all changesets, use
1123 parameters. To create a bundle containing all changesets, use
1124 -a/--all (or --base null).
1124 -a/--all (or --base null).
1125
1125
1126 You can change compression method with the -t/--type option.
1126 You can change compression method with the -t/--type option.
1127 The available compression methods are: none, bzip2, and
1127 The available compression methods are: none, bzip2, and
1128 gzip (by default, bundles are compressed using bzip2).
1128 gzip (by default, bundles are compressed using bzip2).
1129
1129
1130 The bundle file can then be transferred using conventional means
1130 The bundle file can then be transferred using conventional means
1131 and applied to another repository with the unbundle or pull
1131 and applied to another repository with the unbundle or pull
1132 command. This is useful when direct push and pull are not
1132 command. This is useful when direct push and pull are not
1133 available or when exporting an entire repository is undesirable.
1133 available or when exporting an entire repository is undesirable.
1134
1134
1135 Applying bundles preserves all changeset contents including
1135 Applying bundles preserves all changeset contents including
1136 permissions, copy/rename information, and revision history.
1136 permissions, copy/rename information, and revision history.
1137
1137
1138 Returns 0 on success, 1 if no changes found.
1138 Returns 0 on success, 1 if no changes found.
1139 """
1139 """
1140 revs = None
1140 revs = None
1141 if 'rev' in opts:
1141 if 'rev' in opts:
1142 revs = scmutil.revrange(repo, opts['rev'])
1142 revs = scmutil.revrange(repo, opts['rev'])
1143
1143
1144 bundletype = opts.get('type', 'bzip2').lower()
1144 bundletype = opts.get('type', 'bzip2').lower()
1145 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1145 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1146 bundletype = btypes.get(bundletype)
1146 bundletype = btypes.get(bundletype)
1147 if bundletype not in changegroup.bundletypes:
1147 if bundletype not in changegroup.bundletypes:
1148 raise util.Abort(_('unknown bundle type specified with --type'))
1148 raise util.Abort(_('unknown bundle type specified with --type'))
1149
1149
1150 if opts.get('all'):
1150 if opts.get('all'):
1151 base = ['null']
1151 base = ['null']
1152 else:
1152 else:
1153 base = scmutil.revrange(repo, opts.get('base'))
1153 base = scmutil.revrange(repo, opts.get('base'))
1154 # TODO: get desired bundlecaps from command line.
1154 # TODO: get desired bundlecaps from command line.
1155 bundlecaps = None
1155 bundlecaps = None
1156 if base:
1156 if base:
1157 if dest:
1157 if dest:
1158 raise util.Abort(_("--base is incompatible with specifying "
1158 raise util.Abort(_("--base is incompatible with specifying "
1159 "a destination"))
1159 "a destination"))
1160 common = [repo.lookup(rev) for rev in base]
1160 common = [repo.lookup(rev) for rev in base]
1161 heads = revs and map(repo.lookup, revs) or revs
1161 heads = revs and map(repo.lookup, revs) or revs
1162 cg = changegroup.getbundle(repo, 'bundle', heads=heads, common=common,
1162 cg = changegroup.getbundle(repo, 'bundle', heads=heads, common=common,
1163 bundlecaps=bundlecaps)
1163 bundlecaps=bundlecaps)
1164 outgoing = None
1164 outgoing = None
1165 else:
1165 else:
1166 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1166 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1167 dest, branches = hg.parseurl(dest, opts.get('branch'))
1167 dest, branches = hg.parseurl(dest, opts.get('branch'))
1168 other = hg.peer(repo, opts, dest)
1168 other = hg.peer(repo, opts, dest)
1169 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1169 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1170 heads = revs and map(repo.lookup, revs) or revs
1170 heads = revs and map(repo.lookup, revs) or revs
1171 outgoing = discovery.findcommonoutgoing(repo, other,
1171 outgoing = discovery.findcommonoutgoing(repo, other,
1172 onlyheads=heads,
1172 onlyheads=heads,
1173 force=opts.get('force'),
1173 force=opts.get('force'),
1174 portable=True)
1174 portable=True)
1175 cg = changegroup.getlocalbundle(repo, 'bundle', outgoing, bundlecaps)
1175 cg = changegroup.getlocalbundle(repo, 'bundle', outgoing, bundlecaps)
1176 if not cg:
1176 if not cg:
1177 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1177 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1178 return 1
1178 return 1
1179
1179
1180 changegroup.writebundle(cg, fname, bundletype)
1180 changegroup.writebundle(cg, fname, bundletype)
1181
1181
1182 @command('cat',
1182 @command('cat',
1183 [('o', 'output', '',
1183 [('o', 'output', '',
1184 _('print output to file with formatted name'), _('FORMAT')),
1184 _('print output to file with formatted name'), _('FORMAT')),
1185 ('r', 'rev', '', _('print the given revision'), _('REV')),
1185 ('r', 'rev', '', _('print the given revision'), _('REV')),
1186 ('', 'decode', None, _('apply any matching decode filter')),
1186 ('', 'decode', None, _('apply any matching decode filter')),
1187 ] + walkopts,
1187 ] + walkopts,
1188 _('[OPTION]... FILE...'),
1188 _('[OPTION]... FILE...'),
1189 inferrepo=True)
1189 inferrepo=True)
1190 def cat(ui, repo, file1, *pats, **opts):
1190 def cat(ui, repo, file1, *pats, **opts):
1191 """output the current or given revision of files
1191 """output the current or given revision of files
1192
1192
1193 Print the specified files as they were at the given revision. If
1193 Print the specified files as they were at the given revision. If
1194 no revision is given, the parent of the working directory is used.
1194 no revision is given, the parent of the working directory is used.
1195
1195
1196 Output may be to a file, in which case the name of the file is
1196 Output may be to a file, in which case the name of the file is
1197 given using a format string. The formatting rules as follows:
1197 given using a format string. The formatting rules as follows:
1198
1198
1199 :``%%``: literal "%" character
1199 :``%%``: literal "%" character
1200 :``%s``: basename of file being printed
1200 :``%s``: basename of file being printed
1201 :``%d``: dirname of file being printed, or '.' if in repository root
1201 :``%d``: dirname of file being printed, or '.' if in repository root
1202 :``%p``: root-relative path name of file being printed
1202 :``%p``: root-relative path name of file being printed
1203 :``%H``: changeset hash (40 hexadecimal digits)
1203 :``%H``: changeset hash (40 hexadecimal digits)
1204 :``%R``: changeset revision number
1204 :``%R``: changeset revision number
1205 :``%h``: short-form changeset hash (12 hexadecimal digits)
1205 :``%h``: short-form changeset hash (12 hexadecimal digits)
1206 :``%r``: zero-padded changeset revision number
1206 :``%r``: zero-padded changeset revision number
1207 :``%b``: basename of the exporting repository
1207 :``%b``: basename of the exporting repository
1208
1208
1209 Returns 0 on success.
1209 Returns 0 on success.
1210 """
1210 """
1211 ctx = scmutil.revsingle(repo, opts.get('rev'))
1211 ctx = scmutil.revsingle(repo, opts.get('rev'))
1212 m = scmutil.match(ctx, (file1,) + pats, opts)
1212 m = scmutil.match(ctx, (file1,) + pats, opts)
1213
1213
1214 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1214 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1215
1215
1216 @command('^clone',
1216 @command('^clone',
1217 [('U', 'noupdate', None,
1217 [('U', 'noupdate', None,
1218 _('the clone will include an empty working copy (only a repository)')),
1218 _('the clone will include an empty working copy (only a repository)')),
1219 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1219 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1220 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1220 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1221 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1221 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1222 ('', 'pull', None, _('use pull protocol to copy metadata')),
1222 ('', 'pull', None, _('use pull protocol to copy metadata')),
1223 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1223 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1224 ] + remoteopts,
1224 ] + remoteopts,
1225 _('[OPTION]... SOURCE [DEST]'),
1225 _('[OPTION]... SOURCE [DEST]'),
1226 norepo=True)
1226 norepo=True)
1227 def clone(ui, source, dest=None, **opts):
1227 def clone(ui, source, dest=None, **opts):
1228 """make a copy of an existing repository
1228 """make a copy of an existing repository
1229
1229
1230 Create a copy of an existing repository in a new directory.
1230 Create a copy of an existing repository in a new directory.
1231
1231
1232 If no destination directory name is specified, it defaults to the
1232 If no destination directory name is specified, it defaults to the
1233 basename of the source.
1233 basename of the source.
1234
1234
1235 The location of the source is added to the new repository's
1235 The location of the source is added to the new repository's
1236 ``.hg/hgrc`` file, as the default to be used for future pulls.
1236 ``.hg/hgrc`` file, as the default to be used for future pulls.
1237
1237
1238 Only local paths and ``ssh://`` URLs are supported as
1238 Only local paths and ``ssh://`` URLs are supported as
1239 destinations. For ``ssh://`` destinations, no working directory or
1239 destinations. For ``ssh://`` destinations, no working directory or
1240 ``.hg/hgrc`` will be created on the remote side.
1240 ``.hg/hgrc`` will be created on the remote side.
1241
1241
1242 To pull only a subset of changesets, specify one or more revisions
1242 To pull only a subset of changesets, specify one or more revisions
1243 identifiers with -r/--rev or branches with -b/--branch. The
1243 identifiers with -r/--rev or branches with -b/--branch. The
1244 resulting clone will contain only the specified changesets and
1244 resulting clone will contain only the specified changesets and
1245 their ancestors. These options (or 'clone src#rev dest') imply
1245 their ancestors. These options (or 'clone src#rev dest') imply
1246 --pull, even for local source repositories. Note that specifying a
1246 --pull, even for local source repositories. Note that specifying a
1247 tag will include the tagged changeset but not the changeset
1247 tag will include the tagged changeset but not the changeset
1248 containing the tag.
1248 containing the tag.
1249
1249
1250 If the source repository has a bookmark called '@' set, that
1250 If the source repository has a bookmark called '@' set, that
1251 revision will be checked out in the new repository by default.
1251 revision will be checked out in the new repository by default.
1252
1252
1253 To check out a particular version, use -u/--update, or
1253 To check out a particular version, use -u/--update, or
1254 -U/--noupdate to create a clone with no working directory.
1254 -U/--noupdate to create a clone with no working directory.
1255
1255
1256 .. container:: verbose
1256 .. container:: verbose
1257
1257
1258 For efficiency, hardlinks are used for cloning whenever the
1258 For efficiency, hardlinks are used for cloning whenever the
1259 source and destination are on the same filesystem (note this
1259 source and destination are on the same filesystem (note this
1260 applies only to the repository data, not to the working
1260 applies only to the repository data, not to the working
1261 directory). Some filesystems, such as AFS, implement hardlinking
1261 directory). Some filesystems, such as AFS, implement hardlinking
1262 incorrectly, but do not report errors. In these cases, use the
1262 incorrectly, but do not report errors. In these cases, use the
1263 --pull option to avoid hardlinking.
1263 --pull option to avoid hardlinking.
1264
1264
1265 In some cases, you can clone repositories and the working
1265 In some cases, you can clone repositories and the working
1266 directory using full hardlinks with ::
1266 directory using full hardlinks with ::
1267
1267
1268 $ cp -al REPO REPOCLONE
1268 $ cp -al REPO REPOCLONE
1269
1269
1270 This is the fastest way to clone, but it is not always safe. The
1270 This is the fastest way to clone, but it is not always safe. The
1271 operation is not atomic (making sure REPO is not modified during
1271 operation is not atomic (making sure REPO is not modified during
1272 the operation is up to you) and you have to make sure your
1272 the operation is up to you) and you have to make sure your
1273 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1273 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1274 so). Also, this is not compatible with certain extensions that
1274 so). Also, this is not compatible with certain extensions that
1275 place their metadata under the .hg directory, such as mq.
1275 place their metadata under the .hg directory, such as mq.
1276
1276
1277 Mercurial will update the working directory to the first applicable
1277 Mercurial will update the working directory to the first applicable
1278 revision from this list:
1278 revision from this list:
1279
1279
1280 a) null if -U or the source repository has no changesets
1280 a) null if -U or the source repository has no changesets
1281 b) if -u . and the source repository is local, the first parent of
1281 b) if -u . and the source repository is local, the first parent of
1282 the source repository's working directory
1282 the source repository's working directory
1283 c) the changeset specified with -u (if a branch name, this means the
1283 c) the changeset specified with -u (if a branch name, this means the
1284 latest head of that branch)
1284 latest head of that branch)
1285 d) the changeset specified with -r
1285 d) the changeset specified with -r
1286 e) the tipmost head specified with -b
1286 e) the tipmost head specified with -b
1287 f) the tipmost head specified with the url#branch source syntax
1287 f) the tipmost head specified with the url#branch source syntax
1288 g) the revision marked with the '@' bookmark, if present
1288 g) the revision marked with the '@' bookmark, if present
1289 h) the tipmost head of the default branch
1289 h) the tipmost head of the default branch
1290 i) tip
1290 i) tip
1291
1291
1292 Examples:
1292 Examples:
1293
1293
1294 - clone a remote repository to a new directory named hg/::
1294 - clone a remote repository to a new directory named hg/::
1295
1295
1296 hg clone http://selenic.com/hg
1296 hg clone http://selenic.com/hg
1297
1297
1298 - create a lightweight local clone::
1298 - create a lightweight local clone::
1299
1299
1300 hg clone project/ project-feature/
1300 hg clone project/ project-feature/
1301
1301
1302 - clone from an absolute path on an ssh server (note double-slash)::
1302 - clone from an absolute path on an ssh server (note double-slash)::
1303
1303
1304 hg clone ssh://user@server//home/projects/alpha/
1304 hg clone ssh://user@server//home/projects/alpha/
1305
1305
1306 - do a high-speed clone over a LAN while checking out a
1306 - do a high-speed clone over a LAN while checking out a
1307 specified version::
1307 specified version::
1308
1308
1309 hg clone --uncompressed http://server/repo -u 1.5
1309 hg clone --uncompressed http://server/repo -u 1.5
1310
1310
1311 - create a repository without changesets after a particular revision::
1311 - create a repository without changesets after a particular revision::
1312
1312
1313 hg clone -r 04e544 experimental/ good/
1313 hg clone -r 04e544 experimental/ good/
1314
1314
1315 - clone (and track) a particular named branch::
1315 - clone (and track) a particular named branch::
1316
1316
1317 hg clone http://selenic.com/hg#stable
1317 hg clone http://selenic.com/hg#stable
1318
1318
1319 See :hg:`help urls` for details on specifying URLs.
1319 See :hg:`help urls` for details on specifying URLs.
1320
1320
1321 Returns 0 on success.
1321 Returns 0 on success.
1322 """
1322 """
1323 if opts.get('noupdate') and opts.get('updaterev'):
1323 if opts.get('noupdate') and opts.get('updaterev'):
1324 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1324 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1325
1325
1326 r = hg.clone(ui, opts, source, dest,
1326 r = hg.clone(ui, opts, source, dest,
1327 pull=opts.get('pull'),
1327 pull=opts.get('pull'),
1328 stream=opts.get('uncompressed'),
1328 stream=opts.get('uncompressed'),
1329 rev=opts.get('rev'),
1329 rev=opts.get('rev'),
1330 update=opts.get('updaterev') or not opts.get('noupdate'),
1330 update=opts.get('updaterev') or not opts.get('noupdate'),
1331 branch=opts.get('branch'))
1331 branch=opts.get('branch'))
1332
1332
1333 return r is None
1333 return r is None
1334
1334
1335 @command('^commit|ci',
1335 @command('^commit|ci',
1336 [('A', 'addremove', None,
1336 [('A', 'addremove', None,
1337 _('mark new/missing files as added/removed before committing')),
1337 _('mark new/missing files as added/removed before committing')),
1338 ('', 'close-branch', None,
1338 ('', 'close-branch', None,
1339 _('mark a branch as closed, hiding it from the branch list')),
1339 _('mark a branch as closed, hiding it from the branch list')),
1340 ('', 'amend', None, _('amend the parent of the working dir')),
1340 ('', 'amend', None, _('amend the parent of the working dir')),
1341 ('s', 'secret', None, _('use the secret phase for committing')),
1341 ('s', 'secret', None, _('use the secret phase for committing')),
1342 ('e', 'edit', None, _('invoke editor on commit messages')),
1342 ('e', 'edit', None, _('invoke editor on commit messages')),
1343 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1343 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1344 _('[OPTION]... [FILE]...'),
1344 _('[OPTION]... [FILE]...'),
1345 inferrepo=True)
1345 inferrepo=True)
1346 def commit(ui, repo, *pats, **opts):
1346 def commit(ui, repo, *pats, **opts):
1347 """commit the specified files or all outstanding changes
1347 """commit the specified files or all outstanding changes
1348
1348
1349 Commit changes to the given files into the repository. Unlike a
1349 Commit changes to the given files into the repository. Unlike a
1350 centralized SCM, this operation is a local operation. See
1350 centralized SCM, this operation is a local operation. See
1351 :hg:`push` for a way to actively distribute your changes.
1351 :hg:`push` for a way to actively distribute your changes.
1352
1352
1353 If a list of files is omitted, all changes reported by :hg:`status`
1353 If a list of files is omitted, all changes reported by :hg:`status`
1354 will be committed.
1354 will be committed.
1355
1355
1356 If you are committing the result of a merge, do not provide any
1356 If you are committing the result of a merge, do not provide any
1357 filenames or -I/-X filters.
1357 filenames or -I/-X filters.
1358
1358
1359 If no commit message is specified, Mercurial starts your
1359 If no commit message is specified, Mercurial starts your
1360 configured editor where you can enter a message. In case your
1360 configured editor where you can enter a message. In case your
1361 commit fails, you will find a backup of your message in
1361 commit fails, you will find a backup of your message in
1362 ``.hg/last-message.txt``.
1362 ``.hg/last-message.txt``.
1363
1363
1364 The --amend flag can be used to amend the parent of the
1364 The --amend flag can be used to amend the parent of the
1365 working directory with a new commit that contains the changes
1365 working directory with a new commit that contains the changes
1366 in the parent in addition to those currently reported by :hg:`status`,
1366 in the parent in addition to those currently reported by :hg:`status`,
1367 if there are any. The old commit is stored in a backup bundle in
1367 if there are any. The old commit is stored in a backup bundle in
1368 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1368 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1369 on how to restore it).
1369 on how to restore it).
1370
1370
1371 Message, user and date are taken from the amended commit unless
1371 Message, user and date are taken from the amended commit unless
1372 specified. When a message isn't specified on the command line,
1372 specified. When a message isn't specified on the command line,
1373 the editor will open with the message of the amended commit.
1373 the editor will open with the message of the amended commit.
1374
1374
1375 It is not possible to amend public changesets (see :hg:`help phases`)
1375 It is not possible to amend public changesets (see :hg:`help phases`)
1376 or changesets that have children.
1376 or changesets that have children.
1377
1377
1378 See :hg:`help dates` for a list of formats valid for -d/--date.
1378 See :hg:`help dates` for a list of formats valid for -d/--date.
1379
1379
1380 Returns 0 on success, 1 if nothing changed.
1380 Returns 0 on success, 1 if nothing changed.
1381 """
1381 """
1382 if opts.get('subrepos'):
1382 if opts.get('subrepos'):
1383 if opts.get('amend'):
1383 if opts.get('amend'):
1384 raise util.Abort(_('cannot amend with --subrepos'))
1384 raise util.Abort(_('cannot amend with --subrepos'))
1385 # Let --subrepos on the command line override config setting.
1385 # Let --subrepos on the command line override config setting.
1386 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1386 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1387
1387
1388 cmdutil.checkunfinished(repo, commit=True)
1388 cmdutil.checkunfinished(repo, commit=True)
1389
1389
1390 branch = repo[None].branch()
1390 branch = repo[None].branch()
1391 bheads = repo.branchheads(branch)
1391 bheads = repo.branchheads(branch)
1392
1392
1393 extra = {}
1393 extra = {}
1394 if opts.get('close_branch'):
1394 if opts.get('close_branch'):
1395 extra['close'] = 1
1395 extra['close'] = 1
1396
1396
1397 if not bheads:
1397 if not bheads:
1398 raise util.Abort(_('can only close branch heads'))
1398 raise util.Abort(_('can only close branch heads'))
1399 elif opts.get('amend'):
1399 elif opts.get('amend'):
1400 if repo.parents()[0].p1().branch() != branch and \
1400 if repo.parents()[0].p1().branch() != branch and \
1401 repo.parents()[0].p2().branch() != branch:
1401 repo.parents()[0].p2().branch() != branch:
1402 raise util.Abort(_('can only close branch heads'))
1402 raise util.Abort(_('can only close branch heads'))
1403
1403
1404 if opts.get('amend'):
1404 if opts.get('amend'):
1405 if ui.configbool('ui', 'commitsubrepos'):
1405 if ui.configbool('ui', 'commitsubrepos'):
1406 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1406 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1407
1407
1408 old = repo['.']
1408 old = repo['.']
1409 if old.phase() == phases.public:
1409 if old.phase() == phases.public:
1410 raise util.Abort(_('cannot amend public changesets'))
1410 raise util.Abort(_('cannot amend public changesets'))
1411 if len(repo[None].parents()) > 1:
1411 if len(repo[None].parents()) > 1:
1412 raise util.Abort(_('cannot amend while merging'))
1412 raise util.Abort(_('cannot amend while merging'))
1413 if (not obsolete._enabled) and old.children():
1413 if (not obsolete._enabled) and old.children():
1414 raise util.Abort(_('cannot amend changeset with children'))
1414 raise util.Abort(_('cannot amend changeset with children'))
1415
1415
1416 # commitfunc is used only for temporary amend commit by cmdutil.amend
1416 # commitfunc is used only for temporary amend commit by cmdutil.amend
1417 def commitfunc(ui, repo, message, match, opts):
1417 def commitfunc(ui, repo, message, match, opts):
1418 return repo.commit(message,
1418 return repo.commit(message,
1419 opts.get('user') or old.user(),
1419 opts.get('user') or old.user(),
1420 opts.get('date') or old.date(),
1420 opts.get('date') or old.date(),
1421 match,
1421 match,
1422 extra=extra)
1422 extra=extra)
1423
1423
1424 current = repo._bookmarkcurrent
1424 current = repo._bookmarkcurrent
1425 marks = old.bookmarks()
1425 marks = old.bookmarks()
1426 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1426 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1427 if node == old.node():
1427 if node == old.node():
1428 ui.status(_("nothing changed\n"))
1428 ui.status(_("nothing changed\n"))
1429 return 1
1429 return 1
1430 elif marks:
1430 elif marks:
1431 ui.debug('moving bookmarks %r from %s to %s\n' %
1431 ui.debug('moving bookmarks %r from %s to %s\n' %
1432 (marks, old.hex(), hex(node)))
1432 (marks, old.hex(), hex(node)))
1433 newmarks = repo._bookmarks
1433 newmarks = repo._bookmarks
1434 for bm in marks:
1434 for bm in marks:
1435 newmarks[bm] = node
1435 newmarks[bm] = node
1436 if bm == current:
1436 if bm == current:
1437 bookmarks.setcurrent(repo, bm)
1437 bookmarks.setcurrent(repo, bm)
1438 newmarks.write()
1438 newmarks.write()
1439 else:
1439 else:
1440 def commitfunc(ui, repo, message, match, opts):
1440 def commitfunc(ui, repo, message, match, opts):
1441 backup = ui.backupconfig('phases', 'new-commit')
1441 backup = ui.backupconfig('phases', 'new-commit')
1442 baseui = repo.baseui
1442 baseui = repo.baseui
1443 basebackup = baseui.backupconfig('phases', 'new-commit')
1443 basebackup = baseui.backupconfig('phases', 'new-commit')
1444 try:
1444 try:
1445 if opts.get('secret'):
1445 if opts.get('secret'):
1446 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1446 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1447 # Propagate to subrepos
1447 # Propagate to subrepos
1448 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1448 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1449
1449
1450 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1450 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1451 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1451 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1452 return repo.commit(message, opts.get('user'), opts.get('date'),
1452 return repo.commit(message, opts.get('user'), opts.get('date'),
1453 match,
1453 match,
1454 editor=editor,
1454 editor=editor,
1455 extra=extra)
1455 extra=extra)
1456 finally:
1456 finally:
1457 ui.restoreconfig(backup)
1457 ui.restoreconfig(backup)
1458 repo.baseui.restoreconfig(basebackup)
1458 repo.baseui.restoreconfig(basebackup)
1459
1459
1460
1460
1461 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1461 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1462
1462
1463 if not node:
1463 if not node:
1464 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1464 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1465 if stat[3]:
1465 if stat[3]:
1466 ui.status(_("nothing changed (%d missing files, see "
1466 ui.status(_("nothing changed (%d missing files, see "
1467 "'hg status')\n") % len(stat[3]))
1467 "'hg status')\n") % len(stat[3]))
1468 else:
1468 else:
1469 ui.status(_("nothing changed\n"))
1469 ui.status(_("nothing changed\n"))
1470 return 1
1470 return 1
1471
1471
1472 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1472 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1473
1473
1474 @command('config|showconfig|debugconfig',
1474 @command('config|showconfig|debugconfig',
1475 [('u', 'untrusted', None, _('show untrusted configuration options')),
1475 [('u', 'untrusted', None, _('show untrusted configuration options')),
1476 ('e', 'edit', None, _('edit user config')),
1476 ('e', 'edit', None, _('edit user config')),
1477 ('l', 'local', None, _('edit repository config')),
1477 ('l', 'local', None, _('edit repository config')),
1478 ('g', 'global', None, _('edit global config'))],
1478 ('g', 'global', None, _('edit global config'))],
1479 _('[-u] [NAME]...'),
1479 _('[-u] [NAME]...'),
1480 optionalrepo=True)
1480 optionalrepo=True)
1481 def config(ui, repo, *values, **opts):
1481 def config(ui, repo, *values, **opts):
1482 """show combined config settings from all hgrc files
1482 """show combined config settings from all hgrc files
1483
1483
1484 With no arguments, print names and values of all config items.
1484 With no arguments, print names and values of all config items.
1485
1485
1486 With one argument of the form section.name, print just the value
1486 With one argument of the form section.name, print just the value
1487 of that config item.
1487 of that config item.
1488
1488
1489 With multiple arguments, print names and values of all config
1489 With multiple arguments, print names and values of all config
1490 items with matching section names.
1490 items with matching section names.
1491
1491
1492 With --edit, start an editor on the user-level config file. With
1492 With --edit, start an editor on the user-level config file. With
1493 --global, edit the system-wide config file. With --local, edit the
1493 --global, edit the system-wide config file. With --local, edit the
1494 repository-level config file.
1494 repository-level config file.
1495
1495
1496 With --debug, the source (filename and line number) is printed
1496 With --debug, the source (filename and line number) is printed
1497 for each config item.
1497 for each config item.
1498
1498
1499 See :hg:`help config` for more information about config files.
1499 See :hg:`help config` for more information about config files.
1500
1500
1501 Returns 0 on success, 1 if NAME does not exist.
1501 Returns 0 on success, 1 if NAME does not exist.
1502
1502
1503 """
1503 """
1504
1504
1505 if opts.get('edit') or opts.get('local') or opts.get('global'):
1505 if opts.get('edit') or opts.get('local') or opts.get('global'):
1506 if opts.get('local') and opts.get('global'):
1506 if opts.get('local') and opts.get('global'):
1507 raise util.Abort(_("can't use --local and --global together"))
1507 raise util.Abort(_("can't use --local and --global together"))
1508
1508
1509 if opts.get('local'):
1509 if opts.get('local'):
1510 if not repo:
1510 if not repo:
1511 raise util.Abort(_("can't use --local outside a repository"))
1511 raise util.Abort(_("can't use --local outside a repository"))
1512 paths = [repo.join('hgrc')]
1512 paths = [repo.join('hgrc')]
1513 elif opts.get('global'):
1513 elif opts.get('global'):
1514 paths = scmutil.systemrcpath()
1514 paths = scmutil.systemrcpath()
1515 else:
1515 else:
1516 paths = scmutil.userrcpath()
1516 paths = scmutil.userrcpath()
1517
1517
1518 for f in paths:
1518 for f in paths:
1519 if os.path.exists(f):
1519 if os.path.exists(f):
1520 break
1520 break
1521 else:
1521 else:
1522 f = paths[0]
1522 f = paths[0]
1523 fp = open(f, "w")
1523 fp = open(f, "w")
1524 fp.write(
1524 fp.write(
1525 '# example config (see "hg help config" for more info)\n'
1525 '# example config (see "hg help config" for more info)\n'
1526 '\n'
1526 '\n'
1527 '[ui]\n'
1527 '[ui]\n'
1528 '# name and email, e.g.\n'
1528 '# name and email, e.g.\n'
1529 '# username = Jane Doe <jdoe@example.com>\n'
1529 '# username = Jane Doe <jdoe@example.com>\n'
1530 'username =\n'
1530 'username =\n'
1531 '\n'
1531 '\n'
1532 '[extensions]\n'
1532 '[extensions]\n'
1533 '# uncomment these lines to enable some popular extensions\n'
1533 '# uncomment these lines to enable some popular extensions\n'
1534 '# (see "hg help extensions" for more info)\n'
1534 '# (see "hg help extensions" for more info)\n'
1535 '# pager =\n'
1535 '# pager =\n'
1536 '# progress =\n'
1536 '# progress =\n'
1537 '# color =\n')
1537 '# color =\n')
1538 fp.close()
1538 fp.close()
1539
1539
1540 editor = ui.geteditor()
1540 editor = ui.geteditor()
1541 util.system("%s \"%s\"" % (editor, f),
1541 util.system("%s \"%s\"" % (editor, f),
1542 onerr=util.Abort, errprefix=_("edit failed"),
1542 onerr=util.Abort, errprefix=_("edit failed"),
1543 out=ui.fout)
1543 out=ui.fout)
1544 return
1544 return
1545
1545
1546 for f in scmutil.rcpath():
1546 for f in scmutil.rcpath():
1547 ui.debug('read config from: %s\n' % f)
1547 ui.debug('read config from: %s\n' % f)
1548 untrusted = bool(opts.get('untrusted'))
1548 untrusted = bool(opts.get('untrusted'))
1549 if values:
1549 if values:
1550 sections = [v for v in values if '.' not in v]
1550 sections = [v for v in values if '.' not in v]
1551 items = [v for v in values if '.' in v]
1551 items = [v for v in values if '.' in v]
1552 if len(items) > 1 or items and sections:
1552 if len(items) > 1 or items and sections:
1553 raise util.Abort(_('only one config item permitted'))
1553 raise util.Abort(_('only one config item permitted'))
1554 matched = False
1554 matched = False
1555 for section, name, value in ui.walkconfig(untrusted=untrusted):
1555 for section, name, value in ui.walkconfig(untrusted=untrusted):
1556 value = str(value).replace('\n', '\\n')
1556 value = str(value).replace('\n', '\\n')
1557 sectname = section + '.' + name
1557 sectname = section + '.' + name
1558 if values:
1558 if values:
1559 for v in values:
1559 for v in values:
1560 if v == section:
1560 if v == section:
1561 ui.debug('%s: ' %
1561 ui.debug('%s: ' %
1562 ui.configsource(section, name, untrusted))
1562 ui.configsource(section, name, untrusted))
1563 ui.write('%s=%s\n' % (sectname, value))
1563 ui.write('%s=%s\n' % (sectname, value))
1564 matched = True
1564 matched = True
1565 elif v == sectname:
1565 elif v == sectname:
1566 ui.debug('%s: ' %
1566 ui.debug('%s: ' %
1567 ui.configsource(section, name, untrusted))
1567 ui.configsource(section, name, untrusted))
1568 ui.write(value, '\n')
1568 ui.write(value, '\n')
1569 matched = True
1569 matched = True
1570 else:
1570 else:
1571 ui.debug('%s: ' %
1571 ui.debug('%s: ' %
1572 ui.configsource(section, name, untrusted))
1572 ui.configsource(section, name, untrusted))
1573 ui.write('%s=%s\n' % (sectname, value))
1573 ui.write('%s=%s\n' % (sectname, value))
1574 matched = True
1574 matched = True
1575 if matched:
1575 if matched:
1576 return 0
1576 return 0
1577 return 1
1577 return 1
1578
1578
1579 @command('copy|cp',
1579 @command('copy|cp',
1580 [('A', 'after', None, _('record a copy that has already occurred')),
1580 [('A', 'after', None, _('record a copy that has already occurred')),
1581 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1581 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1582 ] + walkopts + dryrunopts,
1582 ] + walkopts + dryrunopts,
1583 _('[OPTION]... [SOURCE]... DEST'))
1583 _('[OPTION]... [SOURCE]... DEST'))
1584 def copy(ui, repo, *pats, **opts):
1584 def copy(ui, repo, *pats, **opts):
1585 """mark files as copied for the next commit
1585 """mark files as copied for the next commit
1586
1586
1587 Mark dest as having copies of source files. If dest is a
1587 Mark dest as having copies of source files. If dest is a
1588 directory, copies are put in that directory. If dest is a file,
1588 directory, copies are put in that directory. If dest is a file,
1589 the source must be a single file.
1589 the source must be a single file.
1590
1590
1591 By default, this command copies the contents of files as they
1591 By default, this command copies the contents of files as they
1592 exist in the working directory. If invoked with -A/--after, the
1592 exist in the working directory. If invoked with -A/--after, the
1593 operation is recorded, but no copying is performed.
1593 operation is recorded, but no copying is performed.
1594
1594
1595 This command takes effect with the next commit. To undo a copy
1595 This command takes effect with the next commit. To undo a copy
1596 before that, see :hg:`revert`.
1596 before that, see :hg:`revert`.
1597
1597
1598 Returns 0 on success, 1 if errors are encountered.
1598 Returns 0 on success, 1 if errors are encountered.
1599 """
1599 """
1600 wlock = repo.wlock(False)
1600 wlock = repo.wlock(False)
1601 try:
1601 try:
1602 return cmdutil.copy(ui, repo, pats, opts)
1602 return cmdutil.copy(ui, repo, pats, opts)
1603 finally:
1603 finally:
1604 wlock.release()
1604 wlock.release()
1605
1605
1606 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1606 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1607 def debugancestor(ui, repo, *args):
1607 def debugancestor(ui, repo, *args):
1608 """find the ancestor revision of two revisions in a given index"""
1608 """find the ancestor revision of two revisions in a given index"""
1609 if len(args) == 3:
1609 if len(args) == 3:
1610 index, rev1, rev2 = args
1610 index, rev1, rev2 = args
1611 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1611 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1612 lookup = r.lookup
1612 lookup = r.lookup
1613 elif len(args) == 2:
1613 elif len(args) == 2:
1614 if not repo:
1614 if not repo:
1615 raise util.Abort(_("there is no Mercurial repository here "
1615 raise util.Abort(_("there is no Mercurial repository here "
1616 "(.hg not found)"))
1616 "(.hg not found)"))
1617 rev1, rev2 = args
1617 rev1, rev2 = args
1618 r = repo.changelog
1618 r = repo.changelog
1619 lookup = repo.lookup
1619 lookup = repo.lookup
1620 else:
1620 else:
1621 raise util.Abort(_('either two or three arguments required'))
1621 raise util.Abort(_('either two or three arguments required'))
1622 a = r.ancestor(lookup(rev1), lookup(rev2))
1622 a = r.ancestor(lookup(rev1), lookup(rev2))
1623 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1623 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1624
1624
1625 @command('debugbuilddag',
1625 @command('debugbuilddag',
1626 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1626 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1627 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1627 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1628 ('n', 'new-file', None, _('add new file at each rev'))],
1628 ('n', 'new-file', None, _('add new file at each rev'))],
1629 _('[OPTION]... [TEXT]'))
1629 _('[OPTION]... [TEXT]'))
1630 def debugbuilddag(ui, repo, text=None,
1630 def debugbuilddag(ui, repo, text=None,
1631 mergeable_file=False,
1631 mergeable_file=False,
1632 overwritten_file=False,
1632 overwritten_file=False,
1633 new_file=False):
1633 new_file=False):
1634 """builds a repo with a given DAG from scratch in the current empty repo
1634 """builds a repo with a given DAG from scratch in the current empty repo
1635
1635
1636 The description of the DAG is read from stdin if not given on the
1636 The description of the DAG is read from stdin if not given on the
1637 command line.
1637 command line.
1638
1638
1639 Elements:
1639 Elements:
1640
1640
1641 - "+n" is a linear run of n nodes based on the current default parent
1641 - "+n" is a linear run of n nodes based on the current default parent
1642 - "." is a single node based on the current default parent
1642 - "." is a single node based on the current default parent
1643 - "$" resets the default parent to null (implied at the start);
1643 - "$" resets the default parent to null (implied at the start);
1644 otherwise the default parent is always the last node created
1644 otherwise the default parent is always the last node created
1645 - "<p" sets the default parent to the backref p
1645 - "<p" sets the default parent to the backref p
1646 - "*p" is a fork at parent p, which is a backref
1646 - "*p" is a fork at parent p, which is a backref
1647 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1647 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1648 - "/p2" is a merge of the preceding node and p2
1648 - "/p2" is a merge of the preceding node and p2
1649 - ":tag" defines a local tag for the preceding node
1649 - ":tag" defines a local tag for the preceding node
1650 - "@branch" sets the named branch for subsequent nodes
1650 - "@branch" sets the named branch for subsequent nodes
1651 - "#...\\n" is a comment up to the end of the line
1651 - "#...\\n" is a comment up to the end of the line
1652
1652
1653 Whitespace between the above elements is ignored.
1653 Whitespace between the above elements is ignored.
1654
1654
1655 A backref is either
1655 A backref is either
1656
1656
1657 - a number n, which references the node curr-n, where curr is the current
1657 - a number n, which references the node curr-n, where curr is the current
1658 node, or
1658 node, or
1659 - the name of a local tag you placed earlier using ":tag", or
1659 - the name of a local tag you placed earlier using ":tag", or
1660 - empty to denote the default parent.
1660 - empty to denote the default parent.
1661
1661
1662 All string valued-elements are either strictly alphanumeric, or must
1662 All string valued-elements are either strictly alphanumeric, or must
1663 be enclosed in double quotes ("..."), with "\\" as escape character.
1663 be enclosed in double quotes ("..."), with "\\" as escape character.
1664 """
1664 """
1665
1665
1666 if text is None:
1666 if text is None:
1667 ui.status(_("reading DAG from stdin\n"))
1667 ui.status(_("reading DAG from stdin\n"))
1668 text = ui.fin.read()
1668 text = ui.fin.read()
1669
1669
1670 cl = repo.changelog
1670 cl = repo.changelog
1671 if len(cl) > 0:
1671 if len(cl) > 0:
1672 raise util.Abort(_('repository is not empty'))
1672 raise util.Abort(_('repository is not empty'))
1673
1673
1674 # determine number of revs in DAG
1674 # determine number of revs in DAG
1675 total = 0
1675 total = 0
1676 for type, data in dagparser.parsedag(text):
1676 for type, data in dagparser.parsedag(text):
1677 if type == 'n':
1677 if type == 'n':
1678 total += 1
1678 total += 1
1679
1679
1680 if mergeable_file:
1680 if mergeable_file:
1681 linesperrev = 2
1681 linesperrev = 2
1682 # make a file with k lines per rev
1682 # make a file with k lines per rev
1683 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1683 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1684 initialmergedlines.append("")
1684 initialmergedlines.append("")
1685
1685
1686 tags = []
1686 tags = []
1687
1687
1688 lock = tr = None
1688 lock = tr = None
1689 try:
1689 try:
1690 lock = repo.lock()
1690 lock = repo.lock()
1691 tr = repo.transaction("builddag")
1691 tr = repo.transaction("builddag")
1692
1692
1693 at = -1
1693 at = -1
1694 atbranch = 'default'
1694 atbranch = 'default'
1695 nodeids = []
1695 nodeids = []
1696 id = 0
1696 id = 0
1697 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1697 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1698 for type, data in dagparser.parsedag(text):
1698 for type, data in dagparser.parsedag(text):
1699 if type == 'n':
1699 if type == 'n':
1700 ui.note(('node %s\n' % str(data)))
1700 ui.note(('node %s\n' % str(data)))
1701 id, ps = data
1701 id, ps = data
1702
1702
1703 files = []
1703 files = []
1704 fctxs = {}
1704 fctxs = {}
1705
1705
1706 p2 = None
1706 p2 = None
1707 if mergeable_file:
1707 if mergeable_file:
1708 fn = "mf"
1708 fn = "mf"
1709 p1 = repo[ps[0]]
1709 p1 = repo[ps[0]]
1710 if len(ps) > 1:
1710 if len(ps) > 1:
1711 p2 = repo[ps[1]]
1711 p2 = repo[ps[1]]
1712 pa = p1.ancestor(p2)
1712 pa = p1.ancestor(p2)
1713 base, local, other = [x[fn].data() for x in (pa, p1,
1713 base, local, other = [x[fn].data() for x in (pa, p1,
1714 p2)]
1714 p2)]
1715 m3 = simplemerge.Merge3Text(base, local, other)
1715 m3 = simplemerge.Merge3Text(base, local, other)
1716 ml = [l.strip() for l in m3.merge_lines()]
1716 ml = [l.strip() for l in m3.merge_lines()]
1717 ml.append("")
1717 ml.append("")
1718 elif at > 0:
1718 elif at > 0:
1719 ml = p1[fn].data().split("\n")
1719 ml = p1[fn].data().split("\n")
1720 else:
1720 else:
1721 ml = initialmergedlines
1721 ml = initialmergedlines
1722 ml[id * linesperrev] += " r%i" % id
1722 ml[id * linesperrev] += " r%i" % id
1723 mergedtext = "\n".join(ml)
1723 mergedtext = "\n".join(ml)
1724 files.append(fn)
1724 files.append(fn)
1725 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1725 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1726
1726
1727 if overwritten_file:
1727 if overwritten_file:
1728 fn = "of"
1728 fn = "of"
1729 files.append(fn)
1729 files.append(fn)
1730 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1730 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1731
1731
1732 if new_file:
1732 if new_file:
1733 fn = "nf%i" % id
1733 fn = "nf%i" % id
1734 files.append(fn)
1734 files.append(fn)
1735 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1735 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1736 if len(ps) > 1:
1736 if len(ps) > 1:
1737 if not p2:
1737 if not p2:
1738 p2 = repo[ps[1]]
1738 p2 = repo[ps[1]]
1739 for fn in p2:
1739 for fn in p2:
1740 if fn.startswith("nf"):
1740 if fn.startswith("nf"):
1741 files.append(fn)
1741 files.append(fn)
1742 fctxs[fn] = p2[fn]
1742 fctxs[fn] = p2[fn]
1743
1743
1744 def fctxfn(repo, cx, path):
1744 def fctxfn(repo, cx, path):
1745 return fctxs.get(path)
1745 return fctxs.get(path)
1746
1746
1747 if len(ps) == 0 or ps[0] < 0:
1747 if len(ps) == 0 or ps[0] < 0:
1748 pars = [None, None]
1748 pars = [None, None]
1749 elif len(ps) == 1:
1749 elif len(ps) == 1:
1750 pars = [nodeids[ps[0]], None]
1750 pars = [nodeids[ps[0]], None]
1751 else:
1751 else:
1752 pars = [nodeids[p] for p in ps]
1752 pars = [nodeids[p] for p in ps]
1753 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1753 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1754 date=(id, 0),
1754 date=(id, 0),
1755 user="debugbuilddag",
1755 user="debugbuilddag",
1756 extra={'branch': atbranch})
1756 extra={'branch': atbranch})
1757 nodeid = repo.commitctx(cx)
1757 nodeid = repo.commitctx(cx)
1758 nodeids.append(nodeid)
1758 nodeids.append(nodeid)
1759 at = id
1759 at = id
1760 elif type == 'l':
1760 elif type == 'l':
1761 id, name = data
1761 id, name = data
1762 ui.note(('tag %s\n' % name))
1762 ui.note(('tag %s\n' % name))
1763 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1763 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1764 elif type == 'a':
1764 elif type == 'a':
1765 ui.note(('branch %s\n' % data))
1765 ui.note(('branch %s\n' % data))
1766 atbranch = data
1766 atbranch = data
1767 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1767 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1768 tr.close()
1768 tr.close()
1769
1769
1770 if tags:
1770 if tags:
1771 repo.opener.write("localtags", "".join(tags))
1771 repo.opener.write("localtags", "".join(tags))
1772 finally:
1772 finally:
1773 ui.progress(_('building'), None)
1773 ui.progress(_('building'), None)
1774 release(tr, lock)
1774 release(tr, lock)
1775
1775
1776 @command('debugbundle',
1776 @command('debugbundle',
1777 [('a', 'all', None, _('show all details'))],
1777 [('a', 'all', None, _('show all details'))],
1778 _('FILE'),
1778 _('FILE'),
1779 norepo=True)
1779 norepo=True)
1780 def debugbundle(ui, bundlepath, all=None, **opts):
1780 def debugbundle(ui, bundlepath, all=None, **opts):
1781 """lists the contents of a bundle"""
1781 """lists the contents of a bundle"""
1782 f = hg.openpath(ui, bundlepath)
1782 f = hg.openpath(ui, bundlepath)
1783 try:
1783 try:
1784 gen = exchange.readbundle(ui, f, bundlepath)
1784 gen = exchange.readbundle(ui, f, bundlepath)
1785 if all:
1785 if all:
1786 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1786 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1787
1787
1788 def showchunks(named):
1788 def showchunks(named):
1789 ui.write("\n%s\n" % named)
1789 ui.write("\n%s\n" % named)
1790 chain = None
1790 chain = None
1791 while True:
1791 while True:
1792 chunkdata = gen.deltachunk(chain)
1792 chunkdata = gen.deltachunk(chain)
1793 if not chunkdata:
1793 if not chunkdata:
1794 break
1794 break
1795 node = chunkdata['node']
1795 node = chunkdata['node']
1796 p1 = chunkdata['p1']
1796 p1 = chunkdata['p1']
1797 p2 = chunkdata['p2']
1797 p2 = chunkdata['p2']
1798 cs = chunkdata['cs']
1798 cs = chunkdata['cs']
1799 deltabase = chunkdata['deltabase']
1799 deltabase = chunkdata['deltabase']
1800 delta = chunkdata['delta']
1800 delta = chunkdata['delta']
1801 ui.write("%s %s %s %s %s %s\n" %
1801 ui.write("%s %s %s %s %s %s\n" %
1802 (hex(node), hex(p1), hex(p2),
1802 (hex(node), hex(p1), hex(p2),
1803 hex(cs), hex(deltabase), len(delta)))
1803 hex(cs), hex(deltabase), len(delta)))
1804 chain = node
1804 chain = node
1805
1805
1806 chunkdata = gen.changelogheader()
1806 chunkdata = gen.changelogheader()
1807 showchunks("changelog")
1807 showchunks("changelog")
1808 chunkdata = gen.manifestheader()
1808 chunkdata = gen.manifestheader()
1809 showchunks("manifest")
1809 showchunks("manifest")
1810 while True:
1810 while True:
1811 chunkdata = gen.filelogheader()
1811 chunkdata = gen.filelogheader()
1812 if not chunkdata:
1812 if not chunkdata:
1813 break
1813 break
1814 fname = chunkdata['filename']
1814 fname = chunkdata['filename']
1815 showchunks(fname)
1815 showchunks(fname)
1816 else:
1816 else:
1817 chunkdata = gen.changelogheader()
1817 chunkdata = gen.changelogheader()
1818 chain = None
1818 chain = None
1819 while True:
1819 while True:
1820 chunkdata = gen.deltachunk(chain)
1820 chunkdata = gen.deltachunk(chain)
1821 if not chunkdata:
1821 if not chunkdata:
1822 break
1822 break
1823 node = chunkdata['node']
1823 node = chunkdata['node']
1824 ui.write("%s\n" % hex(node))
1824 ui.write("%s\n" % hex(node))
1825 chain = node
1825 chain = node
1826 finally:
1826 finally:
1827 f.close()
1827 f.close()
1828
1828
1829 @command('debugcheckstate', [], '')
1829 @command('debugcheckstate', [], '')
1830 def debugcheckstate(ui, repo):
1830 def debugcheckstate(ui, repo):
1831 """validate the correctness of the current dirstate"""
1831 """validate the correctness of the current dirstate"""
1832 parent1, parent2 = repo.dirstate.parents()
1832 parent1, parent2 = repo.dirstate.parents()
1833 m1 = repo[parent1].manifest()
1833 m1 = repo[parent1].manifest()
1834 m2 = repo[parent2].manifest()
1834 m2 = repo[parent2].manifest()
1835 errors = 0
1835 errors = 0
1836 for f in repo.dirstate:
1836 for f in repo.dirstate:
1837 state = repo.dirstate[f]
1837 state = repo.dirstate[f]
1838 if state in "nr" and f not in m1:
1838 if state in "nr" and f not in m1:
1839 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1839 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1840 errors += 1
1840 errors += 1
1841 if state in "a" and f in m1:
1841 if state in "a" and f in m1:
1842 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1842 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1843 errors += 1
1843 errors += 1
1844 if state in "m" and f not in m1 and f not in m2:
1844 if state in "m" and f not in m1 and f not in m2:
1845 ui.warn(_("%s in state %s, but not in either manifest\n") %
1845 ui.warn(_("%s in state %s, but not in either manifest\n") %
1846 (f, state))
1846 (f, state))
1847 errors += 1
1847 errors += 1
1848 for f in m1:
1848 for f in m1:
1849 state = repo.dirstate[f]
1849 state = repo.dirstate[f]
1850 if state not in "nrm":
1850 if state not in "nrm":
1851 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1851 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1852 errors += 1
1852 errors += 1
1853 if errors:
1853 if errors:
1854 error = _(".hg/dirstate inconsistent with current parent's manifest")
1854 error = _(".hg/dirstate inconsistent with current parent's manifest")
1855 raise util.Abort(error)
1855 raise util.Abort(error)
1856
1856
1857 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1857 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1858 def debugcommands(ui, cmd='', *args):
1858 def debugcommands(ui, cmd='', *args):
1859 """list all available commands and options"""
1859 """list all available commands and options"""
1860 for cmd, vals in sorted(table.iteritems()):
1860 for cmd, vals in sorted(table.iteritems()):
1861 cmd = cmd.split('|')[0].strip('^')
1861 cmd = cmd.split('|')[0].strip('^')
1862 opts = ', '.join([i[1] for i in vals[1]])
1862 opts = ', '.join([i[1] for i in vals[1]])
1863 ui.write('%s: %s\n' % (cmd, opts))
1863 ui.write('%s: %s\n' % (cmd, opts))
1864
1864
1865 @command('debugcomplete',
1865 @command('debugcomplete',
1866 [('o', 'options', None, _('show the command options'))],
1866 [('o', 'options', None, _('show the command options'))],
1867 _('[-o] CMD'),
1867 _('[-o] CMD'),
1868 norepo=True)
1868 norepo=True)
1869 def debugcomplete(ui, cmd='', **opts):
1869 def debugcomplete(ui, cmd='', **opts):
1870 """returns the completion list associated with the given command"""
1870 """returns the completion list associated with the given command"""
1871
1871
1872 if opts.get('options'):
1872 if opts.get('options'):
1873 options = []
1873 options = []
1874 otables = [globalopts]
1874 otables = [globalopts]
1875 if cmd:
1875 if cmd:
1876 aliases, entry = cmdutil.findcmd(cmd, table, False)
1876 aliases, entry = cmdutil.findcmd(cmd, table, False)
1877 otables.append(entry[1])
1877 otables.append(entry[1])
1878 for t in otables:
1878 for t in otables:
1879 for o in t:
1879 for o in t:
1880 if "(DEPRECATED)" in o[3]:
1880 if "(DEPRECATED)" in o[3]:
1881 continue
1881 continue
1882 if o[0]:
1882 if o[0]:
1883 options.append('-%s' % o[0])
1883 options.append('-%s' % o[0])
1884 options.append('--%s' % o[1])
1884 options.append('--%s' % o[1])
1885 ui.write("%s\n" % "\n".join(options))
1885 ui.write("%s\n" % "\n".join(options))
1886 return
1886 return
1887
1887
1888 cmdlist = cmdutil.findpossible(cmd, table)
1888 cmdlist = cmdutil.findpossible(cmd, table)
1889 if ui.verbose:
1889 if ui.verbose:
1890 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1890 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1891 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1891 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1892
1892
1893 @command('debugdag',
1893 @command('debugdag',
1894 [('t', 'tags', None, _('use tags as labels')),
1894 [('t', 'tags', None, _('use tags as labels')),
1895 ('b', 'branches', None, _('annotate with branch names')),
1895 ('b', 'branches', None, _('annotate with branch names')),
1896 ('', 'dots', None, _('use dots for runs')),
1896 ('', 'dots', None, _('use dots for runs')),
1897 ('s', 'spaces', None, _('separate elements by spaces'))],
1897 ('s', 'spaces', None, _('separate elements by spaces'))],
1898 _('[OPTION]... [FILE [REV]...]'),
1898 _('[OPTION]... [FILE [REV]...]'),
1899 optionalrepo=True)
1899 optionalrepo=True)
1900 def debugdag(ui, repo, file_=None, *revs, **opts):
1900 def debugdag(ui, repo, file_=None, *revs, **opts):
1901 """format the changelog or an index DAG as a concise textual description
1901 """format the changelog or an index DAG as a concise textual description
1902
1902
1903 If you pass a revlog index, the revlog's DAG is emitted. If you list
1903 If you pass a revlog index, the revlog's DAG is emitted. If you list
1904 revision numbers, they get labeled in the output as rN.
1904 revision numbers, they get labeled in the output as rN.
1905
1905
1906 Otherwise, the changelog DAG of the current repo is emitted.
1906 Otherwise, the changelog DAG of the current repo is emitted.
1907 """
1907 """
1908 spaces = opts.get('spaces')
1908 spaces = opts.get('spaces')
1909 dots = opts.get('dots')
1909 dots = opts.get('dots')
1910 if file_:
1910 if file_:
1911 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1911 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1912 revs = set((int(r) for r in revs))
1912 revs = set((int(r) for r in revs))
1913 def events():
1913 def events():
1914 for r in rlog:
1914 for r in rlog:
1915 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1915 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1916 if p != -1))
1916 if p != -1))
1917 if r in revs:
1917 if r in revs:
1918 yield 'l', (r, "r%i" % r)
1918 yield 'l', (r, "r%i" % r)
1919 elif repo:
1919 elif repo:
1920 cl = repo.changelog
1920 cl = repo.changelog
1921 tags = opts.get('tags')
1921 tags = opts.get('tags')
1922 branches = opts.get('branches')
1922 branches = opts.get('branches')
1923 if tags:
1923 if tags:
1924 labels = {}
1924 labels = {}
1925 for l, n in repo.tags().items():
1925 for l, n in repo.tags().items():
1926 labels.setdefault(cl.rev(n), []).append(l)
1926 labels.setdefault(cl.rev(n), []).append(l)
1927 def events():
1927 def events():
1928 b = "default"
1928 b = "default"
1929 for r in cl:
1929 for r in cl:
1930 if branches:
1930 if branches:
1931 newb = cl.read(cl.node(r))[5]['branch']
1931 newb = cl.read(cl.node(r))[5]['branch']
1932 if newb != b:
1932 if newb != b:
1933 yield 'a', newb
1933 yield 'a', newb
1934 b = newb
1934 b = newb
1935 yield 'n', (r, list(p for p in cl.parentrevs(r)
1935 yield 'n', (r, list(p for p in cl.parentrevs(r)
1936 if p != -1))
1936 if p != -1))
1937 if tags:
1937 if tags:
1938 ls = labels.get(r)
1938 ls = labels.get(r)
1939 if ls:
1939 if ls:
1940 for l in ls:
1940 for l in ls:
1941 yield 'l', (r, l)
1941 yield 'l', (r, l)
1942 else:
1942 else:
1943 raise util.Abort(_('need repo for changelog dag'))
1943 raise util.Abort(_('need repo for changelog dag'))
1944
1944
1945 for line in dagparser.dagtextlines(events(),
1945 for line in dagparser.dagtextlines(events(),
1946 addspaces=spaces,
1946 addspaces=spaces,
1947 wraplabels=True,
1947 wraplabels=True,
1948 wrapannotations=True,
1948 wrapannotations=True,
1949 wrapnonlinear=dots,
1949 wrapnonlinear=dots,
1950 usedots=dots,
1950 usedots=dots,
1951 maxlinewidth=70):
1951 maxlinewidth=70):
1952 ui.write(line)
1952 ui.write(line)
1953 ui.write("\n")
1953 ui.write("\n")
1954
1954
1955 @command('debugdata',
1955 @command('debugdata',
1956 [('c', 'changelog', False, _('open changelog')),
1956 [('c', 'changelog', False, _('open changelog')),
1957 ('m', 'manifest', False, _('open manifest'))],
1957 ('m', 'manifest', False, _('open manifest'))],
1958 _('-c|-m|FILE REV'))
1958 _('-c|-m|FILE REV'))
1959 def debugdata(ui, repo, file_, rev=None, **opts):
1959 def debugdata(ui, repo, file_, rev=None, **opts):
1960 """dump the contents of a data file revision"""
1960 """dump the contents of a data file revision"""
1961 if opts.get('changelog') or opts.get('manifest'):
1961 if opts.get('changelog') or opts.get('manifest'):
1962 file_, rev = None, file_
1962 file_, rev = None, file_
1963 elif rev is None:
1963 elif rev is None:
1964 raise error.CommandError('debugdata', _('invalid arguments'))
1964 raise error.CommandError('debugdata', _('invalid arguments'))
1965 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1965 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1966 try:
1966 try:
1967 ui.write(r.revision(r.lookup(rev)))
1967 ui.write(r.revision(r.lookup(rev)))
1968 except KeyError:
1968 except KeyError:
1969 raise util.Abort(_('invalid revision identifier %s') % rev)
1969 raise util.Abort(_('invalid revision identifier %s') % rev)
1970
1970
1971 @command('debugdate',
1971 @command('debugdate',
1972 [('e', 'extended', None, _('try extended date formats'))],
1972 [('e', 'extended', None, _('try extended date formats'))],
1973 _('[-e] DATE [RANGE]'),
1973 _('[-e] DATE [RANGE]'),
1974 norepo=True, optionalrepo=True)
1974 norepo=True, optionalrepo=True)
1975 def debugdate(ui, date, range=None, **opts):
1975 def debugdate(ui, date, range=None, **opts):
1976 """parse and display a date"""
1976 """parse and display a date"""
1977 if opts["extended"]:
1977 if opts["extended"]:
1978 d = util.parsedate(date, util.extendeddateformats)
1978 d = util.parsedate(date, util.extendeddateformats)
1979 else:
1979 else:
1980 d = util.parsedate(date)
1980 d = util.parsedate(date)
1981 ui.write(("internal: %s %s\n") % d)
1981 ui.write(("internal: %s %s\n") % d)
1982 ui.write(("standard: %s\n") % util.datestr(d))
1982 ui.write(("standard: %s\n") % util.datestr(d))
1983 if range:
1983 if range:
1984 m = util.matchdate(range)
1984 m = util.matchdate(range)
1985 ui.write(("match: %s\n") % m(d[0]))
1985 ui.write(("match: %s\n") % m(d[0]))
1986
1986
1987 @command('debugdiscovery',
1987 @command('debugdiscovery',
1988 [('', 'old', None, _('use old-style discovery')),
1988 [('', 'old', None, _('use old-style discovery')),
1989 ('', 'nonheads', None,
1989 ('', 'nonheads', None,
1990 _('use old-style discovery with non-heads included')),
1990 _('use old-style discovery with non-heads included')),
1991 ] + remoteopts,
1991 ] + remoteopts,
1992 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1992 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1993 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1993 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1994 """runs the changeset discovery protocol in isolation"""
1994 """runs the changeset discovery protocol in isolation"""
1995 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1995 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1996 opts.get('branch'))
1996 opts.get('branch'))
1997 remote = hg.peer(repo, opts, remoteurl)
1997 remote = hg.peer(repo, opts, remoteurl)
1998 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1998 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1999
1999
2000 # make sure tests are repeatable
2000 # make sure tests are repeatable
2001 random.seed(12323)
2001 random.seed(12323)
2002
2002
2003 def doit(localheads, remoteheads, remote=remote):
2003 def doit(localheads, remoteheads, remote=remote):
2004 if opts.get('old'):
2004 if opts.get('old'):
2005 if localheads:
2005 if localheads:
2006 raise util.Abort('cannot use localheads with old style '
2006 raise util.Abort('cannot use localheads with old style '
2007 'discovery')
2007 'discovery')
2008 if not util.safehasattr(remote, 'branches'):
2008 if not util.safehasattr(remote, 'branches'):
2009 # enable in-client legacy support
2009 # enable in-client legacy support
2010 remote = localrepo.locallegacypeer(remote.local())
2010 remote = localrepo.locallegacypeer(remote.local())
2011 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2011 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2012 force=True)
2012 force=True)
2013 common = set(common)
2013 common = set(common)
2014 if not opts.get('nonheads'):
2014 if not opts.get('nonheads'):
2015 ui.write(("unpruned common: %s\n") %
2015 ui.write(("unpruned common: %s\n") %
2016 " ".join(sorted(short(n) for n in common)))
2016 " ".join(sorted(short(n) for n in common)))
2017 dag = dagutil.revlogdag(repo.changelog)
2017 dag = dagutil.revlogdag(repo.changelog)
2018 all = dag.ancestorset(dag.internalizeall(common))
2018 all = dag.ancestorset(dag.internalizeall(common))
2019 common = dag.externalizeall(dag.headsetofconnecteds(all))
2019 common = dag.externalizeall(dag.headsetofconnecteds(all))
2020 else:
2020 else:
2021 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2021 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2022 common = set(common)
2022 common = set(common)
2023 rheads = set(hds)
2023 rheads = set(hds)
2024 lheads = set(repo.heads())
2024 lheads = set(repo.heads())
2025 ui.write(("common heads: %s\n") %
2025 ui.write(("common heads: %s\n") %
2026 " ".join(sorted(short(n) for n in common)))
2026 " ".join(sorted(short(n) for n in common)))
2027 if lheads <= common:
2027 if lheads <= common:
2028 ui.write(("local is subset\n"))
2028 ui.write(("local is subset\n"))
2029 elif rheads <= common:
2029 elif rheads <= common:
2030 ui.write(("remote is subset\n"))
2030 ui.write(("remote is subset\n"))
2031
2031
2032 serverlogs = opts.get('serverlog')
2032 serverlogs = opts.get('serverlog')
2033 if serverlogs:
2033 if serverlogs:
2034 for filename in serverlogs:
2034 for filename in serverlogs:
2035 logfile = open(filename, 'r')
2035 logfile = open(filename, 'r')
2036 try:
2036 try:
2037 line = logfile.readline()
2037 line = logfile.readline()
2038 while line:
2038 while line:
2039 parts = line.strip().split(';')
2039 parts = line.strip().split(';')
2040 op = parts[1]
2040 op = parts[1]
2041 if op == 'cg':
2041 if op == 'cg':
2042 pass
2042 pass
2043 elif op == 'cgss':
2043 elif op == 'cgss':
2044 doit(parts[2].split(' '), parts[3].split(' '))
2044 doit(parts[2].split(' '), parts[3].split(' '))
2045 elif op == 'unb':
2045 elif op == 'unb':
2046 doit(parts[3].split(' '), parts[2].split(' '))
2046 doit(parts[3].split(' '), parts[2].split(' '))
2047 line = logfile.readline()
2047 line = logfile.readline()
2048 finally:
2048 finally:
2049 logfile.close()
2049 logfile.close()
2050
2050
2051 else:
2051 else:
2052 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2052 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2053 opts.get('remote_head'))
2053 opts.get('remote_head'))
2054 localrevs = opts.get('local_head')
2054 localrevs = opts.get('local_head')
2055 doit(localrevs, remoterevs)
2055 doit(localrevs, remoterevs)
2056
2056
2057 @command('debugfileset',
2057 @command('debugfileset',
2058 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2058 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2059 _('[-r REV] FILESPEC'))
2059 _('[-r REV] FILESPEC'))
2060 def debugfileset(ui, repo, expr, **opts):
2060 def debugfileset(ui, repo, expr, **opts):
2061 '''parse and apply a fileset specification'''
2061 '''parse and apply a fileset specification'''
2062 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2062 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2063 if ui.verbose:
2063 if ui.verbose:
2064 tree = fileset.parse(expr)[0]
2064 tree = fileset.parse(expr)[0]
2065 ui.note(tree, "\n")
2065 ui.note(tree, "\n")
2066
2066
2067 for f in ctx.getfileset(expr):
2067 for f in ctx.getfileset(expr):
2068 ui.write("%s\n" % f)
2068 ui.write("%s\n" % f)
2069
2069
2070 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2070 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2071 def debugfsinfo(ui, path="."):
2071 def debugfsinfo(ui, path="."):
2072 """show information detected about current filesystem"""
2072 """show information detected about current filesystem"""
2073 util.writefile('.debugfsinfo', '')
2073 util.writefile('.debugfsinfo', '')
2074 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2074 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2075 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2075 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2076 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2076 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2077 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2077 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2078 and 'yes' or 'no'))
2078 and 'yes' or 'no'))
2079 os.unlink('.debugfsinfo')
2079 os.unlink('.debugfsinfo')
2080
2080
2081 @command('debuggetbundle',
2081 @command('debuggetbundle',
2082 [('H', 'head', [], _('id of head node'), _('ID')),
2082 [('H', 'head', [], _('id of head node'), _('ID')),
2083 ('C', 'common', [], _('id of common node'), _('ID')),
2083 ('C', 'common', [], _('id of common node'), _('ID')),
2084 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2084 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2085 _('REPO FILE [-H|-C ID]...'),
2085 _('REPO FILE [-H|-C ID]...'),
2086 norepo=True)
2086 norepo=True)
2087 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2087 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2088 """retrieves a bundle from a repo
2088 """retrieves a bundle from a repo
2089
2089
2090 Every ID must be a full-length hex node id string. Saves the bundle to the
2090 Every ID must be a full-length hex node id string. Saves the bundle to the
2091 given file.
2091 given file.
2092 """
2092 """
2093 repo = hg.peer(ui, opts, repopath)
2093 repo = hg.peer(ui, opts, repopath)
2094 if not repo.capable('getbundle'):
2094 if not repo.capable('getbundle'):
2095 raise util.Abort("getbundle() not supported by target repository")
2095 raise util.Abort("getbundle() not supported by target repository")
2096 args = {}
2096 args = {}
2097 if common:
2097 if common:
2098 args['common'] = [bin(s) for s in common]
2098 args['common'] = [bin(s) for s in common]
2099 if head:
2099 if head:
2100 args['heads'] = [bin(s) for s in head]
2100 args['heads'] = [bin(s) for s in head]
2101 # TODO: get desired bundlecaps from command line.
2101 # TODO: get desired bundlecaps from command line.
2102 args['bundlecaps'] = None
2102 args['bundlecaps'] = None
2103 bundle = repo.getbundle('debug', **args)
2103 bundle = repo.getbundle('debug', **args)
2104
2104
2105 bundletype = opts.get('type', 'bzip2').lower()
2105 bundletype = opts.get('type', 'bzip2').lower()
2106 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2106 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2107 bundletype = btypes.get(bundletype)
2107 bundletype = btypes.get(bundletype)
2108 if bundletype not in changegroup.bundletypes:
2108 if bundletype not in changegroup.bundletypes:
2109 raise util.Abort(_('unknown bundle type specified with --type'))
2109 raise util.Abort(_('unknown bundle type specified with --type'))
2110 changegroup.writebundle(bundle, bundlepath, bundletype)
2110 changegroup.writebundle(bundle, bundlepath, bundletype)
2111
2111
2112 @command('debugignore', [], '')
2112 @command('debugignore', [], '')
2113 def debugignore(ui, repo, *values, **opts):
2113 def debugignore(ui, repo, *values, **opts):
2114 """display the combined ignore pattern"""
2114 """display the combined ignore pattern"""
2115 ignore = repo.dirstate._ignore
2115 ignore = repo.dirstate._ignore
2116 includepat = getattr(ignore, 'includepat', None)
2116 includepat = getattr(ignore, 'includepat', None)
2117 if includepat is not None:
2117 if includepat is not None:
2118 ui.write("%s\n" % includepat)
2118 ui.write("%s\n" % includepat)
2119 else:
2119 else:
2120 raise util.Abort(_("no ignore patterns found"))
2120 raise util.Abort(_("no ignore patterns found"))
2121
2121
2122 @command('debugindex',
2122 @command('debugindex',
2123 [('c', 'changelog', False, _('open changelog')),
2123 [('c', 'changelog', False, _('open changelog')),
2124 ('m', 'manifest', False, _('open manifest')),
2124 ('m', 'manifest', False, _('open manifest')),
2125 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2125 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2126 _('[-f FORMAT] -c|-m|FILE'),
2126 _('[-f FORMAT] -c|-m|FILE'),
2127 optionalrepo=True)
2127 optionalrepo=True)
2128 def debugindex(ui, repo, file_=None, **opts):
2128 def debugindex(ui, repo, file_=None, **opts):
2129 """dump the contents of an index file"""
2129 """dump the contents of an index file"""
2130 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2130 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2131 format = opts.get('format', 0)
2131 format = opts.get('format', 0)
2132 if format not in (0, 1):
2132 if format not in (0, 1):
2133 raise util.Abort(_("unknown format %d") % format)
2133 raise util.Abort(_("unknown format %d") % format)
2134
2134
2135 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2135 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2136 if generaldelta:
2136 if generaldelta:
2137 basehdr = ' delta'
2137 basehdr = ' delta'
2138 else:
2138 else:
2139 basehdr = ' base'
2139 basehdr = ' base'
2140
2140
2141 if format == 0:
2141 if format == 0:
2142 ui.write(" rev offset length " + basehdr + " linkrev"
2142 ui.write(" rev offset length " + basehdr + " linkrev"
2143 " nodeid p1 p2\n")
2143 " nodeid p1 p2\n")
2144 elif format == 1:
2144 elif format == 1:
2145 ui.write(" rev flag offset length"
2145 ui.write(" rev flag offset length"
2146 " size " + basehdr + " link p1 p2"
2146 " size " + basehdr + " link p1 p2"
2147 " nodeid\n")
2147 " nodeid\n")
2148
2148
2149 for i in r:
2149 for i in r:
2150 node = r.node(i)
2150 node = r.node(i)
2151 if generaldelta:
2151 if generaldelta:
2152 base = r.deltaparent(i)
2152 base = r.deltaparent(i)
2153 else:
2153 else:
2154 base = r.chainbase(i)
2154 base = r.chainbase(i)
2155 if format == 0:
2155 if format == 0:
2156 try:
2156 try:
2157 pp = r.parents(node)
2157 pp = r.parents(node)
2158 except Exception:
2158 except Exception:
2159 pp = [nullid, nullid]
2159 pp = [nullid, nullid]
2160 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2160 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2161 i, r.start(i), r.length(i), base, r.linkrev(i),
2161 i, r.start(i), r.length(i), base, r.linkrev(i),
2162 short(node), short(pp[0]), short(pp[1])))
2162 short(node), short(pp[0]), short(pp[1])))
2163 elif format == 1:
2163 elif format == 1:
2164 pr = r.parentrevs(i)
2164 pr = r.parentrevs(i)
2165 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2165 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2166 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2166 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2167 base, r.linkrev(i), pr[0], pr[1], short(node)))
2167 base, r.linkrev(i), pr[0], pr[1], short(node)))
2168
2168
2169 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2169 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2170 def debugindexdot(ui, repo, file_):
2170 def debugindexdot(ui, repo, file_):
2171 """dump an index DAG as a graphviz dot file"""
2171 """dump an index DAG as a graphviz dot file"""
2172 r = None
2172 r = None
2173 if repo:
2173 if repo:
2174 filelog = repo.file(file_)
2174 filelog = repo.file(file_)
2175 if len(filelog):
2175 if len(filelog):
2176 r = filelog
2176 r = filelog
2177 if not r:
2177 if not r:
2178 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2178 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2179 ui.write(("digraph G {\n"))
2179 ui.write(("digraph G {\n"))
2180 for i in r:
2180 for i in r:
2181 node = r.node(i)
2181 node = r.node(i)
2182 pp = r.parents(node)
2182 pp = r.parents(node)
2183 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2183 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2184 if pp[1] != nullid:
2184 if pp[1] != nullid:
2185 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2185 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2186 ui.write("}\n")
2186 ui.write("}\n")
2187
2187
2188 @command('debuginstall', [], '', norepo=True)
2188 @command('debuginstall', [], '', norepo=True)
2189 def debuginstall(ui):
2189 def debuginstall(ui):
2190 '''test Mercurial installation
2190 '''test Mercurial installation
2191
2191
2192 Returns 0 on success.
2192 Returns 0 on success.
2193 '''
2193 '''
2194
2194
2195 def writetemp(contents):
2195 def writetemp(contents):
2196 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2196 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2197 f = os.fdopen(fd, "wb")
2197 f = os.fdopen(fd, "wb")
2198 f.write(contents)
2198 f.write(contents)
2199 f.close()
2199 f.close()
2200 return name
2200 return name
2201
2201
2202 problems = 0
2202 problems = 0
2203
2203
2204 # encoding
2204 # encoding
2205 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2205 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2206 try:
2206 try:
2207 encoding.fromlocal("test")
2207 encoding.fromlocal("test")
2208 except util.Abort, inst:
2208 except util.Abort, inst:
2209 ui.write(" %s\n" % inst)
2209 ui.write(" %s\n" % inst)
2210 ui.write(_(" (check that your locale is properly set)\n"))
2210 ui.write(_(" (check that your locale is properly set)\n"))
2211 problems += 1
2211 problems += 1
2212
2212
2213 # Python
2213 # Python
2214 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2214 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2215 ui.status(_("checking Python version (%s)\n")
2215 ui.status(_("checking Python version (%s)\n")
2216 % ("%s.%s.%s" % sys.version_info[:3]))
2216 % ("%s.%s.%s" % sys.version_info[:3]))
2217 ui.status(_("checking Python lib (%s)...\n")
2217 ui.status(_("checking Python lib (%s)...\n")
2218 % os.path.dirname(os.__file__))
2218 % os.path.dirname(os.__file__))
2219
2219
2220 # compiled modules
2220 # compiled modules
2221 ui.status(_("checking installed modules (%s)...\n")
2221 ui.status(_("checking installed modules (%s)...\n")
2222 % os.path.dirname(__file__))
2222 % os.path.dirname(__file__))
2223 try:
2223 try:
2224 import bdiff, mpatch, base85, osutil
2224 import bdiff, mpatch, base85, osutil
2225 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2225 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2226 except Exception, inst:
2226 except Exception, inst:
2227 ui.write(" %s\n" % inst)
2227 ui.write(" %s\n" % inst)
2228 ui.write(_(" One or more extensions could not be found"))
2228 ui.write(_(" One or more extensions could not be found"))
2229 ui.write(_(" (check that you compiled the extensions)\n"))
2229 ui.write(_(" (check that you compiled the extensions)\n"))
2230 problems += 1
2230 problems += 1
2231
2231
2232 # templates
2232 # templates
2233 import templater
2233 import templater
2234 p = templater.templatepath()
2234 p = templater.templatepath()
2235 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2235 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2236 if p:
2236 if p:
2237 m = templater.templatepath("map-cmdline.default")
2237 m = templater.templatepath("map-cmdline.default")
2238 if m:
2238 if m:
2239 # template found, check if it is working
2239 # template found, check if it is working
2240 try:
2240 try:
2241 templater.templater(m)
2241 templater.templater(m)
2242 except Exception, inst:
2242 except Exception, inst:
2243 ui.write(" %s\n" % inst)
2243 ui.write(" %s\n" % inst)
2244 p = None
2244 p = None
2245 else:
2245 else:
2246 ui.write(_(" template 'default' not found\n"))
2246 ui.write(_(" template 'default' not found\n"))
2247 p = None
2247 p = None
2248 else:
2248 else:
2249 ui.write(_(" no template directories found\n"))
2249 ui.write(_(" no template directories found\n"))
2250 if not p:
2250 if not p:
2251 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2251 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2252 problems += 1
2252 problems += 1
2253
2253
2254 # editor
2254 # editor
2255 ui.status(_("checking commit editor...\n"))
2255 ui.status(_("checking commit editor...\n"))
2256 editor = ui.geteditor()
2256 editor = ui.geteditor()
2257 cmdpath = util.findexe(shlex.split(editor)[0])
2257 cmdpath = util.findexe(shlex.split(editor)[0])
2258 if not cmdpath:
2258 if not cmdpath:
2259 if editor == 'vi':
2259 if editor == 'vi':
2260 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2260 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2261 ui.write(_(" (specify a commit editor in your configuration"
2261 ui.write(_(" (specify a commit editor in your configuration"
2262 " file)\n"))
2262 " file)\n"))
2263 else:
2263 else:
2264 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2264 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2265 ui.write(_(" (specify a commit editor in your configuration"
2265 ui.write(_(" (specify a commit editor in your configuration"
2266 " file)\n"))
2266 " file)\n"))
2267 problems += 1
2267 problems += 1
2268
2268
2269 # check username
2269 # check username
2270 ui.status(_("checking username...\n"))
2270 ui.status(_("checking username...\n"))
2271 try:
2271 try:
2272 ui.username()
2272 ui.username()
2273 except util.Abort, e:
2273 except util.Abort, e:
2274 ui.write(" %s\n" % e)
2274 ui.write(" %s\n" % e)
2275 ui.write(_(" (specify a username in your configuration file)\n"))
2275 ui.write(_(" (specify a username in your configuration file)\n"))
2276 problems += 1
2276 problems += 1
2277
2277
2278 if not problems:
2278 if not problems:
2279 ui.status(_("no problems detected\n"))
2279 ui.status(_("no problems detected\n"))
2280 else:
2280 else:
2281 ui.write(_("%s problems detected,"
2281 ui.write(_("%s problems detected,"
2282 " please check your install!\n") % problems)
2282 " please check your install!\n") % problems)
2283
2283
2284 return problems
2284 return problems
2285
2285
2286 @command('debugknown', [], _('REPO ID...'), norepo=True)
2286 @command('debugknown', [], _('REPO ID...'), norepo=True)
2287 def debugknown(ui, repopath, *ids, **opts):
2287 def debugknown(ui, repopath, *ids, **opts):
2288 """test whether node ids are known to a repo
2288 """test whether node ids are known to a repo
2289
2289
2290 Every ID must be a full-length hex node id string. Returns a list of 0s
2290 Every ID must be a full-length hex node id string. Returns a list of 0s
2291 and 1s indicating unknown/known.
2291 and 1s indicating unknown/known.
2292 """
2292 """
2293 repo = hg.peer(ui, opts, repopath)
2293 repo = hg.peer(ui, opts, repopath)
2294 if not repo.capable('known'):
2294 if not repo.capable('known'):
2295 raise util.Abort("known() not supported by target repository")
2295 raise util.Abort("known() not supported by target repository")
2296 flags = repo.known([bin(s) for s in ids])
2296 flags = repo.known([bin(s) for s in ids])
2297 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2297 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2298
2298
2299 @command('debuglabelcomplete', [], _('LABEL...'))
2299 @command('debuglabelcomplete', [], _('LABEL...'))
2300 def debuglabelcomplete(ui, repo, *args):
2300 def debuglabelcomplete(ui, repo, *args):
2301 '''complete "labels" - tags, open branch names, bookmark names'''
2301 '''complete "labels" - tags, open branch names, bookmark names'''
2302
2302
2303 labels = set()
2303 labels = set()
2304 labels.update(t[0] for t in repo.tagslist())
2304 labels.update(t[0] for t in repo.tagslist())
2305 labels.update(repo._bookmarks.keys())
2305 labels.update(repo._bookmarks.keys())
2306 labels.update(tag for (tag, heads, tip, closed)
2306 labels.update(tag for (tag, heads, tip, closed)
2307 in repo.branchmap().iterbranches() if not closed)
2307 in repo.branchmap().iterbranches() if not closed)
2308 completions = set()
2308 completions = set()
2309 if not args:
2309 if not args:
2310 args = ['']
2310 args = ['']
2311 for a in args:
2311 for a in args:
2312 completions.update(l for l in labels if l.startswith(a))
2312 completions.update(l for l in labels if l.startswith(a))
2313 ui.write('\n'.join(sorted(completions)))
2313 ui.write('\n'.join(sorted(completions)))
2314 ui.write('\n')
2314 ui.write('\n')
2315
2315
2316 @command('debugobsolete',
2316 @command('debugobsolete',
2317 [('', 'flags', 0, _('markers flag')),
2317 [('', 'flags', 0, _('markers flag')),
2318 ('', 'record-parents', False,
2318 ('', 'record-parents', False,
2319 _('record parent information for the precursor')),
2319 _('record parent information for the precursor')),
2320 ('r', 'rev', [], _('display markers relevant to REV')),
2320 ('r', 'rev', [], _('display markers relevant to REV')),
2321 ] + commitopts2,
2321 ] + commitopts2,
2322 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2322 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2323 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2323 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2324 """create arbitrary obsolete marker
2324 """create arbitrary obsolete marker
2325
2325
2326 With no arguments, displays the list of obsolescence markers."""
2326 With no arguments, displays the list of obsolescence markers."""
2327
2327
2328 def parsenodeid(s):
2328 def parsenodeid(s):
2329 try:
2329 try:
2330 # We do not use revsingle/revrange functions here to accept
2330 # We do not use revsingle/revrange functions here to accept
2331 # arbitrary node identifiers, possibly not present in the
2331 # arbitrary node identifiers, possibly not present in the
2332 # local repository.
2332 # local repository.
2333 n = bin(s)
2333 n = bin(s)
2334 if len(n) != len(nullid):
2334 if len(n) != len(nullid):
2335 raise TypeError()
2335 raise TypeError()
2336 return n
2336 return n
2337 except TypeError:
2337 except TypeError:
2338 raise util.Abort('changeset references must be full hexadecimal '
2338 raise util.Abort('changeset references must be full hexadecimal '
2339 'node identifiers')
2339 'node identifiers')
2340
2340
2341 if precursor is not None:
2341 if precursor is not None:
2342 if opts['rev']:
2342 if opts['rev']:
2343 raise util.Abort('cannot select revision when creating marker')
2343 raise util.Abort('cannot select revision when creating marker')
2344 metadata = {}
2344 metadata = {}
2345 metadata['user'] = opts['user'] or ui.username()
2345 metadata['user'] = opts['user'] or ui.username()
2346 succs = tuple(parsenodeid(succ) for succ in successors)
2346 succs = tuple(parsenodeid(succ) for succ in successors)
2347 l = repo.lock()
2347 l = repo.lock()
2348 try:
2348 try:
2349 tr = repo.transaction('debugobsolete')
2349 tr = repo.transaction('debugobsolete')
2350 try:
2350 try:
2351 try:
2351 try:
2352 date = opts.get('date')
2352 date = opts.get('date')
2353 if date:
2353 if date:
2354 date = util.parsedate(date)
2354 date = util.parsedate(date)
2355 else:
2355 else:
2356 date = None
2356 date = None
2357 prec = parsenodeid(precursor)
2357 prec = parsenodeid(precursor)
2358 parents = None
2358 parents = None
2359 if opts['record_parents']:
2359 if opts['record_parents']:
2360 if prec not in repo.unfiltered():
2360 if prec not in repo.unfiltered():
2361 raise util.Abort('cannot used --record-parents on '
2361 raise util.Abort('cannot used --record-parents on '
2362 'unknown changesets')
2362 'unknown changesets')
2363 parents = repo.unfiltered()[prec].parents()
2363 parents = repo.unfiltered()[prec].parents()
2364 parents = tuple(p.node() for p in parents)
2364 parents = tuple(p.node() for p in parents)
2365 repo.obsstore.create(tr, prec, succs, opts['flags'],
2365 repo.obsstore.create(tr, prec, succs, opts['flags'],
2366 parents=parents, date=date,
2366 parents=parents, date=date,
2367 metadata=metadata)
2367 metadata=metadata)
2368 tr.close()
2368 tr.close()
2369 except ValueError, exc:
2369 except ValueError, exc:
2370 raise util.Abort(_('bad obsmarker input: %s') % exc)
2370 raise util.Abort(_('bad obsmarker input: %s') % exc)
2371 finally:
2371 finally:
2372 tr.release()
2372 tr.release()
2373 finally:
2373 finally:
2374 l.release()
2374 l.release()
2375 else:
2375 else:
2376 if opts['rev']:
2376 if opts['rev']:
2377 revs = scmutil.revrange(repo, opts['rev'])
2377 revs = scmutil.revrange(repo, opts['rev'])
2378 nodes = [repo[r].node() for r in revs]
2378 nodes = [repo[r].node() for r in revs]
2379 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2379 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2380 markers.sort(key=lambda x: x._data)
2380 markers.sort(key=lambda x: x._data)
2381 else:
2381 else:
2382 markers = obsolete.getmarkers(repo)
2382 markers = obsolete.getmarkers(repo)
2383
2383
2384 for m in markers:
2384 for m in markers:
2385 cmdutil.showmarker(ui, m)
2385 cmdutil.showmarker(ui, m)
2386
2386
2387 @command('debugpathcomplete',
2387 @command('debugpathcomplete',
2388 [('f', 'full', None, _('complete an entire path')),
2388 [('f', 'full', None, _('complete an entire path')),
2389 ('n', 'normal', None, _('show only normal files')),
2389 ('n', 'normal', None, _('show only normal files')),
2390 ('a', 'added', None, _('show only added files')),
2390 ('a', 'added', None, _('show only added files')),
2391 ('r', 'removed', None, _('show only removed files'))],
2391 ('r', 'removed', None, _('show only removed files'))],
2392 _('FILESPEC...'))
2392 _('FILESPEC...'))
2393 def debugpathcomplete(ui, repo, *specs, **opts):
2393 def debugpathcomplete(ui, repo, *specs, **opts):
2394 '''complete part or all of a tracked path
2394 '''complete part or all of a tracked path
2395
2395
2396 This command supports shells that offer path name completion. It
2396 This command supports shells that offer path name completion. It
2397 currently completes only files already known to the dirstate.
2397 currently completes only files already known to the dirstate.
2398
2398
2399 Completion extends only to the next path segment unless
2399 Completion extends only to the next path segment unless
2400 --full is specified, in which case entire paths are used.'''
2400 --full is specified, in which case entire paths are used.'''
2401
2401
2402 def complete(path, acceptable):
2402 def complete(path, acceptable):
2403 dirstate = repo.dirstate
2403 dirstate = repo.dirstate
2404 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2404 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2405 rootdir = repo.root + os.sep
2405 rootdir = repo.root + os.sep
2406 if spec != repo.root and not spec.startswith(rootdir):
2406 if spec != repo.root and not spec.startswith(rootdir):
2407 return [], []
2407 return [], []
2408 if os.path.isdir(spec):
2408 if os.path.isdir(spec):
2409 spec += '/'
2409 spec += '/'
2410 spec = spec[len(rootdir):]
2410 spec = spec[len(rootdir):]
2411 fixpaths = os.sep != '/'
2411 fixpaths = os.sep != '/'
2412 if fixpaths:
2412 if fixpaths:
2413 spec = spec.replace(os.sep, '/')
2413 spec = spec.replace(os.sep, '/')
2414 speclen = len(spec)
2414 speclen = len(spec)
2415 fullpaths = opts['full']
2415 fullpaths = opts['full']
2416 files, dirs = set(), set()
2416 files, dirs = set(), set()
2417 adddir, addfile = dirs.add, files.add
2417 adddir, addfile = dirs.add, files.add
2418 for f, st in dirstate.iteritems():
2418 for f, st in dirstate.iteritems():
2419 if f.startswith(spec) and st[0] in acceptable:
2419 if f.startswith(spec) and st[0] in acceptable:
2420 if fixpaths:
2420 if fixpaths:
2421 f = f.replace('/', os.sep)
2421 f = f.replace('/', os.sep)
2422 if fullpaths:
2422 if fullpaths:
2423 addfile(f)
2423 addfile(f)
2424 continue
2424 continue
2425 s = f.find(os.sep, speclen)
2425 s = f.find(os.sep, speclen)
2426 if s >= 0:
2426 if s >= 0:
2427 adddir(f[:s])
2427 adddir(f[:s])
2428 else:
2428 else:
2429 addfile(f)
2429 addfile(f)
2430 return files, dirs
2430 return files, dirs
2431
2431
2432 acceptable = ''
2432 acceptable = ''
2433 if opts['normal']:
2433 if opts['normal']:
2434 acceptable += 'nm'
2434 acceptable += 'nm'
2435 if opts['added']:
2435 if opts['added']:
2436 acceptable += 'a'
2436 acceptable += 'a'
2437 if opts['removed']:
2437 if opts['removed']:
2438 acceptable += 'r'
2438 acceptable += 'r'
2439 cwd = repo.getcwd()
2439 cwd = repo.getcwd()
2440 if not specs:
2440 if not specs:
2441 specs = ['.']
2441 specs = ['.']
2442
2442
2443 files, dirs = set(), set()
2443 files, dirs = set(), set()
2444 for spec in specs:
2444 for spec in specs:
2445 f, d = complete(spec, acceptable or 'nmar')
2445 f, d = complete(spec, acceptable or 'nmar')
2446 files.update(f)
2446 files.update(f)
2447 dirs.update(d)
2447 dirs.update(d)
2448 files.update(dirs)
2448 files.update(dirs)
2449 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2449 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2450 ui.write('\n')
2450 ui.write('\n')
2451
2451
2452 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2452 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2453 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2453 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2454 '''access the pushkey key/value protocol
2454 '''access the pushkey key/value protocol
2455
2455
2456 With two args, list the keys in the given namespace.
2456 With two args, list the keys in the given namespace.
2457
2457
2458 With five args, set a key to new if it currently is set to old.
2458 With five args, set a key to new if it currently is set to old.
2459 Reports success or failure.
2459 Reports success or failure.
2460 '''
2460 '''
2461
2461
2462 target = hg.peer(ui, {}, repopath)
2462 target = hg.peer(ui, {}, repopath)
2463 if keyinfo:
2463 if keyinfo:
2464 key, old, new = keyinfo
2464 key, old, new = keyinfo
2465 r = target.pushkey(namespace, key, old, new)
2465 r = target.pushkey(namespace, key, old, new)
2466 ui.status(str(r) + '\n')
2466 ui.status(str(r) + '\n')
2467 return not r
2467 return not r
2468 else:
2468 else:
2469 for k, v in sorted(target.listkeys(namespace).iteritems()):
2469 for k, v in sorted(target.listkeys(namespace).iteritems()):
2470 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2470 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2471 v.encode('string-escape')))
2471 v.encode('string-escape')))
2472
2472
2473 @command('debugpvec', [], _('A B'))
2473 @command('debugpvec', [], _('A B'))
2474 def debugpvec(ui, repo, a, b=None):
2474 def debugpvec(ui, repo, a, b=None):
2475 ca = scmutil.revsingle(repo, a)
2475 ca = scmutil.revsingle(repo, a)
2476 cb = scmutil.revsingle(repo, b)
2476 cb = scmutil.revsingle(repo, b)
2477 pa = pvec.ctxpvec(ca)
2477 pa = pvec.ctxpvec(ca)
2478 pb = pvec.ctxpvec(cb)
2478 pb = pvec.ctxpvec(cb)
2479 if pa == pb:
2479 if pa == pb:
2480 rel = "="
2480 rel = "="
2481 elif pa > pb:
2481 elif pa > pb:
2482 rel = ">"
2482 rel = ">"
2483 elif pa < pb:
2483 elif pa < pb:
2484 rel = "<"
2484 rel = "<"
2485 elif pa | pb:
2485 elif pa | pb:
2486 rel = "|"
2486 rel = "|"
2487 ui.write(_("a: %s\n") % pa)
2487 ui.write(_("a: %s\n") % pa)
2488 ui.write(_("b: %s\n") % pb)
2488 ui.write(_("b: %s\n") % pb)
2489 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2489 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2490 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2490 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2491 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2491 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2492 pa.distance(pb), rel))
2492 pa.distance(pb), rel))
2493
2493
2494 @command('debugrebuilddirstate|debugrebuildstate',
2494 @command('debugrebuilddirstate|debugrebuildstate',
2495 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2495 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2496 _('[-r REV]'))
2496 _('[-r REV]'))
2497 def debugrebuilddirstate(ui, repo, rev):
2497 def debugrebuilddirstate(ui, repo, rev):
2498 """rebuild the dirstate as it would look like for the given revision
2498 """rebuild the dirstate as it would look like for the given revision
2499
2499
2500 If no revision is specified the first current parent will be used.
2500 If no revision is specified the first current parent will be used.
2501
2501
2502 The dirstate will be set to the files of the given revision.
2502 The dirstate will be set to the files of the given revision.
2503 The actual working directory content or existing dirstate
2503 The actual working directory content or existing dirstate
2504 information such as adds or removes is not considered.
2504 information such as adds or removes is not considered.
2505
2505
2506 One use of this command is to make the next :hg:`status` invocation
2506 One use of this command is to make the next :hg:`status` invocation
2507 check the actual file content.
2507 check the actual file content.
2508 """
2508 """
2509 ctx = scmutil.revsingle(repo, rev)
2509 ctx = scmutil.revsingle(repo, rev)
2510 wlock = repo.wlock()
2510 wlock = repo.wlock()
2511 try:
2511 try:
2512 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2512 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2513 finally:
2513 finally:
2514 wlock.release()
2514 wlock.release()
2515
2515
2516 @command('debugrename',
2516 @command('debugrename',
2517 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2517 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2518 _('[-r REV] FILE'))
2518 _('[-r REV] FILE'))
2519 def debugrename(ui, repo, file1, *pats, **opts):
2519 def debugrename(ui, repo, file1, *pats, **opts):
2520 """dump rename information"""
2520 """dump rename information"""
2521
2521
2522 ctx = scmutil.revsingle(repo, opts.get('rev'))
2522 ctx = scmutil.revsingle(repo, opts.get('rev'))
2523 m = scmutil.match(ctx, (file1,) + pats, opts)
2523 m = scmutil.match(ctx, (file1,) + pats, opts)
2524 for abs in ctx.walk(m):
2524 for abs in ctx.walk(m):
2525 fctx = ctx[abs]
2525 fctx = ctx[abs]
2526 o = fctx.filelog().renamed(fctx.filenode())
2526 o = fctx.filelog().renamed(fctx.filenode())
2527 rel = m.rel(abs)
2527 rel = m.rel(abs)
2528 if o:
2528 if o:
2529 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2529 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2530 else:
2530 else:
2531 ui.write(_("%s not renamed\n") % rel)
2531 ui.write(_("%s not renamed\n") % rel)
2532
2532
2533 @command('debugrevlog',
2533 @command('debugrevlog',
2534 [('c', 'changelog', False, _('open changelog')),
2534 [('c', 'changelog', False, _('open changelog')),
2535 ('m', 'manifest', False, _('open manifest')),
2535 ('m', 'manifest', False, _('open manifest')),
2536 ('d', 'dump', False, _('dump index data'))],
2536 ('d', 'dump', False, _('dump index data'))],
2537 _('-c|-m|FILE'),
2537 _('-c|-m|FILE'),
2538 optionalrepo=True)
2538 optionalrepo=True)
2539 def debugrevlog(ui, repo, file_=None, **opts):
2539 def debugrevlog(ui, repo, file_=None, **opts):
2540 """show data and statistics about a revlog"""
2540 """show data and statistics about a revlog"""
2541 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2541 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2542
2542
2543 if opts.get("dump"):
2543 if opts.get("dump"):
2544 numrevs = len(r)
2544 numrevs = len(r)
2545 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2545 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2546 " rawsize totalsize compression heads chainlen\n")
2546 " rawsize totalsize compression heads chainlen\n")
2547 ts = 0
2547 ts = 0
2548 heads = set()
2548 heads = set()
2549 rindex = r.index
2549 rindex = r.index
2550
2550
2551 def chainbaseandlen(rev):
2551 def chainbaseandlen(rev):
2552 clen = 0
2552 clen = 0
2553 base = rindex[rev][3]
2553 base = rindex[rev][3]
2554 while base != rev:
2554 while base != rev:
2555 clen += 1
2555 clen += 1
2556 rev = base
2556 rev = base
2557 base = rindex[rev][3]
2557 base = rindex[rev][3]
2558 return base, clen
2558 return base, clen
2559
2559
2560 for rev in xrange(numrevs):
2560 for rev in xrange(numrevs):
2561 dbase = r.deltaparent(rev)
2561 dbase = r.deltaparent(rev)
2562 if dbase == -1:
2562 if dbase == -1:
2563 dbase = rev
2563 dbase = rev
2564 cbase, clen = chainbaseandlen(rev)
2564 cbase, clen = chainbaseandlen(rev)
2565 p1, p2 = r.parentrevs(rev)
2565 p1, p2 = r.parentrevs(rev)
2566 rs = r.rawsize(rev)
2566 rs = r.rawsize(rev)
2567 ts = ts + rs
2567 ts = ts + rs
2568 heads -= set(r.parentrevs(rev))
2568 heads -= set(r.parentrevs(rev))
2569 heads.add(rev)
2569 heads.add(rev)
2570 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2570 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2571 "%11d %5d %8d\n" %
2571 "%11d %5d %8d\n" %
2572 (rev, p1, p2, r.start(rev), r.end(rev),
2572 (rev, p1, p2, r.start(rev), r.end(rev),
2573 r.start(dbase), r.start(cbase),
2573 r.start(dbase), r.start(cbase),
2574 r.start(p1), r.start(p2),
2574 r.start(p1), r.start(p2),
2575 rs, ts, ts / r.end(rev), len(heads), clen))
2575 rs, ts, ts / r.end(rev), len(heads), clen))
2576 return 0
2576 return 0
2577
2577
2578 v = r.version
2578 v = r.version
2579 format = v & 0xFFFF
2579 format = v & 0xFFFF
2580 flags = []
2580 flags = []
2581 gdelta = False
2581 gdelta = False
2582 if v & revlog.REVLOGNGINLINEDATA:
2582 if v & revlog.REVLOGNGINLINEDATA:
2583 flags.append('inline')
2583 flags.append('inline')
2584 if v & revlog.REVLOGGENERALDELTA:
2584 if v & revlog.REVLOGGENERALDELTA:
2585 gdelta = True
2585 gdelta = True
2586 flags.append('generaldelta')
2586 flags.append('generaldelta')
2587 if not flags:
2587 if not flags:
2588 flags = ['(none)']
2588 flags = ['(none)']
2589
2589
2590 nummerges = 0
2590 nummerges = 0
2591 numfull = 0
2591 numfull = 0
2592 numprev = 0
2592 numprev = 0
2593 nump1 = 0
2593 nump1 = 0
2594 nump2 = 0
2594 nump2 = 0
2595 numother = 0
2595 numother = 0
2596 nump1prev = 0
2596 nump1prev = 0
2597 nump2prev = 0
2597 nump2prev = 0
2598 chainlengths = []
2598 chainlengths = []
2599
2599
2600 datasize = [None, 0, 0L]
2600 datasize = [None, 0, 0L]
2601 fullsize = [None, 0, 0L]
2601 fullsize = [None, 0, 0L]
2602 deltasize = [None, 0, 0L]
2602 deltasize = [None, 0, 0L]
2603
2603
2604 def addsize(size, l):
2604 def addsize(size, l):
2605 if l[0] is None or size < l[0]:
2605 if l[0] is None or size < l[0]:
2606 l[0] = size
2606 l[0] = size
2607 if size > l[1]:
2607 if size > l[1]:
2608 l[1] = size
2608 l[1] = size
2609 l[2] += size
2609 l[2] += size
2610
2610
2611 numrevs = len(r)
2611 numrevs = len(r)
2612 for rev in xrange(numrevs):
2612 for rev in xrange(numrevs):
2613 p1, p2 = r.parentrevs(rev)
2613 p1, p2 = r.parentrevs(rev)
2614 delta = r.deltaparent(rev)
2614 delta = r.deltaparent(rev)
2615 if format > 0:
2615 if format > 0:
2616 addsize(r.rawsize(rev), datasize)
2616 addsize(r.rawsize(rev), datasize)
2617 if p2 != nullrev:
2617 if p2 != nullrev:
2618 nummerges += 1
2618 nummerges += 1
2619 size = r.length(rev)
2619 size = r.length(rev)
2620 if delta == nullrev:
2620 if delta == nullrev:
2621 chainlengths.append(0)
2621 chainlengths.append(0)
2622 numfull += 1
2622 numfull += 1
2623 addsize(size, fullsize)
2623 addsize(size, fullsize)
2624 else:
2624 else:
2625 chainlengths.append(chainlengths[delta] + 1)
2625 chainlengths.append(chainlengths[delta] + 1)
2626 addsize(size, deltasize)
2626 addsize(size, deltasize)
2627 if delta == rev - 1:
2627 if delta == rev - 1:
2628 numprev += 1
2628 numprev += 1
2629 if delta == p1:
2629 if delta == p1:
2630 nump1prev += 1
2630 nump1prev += 1
2631 elif delta == p2:
2631 elif delta == p2:
2632 nump2prev += 1
2632 nump2prev += 1
2633 elif delta == p1:
2633 elif delta == p1:
2634 nump1 += 1
2634 nump1 += 1
2635 elif delta == p2:
2635 elif delta == p2:
2636 nump2 += 1
2636 nump2 += 1
2637 elif delta != nullrev:
2637 elif delta != nullrev:
2638 numother += 1
2638 numother += 1
2639
2639
2640 # Adjust size min value for empty cases
2640 # Adjust size min value for empty cases
2641 for size in (datasize, fullsize, deltasize):
2641 for size in (datasize, fullsize, deltasize):
2642 if size[0] is None:
2642 if size[0] is None:
2643 size[0] = 0
2643 size[0] = 0
2644
2644
2645 numdeltas = numrevs - numfull
2645 numdeltas = numrevs - numfull
2646 numoprev = numprev - nump1prev - nump2prev
2646 numoprev = numprev - nump1prev - nump2prev
2647 totalrawsize = datasize[2]
2647 totalrawsize = datasize[2]
2648 datasize[2] /= numrevs
2648 datasize[2] /= numrevs
2649 fulltotal = fullsize[2]
2649 fulltotal = fullsize[2]
2650 fullsize[2] /= numfull
2650 fullsize[2] /= numfull
2651 deltatotal = deltasize[2]
2651 deltatotal = deltasize[2]
2652 if numrevs - numfull > 0:
2652 if numrevs - numfull > 0:
2653 deltasize[2] /= numrevs - numfull
2653 deltasize[2] /= numrevs - numfull
2654 totalsize = fulltotal + deltatotal
2654 totalsize = fulltotal + deltatotal
2655 avgchainlen = sum(chainlengths) / numrevs
2655 avgchainlen = sum(chainlengths) / numrevs
2656 compratio = totalrawsize / totalsize
2656 compratio = totalrawsize / totalsize
2657
2657
2658 basedfmtstr = '%%%dd\n'
2658 basedfmtstr = '%%%dd\n'
2659 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2659 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2660
2660
2661 def dfmtstr(max):
2661 def dfmtstr(max):
2662 return basedfmtstr % len(str(max))
2662 return basedfmtstr % len(str(max))
2663 def pcfmtstr(max, padding=0):
2663 def pcfmtstr(max, padding=0):
2664 return basepcfmtstr % (len(str(max)), ' ' * padding)
2664 return basepcfmtstr % (len(str(max)), ' ' * padding)
2665
2665
2666 def pcfmt(value, total):
2666 def pcfmt(value, total):
2667 return (value, 100 * float(value) / total)
2667 return (value, 100 * float(value) / total)
2668
2668
2669 ui.write(('format : %d\n') % format)
2669 ui.write(('format : %d\n') % format)
2670 ui.write(('flags : %s\n') % ', '.join(flags))
2670 ui.write(('flags : %s\n') % ', '.join(flags))
2671
2671
2672 ui.write('\n')
2672 ui.write('\n')
2673 fmt = pcfmtstr(totalsize)
2673 fmt = pcfmtstr(totalsize)
2674 fmt2 = dfmtstr(totalsize)
2674 fmt2 = dfmtstr(totalsize)
2675 ui.write(('revisions : ') + fmt2 % numrevs)
2675 ui.write(('revisions : ') + fmt2 % numrevs)
2676 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2676 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2677 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2677 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2678 ui.write(('revisions : ') + fmt2 % numrevs)
2678 ui.write(('revisions : ') + fmt2 % numrevs)
2679 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2679 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2680 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2680 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2681 ui.write(('revision size : ') + fmt2 % totalsize)
2681 ui.write(('revision size : ') + fmt2 % totalsize)
2682 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2682 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2683 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2683 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2684
2684
2685 ui.write('\n')
2685 ui.write('\n')
2686 fmt = dfmtstr(max(avgchainlen, compratio))
2686 fmt = dfmtstr(max(avgchainlen, compratio))
2687 ui.write(('avg chain length : ') + fmt % avgchainlen)
2687 ui.write(('avg chain length : ') + fmt % avgchainlen)
2688 ui.write(('compression ratio : ') + fmt % compratio)
2688 ui.write(('compression ratio : ') + fmt % compratio)
2689
2689
2690 if format > 0:
2690 if format > 0:
2691 ui.write('\n')
2691 ui.write('\n')
2692 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2692 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2693 % tuple(datasize))
2693 % tuple(datasize))
2694 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2694 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2695 % tuple(fullsize))
2695 % tuple(fullsize))
2696 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2696 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2697 % tuple(deltasize))
2697 % tuple(deltasize))
2698
2698
2699 if numdeltas > 0:
2699 if numdeltas > 0:
2700 ui.write('\n')
2700 ui.write('\n')
2701 fmt = pcfmtstr(numdeltas)
2701 fmt = pcfmtstr(numdeltas)
2702 fmt2 = pcfmtstr(numdeltas, 4)
2702 fmt2 = pcfmtstr(numdeltas, 4)
2703 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2703 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2704 if numprev > 0:
2704 if numprev > 0:
2705 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2705 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2706 numprev))
2706 numprev))
2707 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2707 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2708 numprev))
2708 numprev))
2709 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2709 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2710 numprev))
2710 numprev))
2711 if gdelta:
2711 if gdelta:
2712 ui.write(('deltas against p1 : ')
2712 ui.write(('deltas against p1 : ')
2713 + fmt % pcfmt(nump1, numdeltas))
2713 + fmt % pcfmt(nump1, numdeltas))
2714 ui.write(('deltas against p2 : ')
2714 ui.write(('deltas against p2 : ')
2715 + fmt % pcfmt(nump2, numdeltas))
2715 + fmt % pcfmt(nump2, numdeltas))
2716 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2716 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2717 numdeltas))
2717 numdeltas))
2718
2718
2719 @command('debugrevspec',
2719 @command('debugrevspec',
2720 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2720 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2721 ('REVSPEC'))
2721 ('REVSPEC'))
2722 def debugrevspec(ui, repo, expr, **opts):
2722 def debugrevspec(ui, repo, expr, **opts):
2723 """parse and apply a revision specification
2723 """parse and apply a revision specification
2724
2724
2725 Use --verbose to print the parsed tree before and after aliases
2725 Use --verbose to print the parsed tree before and after aliases
2726 expansion.
2726 expansion.
2727 """
2727 """
2728 if ui.verbose:
2728 if ui.verbose:
2729 tree = revset.parse(expr)[0]
2729 tree = revset.parse(expr)[0]
2730 ui.note(revset.prettyformat(tree), "\n")
2730 ui.note(revset.prettyformat(tree), "\n")
2731 newtree = revset.findaliases(ui, tree)
2731 newtree = revset.findaliases(ui, tree)
2732 if newtree != tree:
2732 if newtree != tree:
2733 ui.note(revset.prettyformat(newtree), "\n")
2733 ui.note(revset.prettyformat(newtree), "\n")
2734 if opts["optimize"]:
2734 if opts["optimize"]:
2735 weight, optimizedtree = revset.optimize(newtree, True)
2735 weight, optimizedtree = revset.optimize(newtree, True)
2736 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2736 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2737 func = revset.match(ui, expr)
2737 func = revset.match(ui, expr)
2738 for c in func(repo, revset.spanset(repo)):
2738 for c in func(repo, revset.spanset(repo)):
2739 ui.write("%s\n" % c)
2739 ui.write("%s\n" % c)
2740
2740
2741 @command('debugsetparents', [], _('REV1 [REV2]'))
2741 @command('debugsetparents', [], _('REV1 [REV2]'))
2742 def debugsetparents(ui, repo, rev1, rev2=None):
2742 def debugsetparents(ui, repo, rev1, rev2=None):
2743 """manually set the parents of the current working directory
2743 """manually set the parents of the current working directory
2744
2744
2745 This is useful for writing repository conversion tools, but should
2745 This is useful for writing repository conversion tools, but should
2746 be used with care.
2746 be used with care.
2747
2747
2748 Returns 0 on success.
2748 Returns 0 on success.
2749 """
2749 """
2750
2750
2751 r1 = scmutil.revsingle(repo, rev1).node()
2751 r1 = scmutil.revsingle(repo, rev1).node()
2752 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2752 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2753
2753
2754 wlock = repo.wlock()
2754 wlock = repo.wlock()
2755 try:
2755 try:
2756 repo.setparents(r1, r2)
2756 repo.setparents(r1, r2)
2757 finally:
2757 finally:
2758 wlock.release()
2758 wlock.release()
2759
2759
2760 @command('debugdirstate|debugstate',
2760 @command('debugdirstate|debugstate',
2761 [('', 'nodates', None, _('do not display the saved mtime')),
2761 [('', 'nodates', None, _('do not display the saved mtime')),
2762 ('', 'datesort', None, _('sort by saved mtime'))],
2762 ('', 'datesort', None, _('sort by saved mtime'))],
2763 _('[OPTION]...'))
2763 _('[OPTION]...'))
2764 def debugstate(ui, repo, nodates=None, datesort=None):
2764 def debugstate(ui, repo, nodates=None, datesort=None):
2765 """show the contents of the current dirstate"""
2765 """show the contents of the current dirstate"""
2766 timestr = ""
2766 timestr = ""
2767 showdate = not nodates
2767 showdate = not nodates
2768 if datesort:
2768 if datesort:
2769 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2769 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2770 else:
2770 else:
2771 keyfunc = None # sort by filename
2771 keyfunc = None # sort by filename
2772 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2772 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2773 if showdate:
2773 if showdate:
2774 if ent[3] == -1:
2774 if ent[3] == -1:
2775 # Pad or slice to locale representation
2775 # Pad or slice to locale representation
2776 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2776 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2777 time.localtime(0)))
2777 time.localtime(0)))
2778 timestr = 'unset'
2778 timestr = 'unset'
2779 timestr = (timestr[:locale_len] +
2779 timestr = (timestr[:locale_len] +
2780 ' ' * (locale_len - len(timestr)))
2780 ' ' * (locale_len - len(timestr)))
2781 else:
2781 else:
2782 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2782 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2783 time.localtime(ent[3]))
2783 time.localtime(ent[3]))
2784 if ent[1] & 020000:
2784 if ent[1] & 020000:
2785 mode = 'lnk'
2785 mode = 'lnk'
2786 else:
2786 else:
2787 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2787 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2788 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2788 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2789 for f in repo.dirstate.copies():
2789 for f in repo.dirstate.copies():
2790 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2790 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2791
2791
2792 @command('debugsub',
2792 @command('debugsub',
2793 [('r', 'rev', '',
2793 [('r', 'rev', '',
2794 _('revision to check'), _('REV'))],
2794 _('revision to check'), _('REV'))],
2795 _('[-r REV] [REV]'))
2795 _('[-r REV] [REV]'))
2796 def debugsub(ui, repo, rev=None):
2796 def debugsub(ui, repo, rev=None):
2797 ctx = scmutil.revsingle(repo, rev, None)
2797 ctx = scmutil.revsingle(repo, rev, None)
2798 for k, v in sorted(ctx.substate.items()):
2798 for k, v in sorted(ctx.substate.items()):
2799 ui.write(('path %s\n') % k)
2799 ui.write(('path %s\n') % k)
2800 ui.write((' source %s\n') % v[0])
2800 ui.write((' source %s\n') % v[0])
2801 ui.write((' revision %s\n') % v[1])
2801 ui.write((' revision %s\n') % v[1])
2802
2802
2803 @command('debugsuccessorssets',
2803 @command('debugsuccessorssets',
2804 [],
2804 [],
2805 _('[REV]'))
2805 _('[REV]'))
2806 def debugsuccessorssets(ui, repo, *revs):
2806 def debugsuccessorssets(ui, repo, *revs):
2807 """show set of successors for revision
2807 """show set of successors for revision
2808
2808
2809 A successors set of changeset A is a consistent group of revisions that
2809 A successors set of changeset A is a consistent group of revisions that
2810 succeed A. It contains non-obsolete changesets only.
2810 succeed A. It contains non-obsolete changesets only.
2811
2811
2812 In most cases a changeset A has a single successors set containing a single
2812 In most cases a changeset A has a single successors set containing a single
2813 successor (changeset A replaced by A').
2813 successor (changeset A replaced by A').
2814
2814
2815 A changeset that is made obsolete with no successors are called "pruned".
2815 A changeset that is made obsolete with no successors are called "pruned".
2816 Such changesets have no successors sets at all.
2816 Such changesets have no successors sets at all.
2817
2817
2818 A changeset that has been "split" will have a successors set containing
2818 A changeset that has been "split" will have a successors set containing
2819 more than one successor.
2819 more than one successor.
2820
2820
2821 A changeset that has been rewritten in multiple different ways is called
2821 A changeset that has been rewritten in multiple different ways is called
2822 "divergent". Such changesets have multiple successor sets (each of which
2822 "divergent". Such changesets have multiple successor sets (each of which
2823 may also be split, i.e. have multiple successors).
2823 may also be split, i.e. have multiple successors).
2824
2824
2825 Results are displayed as follows::
2825 Results are displayed as follows::
2826
2826
2827 <rev1>
2827 <rev1>
2828 <successors-1A>
2828 <successors-1A>
2829 <rev2>
2829 <rev2>
2830 <successors-2A>
2830 <successors-2A>
2831 <successors-2B1> <successors-2B2> <successors-2B3>
2831 <successors-2B1> <successors-2B2> <successors-2B3>
2832
2832
2833 Here rev2 has two possible (i.e. divergent) successors sets. The first
2833 Here rev2 has two possible (i.e. divergent) successors sets. The first
2834 holds one element, whereas the second holds three (i.e. the changeset has
2834 holds one element, whereas the second holds three (i.e. the changeset has
2835 been split).
2835 been split).
2836 """
2836 """
2837 # passed to successorssets caching computation from one call to another
2837 # passed to successorssets caching computation from one call to another
2838 cache = {}
2838 cache = {}
2839 ctx2str = str
2839 ctx2str = str
2840 node2str = short
2840 node2str = short
2841 if ui.debug():
2841 if ui.debug():
2842 def ctx2str(ctx):
2842 def ctx2str(ctx):
2843 return ctx.hex()
2843 return ctx.hex()
2844 node2str = hex
2844 node2str = hex
2845 for rev in scmutil.revrange(repo, revs):
2845 for rev in scmutil.revrange(repo, revs):
2846 ctx = repo[rev]
2846 ctx = repo[rev]
2847 ui.write('%s\n'% ctx2str(ctx))
2847 ui.write('%s\n'% ctx2str(ctx))
2848 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2848 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2849 if succsset:
2849 if succsset:
2850 ui.write(' ')
2850 ui.write(' ')
2851 ui.write(node2str(succsset[0]))
2851 ui.write(node2str(succsset[0]))
2852 for node in succsset[1:]:
2852 for node in succsset[1:]:
2853 ui.write(' ')
2853 ui.write(' ')
2854 ui.write(node2str(node))
2854 ui.write(node2str(node))
2855 ui.write('\n')
2855 ui.write('\n')
2856
2856
2857 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2857 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2858 def debugwalk(ui, repo, *pats, **opts):
2858 def debugwalk(ui, repo, *pats, **opts):
2859 """show how files match on given patterns"""
2859 """show how files match on given patterns"""
2860 m = scmutil.match(repo[None], pats, opts)
2860 m = scmutil.match(repo[None], pats, opts)
2861 items = list(repo.walk(m))
2861 items = list(repo.walk(m))
2862 if not items:
2862 if not items:
2863 return
2863 return
2864 f = lambda fn: fn
2864 f = lambda fn: fn
2865 if ui.configbool('ui', 'slash') and os.sep != '/':
2865 if ui.configbool('ui', 'slash') and os.sep != '/':
2866 f = lambda fn: util.normpath(fn)
2866 f = lambda fn: util.normpath(fn)
2867 fmt = 'f %%-%ds %%-%ds %%s' % (
2867 fmt = 'f %%-%ds %%-%ds %%s' % (
2868 max([len(abs) for abs in items]),
2868 max([len(abs) for abs in items]),
2869 max([len(m.rel(abs)) for abs in items]))
2869 max([len(m.rel(abs)) for abs in items]))
2870 for abs in items:
2870 for abs in items:
2871 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2871 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2872 ui.write("%s\n" % line.rstrip())
2872 ui.write("%s\n" % line.rstrip())
2873
2873
2874 @command('debugwireargs',
2874 @command('debugwireargs',
2875 [('', 'three', '', 'three'),
2875 [('', 'three', '', 'three'),
2876 ('', 'four', '', 'four'),
2876 ('', 'four', '', 'four'),
2877 ('', 'five', '', 'five'),
2877 ('', 'five', '', 'five'),
2878 ] + remoteopts,
2878 ] + remoteopts,
2879 _('REPO [OPTIONS]... [ONE [TWO]]'),
2879 _('REPO [OPTIONS]... [ONE [TWO]]'),
2880 norepo=True)
2880 norepo=True)
2881 def debugwireargs(ui, repopath, *vals, **opts):
2881 def debugwireargs(ui, repopath, *vals, **opts):
2882 repo = hg.peer(ui, opts, repopath)
2882 repo = hg.peer(ui, opts, repopath)
2883 for opt in remoteopts:
2883 for opt in remoteopts:
2884 del opts[opt[1]]
2884 del opts[opt[1]]
2885 args = {}
2885 args = {}
2886 for k, v in opts.iteritems():
2886 for k, v in opts.iteritems():
2887 if v:
2887 if v:
2888 args[k] = v
2888 args[k] = v
2889 # run twice to check that we don't mess up the stream for the next command
2889 # run twice to check that we don't mess up the stream for the next command
2890 res1 = repo.debugwireargs(*vals, **args)
2890 res1 = repo.debugwireargs(*vals, **args)
2891 res2 = repo.debugwireargs(*vals, **args)
2891 res2 = repo.debugwireargs(*vals, **args)
2892 ui.write("%s\n" % res1)
2892 ui.write("%s\n" % res1)
2893 if res1 != res2:
2893 if res1 != res2:
2894 ui.warn("%s\n" % res2)
2894 ui.warn("%s\n" % res2)
2895
2895
2896 @command('^diff',
2896 @command('^diff',
2897 [('r', 'rev', [], _('revision'), _('REV')),
2897 [('r', 'rev', [], _('revision'), _('REV')),
2898 ('c', 'change', '', _('change made by revision'), _('REV'))
2898 ('c', 'change', '', _('change made by revision'), _('REV'))
2899 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2899 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2900 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2900 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2901 inferrepo=True)
2901 inferrepo=True)
2902 def diff(ui, repo, *pats, **opts):
2902 def diff(ui, repo, *pats, **opts):
2903 """diff repository (or selected files)
2903 """diff repository (or selected files)
2904
2904
2905 Show differences between revisions for the specified files.
2905 Show differences between revisions for the specified files.
2906
2906
2907 Differences between files are shown using the unified diff format.
2907 Differences between files are shown using the unified diff format.
2908
2908
2909 .. note::
2909 .. note::
2910
2910
2911 diff may generate unexpected results for merges, as it will
2911 diff may generate unexpected results for merges, as it will
2912 default to comparing against the working directory's first
2912 default to comparing against the working directory's first
2913 parent changeset if no revisions are specified.
2913 parent changeset if no revisions are specified.
2914
2914
2915 When two revision arguments are given, then changes are shown
2915 When two revision arguments are given, then changes are shown
2916 between those revisions. If only one revision is specified then
2916 between those revisions. If only one revision is specified then
2917 that revision is compared to the working directory, and, when no
2917 that revision is compared to the working directory, and, when no
2918 revisions are specified, the working directory files are compared
2918 revisions are specified, the working directory files are compared
2919 to its parent.
2919 to its parent.
2920
2920
2921 Alternatively you can specify -c/--change with a revision to see
2921 Alternatively you can specify -c/--change with a revision to see
2922 the changes in that changeset relative to its first parent.
2922 the changes in that changeset relative to its first parent.
2923
2923
2924 Without the -a/--text option, diff will avoid generating diffs of
2924 Without the -a/--text option, diff will avoid generating diffs of
2925 files it detects as binary. With -a, diff will generate a diff
2925 files it detects as binary. With -a, diff will generate a diff
2926 anyway, probably with undesirable results.
2926 anyway, probably with undesirable results.
2927
2927
2928 Use the -g/--git option to generate diffs in the git extended diff
2928 Use the -g/--git option to generate diffs in the git extended diff
2929 format. For more information, read :hg:`help diffs`.
2929 format. For more information, read :hg:`help diffs`.
2930
2930
2931 .. container:: verbose
2931 .. container:: verbose
2932
2932
2933 Examples:
2933 Examples:
2934
2934
2935 - compare a file in the current working directory to its parent::
2935 - compare a file in the current working directory to its parent::
2936
2936
2937 hg diff foo.c
2937 hg diff foo.c
2938
2938
2939 - compare two historical versions of a directory, with rename info::
2939 - compare two historical versions of a directory, with rename info::
2940
2940
2941 hg diff --git -r 1.0:1.2 lib/
2941 hg diff --git -r 1.0:1.2 lib/
2942
2942
2943 - get change stats relative to the last change on some date::
2943 - get change stats relative to the last change on some date::
2944
2944
2945 hg diff --stat -r "date('may 2')"
2945 hg diff --stat -r "date('may 2')"
2946
2946
2947 - diff all newly-added files that contain a keyword::
2947 - diff all newly-added files that contain a keyword::
2948
2948
2949 hg diff "set:added() and grep(GNU)"
2949 hg diff "set:added() and grep(GNU)"
2950
2950
2951 - compare a revision and its parents::
2951 - compare a revision and its parents::
2952
2952
2953 hg diff -c 9353 # compare against first parent
2953 hg diff -c 9353 # compare against first parent
2954 hg diff -r 9353^:9353 # same using revset syntax
2954 hg diff -r 9353^:9353 # same using revset syntax
2955 hg diff -r 9353^2:9353 # compare against the second parent
2955 hg diff -r 9353^2:9353 # compare against the second parent
2956
2956
2957 Returns 0 on success.
2957 Returns 0 on success.
2958 """
2958 """
2959
2959
2960 revs = opts.get('rev')
2960 revs = opts.get('rev')
2961 change = opts.get('change')
2961 change = opts.get('change')
2962 stat = opts.get('stat')
2962 stat = opts.get('stat')
2963 reverse = opts.get('reverse')
2963 reverse = opts.get('reverse')
2964
2964
2965 if revs and change:
2965 if revs and change:
2966 msg = _('cannot specify --rev and --change at the same time')
2966 msg = _('cannot specify --rev and --change at the same time')
2967 raise util.Abort(msg)
2967 raise util.Abort(msg)
2968 elif change:
2968 elif change:
2969 node2 = scmutil.revsingle(repo, change, None).node()
2969 node2 = scmutil.revsingle(repo, change, None).node()
2970 node1 = repo[node2].p1().node()
2970 node1 = repo[node2].p1().node()
2971 else:
2971 else:
2972 node1, node2 = scmutil.revpair(repo, revs)
2972 node1, node2 = scmutil.revpair(repo, revs)
2973
2973
2974 if reverse:
2974 if reverse:
2975 node1, node2 = node2, node1
2975 node1, node2 = node2, node1
2976
2976
2977 diffopts = patch.diffopts(ui, opts)
2977 diffopts = patch.diffopts(ui, opts)
2978 m = scmutil.match(repo[node2], pats, opts)
2978 m = scmutil.match(repo[node2], pats, opts)
2979 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2979 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2980 listsubrepos=opts.get('subrepos'))
2980 listsubrepos=opts.get('subrepos'))
2981
2981
2982 @command('^export',
2982 @command('^export',
2983 [('o', 'output', '',
2983 [('o', 'output', '',
2984 _('print output to file with formatted name'), _('FORMAT')),
2984 _('print output to file with formatted name'), _('FORMAT')),
2985 ('', 'switch-parent', None, _('diff against the second parent')),
2985 ('', 'switch-parent', None, _('diff against the second parent')),
2986 ('r', 'rev', [], _('revisions to export'), _('REV')),
2986 ('r', 'rev', [], _('revisions to export'), _('REV')),
2987 ] + diffopts,
2987 ] + diffopts,
2988 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2988 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2989 def export(ui, repo, *changesets, **opts):
2989 def export(ui, repo, *changesets, **opts):
2990 """dump the header and diffs for one or more changesets
2990 """dump the header and diffs for one or more changesets
2991
2991
2992 Print the changeset header and diffs for one or more revisions.
2992 Print the changeset header and diffs for one or more revisions.
2993 If no revision is given, the parent of the working directory is used.
2993 If no revision is given, the parent of the working directory is used.
2994
2994
2995 The information shown in the changeset header is: author, date,
2995 The information shown in the changeset header is: author, date,
2996 branch name (if non-default), changeset hash, parent(s) and commit
2996 branch name (if non-default), changeset hash, parent(s) and commit
2997 comment.
2997 comment.
2998
2998
2999 .. note::
2999 .. note::
3000
3000
3001 export may generate unexpected diff output for merge
3001 export may generate unexpected diff output for merge
3002 changesets, as it will compare the merge changeset against its
3002 changesets, as it will compare the merge changeset against its
3003 first parent only.
3003 first parent only.
3004
3004
3005 Output may be to a file, in which case the name of the file is
3005 Output may be to a file, in which case the name of the file is
3006 given using a format string. The formatting rules are as follows:
3006 given using a format string. The formatting rules are as follows:
3007
3007
3008 :``%%``: literal "%" character
3008 :``%%``: literal "%" character
3009 :``%H``: changeset hash (40 hexadecimal digits)
3009 :``%H``: changeset hash (40 hexadecimal digits)
3010 :``%N``: number of patches being generated
3010 :``%N``: number of patches being generated
3011 :``%R``: changeset revision number
3011 :``%R``: changeset revision number
3012 :``%b``: basename of the exporting repository
3012 :``%b``: basename of the exporting repository
3013 :``%h``: short-form changeset hash (12 hexadecimal digits)
3013 :``%h``: short-form changeset hash (12 hexadecimal digits)
3014 :``%m``: first line of the commit message (only alphanumeric characters)
3014 :``%m``: first line of the commit message (only alphanumeric characters)
3015 :``%n``: zero-padded sequence number, starting at 1
3015 :``%n``: zero-padded sequence number, starting at 1
3016 :``%r``: zero-padded changeset revision number
3016 :``%r``: zero-padded changeset revision number
3017
3017
3018 Without the -a/--text option, export will avoid generating diffs
3018 Without the -a/--text option, export will avoid generating diffs
3019 of files it detects as binary. With -a, export will generate a
3019 of files it detects as binary. With -a, export will generate a
3020 diff anyway, probably with undesirable results.
3020 diff anyway, probably with undesirable results.
3021
3021
3022 Use the -g/--git option to generate diffs in the git extended diff
3022 Use the -g/--git option to generate diffs in the git extended diff
3023 format. See :hg:`help diffs` for more information.
3023 format. See :hg:`help diffs` for more information.
3024
3024
3025 With the --switch-parent option, the diff will be against the
3025 With the --switch-parent option, the diff will be against the
3026 second parent. It can be useful to review a merge.
3026 second parent. It can be useful to review a merge.
3027
3027
3028 .. container:: verbose
3028 .. container:: verbose
3029
3029
3030 Examples:
3030 Examples:
3031
3031
3032 - use export and import to transplant a bugfix to the current
3032 - use export and import to transplant a bugfix to the current
3033 branch::
3033 branch::
3034
3034
3035 hg export -r 9353 | hg import -
3035 hg export -r 9353 | hg import -
3036
3036
3037 - export all the changesets between two revisions to a file with
3037 - export all the changesets between two revisions to a file with
3038 rename information::
3038 rename information::
3039
3039
3040 hg export --git -r 123:150 > changes.txt
3040 hg export --git -r 123:150 > changes.txt
3041
3041
3042 - split outgoing changes into a series of patches with
3042 - split outgoing changes into a series of patches with
3043 descriptive names::
3043 descriptive names::
3044
3044
3045 hg export -r "outgoing()" -o "%n-%m.patch"
3045 hg export -r "outgoing()" -o "%n-%m.patch"
3046
3046
3047 Returns 0 on success.
3047 Returns 0 on success.
3048 """
3048 """
3049 changesets += tuple(opts.get('rev', []))
3049 changesets += tuple(opts.get('rev', []))
3050 if not changesets:
3050 if not changesets:
3051 changesets = ['.']
3051 changesets = ['.']
3052 revs = scmutil.revrange(repo, changesets)
3052 revs = scmutil.revrange(repo, changesets)
3053 if not revs:
3053 if not revs:
3054 raise util.Abort(_("export requires at least one changeset"))
3054 raise util.Abort(_("export requires at least one changeset"))
3055 if len(revs) > 1:
3055 if len(revs) > 1:
3056 ui.note(_('exporting patches:\n'))
3056 ui.note(_('exporting patches:\n'))
3057 else:
3057 else:
3058 ui.note(_('exporting patch:\n'))
3058 ui.note(_('exporting patch:\n'))
3059 cmdutil.export(repo, revs, template=opts.get('output'),
3059 cmdutil.export(repo, revs, template=opts.get('output'),
3060 switch_parent=opts.get('switch_parent'),
3060 switch_parent=opts.get('switch_parent'),
3061 opts=patch.diffopts(ui, opts))
3061 opts=patch.diffopts(ui, opts))
3062
3062
3063 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3063 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3064 def forget(ui, repo, *pats, **opts):
3064 def forget(ui, repo, *pats, **opts):
3065 """forget the specified files on the next commit
3065 """forget the specified files on the next commit
3066
3066
3067 Mark the specified files so they will no longer be tracked
3067 Mark the specified files so they will no longer be tracked
3068 after the next commit.
3068 after the next commit.
3069
3069
3070 This only removes files from the current branch, not from the
3070 This only removes files from the current branch, not from the
3071 entire project history, and it does not delete them from the
3071 entire project history, and it does not delete them from the
3072 working directory.
3072 working directory.
3073
3073
3074 To undo a forget before the next commit, see :hg:`add`.
3074 To undo a forget before the next commit, see :hg:`add`.
3075
3075
3076 .. container:: verbose
3076 .. container:: verbose
3077
3077
3078 Examples:
3078 Examples:
3079
3079
3080 - forget newly-added binary files::
3080 - forget newly-added binary files::
3081
3081
3082 hg forget "set:added() and binary()"
3082 hg forget "set:added() and binary()"
3083
3083
3084 - forget files that would be excluded by .hgignore::
3084 - forget files that would be excluded by .hgignore::
3085
3085
3086 hg forget "set:hgignore()"
3086 hg forget "set:hgignore()"
3087
3087
3088 Returns 0 on success.
3088 Returns 0 on success.
3089 """
3089 """
3090
3090
3091 if not pats:
3091 if not pats:
3092 raise util.Abort(_('no files specified'))
3092 raise util.Abort(_('no files specified'))
3093
3093
3094 m = scmutil.match(repo[None], pats, opts)
3094 m = scmutil.match(repo[None], pats, opts)
3095 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3095 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3096 return rejected and 1 or 0
3096 return rejected and 1 or 0
3097
3097
3098 @command(
3098 @command(
3099 'graft',
3099 'graft',
3100 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3100 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3101 ('c', 'continue', False, _('resume interrupted graft')),
3101 ('c', 'continue', False, _('resume interrupted graft')),
3102 ('e', 'edit', False, _('invoke editor on commit messages')),
3102 ('e', 'edit', False, _('invoke editor on commit messages')),
3103 ('', 'log', None, _('append graft info to log message')),
3103 ('', 'log', None, _('append graft info to log message')),
3104 ('f', 'force', False, _('force graft')),
3104 ('f', 'force', False, _('force graft')),
3105 ('D', 'currentdate', False,
3105 ('D', 'currentdate', False,
3106 _('record the current date as commit date')),
3106 _('record the current date as commit date')),
3107 ('U', 'currentuser', False,
3107 ('U', 'currentuser', False,
3108 _('record the current user as committer'), _('DATE'))]
3108 _('record the current user as committer'), _('DATE'))]
3109 + commitopts2 + mergetoolopts + dryrunopts,
3109 + commitopts2 + mergetoolopts + dryrunopts,
3110 _('[OPTION]... [-r] REV...'))
3110 _('[OPTION]... [-r] REV...'))
3111 def graft(ui, repo, *revs, **opts):
3111 def graft(ui, repo, *revs, **opts):
3112 '''copy changes from other branches onto the current branch
3112 '''copy changes from other branches onto the current branch
3113
3113
3114 This command uses Mercurial's merge logic to copy individual
3114 This command uses Mercurial's merge logic to copy individual
3115 changes from other branches without merging branches in the
3115 changes from other branches without merging branches in the
3116 history graph. This is sometimes known as 'backporting' or
3116 history graph. This is sometimes known as 'backporting' or
3117 'cherry-picking'. By default, graft will copy user, date, and
3117 'cherry-picking'. By default, graft will copy user, date, and
3118 description from the source changesets.
3118 description from the source changesets.
3119
3119
3120 Changesets that are ancestors of the current revision, that have
3120 Changesets that are ancestors of the current revision, that have
3121 already been grafted, or that are merges will be skipped.
3121 already been grafted, or that are merges will be skipped.
3122
3122
3123 If --log is specified, log messages will have a comment appended
3123 If --log is specified, log messages will have a comment appended
3124 of the form::
3124 of the form::
3125
3125
3126 (grafted from CHANGESETHASH)
3126 (grafted from CHANGESETHASH)
3127
3127
3128 If --force is specified, revisions will be grafted even if they
3128 If --force is specified, revisions will be grafted even if they
3129 are already ancestors of or have been grafted to the destination.
3129 are already ancestors of or have been grafted to the destination.
3130 This is useful when the revisions have since been backed out.
3130 This is useful when the revisions have since been backed out.
3131
3131
3132 If a graft merge results in conflicts, the graft process is
3132 If a graft merge results in conflicts, the graft process is
3133 interrupted so that the current merge can be manually resolved.
3133 interrupted so that the current merge can be manually resolved.
3134 Once all conflicts are addressed, the graft process can be
3134 Once all conflicts are addressed, the graft process can be
3135 continued with the -c/--continue option.
3135 continued with the -c/--continue option.
3136
3136
3137 .. note::
3137 .. note::
3138
3138
3139 The -c/--continue option does not reapply earlier options, except
3139 The -c/--continue option does not reapply earlier options, except
3140 for --force.
3140 for --force.
3141
3141
3142 .. container:: verbose
3142 .. container:: verbose
3143
3143
3144 Examples:
3144 Examples:
3145
3145
3146 - copy a single change to the stable branch and edit its description::
3146 - copy a single change to the stable branch and edit its description::
3147
3147
3148 hg update stable
3148 hg update stable
3149 hg graft --edit 9393
3149 hg graft --edit 9393
3150
3150
3151 - graft a range of changesets with one exception, updating dates::
3151 - graft a range of changesets with one exception, updating dates::
3152
3152
3153 hg graft -D "2085::2093 and not 2091"
3153 hg graft -D "2085::2093 and not 2091"
3154
3154
3155 - continue a graft after resolving conflicts::
3155 - continue a graft after resolving conflicts::
3156
3156
3157 hg graft -c
3157 hg graft -c
3158
3158
3159 - show the source of a grafted changeset::
3159 - show the source of a grafted changeset::
3160
3160
3161 hg log --debug -r .
3161 hg log --debug -r .
3162
3162
3163 See :hg:`help revisions` and :hg:`help revsets` for more about
3163 See :hg:`help revisions` and :hg:`help revsets` for more about
3164 specifying revisions.
3164 specifying revisions.
3165
3165
3166 Returns 0 on successful completion.
3166 Returns 0 on successful completion.
3167 '''
3167 '''
3168
3168
3169 revs = list(revs)
3169 revs = list(revs)
3170 revs.extend(opts['rev'])
3170 revs.extend(opts['rev'])
3171
3171
3172 if not opts.get('user') and opts.get('currentuser'):
3172 if not opts.get('user') and opts.get('currentuser'):
3173 opts['user'] = ui.username()
3173 opts['user'] = ui.username()
3174 if not opts.get('date') and opts.get('currentdate'):
3174 if not opts.get('date') and opts.get('currentdate'):
3175 opts['date'] = "%d %d" % util.makedate()
3175 opts['date'] = "%d %d" % util.makedate()
3176
3176
3177 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3177 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3178
3178
3179 cont = False
3179 cont = False
3180 if opts['continue']:
3180 if opts['continue']:
3181 cont = True
3181 cont = True
3182 if revs:
3182 if revs:
3183 raise util.Abort(_("can't specify --continue and revisions"))
3183 raise util.Abort(_("can't specify --continue and revisions"))
3184 # read in unfinished revisions
3184 # read in unfinished revisions
3185 try:
3185 try:
3186 nodes = repo.opener.read('graftstate').splitlines()
3186 nodes = repo.opener.read('graftstate').splitlines()
3187 revs = [repo[node].rev() for node in nodes]
3187 revs = [repo[node].rev() for node in nodes]
3188 except IOError, inst:
3188 except IOError, inst:
3189 if inst.errno != errno.ENOENT:
3189 if inst.errno != errno.ENOENT:
3190 raise
3190 raise
3191 raise util.Abort(_("no graft state found, can't continue"))
3191 raise util.Abort(_("no graft state found, can't continue"))
3192 else:
3192 else:
3193 cmdutil.checkunfinished(repo)
3193 cmdutil.checkunfinished(repo)
3194 cmdutil.bailifchanged(repo)
3194 cmdutil.bailifchanged(repo)
3195 if not revs:
3195 if not revs:
3196 raise util.Abort(_('no revisions specified'))
3196 raise util.Abort(_('no revisions specified'))
3197 revs = scmutil.revrange(repo, revs)
3197 revs = scmutil.revrange(repo, revs)
3198
3198
3199 # check for merges
3199 # check for merges
3200 for rev in repo.revs('%ld and merge()', revs):
3200 for rev in repo.revs('%ld and merge()', revs):
3201 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3201 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3202 revs.remove(rev)
3202 revs.remove(rev)
3203 if not revs:
3203 if not revs:
3204 return -1
3204 return -1
3205
3205
3206 # Don't check in the --continue case, in effect retaining --force across
3206 # Don't check in the --continue case, in effect retaining --force across
3207 # --continues. That's because without --force, any revisions we decided to
3207 # --continues. That's because without --force, any revisions we decided to
3208 # skip would have been filtered out here, so they wouldn't have made their
3208 # skip would have been filtered out here, so they wouldn't have made their
3209 # way to the graftstate. With --force, any revisions we would have otherwise
3209 # way to the graftstate. With --force, any revisions we would have otherwise
3210 # skipped would not have been filtered out, and if they hadn't been applied
3210 # skipped would not have been filtered out, and if they hadn't been applied
3211 # already, they'd have been in the graftstate.
3211 # already, they'd have been in the graftstate.
3212 if not (cont or opts.get('force')):
3212 if not (cont or opts.get('force')):
3213 # check for ancestors of dest branch
3213 # check for ancestors of dest branch
3214 crev = repo['.'].rev()
3214 crev = repo['.'].rev()
3215 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3215 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3216 # Cannot use x.remove(y) on smart set, this has to be a list.
3216 # Cannot use x.remove(y) on smart set, this has to be a list.
3217 # XXX make this lazy in the future
3217 # XXX make this lazy in the future
3218 revs = list(revs)
3218 revs = list(revs)
3219 # don't mutate while iterating, create a copy
3219 # don't mutate while iterating, create a copy
3220 for rev in list(revs):
3220 for rev in list(revs):
3221 if rev in ancestors:
3221 if rev in ancestors:
3222 ui.warn(_('skipping ancestor revision %s\n') % rev)
3222 ui.warn(_('skipping ancestor revision %s\n') % rev)
3223 # XXX remove on list is slow
3223 # XXX remove on list is slow
3224 revs.remove(rev)
3224 revs.remove(rev)
3225 if not revs:
3225 if not revs:
3226 return -1
3226 return -1
3227
3227
3228 # analyze revs for earlier grafts
3228 # analyze revs for earlier grafts
3229 ids = {}
3229 ids = {}
3230 for ctx in repo.set("%ld", revs):
3230 for ctx in repo.set("%ld", revs):
3231 ids[ctx.hex()] = ctx.rev()
3231 ids[ctx.hex()] = ctx.rev()
3232 n = ctx.extra().get('source')
3232 n = ctx.extra().get('source')
3233 if n:
3233 if n:
3234 ids[n] = ctx.rev()
3234 ids[n] = ctx.rev()
3235
3235
3236 # check ancestors for earlier grafts
3236 # check ancestors for earlier grafts
3237 ui.debug('scanning for duplicate grafts\n')
3237 ui.debug('scanning for duplicate grafts\n')
3238
3238
3239 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3239 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3240 ctx = repo[rev]
3240 ctx = repo[rev]
3241 n = ctx.extra().get('source')
3241 n = ctx.extra().get('source')
3242 if n in ids:
3242 if n in ids:
3243 try:
3243 try:
3244 r = repo[n].rev()
3244 r = repo[n].rev()
3245 except error.RepoLookupError:
3245 except error.RepoLookupError:
3246 r = None
3246 r = None
3247 if r in revs:
3247 if r in revs:
3248 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3248 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3249 % (r, rev))
3249 % (r, rev))
3250 revs.remove(r)
3250 revs.remove(r)
3251 elif ids[n] in revs:
3251 elif ids[n] in revs:
3252 if r is None:
3252 if r is None:
3253 ui.warn(_('skipping already grafted revision %s '
3253 ui.warn(_('skipping already grafted revision %s '
3254 '(%s also has unknown origin %s)\n')
3254 '(%s also has unknown origin %s)\n')
3255 % (ids[n], rev, n))
3255 % (ids[n], rev, n))
3256 else:
3256 else:
3257 ui.warn(_('skipping already grafted revision %s '
3257 ui.warn(_('skipping already grafted revision %s '
3258 '(%s also has origin %d)\n')
3258 '(%s also has origin %d)\n')
3259 % (ids[n], rev, r))
3259 % (ids[n], rev, r))
3260 revs.remove(ids[n])
3260 revs.remove(ids[n])
3261 elif ctx.hex() in ids:
3261 elif ctx.hex() in ids:
3262 r = ids[ctx.hex()]
3262 r = ids[ctx.hex()]
3263 ui.warn(_('skipping already grafted revision %s '
3263 ui.warn(_('skipping already grafted revision %s '
3264 '(was grafted from %d)\n') % (r, rev))
3264 '(was grafted from %d)\n') % (r, rev))
3265 revs.remove(r)
3265 revs.remove(r)
3266 if not revs:
3266 if not revs:
3267 return -1
3267 return -1
3268
3268
3269 wlock = repo.wlock()
3269 wlock = repo.wlock()
3270 try:
3270 try:
3271 current = repo['.']
3271 current = repo['.']
3272 for pos, ctx in enumerate(repo.set("%ld", revs)):
3272 for pos, ctx in enumerate(repo.set("%ld", revs)):
3273
3273
3274 ui.status(_('grafting revision %s\n') % ctx.rev())
3274 ui.status(_('grafting revision %s\n') % ctx.rev())
3275 if opts.get('dry_run'):
3275 if opts.get('dry_run'):
3276 continue
3276 continue
3277
3277
3278 source = ctx.extra().get('source')
3278 source = ctx.extra().get('source')
3279 if not source:
3279 if not source:
3280 source = ctx.hex()
3280 source = ctx.hex()
3281 extra = {'source': source}
3281 extra = {'source': source}
3282 user = ctx.user()
3282 user = ctx.user()
3283 if opts.get('user'):
3283 if opts.get('user'):
3284 user = opts['user']
3284 user = opts['user']
3285 date = ctx.date()
3285 date = ctx.date()
3286 if opts.get('date'):
3286 if opts.get('date'):
3287 date = opts['date']
3287 date = opts['date']
3288 message = ctx.description()
3288 message = ctx.description()
3289 if opts.get('log'):
3289 if opts.get('log'):
3290 message += '\n(grafted from %s)' % ctx.hex()
3290 message += '\n(grafted from %s)' % ctx.hex()
3291
3291
3292 # we don't merge the first commit when continuing
3292 # we don't merge the first commit when continuing
3293 if not cont:
3293 if not cont:
3294 # perform the graft merge with p1(rev) as 'ancestor'
3294 # perform the graft merge with p1(rev) as 'ancestor'
3295 try:
3295 try:
3296 # ui.forcemerge is an internal variable, do not document
3296 # ui.forcemerge is an internal variable, do not document
3297 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3297 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3298 'graft')
3298 'graft')
3299 stats = mergemod.update(repo, ctx.node(), True, True, False,
3299 stats = mergemod.update(repo, ctx.node(), True, True, False,
3300 ctx.p1().node(),
3300 ctx.p1().node(),
3301 labels=['local', 'graft'])
3301 labels=['local', 'graft'])
3302 finally:
3302 finally:
3303 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3303 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3304 # report any conflicts
3304 # report any conflicts
3305 if stats and stats[3] > 0:
3305 if stats and stats[3] > 0:
3306 # write out state for --continue
3306 # write out state for --continue
3307 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3307 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3308 repo.opener.write('graftstate', ''.join(nodelines))
3308 repo.opener.write('graftstate', ''.join(nodelines))
3309 raise util.Abort(
3309 raise util.Abort(
3310 _("unresolved conflicts, can't continue"),
3310 _("unresolved conflicts, can't continue"),
3311 hint=_('use hg resolve and hg graft --continue'))
3311 hint=_('use hg resolve and hg graft --continue'))
3312 else:
3312 else:
3313 cont = False
3313 cont = False
3314
3314
3315 # drop the second merge parent
3315 # drop the second merge parent
3316 repo.setparents(current.node(), nullid)
3316 repo.setparents(current.node(), nullid)
3317 repo.dirstate.write()
3317 repo.dirstate.write()
3318 # fix up dirstate for copies and renames
3318 # fix up dirstate for copies and renames
3319 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
3319 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
3320
3320
3321 # commit
3321 # commit
3322 node = repo.commit(text=message, user=user,
3322 node = repo.commit(text=message, user=user,
3323 date=date, extra=extra, editor=editor)
3323 date=date, extra=extra, editor=editor)
3324 if node is None:
3324 if node is None:
3325 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3325 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3326 else:
3326 else:
3327 current = repo[node]
3327 current = repo[node]
3328 finally:
3328 finally:
3329 wlock.release()
3329 wlock.release()
3330
3330
3331 # remove state when we complete successfully
3331 # remove state when we complete successfully
3332 if not opts.get('dry_run'):
3332 if not opts.get('dry_run'):
3333 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3333 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3334
3334
3335 return 0
3335 return 0
3336
3336
3337 @command('grep',
3337 @command('grep',
3338 [('0', 'print0', None, _('end fields with NUL')),
3338 [('0', 'print0', None, _('end fields with NUL')),
3339 ('', 'all', None, _('print all revisions that match')),
3339 ('', 'all', None, _('print all revisions that match')),
3340 ('a', 'text', None, _('treat all files as text')),
3340 ('a', 'text', None, _('treat all files as text')),
3341 ('f', 'follow', None,
3341 ('f', 'follow', None,
3342 _('follow changeset history,'
3342 _('follow changeset history,'
3343 ' or file history across copies and renames')),
3343 ' or file history across copies and renames')),
3344 ('i', 'ignore-case', None, _('ignore case when matching')),
3344 ('i', 'ignore-case', None, _('ignore case when matching')),
3345 ('l', 'files-with-matches', None,
3345 ('l', 'files-with-matches', None,
3346 _('print only filenames and revisions that match')),
3346 _('print only filenames and revisions that match')),
3347 ('n', 'line-number', None, _('print matching line numbers')),
3347 ('n', 'line-number', None, _('print matching line numbers')),
3348 ('r', 'rev', [],
3348 ('r', 'rev', [],
3349 _('only search files changed within revision range'), _('REV')),
3349 _('only search files changed within revision range'), _('REV')),
3350 ('u', 'user', None, _('list the author (long with -v)')),
3350 ('u', 'user', None, _('list the author (long with -v)')),
3351 ('d', 'date', None, _('list the date (short with -q)')),
3351 ('d', 'date', None, _('list the date (short with -q)')),
3352 ] + walkopts,
3352 ] + walkopts,
3353 _('[OPTION]... PATTERN [FILE]...'),
3353 _('[OPTION]... PATTERN [FILE]...'),
3354 inferrepo=True)
3354 inferrepo=True)
3355 def grep(ui, repo, pattern, *pats, **opts):
3355 def grep(ui, repo, pattern, *pats, **opts):
3356 """search for a pattern in specified files and revisions
3356 """search for a pattern in specified files and revisions
3357
3357
3358 Search revisions of files for a regular expression.
3358 Search revisions of files for a regular expression.
3359
3359
3360 This command behaves differently than Unix grep. It only accepts
3360 This command behaves differently than Unix grep. It only accepts
3361 Python/Perl regexps. It searches repository history, not the
3361 Python/Perl regexps. It searches repository history, not the
3362 working directory. It always prints the revision number in which a
3362 working directory. It always prints the revision number in which a
3363 match appears.
3363 match appears.
3364
3364
3365 By default, grep only prints output for the first revision of a
3365 By default, grep only prints output for the first revision of a
3366 file in which it finds a match. To get it to print every revision
3366 file in which it finds a match. To get it to print every revision
3367 that contains a change in match status ("-" for a match that
3367 that contains a change in match status ("-" for a match that
3368 becomes a non-match, or "+" for a non-match that becomes a match),
3368 becomes a non-match, or "+" for a non-match that becomes a match),
3369 use the --all flag.
3369 use the --all flag.
3370
3370
3371 Returns 0 if a match is found, 1 otherwise.
3371 Returns 0 if a match is found, 1 otherwise.
3372 """
3372 """
3373 reflags = re.M
3373 reflags = re.M
3374 if opts.get('ignore_case'):
3374 if opts.get('ignore_case'):
3375 reflags |= re.I
3375 reflags |= re.I
3376 try:
3376 try:
3377 regexp = util.re.compile(pattern, reflags)
3377 regexp = util.re.compile(pattern, reflags)
3378 except re.error, inst:
3378 except re.error, inst:
3379 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3379 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3380 return 1
3380 return 1
3381 sep, eol = ':', '\n'
3381 sep, eol = ':', '\n'
3382 if opts.get('print0'):
3382 if opts.get('print0'):
3383 sep = eol = '\0'
3383 sep = eol = '\0'
3384
3384
3385 getfile = util.lrucachefunc(repo.file)
3385 getfile = util.lrucachefunc(repo.file)
3386
3386
3387 def matchlines(body):
3387 def matchlines(body):
3388 begin = 0
3388 begin = 0
3389 linenum = 0
3389 linenum = 0
3390 while begin < len(body):
3390 while begin < len(body):
3391 match = regexp.search(body, begin)
3391 match = regexp.search(body, begin)
3392 if not match:
3392 if not match:
3393 break
3393 break
3394 mstart, mend = match.span()
3394 mstart, mend = match.span()
3395 linenum += body.count('\n', begin, mstart) + 1
3395 linenum += body.count('\n', begin, mstart) + 1
3396 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3396 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3397 begin = body.find('\n', mend) + 1 or len(body) + 1
3397 begin = body.find('\n', mend) + 1 or len(body) + 1
3398 lend = begin - 1
3398 lend = begin - 1
3399 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3399 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3400
3400
3401 class linestate(object):
3401 class linestate(object):
3402 def __init__(self, line, linenum, colstart, colend):
3402 def __init__(self, line, linenum, colstart, colend):
3403 self.line = line
3403 self.line = line
3404 self.linenum = linenum
3404 self.linenum = linenum
3405 self.colstart = colstart
3405 self.colstart = colstart
3406 self.colend = colend
3406 self.colend = colend
3407
3407
3408 def __hash__(self):
3408 def __hash__(self):
3409 return hash((self.linenum, self.line))
3409 return hash((self.linenum, self.line))
3410
3410
3411 def __eq__(self, other):
3411 def __eq__(self, other):
3412 return self.line == other.line
3412 return self.line == other.line
3413
3413
3414 def __iter__(self):
3414 def __iter__(self):
3415 yield (self.line[:self.colstart], '')
3415 yield (self.line[:self.colstart], '')
3416 yield (self.line[self.colstart:self.colend], 'grep.match')
3416 yield (self.line[self.colstart:self.colend], 'grep.match')
3417 rest = self.line[self.colend:]
3417 rest = self.line[self.colend:]
3418 while rest != '':
3418 while rest != '':
3419 match = regexp.search(rest)
3419 match = regexp.search(rest)
3420 if not match:
3420 if not match:
3421 yield (rest, '')
3421 yield (rest, '')
3422 break
3422 break
3423 mstart, mend = match.span()
3423 mstart, mend = match.span()
3424 yield (rest[:mstart], '')
3424 yield (rest[:mstart], '')
3425 yield (rest[mstart:mend], 'grep.match')
3425 yield (rest[mstart:mend], 'grep.match')
3426 rest = rest[mend:]
3426 rest = rest[mend:]
3427
3427
3428 matches = {}
3428 matches = {}
3429 copies = {}
3429 copies = {}
3430 def grepbody(fn, rev, body):
3430 def grepbody(fn, rev, body):
3431 matches[rev].setdefault(fn, [])
3431 matches[rev].setdefault(fn, [])
3432 m = matches[rev][fn]
3432 m = matches[rev][fn]
3433 for lnum, cstart, cend, line in matchlines(body):
3433 for lnum, cstart, cend, line in matchlines(body):
3434 s = linestate(line, lnum, cstart, cend)
3434 s = linestate(line, lnum, cstart, cend)
3435 m.append(s)
3435 m.append(s)
3436
3436
3437 def difflinestates(a, b):
3437 def difflinestates(a, b):
3438 sm = difflib.SequenceMatcher(None, a, b)
3438 sm = difflib.SequenceMatcher(None, a, b)
3439 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3439 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3440 if tag == 'insert':
3440 if tag == 'insert':
3441 for i in xrange(blo, bhi):
3441 for i in xrange(blo, bhi):
3442 yield ('+', b[i])
3442 yield ('+', b[i])
3443 elif tag == 'delete':
3443 elif tag == 'delete':
3444 for i in xrange(alo, ahi):
3444 for i in xrange(alo, ahi):
3445 yield ('-', a[i])
3445 yield ('-', a[i])
3446 elif tag == 'replace':
3446 elif tag == 'replace':
3447 for i in xrange(alo, ahi):
3447 for i in xrange(alo, ahi):
3448 yield ('-', a[i])
3448 yield ('-', a[i])
3449 for i in xrange(blo, bhi):
3449 for i in xrange(blo, bhi):
3450 yield ('+', b[i])
3450 yield ('+', b[i])
3451
3451
3452 def display(fn, ctx, pstates, states):
3452 def display(fn, ctx, pstates, states):
3453 rev = ctx.rev()
3453 rev = ctx.rev()
3454 datefunc = ui.quiet and util.shortdate or util.datestr
3454 datefunc = ui.quiet and util.shortdate or util.datestr
3455 found = False
3455 found = False
3456 @util.cachefunc
3456 @util.cachefunc
3457 def binary():
3457 def binary():
3458 flog = getfile(fn)
3458 flog = getfile(fn)
3459 return util.binary(flog.read(ctx.filenode(fn)))
3459 return util.binary(flog.read(ctx.filenode(fn)))
3460
3460
3461 if opts.get('all'):
3461 if opts.get('all'):
3462 iter = difflinestates(pstates, states)
3462 iter = difflinestates(pstates, states)
3463 else:
3463 else:
3464 iter = [('', l) for l in states]
3464 iter = [('', l) for l in states]
3465 for change, l in iter:
3465 for change, l in iter:
3466 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3466 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3467
3467
3468 if opts.get('line_number'):
3468 if opts.get('line_number'):
3469 cols.append((str(l.linenum), 'grep.linenumber'))
3469 cols.append((str(l.linenum), 'grep.linenumber'))
3470 if opts.get('all'):
3470 if opts.get('all'):
3471 cols.append((change, 'grep.change'))
3471 cols.append((change, 'grep.change'))
3472 if opts.get('user'):
3472 if opts.get('user'):
3473 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3473 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3474 if opts.get('date'):
3474 if opts.get('date'):
3475 cols.append((datefunc(ctx.date()), 'grep.date'))
3475 cols.append((datefunc(ctx.date()), 'grep.date'))
3476 for col, label in cols[:-1]:
3476 for col, label in cols[:-1]:
3477 ui.write(col, label=label)
3477 ui.write(col, label=label)
3478 ui.write(sep, label='grep.sep')
3478 ui.write(sep, label='grep.sep')
3479 ui.write(cols[-1][0], label=cols[-1][1])
3479 ui.write(cols[-1][0], label=cols[-1][1])
3480 if not opts.get('files_with_matches'):
3480 if not opts.get('files_with_matches'):
3481 ui.write(sep, label='grep.sep')
3481 ui.write(sep, label='grep.sep')
3482 if not opts.get('text') and binary():
3482 if not opts.get('text') and binary():
3483 ui.write(" Binary file matches")
3483 ui.write(" Binary file matches")
3484 else:
3484 else:
3485 for s, label in l:
3485 for s, label in l:
3486 ui.write(s, label=label)
3486 ui.write(s, label=label)
3487 ui.write(eol)
3487 ui.write(eol)
3488 found = True
3488 found = True
3489 if opts.get('files_with_matches'):
3489 if opts.get('files_with_matches'):
3490 break
3490 break
3491 return found
3491 return found
3492
3492
3493 skip = {}
3493 skip = {}
3494 revfiles = {}
3494 revfiles = {}
3495 matchfn = scmutil.match(repo[None], pats, opts)
3495 matchfn = scmutil.match(repo[None], pats, opts)
3496 found = False
3496 found = False
3497 follow = opts.get('follow')
3497 follow = opts.get('follow')
3498
3498
3499 def prep(ctx, fns):
3499 def prep(ctx, fns):
3500 rev = ctx.rev()
3500 rev = ctx.rev()
3501 pctx = ctx.p1()
3501 pctx = ctx.p1()
3502 parent = pctx.rev()
3502 parent = pctx.rev()
3503 matches.setdefault(rev, {})
3503 matches.setdefault(rev, {})
3504 matches.setdefault(parent, {})
3504 matches.setdefault(parent, {})
3505 files = revfiles.setdefault(rev, [])
3505 files = revfiles.setdefault(rev, [])
3506 for fn in fns:
3506 for fn in fns:
3507 flog = getfile(fn)
3507 flog = getfile(fn)
3508 try:
3508 try:
3509 fnode = ctx.filenode(fn)
3509 fnode = ctx.filenode(fn)
3510 except error.LookupError:
3510 except error.LookupError:
3511 continue
3511 continue
3512
3512
3513 copied = flog.renamed(fnode)
3513 copied = flog.renamed(fnode)
3514 copy = follow and copied and copied[0]
3514 copy = follow and copied and copied[0]
3515 if copy:
3515 if copy:
3516 copies.setdefault(rev, {})[fn] = copy
3516 copies.setdefault(rev, {})[fn] = copy
3517 if fn in skip:
3517 if fn in skip:
3518 if copy:
3518 if copy:
3519 skip[copy] = True
3519 skip[copy] = True
3520 continue
3520 continue
3521 files.append(fn)
3521 files.append(fn)
3522
3522
3523 if fn not in matches[rev]:
3523 if fn not in matches[rev]:
3524 grepbody(fn, rev, flog.read(fnode))
3524 grepbody(fn, rev, flog.read(fnode))
3525
3525
3526 pfn = copy or fn
3526 pfn = copy or fn
3527 if pfn not in matches[parent]:
3527 if pfn not in matches[parent]:
3528 try:
3528 try:
3529 fnode = pctx.filenode(pfn)
3529 fnode = pctx.filenode(pfn)
3530 grepbody(pfn, parent, flog.read(fnode))
3530 grepbody(pfn, parent, flog.read(fnode))
3531 except error.LookupError:
3531 except error.LookupError:
3532 pass
3532 pass
3533
3533
3534 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3534 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3535 rev = ctx.rev()
3535 rev = ctx.rev()
3536 parent = ctx.p1().rev()
3536 parent = ctx.p1().rev()
3537 for fn in sorted(revfiles.get(rev, [])):
3537 for fn in sorted(revfiles.get(rev, [])):
3538 states = matches[rev][fn]
3538 states = matches[rev][fn]
3539 copy = copies.get(rev, {}).get(fn)
3539 copy = copies.get(rev, {}).get(fn)
3540 if fn in skip:
3540 if fn in skip:
3541 if copy:
3541 if copy:
3542 skip[copy] = True
3542 skip[copy] = True
3543 continue
3543 continue
3544 pstates = matches.get(parent, {}).get(copy or fn, [])
3544 pstates = matches.get(parent, {}).get(copy or fn, [])
3545 if pstates or states:
3545 if pstates or states:
3546 r = display(fn, ctx, pstates, states)
3546 r = display(fn, ctx, pstates, states)
3547 found = found or r
3547 found = found or r
3548 if r and not opts.get('all'):
3548 if r and not opts.get('all'):
3549 skip[fn] = True
3549 skip[fn] = True
3550 if copy:
3550 if copy:
3551 skip[copy] = True
3551 skip[copy] = True
3552 del matches[rev]
3552 del matches[rev]
3553 del revfiles[rev]
3553 del revfiles[rev]
3554
3554
3555 return not found
3555 return not found
3556
3556
3557 @command('heads',
3557 @command('heads',
3558 [('r', 'rev', '',
3558 [('r', 'rev', '',
3559 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3559 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3560 ('t', 'topo', False, _('show topological heads only')),
3560 ('t', 'topo', False, _('show topological heads only')),
3561 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3561 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3562 ('c', 'closed', False, _('show normal and closed branch heads')),
3562 ('c', 'closed', False, _('show normal and closed branch heads')),
3563 ] + templateopts,
3563 ] + templateopts,
3564 _('[-ct] [-r STARTREV] [REV]...'))
3564 _('[-ct] [-r STARTREV] [REV]...'))
3565 def heads(ui, repo, *branchrevs, **opts):
3565 def heads(ui, repo, *branchrevs, **opts):
3566 """show branch heads
3566 """show branch heads
3567
3567
3568 With no arguments, show all open branch heads in the repository.
3568 With no arguments, show all open branch heads in the repository.
3569 Branch heads are changesets that have no descendants on the
3569 Branch heads are changesets that have no descendants on the
3570 same branch. They are where development generally takes place and
3570 same branch. They are where development generally takes place and
3571 are the usual targets for update and merge operations.
3571 are the usual targets for update and merge operations.
3572
3572
3573 If one or more REVs are given, only open branch heads on the
3573 If one or more REVs are given, only open branch heads on the
3574 branches associated with the specified changesets are shown. This
3574 branches associated with the specified changesets are shown. This
3575 means that you can use :hg:`heads .` to see the heads on the
3575 means that you can use :hg:`heads .` to see the heads on the
3576 currently checked-out branch.
3576 currently checked-out branch.
3577
3577
3578 If -c/--closed is specified, also show branch heads marked closed
3578 If -c/--closed is specified, also show branch heads marked closed
3579 (see :hg:`commit --close-branch`).
3579 (see :hg:`commit --close-branch`).
3580
3580
3581 If STARTREV is specified, only those heads that are descendants of
3581 If STARTREV is specified, only those heads that are descendants of
3582 STARTREV will be displayed.
3582 STARTREV will be displayed.
3583
3583
3584 If -t/--topo is specified, named branch mechanics will be ignored and only
3584 If -t/--topo is specified, named branch mechanics will be ignored and only
3585 topological heads (changesets with no children) will be shown.
3585 topological heads (changesets with no children) will be shown.
3586
3586
3587 Returns 0 if matching heads are found, 1 if not.
3587 Returns 0 if matching heads are found, 1 if not.
3588 """
3588 """
3589
3589
3590 start = None
3590 start = None
3591 if 'rev' in opts:
3591 if 'rev' in opts:
3592 start = scmutil.revsingle(repo, opts['rev'], None).node()
3592 start = scmutil.revsingle(repo, opts['rev'], None).node()
3593
3593
3594 if opts.get('topo'):
3594 if opts.get('topo'):
3595 heads = [repo[h] for h in repo.heads(start)]
3595 heads = [repo[h] for h in repo.heads(start)]
3596 else:
3596 else:
3597 heads = []
3597 heads = []
3598 for branch in repo.branchmap():
3598 for branch in repo.branchmap():
3599 heads += repo.branchheads(branch, start, opts.get('closed'))
3599 heads += repo.branchheads(branch, start, opts.get('closed'))
3600 heads = [repo[h] for h in heads]
3600 heads = [repo[h] for h in heads]
3601
3601
3602 if branchrevs:
3602 if branchrevs:
3603 branches = set(repo[br].branch() for br in branchrevs)
3603 branches = set(repo[br].branch() for br in branchrevs)
3604 heads = [h for h in heads if h.branch() in branches]
3604 heads = [h for h in heads if h.branch() in branches]
3605
3605
3606 if opts.get('active') and branchrevs:
3606 if opts.get('active') and branchrevs:
3607 dagheads = repo.heads(start)
3607 dagheads = repo.heads(start)
3608 heads = [h for h in heads if h.node() in dagheads]
3608 heads = [h for h in heads if h.node() in dagheads]
3609
3609
3610 if branchrevs:
3610 if branchrevs:
3611 haveheads = set(h.branch() for h in heads)
3611 haveheads = set(h.branch() for h in heads)
3612 if branches - haveheads:
3612 if branches - haveheads:
3613 headless = ', '.join(b for b in branches - haveheads)
3613 headless = ', '.join(b for b in branches - haveheads)
3614 msg = _('no open branch heads found on branches %s')
3614 msg = _('no open branch heads found on branches %s')
3615 if opts.get('rev'):
3615 if opts.get('rev'):
3616 msg += _(' (started at %s)') % opts['rev']
3616 msg += _(' (started at %s)') % opts['rev']
3617 ui.warn((msg + '\n') % headless)
3617 ui.warn((msg + '\n') % headless)
3618
3618
3619 if not heads:
3619 if not heads:
3620 return 1
3620 return 1
3621
3621
3622 heads = sorted(heads, key=lambda x: -x.rev())
3622 heads = sorted(heads, key=lambda x: -x.rev())
3623 displayer = cmdutil.show_changeset(ui, repo, opts)
3623 displayer = cmdutil.show_changeset(ui, repo, opts)
3624 for ctx in heads:
3624 for ctx in heads:
3625 displayer.show(ctx)
3625 displayer.show(ctx)
3626 displayer.close()
3626 displayer.close()
3627
3627
3628 @command('help',
3628 @command('help',
3629 [('e', 'extension', None, _('show only help for extensions')),
3629 [('e', 'extension', None, _('show only help for extensions')),
3630 ('c', 'command', None, _('show only help for commands')),
3630 ('c', 'command', None, _('show only help for commands')),
3631 ('k', 'keyword', '', _('show topics matching keyword')),
3631 ('k', 'keyword', '', _('show topics matching keyword')),
3632 ],
3632 ],
3633 _('[-ec] [TOPIC]'),
3633 _('[-ec] [TOPIC]'),
3634 norepo=True)
3634 norepo=True)
3635 def help_(ui, name=None, **opts):
3635 def help_(ui, name=None, **opts):
3636 """show help for a given topic or a help overview
3636 """show help for a given topic or a help overview
3637
3637
3638 With no arguments, print a list of commands with short help messages.
3638 With no arguments, print a list of commands with short help messages.
3639
3639
3640 Given a topic, extension, or command name, print help for that
3640 Given a topic, extension, or command name, print help for that
3641 topic.
3641 topic.
3642
3642
3643 Returns 0 if successful.
3643 Returns 0 if successful.
3644 """
3644 """
3645
3645
3646 textwidth = min(ui.termwidth(), 80) - 2
3646 textwidth = min(ui.termwidth(), 80) - 2
3647
3647
3648 keep = ui.verbose and ['verbose'] or []
3648 keep = ui.verbose and ['verbose'] or []
3649 text = help.help_(ui, name, **opts)
3649 text = help.help_(ui, name, **opts)
3650
3650
3651 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3651 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3652 if 'verbose' in pruned:
3652 if 'verbose' in pruned:
3653 keep.append('omitted')
3653 keep.append('omitted')
3654 else:
3654 else:
3655 keep.append('notomitted')
3655 keep.append('notomitted')
3656 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3656 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3657 ui.write(formatted)
3657 ui.write(formatted)
3658
3658
3659
3659
3660 @command('identify|id',
3660 @command('identify|id',
3661 [('r', 'rev', '',
3661 [('r', 'rev', '',
3662 _('identify the specified revision'), _('REV')),
3662 _('identify the specified revision'), _('REV')),
3663 ('n', 'num', None, _('show local revision number')),
3663 ('n', 'num', None, _('show local revision number')),
3664 ('i', 'id', None, _('show global revision id')),
3664 ('i', 'id', None, _('show global revision id')),
3665 ('b', 'branch', None, _('show branch')),
3665 ('b', 'branch', None, _('show branch')),
3666 ('t', 'tags', None, _('show tags')),
3666 ('t', 'tags', None, _('show tags')),
3667 ('B', 'bookmarks', None, _('show bookmarks')),
3667 ('B', 'bookmarks', None, _('show bookmarks')),
3668 ] + remoteopts,
3668 ] + remoteopts,
3669 _('[-nibtB] [-r REV] [SOURCE]'),
3669 _('[-nibtB] [-r REV] [SOURCE]'),
3670 optionalrepo=True)
3670 optionalrepo=True)
3671 def identify(ui, repo, source=None, rev=None,
3671 def identify(ui, repo, source=None, rev=None,
3672 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3672 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3673 """identify the working copy or specified revision
3673 """identify the working copy or specified revision
3674
3674
3675 Print a summary identifying the repository state at REV using one or
3675 Print a summary identifying the repository state at REV using one or
3676 two parent hash identifiers, followed by a "+" if the working
3676 two parent hash identifiers, followed by a "+" if the working
3677 directory has uncommitted changes, the branch name (if not default),
3677 directory has uncommitted changes, the branch name (if not default),
3678 a list of tags, and a list of bookmarks.
3678 a list of tags, and a list of bookmarks.
3679
3679
3680 When REV is not given, print a summary of the current state of the
3680 When REV is not given, print a summary of the current state of the
3681 repository.
3681 repository.
3682
3682
3683 Specifying a path to a repository root or Mercurial bundle will
3683 Specifying a path to a repository root or Mercurial bundle will
3684 cause lookup to operate on that repository/bundle.
3684 cause lookup to operate on that repository/bundle.
3685
3685
3686 .. container:: verbose
3686 .. container:: verbose
3687
3687
3688 Examples:
3688 Examples:
3689
3689
3690 - generate a build identifier for the working directory::
3690 - generate a build identifier for the working directory::
3691
3691
3692 hg id --id > build-id.dat
3692 hg id --id > build-id.dat
3693
3693
3694 - find the revision corresponding to a tag::
3694 - find the revision corresponding to a tag::
3695
3695
3696 hg id -n -r 1.3
3696 hg id -n -r 1.3
3697
3697
3698 - check the most recent revision of a remote repository::
3698 - check the most recent revision of a remote repository::
3699
3699
3700 hg id -r tip http://selenic.com/hg/
3700 hg id -r tip http://selenic.com/hg/
3701
3701
3702 Returns 0 if successful.
3702 Returns 0 if successful.
3703 """
3703 """
3704
3704
3705 if not repo and not source:
3705 if not repo and not source:
3706 raise util.Abort(_("there is no Mercurial repository here "
3706 raise util.Abort(_("there is no Mercurial repository here "
3707 "(.hg not found)"))
3707 "(.hg not found)"))
3708
3708
3709 hexfunc = ui.debugflag and hex or short
3709 hexfunc = ui.debugflag and hex or short
3710 default = not (num or id or branch or tags or bookmarks)
3710 default = not (num or id or branch or tags or bookmarks)
3711 output = []
3711 output = []
3712 revs = []
3712 revs = []
3713
3713
3714 if source:
3714 if source:
3715 source, branches = hg.parseurl(ui.expandpath(source))
3715 source, branches = hg.parseurl(ui.expandpath(source))
3716 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3716 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3717 repo = peer.local()
3717 repo = peer.local()
3718 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3718 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3719
3719
3720 if not repo:
3720 if not repo:
3721 if num or branch or tags:
3721 if num or branch or tags:
3722 raise util.Abort(
3722 raise util.Abort(
3723 _("can't query remote revision number, branch, or tags"))
3723 _("can't query remote revision number, branch, or tags"))
3724 if not rev and revs:
3724 if not rev and revs:
3725 rev = revs[0]
3725 rev = revs[0]
3726 if not rev:
3726 if not rev:
3727 rev = "tip"
3727 rev = "tip"
3728
3728
3729 remoterev = peer.lookup(rev)
3729 remoterev = peer.lookup(rev)
3730 if default or id:
3730 if default or id:
3731 output = [hexfunc(remoterev)]
3731 output = [hexfunc(remoterev)]
3732
3732
3733 def getbms():
3733 def getbms():
3734 bms = []
3734 bms = []
3735
3735
3736 if 'bookmarks' in peer.listkeys('namespaces'):
3736 if 'bookmarks' in peer.listkeys('namespaces'):
3737 hexremoterev = hex(remoterev)
3737 hexremoterev = hex(remoterev)
3738 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3738 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3739 if bmr == hexremoterev]
3739 if bmr == hexremoterev]
3740
3740
3741 return sorted(bms)
3741 return sorted(bms)
3742
3742
3743 if bookmarks:
3743 if bookmarks:
3744 output.extend(getbms())
3744 output.extend(getbms())
3745 elif default and not ui.quiet:
3745 elif default and not ui.quiet:
3746 # multiple bookmarks for a single parent separated by '/'
3746 # multiple bookmarks for a single parent separated by '/'
3747 bm = '/'.join(getbms())
3747 bm = '/'.join(getbms())
3748 if bm:
3748 if bm:
3749 output.append(bm)
3749 output.append(bm)
3750 else:
3750 else:
3751 if not rev:
3751 if not rev:
3752 ctx = repo[None]
3752 ctx = repo[None]
3753 parents = ctx.parents()
3753 parents = ctx.parents()
3754 changed = ""
3754 changed = ""
3755 if default or id or num:
3755 if default or id or num:
3756 if (util.any(repo.status())
3756 if (util.any(repo.status())
3757 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3757 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3758 changed = '+'
3758 changed = '+'
3759 if default or id:
3759 if default or id:
3760 output = ["%s%s" %
3760 output = ["%s%s" %
3761 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3761 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3762 if num:
3762 if num:
3763 output.append("%s%s" %
3763 output.append("%s%s" %
3764 ('+'.join([str(p.rev()) for p in parents]), changed))
3764 ('+'.join([str(p.rev()) for p in parents]), changed))
3765 else:
3765 else:
3766 ctx = scmutil.revsingle(repo, rev)
3766 ctx = scmutil.revsingle(repo, rev)
3767 if default or id:
3767 if default or id:
3768 output = [hexfunc(ctx.node())]
3768 output = [hexfunc(ctx.node())]
3769 if num:
3769 if num:
3770 output.append(str(ctx.rev()))
3770 output.append(str(ctx.rev()))
3771
3771
3772 if default and not ui.quiet:
3772 if default and not ui.quiet:
3773 b = ctx.branch()
3773 b = ctx.branch()
3774 if b != 'default':
3774 if b != 'default':
3775 output.append("(%s)" % b)
3775 output.append("(%s)" % b)
3776
3776
3777 # multiple tags for a single parent separated by '/'
3777 # multiple tags for a single parent separated by '/'
3778 t = '/'.join(ctx.tags())
3778 t = '/'.join(ctx.tags())
3779 if t:
3779 if t:
3780 output.append(t)
3780 output.append(t)
3781
3781
3782 # multiple bookmarks for a single parent separated by '/'
3782 # multiple bookmarks for a single parent separated by '/'
3783 bm = '/'.join(ctx.bookmarks())
3783 bm = '/'.join(ctx.bookmarks())
3784 if bm:
3784 if bm:
3785 output.append(bm)
3785 output.append(bm)
3786 else:
3786 else:
3787 if branch:
3787 if branch:
3788 output.append(ctx.branch())
3788 output.append(ctx.branch())
3789
3789
3790 if tags:
3790 if tags:
3791 output.extend(ctx.tags())
3791 output.extend(ctx.tags())
3792
3792
3793 if bookmarks:
3793 if bookmarks:
3794 output.extend(ctx.bookmarks())
3794 output.extend(ctx.bookmarks())
3795
3795
3796 ui.write("%s\n" % ' '.join(output))
3796 ui.write("%s\n" % ' '.join(output))
3797
3797
3798 @command('import|patch',
3798 @command('import|patch',
3799 [('p', 'strip', 1,
3799 [('p', 'strip', 1,
3800 _('directory strip option for patch. This has the same '
3800 _('directory strip option for patch. This has the same '
3801 'meaning as the corresponding patch option'), _('NUM')),
3801 'meaning as the corresponding patch option'), _('NUM')),
3802 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3802 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3803 ('e', 'edit', False, _('invoke editor on commit messages')),
3803 ('e', 'edit', False, _('invoke editor on commit messages')),
3804 ('f', 'force', None,
3804 ('f', 'force', None,
3805 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3805 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3806 ('', 'no-commit', None,
3806 ('', 'no-commit', None,
3807 _("don't commit, just update the working directory")),
3807 _("don't commit, just update the working directory")),
3808 ('', 'bypass', None,
3808 ('', 'bypass', None,
3809 _("apply patch without touching the working directory")),
3809 _("apply patch without touching the working directory")),
3810 ('', 'partial', None,
3810 ('', 'partial', None,
3811 _('commit even if some hunks fail')),
3811 _('commit even if some hunks fail')),
3812 ('', 'exact', None,
3812 ('', 'exact', None,
3813 _('apply patch to the nodes from which it was generated')),
3813 _('apply patch to the nodes from which it was generated')),
3814 ('', 'import-branch', None,
3814 ('', 'import-branch', None,
3815 _('use any branch information in patch (implied by --exact)'))] +
3815 _('use any branch information in patch (implied by --exact)'))] +
3816 commitopts + commitopts2 + similarityopts,
3816 commitopts + commitopts2 + similarityopts,
3817 _('[OPTION]... PATCH...'))
3817 _('[OPTION]... PATCH...'))
3818 def import_(ui, repo, patch1=None, *patches, **opts):
3818 def import_(ui, repo, patch1=None, *patches, **opts):
3819 """import an ordered set of patches
3819 """import an ordered set of patches
3820
3820
3821 Import a list of patches and commit them individually (unless
3821 Import a list of patches and commit them individually (unless
3822 --no-commit is specified).
3822 --no-commit is specified).
3823
3823
3824 Because import first applies changes to the working directory,
3824 Because import first applies changes to the working directory,
3825 import will abort if there are outstanding changes.
3825 import will abort if there are outstanding changes.
3826
3826
3827 You can import a patch straight from a mail message. Even patches
3827 You can import a patch straight from a mail message. Even patches
3828 as attachments work (to use the body part, it must have type
3828 as attachments work (to use the body part, it must have type
3829 text/plain or text/x-patch). From and Subject headers of email
3829 text/plain or text/x-patch). From and Subject headers of email
3830 message are used as default committer and commit message. All
3830 message are used as default committer and commit message. All
3831 text/plain body parts before first diff are added to commit
3831 text/plain body parts before first diff are added to commit
3832 message.
3832 message.
3833
3833
3834 If the imported patch was generated by :hg:`export`, user and
3834 If the imported patch was generated by :hg:`export`, user and
3835 description from patch override values from message headers and
3835 description from patch override values from message headers and
3836 body. Values given on command line with -m/--message and -u/--user
3836 body. Values given on command line with -m/--message and -u/--user
3837 override these.
3837 override these.
3838
3838
3839 If --exact is specified, import will set the working directory to
3839 If --exact is specified, import will set the working directory to
3840 the parent of each patch before applying it, and will abort if the
3840 the parent of each patch before applying it, and will abort if the
3841 resulting changeset has a different ID than the one recorded in
3841 resulting changeset has a different ID than the one recorded in
3842 the patch. This may happen due to character set problems or other
3842 the patch. This may happen due to character set problems or other
3843 deficiencies in the text patch format.
3843 deficiencies in the text patch format.
3844
3844
3845 Use --bypass to apply and commit patches directly to the
3845 Use --bypass to apply and commit patches directly to the
3846 repository, not touching the working directory. Without --exact,
3846 repository, not touching the working directory. Without --exact,
3847 patches will be applied on top of the working directory parent
3847 patches will be applied on top of the working directory parent
3848 revision.
3848 revision.
3849
3849
3850 With -s/--similarity, hg will attempt to discover renames and
3850 With -s/--similarity, hg will attempt to discover renames and
3851 copies in the patch in the same way as :hg:`addremove`.
3851 copies in the patch in the same way as :hg:`addremove`.
3852
3852
3853 Use --partial to ensure a changeset will be created from the patch
3853 Use --partial to ensure a changeset will be created from the patch
3854 even if some hunks fail to apply. Hunks that fail to apply will be
3854 even if some hunks fail to apply. Hunks that fail to apply will be
3855 written to a <target-file>.rej file. Conflicts can then be resolved
3855 written to a <target-file>.rej file. Conflicts can then be resolved
3856 by hand before :hg:`commit --amend` is run to update the created
3856 by hand before :hg:`commit --amend` is run to update the created
3857 changeset. This flag exists to let people import patches that
3857 changeset. This flag exists to let people import patches that
3858 partially apply without losing the associated metadata (author,
3858 partially apply without losing the associated metadata (author,
3859 date, description, ...). Note that when none of the hunk applies
3859 date, description, ...). Note that when none of the hunk applies
3860 cleanly, :hg:`import --partial` will create an empty changeset,
3860 cleanly, :hg:`import --partial` will create an empty changeset,
3861 importing only the patch metadata.
3861 importing only the patch metadata.
3862
3862
3863 To read a patch from standard input, use "-" as the patch name. If
3863 To read a patch from standard input, use "-" as the patch name. If
3864 a URL is specified, the patch will be downloaded from it.
3864 a URL is specified, the patch will be downloaded from it.
3865 See :hg:`help dates` for a list of formats valid for -d/--date.
3865 See :hg:`help dates` for a list of formats valid for -d/--date.
3866
3866
3867 .. container:: verbose
3867 .. container:: verbose
3868
3868
3869 Examples:
3869 Examples:
3870
3870
3871 - import a traditional patch from a website and detect renames::
3871 - import a traditional patch from a website and detect renames::
3872
3872
3873 hg import -s 80 http://example.com/bugfix.patch
3873 hg import -s 80 http://example.com/bugfix.patch
3874
3874
3875 - import a changeset from an hgweb server::
3875 - import a changeset from an hgweb server::
3876
3876
3877 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3877 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3878
3878
3879 - import all the patches in an Unix-style mbox::
3879 - import all the patches in an Unix-style mbox::
3880
3880
3881 hg import incoming-patches.mbox
3881 hg import incoming-patches.mbox
3882
3882
3883 - attempt to exactly restore an exported changeset (not always
3883 - attempt to exactly restore an exported changeset (not always
3884 possible)::
3884 possible)::
3885
3885
3886 hg import --exact proposed-fix.patch
3886 hg import --exact proposed-fix.patch
3887
3887
3888 Returns 0 on success, 1 on partial success (see --partial).
3888 Returns 0 on success, 1 on partial success (see --partial).
3889 """
3889 """
3890
3890
3891 if not patch1:
3891 if not patch1:
3892 raise util.Abort(_('need at least one patch to import'))
3892 raise util.Abort(_('need at least one patch to import'))
3893
3893
3894 patches = (patch1,) + patches
3894 patches = (patch1,) + patches
3895
3895
3896 date = opts.get('date')
3896 date = opts.get('date')
3897 if date:
3897 if date:
3898 opts['date'] = util.parsedate(date)
3898 opts['date'] = util.parsedate(date)
3899
3899
3900 update = not opts.get('bypass')
3900 update = not opts.get('bypass')
3901 if not update and opts.get('no_commit'):
3901 if not update and opts.get('no_commit'):
3902 raise util.Abort(_('cannot use --no-commit with --bypass'))
3902 raise util.Abort(_('cannot use --no-commit with --bypass'))
3903 try:
3903 try:
3904 sim = float(opts.get('similarity') or 0)
3904 sim = float(opts.get('similarity') or 0)
3905 except ValueError:
3905 except ValueError:
3906 raise util.Abort(_('similarity must be a number'))
3906 raise util.Abort(_('similarity must be a number'))
3907 if sim < 0 or sim > 100:
3907 if sim < 0 or sim > 100:
3908 raise util.Abort(_('similarity must be between 0 and 100'))
3908 raise util.Abort(_('similarity must be between 0 and 100'))
3909 if sim and not update:
3909 if sim and not update:
3910 raise util.Abort(_('cannot use --similarity with --bypass'))
3910 raise util.Abort(_('cannot use --similarity with --bypass'))
3911 if opts.get('exact') and opts.get('edit'):
3911 if opts.get('exact') and opts.get('edit'):
3912 raise util.Abort(_('cannot use --exact with --edit'))
3912 raise util.Abort(_('cannot use --exact with --edit'))
3913
3913
3914 if update:
3914 if update:
3915 cmdutil.checkunfinished(repo)
3915 cmdutil.checkunfinished(repo)
3916 if (opts.get('exact') or not opts.get('force')) and update:
3916 if (opts.get('exact') or not opts.get('force')) and update:
3917 cmdutil.bailifchanged(repo)
3917 cmdutil.bailifchanged(repo)
3918
3918
3919 base = opts["base"]
3919 base = opts["base"]
3920 wlock = lock = tr = None
3920 wlock = lock = tr = None
3921 msgs = []
3921 msgs = []
3922 ret = 0
3922 ret = 0
3923
3923
3924
3924
3925 try:
3925 try:
3926 try:
3926 try:
3927 wlock = repo.wlock()
3927 wlock = repo.wlock()
3928 if not opts.get('no_commit'):
3928 if not opts.get('no_commit'):
3929 lock = repo.lock()
3929 lock = repo.lock()
3930 tr = repo.transaction('import')
3930 tr = repo.transaction('import')
3931 parents = repo.parents()
3931 parents = repo.parents()
3932 for patchurl in patches:
3932 for patchurl in patches:
3933 if patchurl == '-':
3933 if patchurl == '-':
3934 ui.status(_('applying patch from stdin\n'))
3934 ui.status(_('applying patch from stdin\n'))
3935 patchfile = ui.fin
3935 patchfile = ui.fin
3936 patchurl = 'stdin' # for error message
3936 patchurl = 'stdin' # for error message
3937 else:
3937 else:
3938 patchurl = os.path.join(base, patchurl)
3938 patchurl = os.path.join(base, patchurl)
3939 ui.status(_('applying %s\n') % patchurl)
3939 ui.status(_('applying %s\n') % patchurl)
3940 patchfile = hg.openpath(ui, patchurl)
3940 patchfile = hg.openpath(ui, patchurl)
3941
3941
3942 haspatch = False
3942 haspatch = False
3943 for hunk in patch.split(patchfile):
3943 for hunk in patch.split(patchfile):
3944 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3944 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3945 parents, opts,
3945 parents, opts,
3946 msgs, hg.clean)
3946 msgs, hg.clean)
3947 if msg:
3947 if msg:
3948 haspatch = True
3948 haspatch = True
3949 ui.note(msg + '\n')
3949 ui.note(msg + '\n')
3950 if update or opts.get('exact'):
3950 if update or opts.get('exact'):
3951 parents = repo.parents()
3951 parents = repo.parents()
3952 else:
3952 else:
3953 parents = [repo[node]]
3953 parents = [repo[node]]
3954 if rej:
3954 if rej:
3955 ui.write_err(_("patch applied partially\n"))
3955 ui.write_err(_("patch applied partially\n"))
3956 ui.write_err(_("(fix the .rej files and run "
3956 ui.write_err(_("(fix the .rej files and run "
3957 "`hg commit --amend`)\n"))
3957 "`hg commit --amend`)\n"))
3958 ret = 1
3958 ret = 1
3959 break
3959 break
3960
3960
3961 if not haspatch:
3961 if not haspatch:
3962 raise util.Abort(_('%s: no diffs found') % patchurl)
3962 raise util.Abort(_('%s: no diffs found') % patchurl)
3963
3963
3964 if tr:
3964 if tr:
3965 tr.close()
3965 tr.close()
3966 if msgs:
3966 if msgs:
3967 repo.savecommitmessage('\n* * *\n'.join(msgs))
3967 repo.savecommitmessage('\n* * *\n'.join(msgs))
3968 return ret
3968 return ret
3969 except: # re-raises
3969 except: # re-raises
3970 # wlock.release() indirectly calls dirstate.write(): since
3970 # wlock.release() indirectly calls dirstate.write(): since
3971 # we're crashing, we do not want to change the working dir
3971 # we're crashing, we do not want to change the working dir
3972 # parent after all, so make sure it writes nothing
3972 # parent after all, so make sure it writes nothing
3973 repo.dirstate.invalidate()
3973 repo.dirstate.invalidate()
3974 raise
3974 raise
3975 finally:
3975 finally:
3976 if tr:
3976 if tr:
3977 tr.release()
3977 tr.release()
3978 release(lock, wlock)
3978 release(lock, wlock)
3979
3979
3980 @command('incoming|in',
3980 @command('incoming|in',
3981 [('f', 'force', None,
3981 [('f', 'force', None,
3982 _('run even if remote repository is unrelated')),
3982 _('run even if remote repository is unrelated')),
3983 ('n', 'newest-first', None, _('show newest record first')),
3983 ('n', 'newest-first', None, _('show newest record first')),
3984 ('', 'bundle', '',
3984 ('', 'bundle', '',
3985 _('file to store the bundles into'), _('FILE')),
3985 _('file to store the bundles into'), _('FILE')),
3986 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3986 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3987 ('B', 'bookmarks', False, _("compare bookmarks")),
3987 ('B', 'bookmarks', False, _("compare bookmarks")),
3988 ('b', 'branch', [],
3988 ('b', 'branch', [],
3989 _('a specific branch you would like to pull'), _('BRANCH')),
3989 _('a specific branch you would like to pull'), _('BRANCH')),
3990 ] + logopts + remoteopts + subrepoopts,
3990 ] + logopts + remoteopts + subrepoopts,
3991 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3991 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3992 def incoming(ui, repo, source="default", **opts):
3992 def incoming(ui, repo, source="default", **opts):
3993 """show new changesets found in source
3993 """show new changesets found in source
3994
3994
3995 Show new changesets found in the specified path/URL or the default
3995 Show new changesets found in the specified path/URL or the default
3996 pull location. These are the changesets that would have been pulled
3996 pull location. These are the changesets that would have been pulled
3997 if a pull at the time you issued this command.
3997 if a pull at the time you issued this command.
3998
3998
3999 For remote repository, using --bundle avoids downloading the
3999 For remote repository, using --bundle avoids downloading the
4000 changesets twice if the incoming is followed by a pull.
4000 changesets twice if the incoming is followed by a pull.
4001
4001
4002 See pull for valid source format details.
4002 See pull for valid source format details.
4003
4003
4004 .. container:: verbose
4004 .. container:: verbose
4005
4005
4006 Examples:
4006 Examples:
4007
4007
4008 - show incoming changes with patches and full description::
4008 - show incoming changes with patches and full description::
4009
4009
4010 hg incoming -vp
4010 hg incoming -vp
4011
4011
4012 - show incoming changes excluding merges, store a bundle::
4012 - show incoming changes excluding merges, store a bundle::
4013
4013
4014 hg in -vpM --bundle incoming.hg
4014 hg in -vpM --bundle incoming.hg
4015 hg pull incoming.hg
4015 hg pull incoming.hg
4016
4016
4017 - briefly list changes inside a bundle::
4017 - briefly list changes inside a bundle::
4018
4018
4019 hg in changes.hg -T "{desc|firstline}\\n"
4019 hg in changes.hg -T "{desc|firstline}\\n"
4020
4020
4021 Returns 0 if there are incoming changes, 1 otherwise.
4021 Returns 0 if there are incoming changes, 1 otherwise.
4022 """
4022 """
4023 if opts.get('graph'):
4023 if opts.get('graph'):
4024 cmdutil.checkunsupportedgraphflags([], opts)
4024 cmdutil.checkunsupportedgraphflags([], opts)
4025 def display(other, chlist, displayer):
4025 def display(other, chlist, displayer):
4026 revdag = cmdutil.graphrevs(other, chlist, opts)
4026 revdag = cmdutil.graphrevs(other, chlist, opts)
4027 showparents = [ctx.node() for ctx in repo[None].parents()]
4027 showparents = [ctx.node() for ctx in repo[None].parents()]
4028 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4028 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4029 graphmod.asciiedges)
4029 graphmod.asciiedges)
4030
4030
4031 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4031 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4032 return 0
4032 return 0
4033
4033
4034 if opts.get('bundle') and opts.get('subrepos'):
4034 if opts.get('bundle') and opts.get('subrepos'):
4035 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4035 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4036
4036
4037 if opts.get('bookmarks'):
4037 if opts.get('bookmarks'):
4038 source, branches = hg.parseurl(ui.expandpath(source),
4038 source, branches = hg.parseurl(ui.expandpath(source),
4039 opts.get('branch'))
4039 opts.get('branch'))
4040 other = hg.peer(repo, opts, source)
4040 other = hg.peer(repo, opts, source)
4041 if 'bookmarks' not in other.listkeys('namespaces'):
4041 if 'bookmarks' not in other.listkeys('namespaces'):
4042 ui.warn(_("remote doesn't support bookmarks\n"))
4042 ui.warn(_("remote doesn't support bookmarks\n"))
4043 return 0
4043 return 0
4044 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4044 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4045 return bookmarks.diff(ui, repo, other)
4045 return bookmarks.diff(ui, repo, other)
4046
4046
4047 repo._subtoppath = ui.expandpath(source)
4047 repo._subtoppath = ui.expandpath(source)
4048 try:
4048 try:
4049 return hg.incoming(ui, repo, source, opts)
4049 return hg.incoming(ui, repo, source, opts)
4050 finally:
4050 finally:
4051 del repo._subtoppath
4051 del repo._subtoppath
4052
4052
4053
4053
4054 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4054 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4055 norepo=True)
4055 norepo=True)
4056 def init(ui, dest=".", **opts):
4056 def init(ui, dest=".", **opts):
4057 """create a new repository in the given directory
4057 """create a new repository in the given directory
4058
4058
4059 Initialize a new repository in the given directory. If the given
4059 Initialize a new repository in the given directory. If the given
4060 directory does not exist, it will be created.
4060 directory does not exist, it will be created.
4061
4061
4062 If no directory is given, the current directory is used.
4062 If no directory is given, the current directory is used.
4063
4063
4064 It is possible to specify an ``ssh://`` URL as the destination.
4064 It is possible to specify an ``ssh://`` URL as the destination.
4065 See :hg:`help urls` for more information.
4065 See :hg:`help urls` for more information.
4066
4066
4067 Returns 0 on success.
4067 Returns 0 on success.
4068 """
4068 """
4069 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4069 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4070
4070
4071 @command('locate',
4071 @command('locate',
4072 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4072 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4073 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4073 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4074 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4074 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4075 ] + walkopts,
4075 ] + walkopts,
4076 _('[OPTION]... [PATTERN]...'))
4076 _('[OPTION]... [PATTERN]...'))
4077 def locate(ui, repo, *pats, **opts):
4077 def locate(ui, repo, *pats, **opts):
4078 """locate files matching specific patterns
4078 """locate files matching specific patterns
4079
4079
4080 Print files under Mercurial control in the working directory whose
4080 Print files under Mercurial control in the working directory whose
4081 names match the given patterns.
4081 names match the given patterns.
4082
4082
4083 By default, this command searches all directories in the working
4083 By default, this command searches all directories in the working
4084 directory. To search just the current directory and its
4084 directory. To search just the current directory and its
4085 subdirectories, use "--include .".
4085 subdirectories, use "--include .".
4086
4086
4087 If no patterns are given to match, this command prints the names
4087 If no patterns are given to match, this command prints the names
4088 of all files under Mercurial control in the working directory.
4088 of all files under Mercurial control in the working directory.
4089
4089
4090 If you want to feed the output of this command into the "xargs"
4090 If you want to feed the output of this command into the "xargs"
4091 command, use the -0 option to both this command and "xargs". This
4091 command, use the -0 option to both this command and "xargs". This
4092 will avoid the problem of "xargs" treating single filenames that
4092 will avoid the problem of "xargs" treating single filenames that
4093 contain whitespace as multiple filenames.
4093 contain whitespace as multiple filenames.
4094
4094
4095 Returns 0 if a match is found, 1 otherwise.
4095 Returns 0 if a match is found, 1 otherwise.
4096 """
4096 """
4097 end = opts.get('print0') and '\0' or '\n'
4097 end = opts.get('print0') and '\0' or '\n'
4098 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4098 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4099
4099
4100 ret = 1
4100 ret = 1
4101 ctx = repo[rev]
4101 ctx = repo[rev]
4102 m = scmutil.match(ctx, pats, opts, default='relglob')
4102 m = scmutil.match(ctx, pats, opts, default='relglob')
4103 m.bad = lambda x, y: False
4103 m.bad = lambda x, y: False
4104
4104
4105 for abs in ctx.matches(m):
4105 for abs in ctx.matches(m):
4106 if opts.get('fullpath'):
4106 if opts.get('fullpath'):
4107 ui.write(repo.wjoin(abs), end)
4107 ui.write(repo.wjoin(abs), end)
4108 else:
4108 else:
4109 ui.write(((pats and m.rel(abs)) or abs), end)
4109 ui.write(((pats and m.rel(abs)) or abs), end)
4110 ret = 0
4110 ret = 0
4111
4111
4112 return ret
4112 return ret
4113
4113
4114 @command('^log|history',
4114 @command('^log|history',
4115 [('f', 'follow', None,
4115 [('f', 'follow', None,
4116 _('follow changeset history, or file history across copies and renames')),
4116 _('follow changeset history, or file history across copies and renames')),
4117 ('', 'follow-first', None,
4117 ('', 'follow-first', None,
4118 _('only follow the first parent of merge changesets (DEPRECATED)')),
4118 _('only follow the first parent of merge changesets (DEPRECATED)')),
4119 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4119 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4120 ('C', 'copies', None, _('show copied files')),
4120 ('C', 'copies', None, _('show copied files')),
4121 ('k', 'keyword', [],
4121 ('k', 'keyword', [],
4122 _('do case-insensitive search for a given text'), _('TEXT')),
4122 _('do case-insensitive search for a given text'), _('TEXT')),
4123 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4123 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4124 ('', 'removed', None, _('include revisions where files were removed')),
4124 ('', 'removed', None, _('include revisions where files were removed')),
4125 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4125 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4126 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4126 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4127 ('', 'only-branch', [],
4127 ('', 'only-branch', [],
4128 _('show only changesets within the given named branch (DEPRECATED)'),
4128 _('show only changesets within the given named branch (DEPRECATED)'),
4129 _('BRANCH')),
4129 _('BRANCH')),
4130 ('b', 'branch', [],
4130 ('b', 'branch', [],
4131 _('show changesets within the given named branch'), _('BRANCH')),
4131 _('show changesets within the given named branch'), _('BRANCH')),
4132 ('P', 'prune', [],
4132 ('P', 'prune', [],
4133 _('do not display revision or any of its ancestors'), _('REV')),
4133 _('do not display revision or any of its ancestors'), _('REV')),
4134 ] + logopts + walkopts,
4134 ] + logopts + walkopts,
4135 _('[OPTION]... [FILE]'),
4135 _('[OPTION]... [FILE]'),
4136 inferrepo=True)
4136 inferrepo=True)
4137 def log(ui, repo, *pats, **opts):
4137 def log(ui, repo, *pats, **opts):
4138 """show revision history of entire repository or files
4138 """show revision history of entire repository or files
4139
4139
4140 Print the revision history of the specified files or the entire
4140 Print the revision history of the specified files or the entire
4141 project.
4141 project.
4142
4142
4143 If no revision range is specified, the default is ``tip:0`` unless
4143 If no revision range is specified, the default is ``tip:0`` unless
4144 --follow is set, in which case the working directory parent is
4144 --follow is set, in which case the working directory parent is
4145 used as the starting revision.
4145 used as the starting revision.
4146
4146
4147 File history is shown without following rename or copy history of
4147 File history is shown without following rename or copy history of
4148 files. Use -f/--follow with a filename to follow history across
4148 files. Use -f/--follow with a filename to follow history across
4149 renames and copies. --follow without a filename will only show
4149 renames and copies. --follow without a filename will only show
4150 ancestors or descendants of the starting revision.
4150 ancestors or descendants of the starting revision.
4151
4151
4152 By default this command prints revision number and changeset id,
4152 By default this command prints revision number and changeset id,
4153 tags, non-trivial parents, user, date and time, and a summary for
4153 tags, non-trivial parents, user, date and time, and a summary for
4154 each commit. When the -v/--verbose switch is used, the list of
4154 each commit. When the -v/--verbose switch is used, the list of
4155 changed files and full commit message are shown.
4155 changed files and full commit message are shown.
4156
4156
4157 With --graph the revisions are shown as an ASCII art DAG with the most
4157 With --graph the revisions are shown as an ASCII art DAG with the most
4158 recent changeset at the top.
4158 recent changeset at the top.
4159 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4159 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4160 and '+' represents a fork where the changeset from the lines below is a
4160 and '+' represents a fork where the changeset from the lines below is a
4161 parent of the 'o' merge on the same line.
4161 parent of the 'o' merge on the same line.
4162
4162
4163 .. note::
4163 .. note::
4164
4164
4165 log -p/--patch may generate unexpected diff output for merge
4165 log -p/--patch may generate unexpected diff output for merge
4166 changesets, as it will only compare the merge changeset against
4166 changesets, as it will only compare the merge changeset against
4167 its first parent. Also, only files different from BOTH parents
4167 its first parent. Also, only files different from BOTH parents
4168 will appear in files:.
4168 will appear in files:.
4169
4169
4170 .. note::
4170 .. note::
4171
4171
4172 for performance reasons, log FILE may omit duplicate changes
4172 for performance reasons, log FILE may omit duplicate changes
4173 made on branches and will not show deletions. To see all
4173 made on branches and will not show deletions. To see all
4174 changes including duplicates and deletions, use the --removed
4174 changes including duplicates and deletions, use the --removed
4175 switch.
4175 switch.
4176
4176
4177 .. container:: verbose
4177 .. container:: verbose
4178
4178
4179 Some examples:
4179 Some examples:
4180
4180
4181 - changesets with full descriptions and file lists::
4181 - changesets with full descriptions and file lists::
4182
4182
4183 hg log -v
4183 hg log -v
4184
4184
4185 - changesets ancestral to the working directory::
4185 - changesets ancestral to the working directory::
4186
4186
4187 hg log -f
4187 hg log -f
4188
4188
4189 - last 10 commits on the current branch::
4189 - last 10 commits on the current branch::
4190
4190
4191 hg log -l 10 -b .
4191 hg log -l 10 -b .
4192
4192
4193 - changesets showing all modifications of a file, including removals::
4193 - changesets showing all modifications of a file, including removals::
4194
4194
4195 hg log --removed file.c
4195 hg log --removed file.c
4196
4196
4197 - all changesets that touch a directory, with diffs, excluding merges::
4197 - all changesets that touch a directory, with diffs, excluding merges::
4198
4198
4199 hg log -Mp lib/
4199 hg log -Mp lib/
4200
4200
4201 - all revision numbers that match a keyword::
4201 - all revision numbers that match a keyword::
4202
4202
4203 hg log -k bug --template "{rev}\\n"
4203 hg log -k bug --template "{rev}\\n"
4204
4204
4205 - list available log templates::
4205 - list available log templates::
4206
4206
4207 hg log -T list
4207 hg log -T list
4208
4208
4209 - check if a given changeset is included is a tagged release::
4209 - check if a given changeset is included is a tagged release::
4210
4210
4211 hg log -r "a21ccf and ancestor(1.9)"
4211 hg log -r "a21ccf and ancestor(1.9)"
4212
4212
4213 - find all changesets by some user in a date range::
4213 - find all changesets by some user in a date range::
4214
4214
4215 hg log -k alice -d "may 2008 to jul 2008"
4215 hg log -k alice -d "may 2008 to jul 2008"
4216
4216
4217 - summary of all changesets after the last tag::
4217 - summary of all changesets after the last tag::
4218
4218
4219 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4219 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4220
4220
4221 See :hg:`help dates` for a list of formats valid for -d/--date.
4221 See :hg:`help dates` for a list of formats valid for -d/--date.
4222
4222
4223 See :hg:`help revisions` and :hg:`help revsets` for more about
4223 See :hg:`help revisions` and :hg:`help revsets` for more about
4224 specifying revisions.
4224 specifying revisions.
4225
4225
4226 See :hg:`help templates` for more about pre-packaged styles and
4226 See :hg:`help templates` for more about pre-packaged styles and
4227 specifying custom templates.
4227 specifying custom templates.
4228
4228
4229 Returns 0 on success.
4229 Returns 0 on success.
4230 """
4230 """
4231 if opts.get('graph'):
4231 if opts.get('graph'):
4232 return cmdutil.graphlog(ui, repo, *pats, **opts)
4232 return cmdutil.graphlog(ui, repo, *pats, **opts)
4233
4233
4234 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4234 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4235 limit = cmdutil.loglimit(opts)
4235 limit = cmdutil.loglimit(opts)
4236 count = 0
4236 count = 0
4237
4237
4238 getrenamed = None
4238 getrenamed = None
4239 if opts.get('copies'):
4239 if opts.get('copies'):
4240 endrev = None
4240 endrev = None
4241 if opts.get('rev'):
4241 if opts.get('rev'):
4242 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4242 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4243 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4243 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4244
4244
4245 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4245 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4246 for rev in revs:
4246 for rev in revs:
4247 if count == limit:
4247 if count == limit:
4248 break
4248 break
4249 ctx = repo[rev]
4249 ctx = repo[rev]
4250 copies = None
4250 copies = None
4251 if getrenamed is not None and rev:
4251 if getrenamed is not None and rev:
4252 copies = []
4252 copies = []
4253 for fn in ctx.files():
4253 for fn in ctx.files():
4254 rename = getrenamed(fn, rev)
4254 rename = getrenamed(fn, rev)
4255 if rename:
4255 if rename:
4256 copies.append((fn, rename[0]))
4256 copies.append((fn, rename[0]))
4257 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4257 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4258 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4258 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4259 if displayer.flush(rev):
4259 if displayer.flush(rev):
4260 count += 1
4260 count += 1
4261
4261
4262 displayer.close()
4262 displayer.close()
4263
4263
4264 @command('manifest',
4264 @command('manifest',
4265 [('r', 'rev', '', _('revision to display'), _('REV')),
4265 [('r', 'rev', '', _('revision to display'), _('REV')),
4266 ('', 'all', False, _("list files from all revisions"))],
4266 ('', 'all', False, _("list files from all revisions"))],
4267 _('[-r REV]'))
4267 _('[-r REV]'))
4268 def manifest(ui, repo, node=None, rev=None, **opts):
4268 def manifest(ui, repo, node=None, rev=None, **opts):
4269 """output the current or given revision of the project manifest
4269 """output the current or given revision of the project manifest
4270
4270
4271 Print a list of version controlled files for the given revision.
4271 Print a list of version controlled files for the given revision.
4272 If no revision is given, the first parent of the working directory
4272 If no revision is given, the first parent of the working directory
4273 is used, or the null revision if no revision is checked out.
4273 is used, or the null revision if no revision is checked out.
4274
4274
4275 With -v, print file permissions, symlink and executable bits.
4275 With -v, print file permissions, symlink and executable bits.
4276 With --debug, print file revision hashes.
4276 With --debug, print file revision hashes.
4277
4277
4278 If option --all is specified, the list of all files from all revisions
4278 If option --all is specified, the list of all files from all revisions
4279 is printed. This includes deleted and renamed files.
4279 is printed. This includes deleted and renamed files.
4280
4280
4281 Returns 0 on success.
4281 Returns 0 on success.
4282 """
4282 """
4283
4283
4284 fm = ui.formatter('manifest', opts)
4284 fm = ui.formatter('manifest', opts)
4285
4285
4286 if opts.get('all'):
4286 if opts.get('all'):
4287 if rev or node:
4287 if rev or node:
4288 raise util.Abort(_("can't specify a revision with --all"))
4288 raise util.Abort(_("can't specify a revision with --all"))
4289
4289
4290 res = []
4290 res = []
4291 prefix = "data/"
4291 prefix = "data/"
4292 suffix = ".i"
4292 suffix = ".i"
4293 plen = len(prefix)
4293 plen = len(prefix)
4294 slen = len(suffix)
4294 slen = len(suffix)
4295 lock = repo.lock()
4295 lock = repo.lock()
4296 try:
4296 try:
4297 for fn, b, size in repo.store.datafiles():
4297 for fn, b, size in repo.store.datafiles():
4298 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4298 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4299 res.append(fn[plen:-slen])
4299 res.append(fn[plen:-slen])
4300 finally:
4300 finally:
4301 lock.release()
4301 lock.release()
4302 for f in res:
4302 for f in res:
4303 fm.startitem()
4303 fm.startitem()
4304 fm.write("path", '%s\n', f)
4304 fm.write("path", '%s\n', f)
4305 fm.end()
4305 fm.end()
4306 return
4306 return
4307
4307
4308 if rev and node:
4308 if rev and node:
4309 raise util.Abort(_("please specify just one revision"))
4309 raise util.Abort(_("please specify just one revision"))
4310
4310
4311 if not node:
4311 if not node:
4312 node = rev
4312 node = rev
4313
4313
4314 char = {'l': '@', 'x': '*', '': ''}
4314 char = {'l': '@', 'x': '*', '': ''}
4315 mode = {'l': '644', 'x': '755', '': '644'}
4315 mode = {'l': '644', 'x': '755', '': '644'}
4316 ctx = scmutil.revsingle(repo, node)
4316 ctx = scmutil.revsingle(repo, node)
4317 mf = ctx.manifest()
4317 mf = ctx.manifest()
4318 for f in ctx:
4318 for f in ctx:
4319 fm.startitem()
4319 fm.startitem()
4320 fl = ctx[f].flags()
4320 fl = ctx[f].flags()
4321 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4321 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4322 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4322 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4323 fm.write('path', '%s\n', f)
4323 fm.write('path', '%s\n', f)
4324 fm.end()
4324 fm.end()
4325
4325
4326 @command('^merge',
4326 @command('^merge',
4327 [('f', 'force', None,
4327 [('f', 'force', None,
4328 _('force a merge including outstanding changes (DEPRECATED)')),
4328 _('force a merge including outstanding changes (DEPRECATED)')),
4329 ('r', 'rev', '', _('revision to merge'), _('REV')),
4329 ('r', 'rev', '', _('revision to merge'), _('REV')),
4330 ('P', 'preview', None,
4330 ('P', 'preview', None,
4331 _('review revisions to merge (no merge is performed)'))
4331 _('review revisions to merge (no merge is performed)'))
4332 ] + mergetoolopts,
4332 ] + mergetoolopts,
4333 _('[-P] [-f] [[-r] REV]'))
4333 _('[-P] [-f] [[-r] REV]'))
4334 def merge(ui, repo, node=None, **opts):
4334 def merge(ui, repo, node=None, **opts):
4335 """merge working directory with another revision
4335 """merge working directory with another revision
4336
4336
4337 The current working directory is updated with all changes made in
4337 The current working directory is updated with all changes made in
4338 the requested revision since the last common predecessor revision.
4338 the requested revision since the last common predecessor revision.
4339
4339
4340 Files that changed between either parent are marked as changed for
4340 Files that changed between either parent are marked as changed for
4341 the next commit and a commit must be performed before any further
4341 the next commit and a commit must be performed before any further
4342 updates to the repository are allowed. The next commit will have
4342 updates to the repository are allowed. The next commit will have
4343 two parents.
4343 two parents.
4344
4344
4345 ``--tool`` can be used to specify the merge tool used for file
4345 ``--tool`` can be used to specify the merge tool used for file
4346 merges. It overrides the HGMERGE environment variable and your
4346 merges. It overrides the HGMERGE environment variable and your
4347 configuration files. See :hg:`help merge-tools` for options.
4347 configuration files. See :hg:`help merge-tools` for options.
4348
4348
4349 If no revision is specified, the working directory's parent is a
4349 If no revision is specified, the working directory's parent is a
4350 head revision, and the current branch contains exactly one other
4350 head revision, and the current branch contains exactly one other
4351 head, the other head is merged with by default. Otherwise, an
4351 head, the other head is merged with by default. Otherwise, an
4352 explicit revision with which to merge with must be provided.
4352 explicit revision with which to merge with must be provided.
4353
4353
4354 :hg:`resolve` must be used to resolve unresolved files.
4354 :hg:`resolve` must be used to resolve unresolved files.
4355
4355
4356 To undo an uncommitted merge, use :hg:`update --clean .` which
4356 To undo an uncommitted merge, use :hg:`update --clean .` which
4357 will check out a clean copy of the original merge parent, losing
4357 will check out a clean copy of the original merge parent, losing
4358 all changes.
4358 all changes.
4359
4359
4360 Returns 0 on success, 1 if there are unresolved files.
4360 Returns 0 on success, 1 if there are unresolved files.
4361 """
4361 """
4362
4362
4363 if opts.get('rev') and node:
4363 if opts.get('rev') and node:
4364 raise util.Abort(_("please specify just one revision"))
4364 raise util.Abort(_("please specify just one revision"))
4365 if not node:
4365 if not node:
4366 node = opts.get('rev')
4366 node = opts.get('rev')
4367
4367
4368 if node:
4368 if node:
4369 node = scmutil.revsingle(repo, node).node()
4369 node = scmutil.revsingle(repo, node).node()
4370
4370
4371 if not node and repo._bookmarkcurrent:
4371 if not node and repo._bookmarkcurrent:
4372 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4372 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4373 curhead = repo[repo._bookmarkcurrent].node()
4373 curhead = repo[repo._bookmarkcurrent].node()
4374 if len(bmheads) == 2:
4374 if len(bmheads) == 2:
4375 if curhead == bmheads[0]:
4375 if curhead == bmheads[0]:
4376 node = bmheads[1]
4376 node = bmheads[1]
4377 else:
4377 else:
4378 node = bmheads[0]
4378 node = bmheads[0]
4379 elif len(bmheads) > 2:
4379 elif len(bmheads) > 2:
4380 raise util.Abort(_("multiple matching bookmarks to merge - "
4380 raise util.Abort(_("multiple matching bookmarks to merge - "
4381 "please merge with an explicit rev or bookmark"),
4381 "please merge with an explicit rev or bookmark"),
4382 hint=_("run 'hg heads' to see all heads"))
4382 hint=_("run 'hg heads' to see all heads"))
4383 elif len(bmheads) <= 1:
4383 elif len(bmheads) <= 1:
4384 raise util.Abort(_("no matching bookmark to merge - "
4384 raise util.Abort(_("no matching bookmark to merge - "
4385 "please merge with an explicit rev or bookmark"),
4385 "please merge with an explicit rev or bookmark"),
4386 hint=_("run 'hg heads' to see all heads"))
4386 hint=_("run 'hg heads' to see all heads"))
4387
4387
4388 if not node and not repo._bookmarkcurrent:
4388 if not node and not repo._bookmarkcurrent:
4389 branch = repo[None].branch()
4389 branch = repo[None].branch()
4390 bheads = repo.branchheads(branch)
4390 bheads = repo.branchheads(branch)
4391 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4391 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4392
4392
4393 if len(nbhs) > 2:
4393 if len(nbhs) > 2:
4394 raise util.Abort(_("branch '%s' has %d heads - "
4394 raise util.Abort(_("branch '%s' has %d heads - "
4395 "please merge with an explicit rev")
4395 "please merge with an explicit rev")
4396 % (branch, len(bheads)),
4396 % (branch, len(bheads)),
4397 hint=_("run 'hg heads .' to see heads"))
4397 hint=_("run 'hg heads .' to see heads"))
4398
4398
4399 parent = repo.dirstate.p1()
4399 parent = repo.dirstate.p1()
4400 if len(nbhs) <= 1:
4400 if len(nbhs) <= 1:
4401 if len(bheads) > 1:
4401 if len(bheads) > 1:
4402 raise util.Abort(_("heads are bookmarked - "
4402 raise util.Abort(_("heads are bookmarked - "
4403 "please merge with an explicit rev"),
4403 "please merge with an explicit rev"),
4404 hint=_("run 'hg heads' to see all heads"))
4404 hint=_("run 'hg heads' to see all heads"))
4405 if len(repo.heads()) > 1:
4405 if len(repo.heads()) > 1:
4406 raise util.Abort(_("branch '%s' has one head - "
4406 raise util.Abort(_("branch '%s' has one head - "
4407 "please merge with an explicit rev")
4407 "please merge with an explicit rev")
4408 % branch,
4408 % branch,
4409 hint=_("run 'hg heads' to see all heads"))
4409 hint=_("run 'hg heads' to see all heads"))
4410 msg, hint = _('nothing to merge'), None
4410 msg, hint = _('nothing to merge'), None
4411 if parent != repo.lookup(branch):
4411 if parent != repo.lookup(branch):
4412 hint = _("use 'hg update' instead")
4412 hint = _("use 'hg update' instead")
4413 raise util.Abort(msg, hint=hint)
4413 raise util.Abort(msg, hint=hint)
4414
4414
4415 if parent not in bheads:
4415 if parent not in bheads:
4416 raise util.Abort(_('working directory not at a head revision'),
4416 raise util.Abort(_('working directory not at a head revision'),
4417 hint=_("use 'hg update' or merge with an "
4417 hint=_("use 'hg update' or merge with an "
4418 "explicit revision"))
4418 "explicit revision"))
4419 if parent == nbhs[0]:
4419 if parent == nbhs[0]:
4420 node = nbhs[-1]
4420 node = nbhs[-1]
4421 else:
4421 else:
4422 node = nbhs[0]
4422 node = nbhs[0]
4423
4423
4424 if opts.get('preview'):
4424 if opts.get('preview'):
4425 # find nodes that are ancestors of p2 but not of p1
4425 # find nodes that are ancestors of p2 but not of p1
4426 p1 = repo.lookup('.')
4426 p1 = repo.lookup('.')
4427 p2 = repo.lookup(node)
4427 p2 = repo.lookup(node)
4428 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4428 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4429
4429
4430 displayer = cmdutil.show_changeset(ui, repo, opts)
4430 displayer = cmdutil.show_changeset(ui, repo, opts)
4431 for node in nodes:
4431 for node in nodes:
4432 displayer.show(repo[node])
4432 displayer.show(repo[node])
4433 displayer.close()
4433 displayer.close()
4434 return 0
4434 return 0
4435
4435
4436 try:
4436 try:
4437 # ui.forcemerge is an internal variable, do not document
4437 # ui.forcemerge is an internal variable, do not document
4438 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4438 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4439 return hg.merge(repo, node, force=opts.get('force'))
4439 return hg.merge(repo, node, force=opts.get('force'))
4440 finally:
4440 finally:
4441 ui.setconfig('ui', 'forcemerge', '', 'merge')
4441 ui.setconfig('ui', 'forcemerge', '', 'merge')
4442
4442
4443 @command('outgoing|out',
4443 @command('outgoing|out',
4444 [('f', 'force', None, _('run even when the destination is unrelated')),
4444 [('f', 'force', None, _('run even when the destination is unrelated')),
4445 ('r', 'rev', [],
4445 ('r', 'rev', [],
4446 _('a changeset intended to be included in the destination'), _('REV')),
4446 _('a changeset intended to be included in the destination'), _('REV')),
4447 ('n', 'newest-first', None, _('show newest record first')),
4447 ('n', 'newest-first', None, _('show newest record first')),
4448 ('B', 'bookmarks', False, _('compare bookmarks')),
4448 ('B', 'bookmarks', False, _('compare bookmarks')),
4449 ('b', 'branch', [], _('a specific branch you would like to push'),
4449 ('b', 'branch', [], _('a specific branch you would like to push'),
4450 _('BRANCH')),
4450 _('BRANCH')),
4451 ] + logopts + remoteopts + subrepoopts,
4451 ] + logopts + remoteopts + subrepoopts,
4452 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4452 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4453 def outgoing(ui, repo, dest=None, **opts):
4453 def outgoing(ui, repo, dest=None, **opts):
4454 """show changesets not found in the destination
4454 """show changesets not found in the destination
4455
4455
4456 Show changesets not found in the specified destination repository
4456 Show changesets not found in the specified destination repository
4457 or the default push location. These are the changesets that would
4457 or the default push location. These are the changesets that would
4458 be pushed if a push was requested.
4458 be pushed if a push was requested.
4459
4459
4460 See pull for details of valid destination formats.
4460 See pull for details of valid destination formats.
4461
4461
4462 Returns 0 if there are outgoing changes, 1 otherwise.
4462 Returns 0 if there are outgoing changes, 1 otherwise.
4463 """
4463 """
4464 if opts.get('graph'):
4464 if opts.get('graph'):
4465 cmdutil.checkunsupportedgraphflags([], opts)
4465 cmdutil.checkunsupportedgraphflags([], opts)
4466 o, other = hg._outgoing(ui, repo, dest, opts)
4466 o, other = hg._outgoing(ui, repo, dest, opts)
4467 if not o:
4467 if not o:
4468 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4468 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4469 return
4469 return
4470
4470
4471 revdag = cmdutil.graphrevs(repo, o, opts)
4471 revdag = cmdutil.graphrevs(repo, o, opts)
4472 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4472 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4473 showparents = [ctx.node() for ctx in repo[None].parents()]
4473 showparents = [ctx.node() for ctx in repo[None].parents()]
4474 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4474 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4475 graphmod.asciiedges)
4475 graphmod.asciiedges)
4476 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4476 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4477 return 0
4477 return 0
4478
4478
4479 if opts.get('bookmarks'):
4479 if opts.get('bookmarks'):
4480 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4480 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4481 dest, branches = hg.parseurl(dest, opts.get('branch'))
4481 dest, branches = hg.parseurl(dest, opts.get('branch'))
4482 other = hg.peer(repo, opts, dest)
4482 other = hg.peer(repo, opts, dest)
4483 if 'bookmarks' not in other.listkeys('namespaces'):
4483 if 'bookmarks' not in other.listkeys('namespaces'):
4484 ui.warn(_("remote doesn't support bookmarks\n"))
4484 ui.warn(_("remote doesn't support bookmarks\n"))
4485 return 0
4485 return 0
4486 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4486 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4487 return bookmarks.diff(ui, other, repo)
4487 return bookmarks.diff(ui, other, repo)
4488
4488
4489 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4489 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4490 try:
4490 try:
4491 return hg.outgoing(ui, repo, dest, opts)
4491 return hg.outgoing(ui, repo, dest, opts)
4492 finally:
4492 finally:
4493 del repo._subtoppath
4493 del repo._subtoppath
4494
4494
4495 @command('parents',
4495 @command('parents',
4496 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4496 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4497 ] + templateopts,
4497 ] + templateopts,
4498 _('[-r REV] [FILE]'),
4498 _('[-r REV] [FILE]'),
4499 inferrepo=True)
4499 inferrepo=True)
4500 def parents(ui, repo, file_=None, **opts):
4500 def parents(ui, repo, file_=None, **opts):
4501 """show the parents of the working directory or revision
4501 """show the parents of the working directory or revision
4502
4502
4503 Print the working directory's parent revisions. If a revision is
4503 Print the working directory's parent revisions. If a revision is
4504 given via -r/--rev, the parent of that revision will be printed.
4504 given via -r/--rev, the parent of that revision will be printed.
4505 If a file argument is given, the revision in which the file was
4505 If a file argument is given, the revision in which the file was
4506 last changed (before the working directory revision or the
4506 last changed (before the working directory revision or the
4507 argument to --rev if given) is printed.
4507 argument to --rev if given) is printed.
4508
4508
4509 Returns 0 on success.
4509 Returns 0 on success.
4510 """
4510 """
4511
4511
4512 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4512 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4513
4513
4514 if file_:
4514 if file_:
4515 m = scmutil.match(ctx, (file_,), opts)
4515 m = scmutil.match(ctx, (file_,), opts)
4516 if m.anypats() or len(m.files()) != 1:
4516 if m.anypats() or len(m.files()) != 1:
4517 raise util.Abort(_('can only specify an explicit filename'))
4517 raise util.Abort(_('can only specify an explicit filename'))
4518 file_ = m.files()[0]
4518 file_ = m.files()[0]
4519 filenodes = []
4519 filenodes = []
4520 for cp in ctx.parents():
4520 for cp in ctx.parents():
4521 if not cp:
4521 if not cp:
4522 continue
4522 continue
4523 try:
4523 try:
4524 filenodes.append(cp.filenode(file_))
4524 filenodes.append(cp.filenode(file_))
4525 except error.LookupError:
4525 except error.LookupError:
4526 pass
4526 pass
4527 if not filenodes:
4527 if not filenodes:
4528 raise util.Abort(_("'%s' not found in manifest!") % file_)
4528 raise util.Abort(_("'%s' not found in manifest!") % file_)
4529 p = []
4529 p = []
4530 for fn in filenodes:
4530 for fn in filenodes:
4531 fctx = repo.filectx(file_, fileid=fn)
4531 fctx = repo.filectx(file_, fileid=fn)
4532 p.append(fctx.node())
4532 p.append(fctx.node())
4533 else:
4533 else:
4534 p = [cp.node() for cp in ctx.parents()]
4534 p = [cp.node() for cp in ctx.parents()]
4535
4535
4536 displayer = cmdutil.show_changeset(ui, repo, opts)
4536 displayer = cmdutil.show_changeset(ui, repo, opts)
4537 for n in p:
4537 for n in p:
4538 if n != nullid:
4538 if n != nullid:
4539 displayer.show(repo[n])
4539 displayer.show(repo[n])
4540 displayer.close()
4540 displayer.close()
4541
4541
4542 @command('paths', [], _('[NAME]'), optionalrepo=True)
4542 @command('paths', [], _('[NAME]'), optionalrepo=True)
4543 def paths(ui, repo, search=None):
4543 def paths(ui, repo, search=None):
4544 """show aliases for remote repositories
4544 """show aliases for remote repositories
4545
4545
4546 Show definition of symbolic path name NAME. If no name is given,
4546 Show definition of symbolic path name NAME. If no name is given,
4547 show definition of all available names.
4547 show definition of all available names.
4548
4548
4549 Option -q/--quiet suppresses all output when searching for NAME
4549 Option -q/--quiet suppresses all output when searching for NAME
4550 and shows only the path names when listing all definitions.
4550 and shows only the path names when listing all definitions.
4551
4551
4552 Path names are defined in the [paths] section of your
4552 Path names are defined in the [paths] section of your
4553 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4553 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4554 repository, ``.hg/hgrc`` is used, too.
4554 repository, ``.hg/hgrc`` is used, too.
4555
4555
4556 The path names ``default`` and ``default-push`` have a special
4556 The path names ``default`` and ``default-push`` have a special
4557 meaning. When performing a push or pull operation, they are used
4557 meaning. When performing a push or pull operation, they are used
4558 as fallbacks if no location is specified on the command-line.
4558 as fallbacks if no location is specified on the command-line.
4559 When ``default-push`` is set, it will be used for push and
4559 When ``default-push`` is set, it will be used for push and
4560 ``default`` will be used for pull; otherwise ``default`` is used
4560 ``default`` will be used for pull; otherwise ``default`` is used
4561 as the fallback for both. When cloning a repository, the clone
4561 as the fallback for both. When cloning a repository, the clone
4562 source is written as ``default`` in ``.hg/hgrc``. Note that
4562 source is written as ``default`` in ``.hg/hgrc``. Note that
4563 ``default`` and ``default-push`` apply to all inbound (e.g.
4563 ``default`` and ``default-push`` apply to all inbound (e.g.
4564 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4564 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4565 :hg:`bundle`) operations.
4565 :hg:`bundle`) operations.
4566
4566
4567 See :hg:`help urls` for more information.
4567 See :hg:`help urls` for more information.
4568
4568
4569 Returns 0 on success.
4569 Returns 0 on success.
4570 """
4570 """
4571 if search:
4571 if search:
4572 for name, path in ui.configitems("paths"):
4572 for name, path in ui.configitems("paths"):
4573 if name == search:
4573 if name == search:
4574 ui.status("%s\n" % util.hidepassword(path))
4574 ui.status("%s\n" % util.hidepassword(path))
4575 return
4575 return
4576 if not ui.quiet:
4576 if not ui.quiet:
4577 ui.warn(_("not found!\n"))
4577 ui.warn(_("not found!\n"))
4578 return 1
4578 return 1
4579 else:
4579 else:
4580 for name, path in ui.configitems("paths"):
4580 for name, path in ui.configitems("paths"):
4581 if ui.quiet:
4581 if ui.quiet:
4582 ui.write("%s\n" % name)
4582 ui.write("%s\n" % name)
4583 else:
4583 else:
4584 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4584 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4585
4585
4586 @command('phase',
4586 @command('phase',
4587 [('p', 'public', False, _('set changeset phase to public')),
4587 [('p', 'public', False, _('set changeset phase to public')),
4588 ('d', 'draft', False, _('set changeset phase to draft')),
4588 ('d', 'draft', False, _('set changeset phase to draft')),
4589 ('s', 'secret', False, _('set changeset phase to secret')),
4589 ('s', 'secret', False, _('set changeset phase to secret')),
4590 ('f', 'force', False, _('allow to move boundary backward')),
4590 ('f', 'force', False, _('allow to move boundary backward')),
4591 ('r', 'rev', [], _('target revision'), _('REV')),
4591 ('r', 'rev', [], _('target revision'), _('REV')),
4592 ],
4592 ],
4593 _('[-p|-d|-s] [-f] [-r] REV...'))
4593 _('[-p|-d|-s] [-f] [-r] REV...'))
4594 def phase(ui, repo, *revs, **opts):
4594 def phase(ui, repo, *revs, **opts):
4595 """set or show the current phase name
4595 """set or show the current phase name
4596
4596
4597 With no argument, show the phase name of specified revisions.
4597 With no argument, show the phase name of specified revisions.
4598
4598
4599 With one of -p/--public, -d/--draft or -s/--secret, change the
4599 With one of -p/--public, -d/--draft or -s/--secret, change the
4600 phase value of the specified revisions.
4600 phase value of the specified revisions.
4601
4601
4602 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4602 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4603 lower phase to an higher phase. Phases are ordered as follows::
4603 lower phase to an higher phase. Phases are ordered as follows::
4604
4604
4605 public < draft < secret
4605 public < draft < secret
4606
4606
4607 Returns 0 on success, 1 if no phases were changed or some could not
4607 Returns 0 on success, 1 if no phases were changed or some could not
4608 be changed.
4608 be changed.
4609 """
4609 """
4610 # search for a unique phase argument
4610 # search for a unique phase argument
4611 targetphase = None
4611 targetphase = None
4612 for idx, name in enumerate(phases.phasenames):
4612 for idx, name in enumerate(phases.phasenames):
4613 if opts[name]:
4613 if opts[name]:
4614 if targetphase is not None:
4614 if targetphase is not None:
4615 raise util.Abort(_('only one phase can be specified'))
4615 raise util.Abort(_('only one phase can be specified'))
4616 targetphase = idx
4616 targetphase = idx
4617
4617
4618 # look for specified revision
4618 # look for specified revision
4619 revs = list(revs)
4619 revs = list(revs)
4620 revs.extend(opts['rev'])
4620 revs.extend(opts['rev'])
4621 if not revs:
4621 if not revs:
4622 raise util.Abort(_('no revisions specified'))
4622 raise util.Abort(_('no revisions specified'))
4623
4623
4624 revs = scmutil.revrange(repo, revs)
4624 revs = scmutil.revrange(repo, revs)
4625
4625
4626 lock = None
4626 lock = None
4627 ret = 0
4627 ret = 0
4628 if targetphase is None:
4628 if targetphase is None:
4629 # display
4629 # display
4630 for r in revs:
4630 for r in revs:
4631 ctx = repo[r]
4631 ctx = repo[r]
4632 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4632 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4633 else:
4633 else:
4634 tr = None
4634 tr = None
4635 lock = repo.lock()
4635 lock = repo.lock()
4636 try:
4636 try:
4637 tr = repo.transaction("phase")
4637 tr = repo.transaction("phase")
4638 # set phase
4638 # set phase
4639 if not revs:
4639 if not revs:
4640 raise util.Abort(_('empty revision set'))
4640 raise util.Abort(_('empty revision set'))
4641 nodes = [repo[r].node() for r in revs]
4641 nodes = [repo[r].node() for r in revs]
4642 olddata = repo._phasecache.getphaserevs(repo)[:]
4642 olddata = repo._phasecache.getphaserevs(repo)[:]
4643 phases.advanceboundary(repo, tr, targetphase, nodes)
4643 phases.advanceboundary(repo, tr, targetphase, nodes)
4644 if opts['force']:
4644 if opts['force']:
4645 phases.retractboundary(repo, tr, targetphase, nodes)
4645 phases.retractboundary(repo, tr, targetphase, nodes)
4646 tr.close()
4646 tr.close()
4647 finally:
4647 finally:
4648 if tr is not None:
4648 if tr is not None:
4649 tr.release()
4649 tr.release()
4650 lock.release()
4650 lock.release()
4651 # moving revision from public to draft may hide them
4651 # moving revision from public to draft may hide them
4652 # We have to check result on an unfiltered repository
4652 # We have to check result on an unfiltered repository
4653 unfi = repo.unfiltered()
4653 unfi = repo.unfiltered()
4654 newdata = repo._phasecache.getphaserevs(unfi)
4654 newdata = repo._phasecache.getphaserevs(unfi)
4655 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4655 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4656 cl = unfi.changelog
4656 cl = unfi.changelog
4657 rejected = [n for n in nodes
4657 rejected = [n for n in nodes
4658 if newdata[cl.rev(n)] < targetphase]
4658 if newdata[cl.rev(n)] < targetphase]
4659 if rejected:
4659 if rejected:
4660 ui.warn(_('cannot move %i changesets to a higher '
4660 ui.warn(_('cannot move %i changesets to a higher '
4661 'phase, use --force\n') % len(rejected))
4661 'phase, use --force\n') % len(rejected))
4662 ret = 1
4662 ret = 1
4663 if changes:
4663 if changes:
4664 msg = _('phase changed for %i changesets\n') % changes
4664 msg = _('phase changed for %i changesets\n') % changes
4665 if ret:
4665 if ret:
4666 ui.status(msg)
4666 ui.status(msg)
4667 else:
4667 else:
4668 ui.note(msg)
4668 ui.note(msg)
4669 else:
4669 else:
4670 ui.warn(_('no phases changed\n'))
4670 ui.warn(_('no phases changed\n'))
4671 ret = 1
4671 ret = 1
4672 return ret
4672 return ret
4673
4673
4674 def postincoming(ui, repo, modheads, optupdate, checkout):
4674 def postincoming(ui, repo, modheads, optupdate, checkout):
4675 if modheads == 0:
4675 if modheads == 0:
4676 return
4676 return
4677 if optupdate:
4677 if optupdate:
4678 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4678 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4679 try:
4679 try:
4680 ret = hg.update(repo, checkout)
4680 ret = hg.update(repo, checkout)
4681 except util.Abort, inst:
4681 except util.Abort, inst:
4682 ui.warn(_("not updating: %s\n") % str(inst))
4682 ui.warn(_("not updating: %s\n") % str(inst))
4683 if inst.hint:
4683 if inst.hint:
4684 ui.warn(_("(%s)\n") % inst.hint)
4684 ui.warn(_("(%s)\n") % inst.hint)
4685 return 0
4685 return 0
4686 if not ret and not checkout:
4686 if not ret and not checkout:
4687 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4687 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4688 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4688 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4689 return ret
4689 return ret
4690 if modheads > 1:
4690 if modheads > 1:
4691 currentbranchheads = len(repo.branchheads())
4691 currentbranchheads = len(repo.branchheads())
4692 if currentbranchheads == modheads:
4692 if currentbranchheads == modheads:
4693 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4693 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4694 elif currentbranchheads > 1:
4694 elif currentbranchheads > 1:
4695 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4695 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4696 "merge)\n"))
4696 "merge)\n"))
4697 else:
4697 else:
4698 ui.status(_("(run 'hg heads' to see heads)\n"))
4698 ui.status(_("(run 'hg heads' to see heads)\n"))
4699 else:
4699 else:
4700 ui.status(_("(run 'hg update' to get a working copy)\n"))
4700 ui.status(_("(run 'hg update' to get a working copy)\n"))
4701
4701
4702 @command('^pull',
4702 @command('^pull',
4703 [('u', 'update', None,
4703 [('u', 'update', None,
4704 _('update to new branch head if changesets were pulled')),
4704 _('update to new branch head if changesets were pulled')),
4705 ('f', 'force', None, _('run even when remote repository is unrelated')),
4705 ('f', 'force', None, _('run even when remote repository is unrelated')),
4706 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4706 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4707 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4707 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4708 ('b', 'branch', [], _('a specific branch you would like to pull'),
4708 ('b', 'branch', [], _('a specific branch you would like to pull'),
4709 _('BRANCH')),
4709 _('BRANCH')),
4710 ] + remoteopts,
4710 ] + remoteopts,
4711 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4711 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4712 def pull(ui, repo, source="default", **opts):
4712 def pull(ui, repo, source="default", **opts):
4713 """pull changes from the specified source
4713 """pull changes from the specified source
4714
4714
4715 Pull changes from a remote repository to a local one.
4715 Pull changes from a remote repository to a local one.
4716
4716
4717 This finds all changes from the repository at the specified path
4717 This finds all changes from the repository at the specified path
4718 or URL and adds them to a local repository (the current one unless
4718 or URL and adds them to a local repository (the current one unless
4719 -R is specified). By default, this does not update the copy of the
4719 -R is specified). By default, this does not update the copy of the
4720 project in the working directory.
4720 project in the working directory.
4721
4721
4722 Use :hg:`incoming` if you want to see what would have been added
4722 Use :hg:`incoming` if you want to see what would have been added
4723 by a pull at the time you issued this command. If you then decide
4723 by a pull at the time you issued this command. If you then decide
4724 to add those changes to the repository, you should use :hg:`pull
4724 to add those changes to the repository, you should use :hg:`pull
4725 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4725 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4726
4726
4727 If SOURCE is omitted, the 'default' path will be used.
4727 If SOURCE is omitted, the 'default' path will be used.
4728 See :hg:`help urls` for more information.
4728 See :hg:`help urls` for more information.
4729
4729
4730 Returns 0 on success, 1 if an update had unresolved files.
4730 Returns 0 on success, 1 if an update had unresolved files.
4731 """
4731 """
4732 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4732 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4733 other = hg.peer(repo, opts, source)
4733 other = hg.peer(repo, opts, source)
4734 try:
4734 try:
4735 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4735 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4736 revs, checkout = hg.addbranchrevs(repo, other, branches,
4736 revs, checkout = hg.addbranchrevs(repo, other, branches,
4737 opts.get('rev'))
4737 opts.get('rev'))
4738
4738
4739 remotebookmarks = other.listkeys('bookmarks')
4739 remotebookmarks = other.listkeys('bookmarks')
4740
4740
4741 if opts.get('bookmark'):
4741 if opts.get('bookmark'):
4742 if not revs:
4742 if not revs:
4743 revs = []
4743 revs = []
4744 for b in opts['bookmark']:
4744 for b in opts['bookmark']:
4745 if b not in remotebookmarks:
4745 if b not in remotebookmarks:
4746 raise util.Abort(_('remote bookmark %s not found!') % b)
4746 raise util.Abort(_('remote bookmark %s not found!') % b)
4747 revs.append(remotebookmarks[b])
4747 revs.append(remotebookmarks[b])
4748
4748
4749 if revs:
4749 if revs:
4750 try:
4750 try:
4751 revs = [other.lookup(rev) for rev in revs]
4751 revs = [other.lookup(rev) for rev in revs]
4752 except error.CapabilityError:
4752 except error.CapabilityError:
4753 err = _("other repository doesn't support revision lookup, "
4753 err = _("other repository doesn't support revision lookup, "
4754 "so a rev cannot be specified.")
4754 "so a rev cannot be specified.")
4755 raise util.Abort(err)
4755 raise util.Abort(err)
4756
4756
4757 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4757 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4758 bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
4758 bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
4759 if checkout:
4759 if checkout:
4760 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4760 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4761 repo._subtoppath = source
4761 repo._subtoppath = source
4762 try:
4762 try:
4763 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4763 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4764
4764
4765 finally:
4765 finally:
4766 del repo._subtoppath
4766 del repo._subtoppath
4767
4767
4768 # update specified bookmarks
4768 # update specified bookmarks
4769 if opts.get('bookmark'):
4769 if opts.get('bookmark'):
4770 marks = repo._bookmarks
4770 marks = repo._bookmarks
4771 for b in opts['bookmark']:
4771 for b in opts['bookmark']:
4772 # explicit pull overrides local bookmark if any
4772 # explicit pull overrides local bookmark if any
4773 ui.status(_("importing bookmark %s\n") % b)
4773 ui.status(_("importing bookmark %s\n") % b)
4774 marks[b] = repo[remotebookmarks[b]].node()
4774 marks[b] = repo[remotebookmarks[b]].node()
4775 marks.write()
4775 marks.write()
4776 finally:
4776 finally:
4777 other.close()
4777 other.close()
4778 return ret
4778 return ret
4779
4779
4780 @command('^push',
4780 @command('^push',
4781 [('f', 'force', None, _('force push')),
4781 [('f', 'force', None, _('force push')),
4782 ('r', 'rev', [],
4782 ('r', 'rev', [],
4783 _('a changeset intended to be included in the destination'),
4783 _('a changeset intended to be included in the destination'),
4784 _('REV')),
4784 _('REV')),
4785 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4785 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4786 ('b', 'branch', [],
4786 ('b', 'branch', [],
4787 _('a specific branch you would like to push'), _('BRANCH')),
4787 _('a specific branch you would like to push'), _('BRANCH')),
4788 ('', 'new-branch', False, _('allow pushing a new branch')),
4788 ('', 'new-branch', False, _('allow pushing a new branch')),
4789 ] + remoteopts,
4789 ] + remoteopts,
4790 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4790 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4791 def push(ui, repo, dest=None, **opts):
4791 def push(ui, repo, dest=None, **opts):
4792 """push changes to the specified destination
4792 """push changes to the specified destination
4793
4793
4794 Push changesets from the local repository to the specified
4794 Push changesets from the local repository to the specified
4795 destination.
4795 destination.
4796
4796
4797 This operation is symmetrical to pull: it is identical to a pull
4797 This operation is symmetrical to pull: it is identical to a pull
4798 in the destination repository from the current one.
4798 in the destination repository from the current one.
4799
4799
4800 By default, push will not allow creation of new heads at the
4800 By default, push will not allow creation of new heads at the
4801 destination, since multiple heads would make it unclear which head
4801 destination, since multiple heads would make it unclear which head
4802 to use. In this situation, it is recommended to pull and merge
4802 to use. In this situation, it is recommended to pull and merge
4803 before pushing.
4803 before pushing.
4804
4804
4805 Use --new-branch if you want to allow push to create a new named
4805 Use --new-branch if you want to allow push to create a new named
4806 branch that is not present at the destination. This allows you to
4806 branch that is not present at the destination. This allows you to
4807 only create a new branch without forcing other changes.
4807 only create a new branch without forcing other changes.
4808
4808
4809 .. note::
4809 .. note::
4810
4810
4811 Extra care should be taken with the -f/--force option,
4811 Extra care should be taken with the -f/--force option,
4812 which will push all new heads on all branches, an action which will
4812 which will push all new heads on all branches, an action which will
4813 almost always cause confusion for collaborators.
4813 almost always cause confusion for collaborators.
4814
4814
4815 If -r/--rev is used, the specified revision and all its ancestors
4815 If -r/--rev is used, the specified revision and all its ancestors
4816 will be pushed to the remote repository.
4816 will be pushed to the remote repository.
4817
4817
4818 If -B/--bookmark is used, the specified bookmarked revision, its
4818 If -B/--bookmark is used, the specified bookmarked revision, its
4819 ancestors, and the bookmark will be pushed to the remote
4819 ancestors, and the bookmark will be pushed to the remote
4820 repository.
4820 repository.
4821
4821
4822 Please see :hg:`help urls` for important details about ``ssh://``
4822 Please see :hg:`help urls` for important details about ``ssh://``
4823 URLs. If DESTINATION is omitted, a default path will be used.
4823 URLs. If DESTINATION is omitted, a default path will be used.
4824
4824
4825 Returns 0 if push was successful, 1 if nothing to push.
4825 Returns 0 if push was successful, 1 if nothing to push.
4826 """
4826 """
4827
4827
4828 if opts.get('bookmark'):
4828 if opts.get('bookmark'):
4829 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4829 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4830 for b in opts['bookmark']:
4830 for b in opts['bookmark']:
4831 # translate -B options to -r so changesets get pushed
4831 # translate -B options to -r so changesets get pushed
4832 if b in repo._bookmarks:
4832 if b in repo._bookmarks:
4833 opts.setdefault('rev', []).append(b)
4833 opts.setdefault('rev', []).append(b)
4834 else:
4834 else:
4835 # if we try to push a deleted bookmark, translate it to null
4835 # if we try to push a deleted bookmark, translate it to null
4836 # this lets simultaneous -r, -b options continue working
4836 # this lets simultaneous -r, -b options continue working
4837 opts.setdefault('rev', []).append("null")
4837 opts.setdefault('rev', []).append("null")
4838
4838
4839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4839 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4840 dest, branches = hg.parseurl(dest, opts.get('branch'))
4840 dest, branches = hg.parseurl(dest, opts.get('branch'))
4841 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4841 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4842 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4842 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4843 try:
4843 try:
4844 other = hg.peer(repo, opts, dest)
4844 other = hg.peer(repo, opts, dest)
4845 except error.RepoError:
4845 except error.RepoError:
4846 if dest == "default-push":
4846 if dest == "default-push":
4847 raise util.Abort(_("default repository not configured!"),
4847 raise util.Abort(_("default repository not configured!"),
4848 hint=_('see the "path" section in "hg help config"'))
4848 hint=_('see the "path" section in "hg help config"'))
4849 else:
4849 else:
4850 raise
4850 raise
4851
4851
4852 if revs:
4852 if revs:
4853 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4853 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4854
4854
4855 repo._subtoppath = dest
4855 repo._subtoppath = dest
4856 try:
4856 try:
4857 # push subrepos depth-first for coherent ordering
4857 # push subrepos depth-first for coherent ordering
4858 c = repo['']
4858 c = repo['']
4859 subs = c.substate # only repos that are committed
4859 subs = c.substate # only repos that are committed
4860 for s in sorted(subs):
4860 for s in sorted(subs):
4861 result = c.sub(s).push(opts)
4861 result = c.sub(s).push(opts)
4862 if result == 0:
4862 if result == 0:
4863 return not result
4863 return not result
4864 finally:
4864 finally:
4865 del repo._subtoppath
4865 del repo._subtoppath
4866 result = repo.push(other, opts.get('force'), revs=revs,
4866 result = repo.push(other, opts.get('force'), revs=revs,
4867 newbranch=opts.get('new_branch'))
4867 newbranch=opts.get('new_branch'))
4868
4868
4869 result = not result
4869 result = not result
4870
4870
4871 if opts.get('bookmark'):
4871 if opts.get('bookmark'):
4872 bresult = bookmarks.pushtoremote(ui, repo, other, opts['bookmark'])
4872 bresult = bookmarks.pushtoremote(ui, repo, other, opts['bookmark'])
4873 if bresult == 2:
4873 if bresult == 2:
4874 return 2
4874 return 2
4875 if not result and bresult:
4875 if not result and bresult:
4876 result = 2
4876 result = 2
4877
4877
4878 return result
4878 return result
4879
4879
4880 @command('recover', [])
4880 @command('recover', [])
4881 def recover(ui, repo):
4881 def recover(ui, repo):
4882 """roll back an interrupted transaction
4882 """roll back an interrupted transaction
4883
4883
4884 Recover from an interrupted commit or pull.
4884 Recover from an interrupted commit or pull.
4885
4885
4886 This command tries to fix the repository status after an
4886 This command tries to fix the repository status after an
4887 interrupted operation. It should only be necessary when Mercurial
4887 interrupted operation. It should only be necessary when Mercurial
4888 suggests it.
4888 suggests it.
4889
4889
4890 Returns 0 if successful, 1 if nothing to recover or verify fails.
4890 Returns 0 if successful, 1 if nothing to recover or verify fails.
4891 """
4891 """
4892 if repo.recover():
4892 if repo.recover():
4893 return hg.verify(repo)
4893 return hg.verify(repo)
4894 return 1
4894 return 1
4895
4895
4896 @command('^remove|rm',
4896 @command('^remove|rm',
4897 [('A', 'after', None, _('record delete for missing files')),
4897 [('A', 'after', None, _('record delete for missing files')),
4898 ('f', 'force', None,
4898 ('f', 'force', None,
4899 _('remove (and delete) file even if added or modified')),
4899 _('remove (and delete) file even if added or modified')),
4900 ] + walkopts,
4900 ] + walkopts,
4901 _('[OPTION]... FILE...'),
4901 _('[OPTION]... FILE...'),
4902 inferrepo=True)
4902 inferrepo=True)
4903 def remove(ui, repo, *pats, **opts):
4903 def remove(ui, repo, *pats, **opts):
4904 """remove the specified files on the next commit
4904 """remove the specified files on the next commit
4905
4905
4906 Schedule the indicated files for removal from the current branch.
4906 Schedule the indicated files for removal from the current branch.
4907
4907
4908 This command schedules the files to be removed at the next commit.
4908 This command schedules the files to be removed at the next commit.
4909 To undo a remove before that, see :hg:`revert`. To undo added
4909 To undo a remove before that, see :hg:`revert`. To undo added
4910 files, see :hg:`forget`.
4910 files, see :hg:`forget`.
4911
4911
4912 .. container:: verbose
4912 .. container:: verbose
4913
4913
4914 -A/--after can be used to remove only files that have already
4914 -A/--after can be used to remove only files that have already
4915 been deleted, -f/--force can be used to force deletion, and -Af
4915 been deleted, -f/--force can be used to force deletion, and -Af
4916 can be used to remove files from the next revision without
4916 can be used to remove files from the next revision without
4917 deleting them from the working directory.
4917 deleting them from the working directory.
4918
4918
4919 The following table details the behavior of remove for different
4919 The following table details the behavior of remove for different
4920 file states (columns) and option combinations (rows). The file
4920 file states (columns) and option combinations (rows). The file
4921 states are Added [A], Clean [C], Modified [M] and Missing [!]
4921 states are Added [A], Clean [C], Modified [M] and Missing [!]
4922 (as reported by :hg:`status`). The actions are Warn, Remove
4922 (as reported by :hg:`status`). The actions are Warn, Remove
4923 (from branch) and Delete (from disk):
4923 (from branch) and Delete (from disk):
4924
4924
4925 ========= == == == ==
4925 ========= == == == ==
4926 opt/state A C M !
4926 opt/state A C M !
4927 ========= == == == ==
4927 ========= == == == ==
4928 none W RD W R
4928 none W RD W R
4929 -f R RD RD R
4929 -f R RD RD R
4930 -A W W W R
4930 -A W W W R
4931 -Af R R R R
4931 -Af R R R R
4932 ========= == == == ==
4932 ========= == == == ==
4933
4933
4934 Note that remove never deletes files in Added [A] state from the
4934 Note that remove never deletes files in Added [A] state from the
4935 working directory, not even if option --force is specified.
4935 working directory, not even if option --force is specified.
4936
4936
4937 Returns 0 on success, 1 if any warnings encountered.
4937 Returns 0 on success, 1 if any warnings encountered.
4938 """
4938 """
4939
4939
4940 ret = 0
4940 ret = 0
4941 after, force = opts.get('after'), opts.get('force')
4941 after, force = opts.get('after'), opts.get('force')
4942 if not pats and not after:
4942 if not pats and not after:
4943 raise util.Abort(_('no files specified'))
4943 raise util.Abort(_('no files specified'))
4944
4944
4945 m = scmutil.match(repo[None], pats, opts)
4945 m = scmutil.match(repo[None], pats, opts)
4946 s = repo.status(match=m, clean=True)
4946 s = repo.status(match=m, clean=True)
4947 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4947 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4948
4948
4949 # warn about failure to delete explicit files/dirs
4949 # warn about failure to delete explicit files/dirs
4950 wctx = repo[None]
4950 wctx = repo[None]
4951 for f in m.files():
4951 for f in m.files():
4952 if f in repo.dirstate or f in wctx.dirs():
4952 if f in repo.dirstate or f in wctx.dirs():
4953 continue
4953 continue
4954 if os.path.exists(m.rel(f)):
4954 if os.path.exists(m.rel(f)):
4955 if os.path.isdir(m.rel(f)):
4955 if os.path.isdir(m.rel(f)):
4956 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4956 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4957 else:
4957 else:
4958 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4958 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4959 # missing files will generate a warning elsewhere
4959 # missing files will generate a warning elsewhere
4960 ret = 1
4960 ret = 1
4961
4961
4962 if force:
4962 if force:
4963 list = modified + deleted + clean + added
4963 list = modified + deleted + clean + added
4964 elif after:
4964 elif after:
4965 list = deleted
4965 list = deleted
4966 for f in modified + added + clean:
4966 for f in modified + added + clean:
4967 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4967 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4968 ret = 1
4968 ret = 1
4969 else:
4969 else:
4970 list = deleted + clean
4970 list = deleted + clean
4971 for f in modified:
4971 for f in modified:
4972 ui.warn(_('not removing %s: file is modified (use -f'
4972 ui.warn(_('not removing %s: file is modified (use -f'
4973 ' to force removal)\n') % m.rel(f))
4973 ' to force removal)\n') % m.rel(f))
4974 ret = 1
4974 ret = 1
4975 for f in added:
4975 for f in added:
4976 ui.warn(_('not removing %s: file has been marked for add'
4976 ui.warn(_('not removing %s: file has been marked for add'
4977 ' (use forget to undo)\n') % m.rel(f))
4977 ' (use forget to undo)\n') % m.rel(f))
4978 ret = 1
4978 ret = 1
4979
4979
4980 for f in sorted(list):
4980 for f in sorted(list):
4981 if ui.verbose or not m.exact(f):
4981 if ui.verbose or not m.exact(f):
4982 ui.status(_('removing %s\n') % m.rel(f))
4982 ui.status(_('removing %s\n') % m.rel(f))
4983
4983
4984 wlock = repo.wlock()
4984 wlock = repo.wlock()
4985 try:
4985 try:
4986 if not after:
4986 if not after:
4987 for f in list:
4987 for f in list:
4988 if f in added:
4988 if f in added:
4989 continue # we never unlink added files on remove
4989 continue # we never unlink added files on remove
4990 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4990 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4991 repo[None].forget(list)
4991 repo[None].forget(list)
4992 finally:
4992 finally:
4993 wlock.release()
4993 wlock.release()
4994
4994
4995 return ret
4995 return ret
4996
4996
4997 @command('rename|move|mv',
4997 @command('rename|move|mv',
4998 [('A', 'after', None, _('record a rename that has already occurred')),
4998 [('A', 'after', None, _('record a rename that has already occurred')),
4999 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4999 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5000 ] + walkopts + dryrunopts,
5000 ] + walkopts + dryrunopts,
5001 _('[OPTION]... SOURCE... DEST'))
5001 _('[OPTION]... SOURCE... DEST'))
5002 def rename(ui, repo, *pats, **opts):
5002 def rename(ui, repo, *pats, **opts):
5003 """rename files; equivalent of copy + remove
5003 """rename files; equivalent of copy + remove
5004
5004
5005 Mark dest as copies of sources; mark sources for deletion. If dest
5005 Mark dest as copies of sources; mark sources for deletion. If dest
5006 is a directory, copies are put in that directory. If dest is a
5006 is a directory, copies are put in that directory. If dest is a
5007 file, there can only be one source.
5007 file, there can only be one source.
5008
5008
5009 By default, this command copies the contents of files as they
5009 By default, this command copies the contents of files as they
5010 exist in the working directory. If invoked with -A/--after, the
5010 exist in the working directory. If invoked with -A/--after, the
5011 operation is recorded, but no copying is performed.
5011 operation is recorded, but no copying is performed.
5012
5012
5013 This command takes effect at the next commit. To undo a rename
5013 This command takes effect at the next commit. To undo a rename
5014 before that, see :hg:`revert`.
5014 before that, see :hg:`revert`.
5015
5015
5016 Returns 0 on success, 1 if errors are encountered.
5016 Returns 0 on success, 1 if errors are encountered.
5017 """
5017 """
5018 wlock = repo.wlock(False)
5018 wlock = repo.wlock(False)
5019 try:
5019 try:
5020 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5020 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5021 finally:
5021 finally:
5022 wlock.release()
5022 wlock.release()
5023
5023
5024 @command('resolve',
5024 @command('resolve',
5025 [('a', 'all', None, _('select all unresolved files')),
5025 [('a', 'all', None, _('select all unresolved files')),
5026 ('l', 'list', None, _('list state of files needing merge')),
5026 ('l', 'list', None, _('list state of files needing merge')),
5027 ('m', 'mark', None, _('mark files as resolved')),
5027 ('m', 'mark', None, _('mark files as resolved')),
5028 ('u', 'unmark', None, _('mark files as unresolved')),
5028 ('u', 'unmark', None, _('mark files as unresolved')),
5029 ('n', 'no-status', None, _('hide status prefix'))]
5029 ('n', 'no-status', None, _('hide status prefix'))]
5030 + mergetoolopts + walkopts,
5030 + mergetoolopts + walkopts,
5031 _('[OPTION]... [FILE]...'),
5031 _('[OPTION]... [FILE]...'),
5032 inferrepo=True)
5032 inferrepo=True)
5033 def resolve(ui, repo, *pats, **opts):
5033 def resolve(ui, repo, *pats, **opts):
5034 """redo merges or set/view the merge status of files
5034 """redo merges or set/view the merge status of files
5035
5035
5036 Merges with unresolved conflicts are often the result of
5036 Merges with unresolved conflicts are often the result of
5037 non-interactive merging using the ``internal:merge`` configuration
5037 non-interactive merging using the ``internal:merge`` configuration
5038 setting, or a command-line merge tool like ``diff3``. The resolve
5038 setting, or a command-line merge tool like ``diff3``. The resolve
5039 command is used to manage the files involved in a merge, after
5039 command is used to manage the files involved in a merge, after
5040 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5040 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5041 working directory must have two parents). See :hg:`help
5041 working directory must have two parents). See :hg:`help
5042 merge-tools` for information on configuring merge tools.
5042 merge-tools` for information on configuring merge tools.
5043
5043
5044 The resolve command can be used in the following ways:
5044 The resolve command can be used in the following ways:
5045
5045
5046 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5046 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5047 files, discarding any previous merge attempts. Re-merging is not
5047 files, discarding any previous merge attempts. Re-merging is not
5048 performed for files already marked as resolved. Use ``--all/-a``
5048 performed for files already marked as resolved. Use ``--all/-a``
5049 to select all unresolved files. ``--tool`` can be used to specify
5049 to select all unresolved files. ``--tool`` can be used to specify
5050 the merge tool used for the given files. It overrides the HGMERGE
5050 the merge tool used for the given files. It overrides the HGMERGE
5051 environment variable and your configuration files. Previous file
5051 environment variable and your configuration files. Previous file
5052 contents are saved with a ``.orig`` suffix.
5052 contents are saved with a ``.orig`` suffix.
5053
5053
5054 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5054 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5055 (e.g. after having manually fixed-up the files). The default is
5055 (e.g. after having manually fixed-up the files). The default is
5056 to mark all unresolved files.
5056 to mark all unresolved files.
5057
5057
5058 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5058 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5059 default is to mark all resolved files.
5059 default is to mark all resolved files.
5060
5060
5061 - :hg:`resolve -l`: list files which had or still have conflicts.
5061 - :hg:`resolve -l`: list files which had or still have conflicts.
5062 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5062 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5063
5063
5064 Note that Mercurial will not let you commit files with unresolved
5064 Note that Mercurial will not let you commit files with unresolved
5065 merge conflicts. You must use :hg:`resolve -m ...` before you can
5065 merge conflicts. You must use :hg:`resolve -m ...` before you can
5066 commit after a conflicting merge.
5066 commit after a conflicting merge.
5067
5067
5068 Returns 0 on success, 1 if any files fail a resolve attempt.
5068 Returns 0 on success, 1 if any files fail a resolve attempt.
5069 """
5069 """
5070
5070
5071 all, mark, unmark, show, nostatus = \
5071 all, mark, unmark, show, nostatus = \
5072 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5072 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5073
5073
5074 if (show and (mark or unmark)) or (mark and unmark):
5074 if (show and (mark or unmark)) or (mark and unmark):
5075 raise util.Abort(_("too many options specified"))
5075 raise util.Abort(_("too many options specified"))
5076 if pats and all:
5076 if pats and all:
5077 raise util.Abort(_("can't specify --all and patterns"))
5077 raise util.Abort(_("can't specify --all and patterns"))
5078 if not (all or pats or show or mark or unmark):
5078 if not (all or pats or show or mark or unmark):
5079 raise util.Abort(_('no files or directories specified'),
5079 raise util.Abort(_('no files or directories specified'),
5080 hint=('use --all to remerge all files'))
5080 hint=('use --all to remerge all files'))
5081
5081
5082 wlock = repo.wlock()
5082 wlock = repo.wlock()
5083 try:
5083 try:
5084 ms = mergemod.mergestate(repo)
5084 ms = mergemod.mergestate(repo)
5085
5085
5086 if not ms.active() and not show:
5086 if not ms.active() and not show:
5087 raise util.Abort(
5087 raise util.Abort(
5088 _('resolve command not applicable when not merging'))
5088 _('resolve command not applicable when not merging'))
5089
5089
5090 m = scmutil.match(repo[None], pats, opts)
5090 m = scmutil.match(repo[None], pats, opts)
5091 ret = 0
5091 ret = 0
5092 didwork = False
5092 didwork = False
5093
5093
5094 for f in ms:
5094 for f in ms:
5095 if not m(f):
5095 if not m(f):
5096 continue
5096 continue
5097
5097
5098 didwork = True
5098 didwork = True
5099
5099
5100 if show:
5100 if show:
5101 if nostatus:
5101 if nostatus:
5102 ui.write("%s\n" % f)
5102 ui.write("%s\n" % f)
5103 else:
5103 else:
5104 ui.write("%s %s\n" % (ms[f].upper(), f),
5104 ui.write("%s %s\n" % (ms[f].upper(), f),
5105 label='resolve.' +
5105 label='resolve.' +
5106 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5106 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5107 elif mark:
5107 elif mark:
5108 ms.mark(f, "r")
5108 ms.mark(f, "r")
5109 elif unmark:
5109 elif unmark:
5110 ms.mark(f, "u")
5110 ms.mark(f, "u")
5111 else:
5111 else:
5112 wctx = repo[None]
5112 wctx = repo[None]
5113
5113
5114 # backup pre-resolve (merge uses .orig for its own purposes)
5114 # backup pre-resolve (merge uses .orig for its own purposes)
5115 a = repo.wjoin(f)
5115 a = repo.wjoin(f)
5116 util.copyfile(a, a + ".resolve")
5116 util.copyfile(a, a + ".resolve")
5117
5117
5118 try:
5118 try:
5119 # resolve file
5119 # resolve file
5120 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5120 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5121 'resolve')
5121 'resolve')
5122 if ms.resolve(f, wctx):
5122 if ms.resolve(f, wctx):
5123 ret = 1
5123 ret = 1
5124 finally:
5124 finally:
5125 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5125 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5126 ms.commit()
5126 ms.commit()
5127
5127
5128 # replace filemerge's .orig file with our resolve file
5128 # replace filemerge's .orig file with our resolve file
5129 util.rename(a + ".resolve", a + ".orig")
5129 util.rename(a + ".resolve", a + ".orig")
5130
5130
5131 ms.commit()
5131 ms.commit()
5132
5132
5133 if not didwork and pats:
5133 if not didwork and pats:
5134 ui.warn(_("arguments do not match paths that need resolving\n"))
5134 ui.warn(_("arguments do not match paths that need resolving\n"))
5135
5135
5136 finally:
5136 finally:
5137 wlock.release()
5137 wlock.release()
5138
5138
5139 # Nudge users into finishing an unfinished operation. We don't print
5139 # Nudge users into finishing an unfinished operation. We don't print
5140 # this with the list/show operation because we want list/show to remain
5140 # this with the list/show operation because we want list/show to remain
5141 # machine readable.
5141 # machine readable.
5142 if not list(ms.unresolved()) and not show:
5142 if not list(ms.unresolved()) and not show:
5143 ui.status(_('(no more unresolved files)\n'))
5143 ui.status(_('(no more unresolved files)\n'))
5144
5144
5145 return ret
5145 return ret
5146
5146
5147 @command('revert',
5147 @command('revert',
5148 [('a', 'all', None, _('revert all changes when no arguments given')),
5148 [('a', 'all', None, _('revert all changes when no arguments given')),
5149 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5149 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5150 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5150 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5151 ('C', 'no-backup', None, _('do not save backup copies of files')),
5151 ('C', 'no-backup', None, _('do not save backup copies of files')),
5152 ] + walkopts + dryrunopts,
5152 ] + walkopts + dryrunopts,
5153 _('[OPTION]... [-r REV] [NAME]...'))
5153 _('[OPTION]... [-r REV] [NAME]...'))
5154 def revert(ui, repo, *pats, **opts):
5154 def revert(ui, repo, *pats, **opts):
5155 """restore files to their checkout state
5155 """restore files to their checkout state
5156
5156
5157 .. note::
5157 .. note::
5158
5158
5159 To check out earlier revisions, you should use :hg:`update REV`.
5159 To check out earlier revisions, you should use :hg:`update REV`.
5160 To cancel an uncommitted merge (and lose your changes),
5160 To cancel an uncommitted merge (and lose your changes),
5161 use :hg:`update --clean .`.
5161 use :hg:`update --clean .`.
5162
5162
5163 With no revision specified, revert the specified files or directories
5163 With no revision specified, revert the specified files or directories
5164 to the contents they had in the parent of the working directory.
5164 to the contents they had in the parent of the working directory.
5165 This restores the contents of files to an unmodified
5165 This restores the contents of files to an unmodified
5166 state and unschedules adds, removes, copies, and renames. If the
5166 state and unschedules adds, removes, copies, and renames. If the
5167 working directory has two parents, you must explicitly specify a
5167 working directory has two parents, you must explicitly specify a
5168 revision.
5168 revision.
5169
5169
5170 Using the -r/--rev or -d/--date options, revert the given files or
5170 Using the -r/--rev or -d/--date options, revert the given files or
5171 directories to their states as of a specific revision. Because
5171 directories to their states as of a specific revision. Because
5172 revert does not change the working directory parents, this will
5172 revert does not change the working directory parents, this will
5173 cause these files to appear modified. This can be helpful to "back
5173 cause these files to appear modified. This can be helpful to "back
5174 out" some or all of an earlier change. See :hg:`backout` for a
5174 out" some or all of an earlier change. See :hg:`backout` for a
5175 related method.
5175 related method.
5176
5176
5177 Modified files are saved with a .orig suffix before reverting.
5177 Modified files are saved with a .orig suffix before reverting.
5178 To disable these backups, use --no-backup.
5178 To disable these backups, use --no-backup.
5179
5179
5180 See :hg:`help dates` for a list of formats valid for -d/--date.
5180 See :hg:`help dates` for a list of formats valid for -d/--date.
5181
5181
5182 Returns 0 on success.
5182 Returns 0 on success.
5183 """
5183 """
5184
5184
5185 if opts.get("date"):
5185 if opts.get("date"):
5186 if opts.get("rev"):
5186 if opts.get("rev"):
5187 raise util.Abort(_("you can't specify a revision and a date"))
5187 raise util.Abort(_("you can't specify a revision and a date"))
5188 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5188 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5189
5189
5190 parent, p2 = repo.dirstate.parents()
5190 parent, p2 = repo.dirstate.parents()
5191 if not opts.get('rev') and p2 != nullid:
5191 if not opts.get('rev') and p2 != nullid:
5192 # revert after merge is a trap for new users (issue2915)
5192 # revert after merge is a trap for new users (issue2915)
5193 raise util.Abort(_('uncommitted merge with no revision specified'),
5193 raise util.Abort(_('uncommitted merge with no revision specified'),
5194 hint=_('use "hg update" or see "hg help revert"'))
5194 hint=_('use "hg update" or see "hg help revert"'))
5195
5195
5196 ctx = scmutil.revsingle(repo, opts.get('rev'))
5196 ctx = scmutil.revsingle(repo, opts.get('rev'))
5197
5197
5198 if not pats and not opts.get('all'):
5198 if not pats and not opts.get('all'):
5199 msg = _("no files or directories specified")
5199 msg = _("no files or directories specified")
5200 if p2 != nullid:
5200 if p2 != nullid:
5201 hint = _("uncommitted merge, use --all to discard all changes,"
5201 hint = _("uncommitted merge, use --all to discard all changes,"
5202 " or 'hg update -C .' to abort the merge")
5202 " or 'hg update -C .' to abort the merge")
5203 raise util.Abort(msg, hint=hint)
5203 raise util.Abort(msg, hint=hint)
5204 dirty = util.any(repo.status())
5204 dirty = util.any(repo.status())
5205 node = ctx.node()
5205 node = ctx.node()
5206 if node != parent:
5206 if node != parent:
5207 if dirty:
5207 if dirty:
5208 hint = _("uncommitted changes, use --all to discard all"
5208 hint = _("uncommitted changes, use --all to discard all"
5209 " changes, or 'hg update %s' to update") % ctx.rev()
5209 " changes, or 'hg update %s' to update") % ctx.rev()
5210 else:
5210 else:
5211 hint = _("use --all to revert all files,"
5211 hint = _("use --all to revert all files,"
5212 " or 'hg update %s' to update") % ctx.rev()
5212 " or 'hg update %s' to update") % ctx.rev()
5213 elif dirty:
5213 elif dirty:
5214 hint = _("uncommitted changes, use --all to discard all changes")
5214 hint = _("uncommitted changes, use --all to discard all changes")
5215 else:
5215 else:
5216 hint = _("use --all to revert all files")
5216 hint = _("use --all to revert all files")
5217 raise util.Abort(msg, hint=hint)
5217 raise util.Abort(msg, hint=hint)
5218
5218
5219 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5219 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5220
5220
5221 @command('rollback', dryrunopts +
5221 @command('rollback', dryrunopts +
5222 [('f', 'force', False, _('ignore safety measures'))])
5222 [('f', 'force', False, _('ignore safety measures'))])
5223 def rollback(ui, repo, **opts):
5223 def rollback(ui, repo, **opts):
5224 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5224 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5225
5225
5226 Please use :hg:`commit --amend` instead of rollback to correct
5226 Please use :hg:`commit --amend` instead of rollback to correct
5227 mistakes in the last commit.
5227 mistakes in the last commit.
5228
5228
5229 This command should be used with care. There is only one level of
5229 This command should be used with care. There is only one level of
5230 rollback, and there is no way to undo a rollback. It will also
5230 rollback, and there is no way to undo a rollback. It will also
5231 restore the dirstate at the time of the last transaction, losing
5231 restore the dirstate at the time of the last transaction, losing
5232 any dirstate changes since that time. This command does not alter
5232 any dirstate changes since that time. This command does not alter
5233 the working directory.
5233 the working directory.
5234
5234
5235 Transactions are used to encapsulate the effects of all commands
5235 Transactions are used to encapsulate the effects of all commands
5236 that create new changesets or propagate existing changesets into a
5236 that create new changesets or propagate existing changesets into a
5237 repository.
5237 repository.
5238
5238
5239 .. container:: verbose
5239 .. container:: verbose
5240
5240
5241 For example, the following commands are transactional, and their
5241 For example, the following commands are transactional, and their
5242 effects can be rolled back:
5242 effects can be rolled back:
5243
5243
5244 - commit
5244 - commit
5245 - import
5245 - import
5246 - pull
5246 - pull
5247 - push (with this repository as the destination)
5247 - push (with this repository as the destination)
5248 - unbundle
5248 - unbundle
5249
5249
5250 To avoid permanent data loss, rollback will refuse to rollback a
5250 To avoid permanent data loss, rollback will refuse to rollback a
5251 commit transaction if it isn't checked out. Use --force to
5251 commit transaction if it isn't checked out. Use --force to
5252 override this protection.
5252 override this protection.
5253
5253
5254 This command is not intended for use on public repositories. Once
5254 This command is not intended for use on public repositories. Once
5255 changes are visible for pull by other users, rolling a transaction
5255 changes are visible for pull by other users, rolling a transaction
5256 back locally is ineffective (someone else may already have pulled
5256 back locally is ineffective (someone else may already have pulled
5257 the changes). Furthermore, a race is possible with readers of the
5257 the changes). Furthermore, a race is possible with readers of the
5258 repository; for example an in-progress pull from the repository
5258 repository; for example an in-progress pull from the repository
5259 may fail if a rollback is performed.
5259 may fail if a rollback is performed.
5260
5260
5261 Returns 0 on success, 1 if no rollback data is available.
5261 Returns 0 on success, 1 if no rollback data is available.
5262 """
5262 """
5263 return repo.rollback(dryrun=opts.get('dry_run'),
5263 return repo.rollback(dryrun=opts.get('dry_run'),
5264 force=opts.get('force'))
5264 force=opts.get('force'))
5265
5265
5266 @command('root', [])
5266 @command('root', [])
5267 def root(ui, repo):
5267 def root(ui, repo):
5268 """print the root (top) of the current working directory
5268 """print the root (top) of the current working directory
5269
5269
5270 Print the root directory of the current repository.
5270 Print the root directory of the current repository.
5271
5271
5272 Returns 0 on success.
5272 Returns 0 on success.
5273 """
5273 """
5274 ui.write(repo.root + "\n")
5274 ui.write(repo.root + "\n")
5275
5275
5276 @command('^serve',
5276 @command('^serve',
5277 [('A', 'accesslog', '', _('name of access log file to write to'),
5277 [('A', 'accesslog', '', _('name of access log file to write to'),
5278 _('FILE')),
5278 _('FILE')),
5279 ('d', 'daemon', None, _('run server in background')),
5279 ('d', 'daemon', None, _('run server in background')),
5280 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5280 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5281 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5281 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5282 # use string type, then we can check if something was passed
5282 # use string type, then we can check if something was passed
5283 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5283 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5284 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5284 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5285 _('ADDR')),
5285 _('ADDR')),
5286 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5286 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5287 _('PREFIX')),
5287 _('PREFIX')),
5288 ('n', 'name', '',
5288 ('n', 'name', '',
5289 _('name to show in web pages (default: working directory)'), _('NAME')),
5289 _('name to show in web pages (default: working directory)'), _('NAME')),
5290 ('', 'web-conf', '',
5290 ('', 'web-conf', '',
5291 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5291 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5292 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5292 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5293 _('FILE')),
5293 _('FILE')),
5294 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5294 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5295 ('', 'stdio', None, _('for remote clients')),
5295 ('', 'stdio', None, _('for remote clients')),
5296 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5296 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5297 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5297 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5298 ('', 'style', '', _('template style to use'), _('STYLE')),
5298 ('', 'style', '', _('template style to use'), _('STYLE')),
5299 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5299 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5300 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5300 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5301 _('[OPTION]...'),
5301 _('[OPTION]...'),
5302 optionalrepo=True)
5302 optionalrepo=True)
5303 def serve(ui, repo, **opts):
5303 def serve(ui, repo, **opts):
5304 """start stand-alone webserver
5304 """start stand-alone webserver
5305
5305
5306 Start a local HTTP repository browser and pull server. You can use
5306 Start a local HTTP repository browser and pull server. You can use
5307 this for ad-hoc sharing and browsing of repositories. It is
5307 this for ad-hoc sharing and browsing of repositories. It is
5308 recommended to use a real web server to serve a repository for
5308 recommended to use a real web server to serve a repository for
5309 longer periods of time.
5309 longer periods of time.
5310
5310
5311 Please note that the server does not implement access control.
5311 Please note that the server does not implement access control.
5312 This means that, by default, anybody can read from the server and
5312 This means that, by default, anybody can read from the server and
5313 nobody can write to it by default. Set the ``web.allow_push``
5313 nobody can write to it by default. Set the ``web.allow_push``
5314 option to ``*`` to allow everybody to push to the server. You
5314 option to ``*`` to allow everybody to push to the server. You
5315 should use a real web server if you need to authenticate users.
5315 should use a real web server if you need to authenticate users.
5316
5316
5317 By default, the server logs accesses to stdout and errors to
5317 By default, the server logs accesses to stdout and errors to
5318 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5318 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5319 files.
5319 files.
5320
5320
5321 To have the server choose a free port number to listen on, specify
5321 To have the server choose a free port number to listen on, specify
5322 a port number of 0; in this case, the server will print the port
5322 a port number of 0; in this case, the server will print the port
5323 number it uses.
5323 number it uses.
5324
5324
5325 Returns 0 on success.
5325 Returns 0 on success.
5326 """
5326 """
5327
5327
5328 if opts["stdio"] and opts["cmdserver"]:
5328 if opts["stdio"] and opts["cmdserver"]:
5329 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5329 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5330
5330
5331 if opts["stdio"]:
5331 if opts["stdio"]:
5332 if repo is None:
5332 if repo is None:
5333 raise error.RepoError(_("there is no Mercurial repository here"
5333 raise error.RepoError(_("there is no Mercurial repository here"
5334 " (.hg not found)"))
5334 " (.hg not found)"))
5335 s = sshserver.sshserver(ui, repo)
5335 s = sshserver.sshserver(ui, repo)
5336 s.serve_forever()
5336 s.serve_forever()
5337
5337
5338 if opts["cmdserver"]:
5338 if opts["cmdserver"]:
5339 s = commandserver.server(ui, repo, opts["cmdserver"])
5339 s = commandserver.server(ui, repo, opts["cmdserver"])
5340 return s.serve()
5340 return s.serve()
5341
5341
5342 # this way we can check if something was given in the command-line
5342 # this way we can check if something was given in the command-line
5343 if opts.get('port'):
5343 if opts.get('port'):
5344 opts['port'] = util.getport(opts.get('port'))
5344 opts['port'] = util.getport(opts.get('port'))
5345
5345
5346 baseui = repo and repo.baseui or ui
5346 baseui = repo and repo.baseui or ui
5347 optlist = ("name templates style address port prefix ipv6"
5347 optlist = ("name templates style address port prefix ipv6"
5348 " accesslog errorlog certificate encoding")
5348 " accesslog errorlog certificate encoding")
5349 for o in optlist.split():
5349 for o in optlist.split():
5350 val = opts.get(o, '')
5350 val = opts.get(o, '')
5351 if val in (None, ''): # should check against default options instead
5351 if val in (None, ''): # should check against default options instead
5352 continue
5352 continue
5353 baseui.setconfig("web", o, val, 'serve')
5353 baseui.setconfig("web", o, val, 'serve')
5354 if repo and repo.ui != baseui:
5354 if repo and repo.ui != baseui:
5355 repo.ui.setconfig("web", o, val, 'serve')
5355 repo.ui.setconfig("web", o, val, 'serve')
5356
5356
5357 o = opts.get('web_conf') or opts.get('webdir_conf')
5357 o = opts.get('web_conf') or opts.get('webdir_conf')
5358 if not o:
5358 if not o:
5359 if not repo:
5359 if not repo:
5360 raise error.RepoError(_("there is no Mercurial repository"
5360 raise error.RepoError(_("there is no Mercurial repository"
5361 " here (.hg not found)"))
5361 " here (.hg not found)"))
5362 o = repo
5362 o = repo
5363
5363
5364 app = hgweb.hgweb(o, baseui=baseui)
5364 app = hgweb.hgweb(o, baseui=baseui)
5365 service = httpservice(ui, app, opts)
5365 service = httpservice(ui, app, opts)
5366 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5366 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5367
5367
5368 class httpservice(object):
5368 class httpservice(object):
5369 def __init__(self, ui, app, opts):
5369 def __init__(self, ui, app, opts):
5370 self.ui = ui
5370 self.ui = ui
5371 self.app = app
5371 self.app = app
5372 self.opts = opts
5372 self.opts = opts
5373
5373
5374 def init(self):
5374 def init(self):
5375 util.setsignalhandler()
5375 util.setsignalhandler()
5376 self.httpd = hgweb_server.create_server(self.ui, self.app)
5376 self.httpd = hgweb_server.create_server(self.ui, self.app)
5377
5377
5378 if self.opts['port'] and not self.ui.verbose:
5378 if self.opts['port'] and not self.ui.verbose:
5379 return
5379 return
5380
5380
5381 if self.httpd.prefix:
5381 if self.httpd.prefix:
5382 prefix = self.httpd.prefix.strip('/') + '/'
5382 prefix = self.httpd.prefix.strip('/') + '/'
5383 else:
5383 else:
5384 prefix = ''
5384 prefix = ''
5385
5385
5386 port = ':%d' % self.httpd.port
5386 port = ':%d' % self.httpd.port
5387 if port == ':80':
5387 if port == ':80':
5388 port = ''
5388 port = ''
5389
5389
5390 bindaddr = self.httpd.addr
5390 bindaddr = self.httpd.addr
5391 if bindaddr == '0.0.0.0':
5391 if bindaddr == '0.0.0.0':
5392 bindaddr = '*'
5392 bindaddr = '*'
5393 elif ':' in bindaddr: # IPv6
5393 elif ':' in bindaddr: # IPv6
5394 bindaddr = '[%s]' % bindaddr
5394 bindaddr = '[%s]' % bindaddr
5395
5395
5396 fqaddr = self.httpd.fqaddr
5396 fqaddr = self.httpd.fqaddr
5397 if ':' in fqaddr:
5397 if ':' in fqaddr:
5398 fqaddr = '[%s]' % fqaddr
5398 fqaddr = '[%s]' % fqaddr
5399 if self.opts['port']:
5399 if self.opts['port']:
5400 write = self.ui.status
5400 write = self.ui.status
5401 else:
5401 else:
5402 write = self.ui.write
5402 write = self.ui.write
5403 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5403 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5404 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5404 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5405 self.ui.flush() # avoid buffering of status message
5405 self.ui.flush() # avoid buffering of status message
5406
5406
5407 def run(self):
5407 def run(self):
5408 self.httpd.serve_forever()
5408 self.httpd.serve_forever()
5409
5409
5410
5410
5411 @command('^status|st',
5411 @command('^status|st',
5412 [('A', 'all', None, _('show status of all files')),
5412 [('A', 'all', None, _('show status of all files')),
5413 ('m', 'modified', None, _('show only modified files')),
5413 ('m', 'modified', None, _('show only modified files')),
5414 ('a', 'added', None, _('show only added files')),
5414 ('a', 'added', None, _('show only added files')),
5415 ('r', 'removed', None, _('show only removed files')),
5415 ('r', 'removed', None, _('show only removed files')),
5416 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5416 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5417 ('c', 'clean', None, _('show only files without changes')),
5417 ('c', 'clean', None, _('show only files without changes')),
5418 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5418 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5419 ('i', 'ignored', None, _('show only ignored files')),
5419 ('i', 'ignored', None, _('show only ignored files')),
5420 ('n', 'no-status', None, _('hide status prefix')),
5420 ('n', 'no-status', None, _('hide status prefix')),
5421 ('C', 'copies', None, _('show source of copied files')),
5421 ('C', 'copies', None, _('show source of copied files')),
5422 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5422 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5423 ('', 'rev', [], _('show difference from revision'), _('REV')),
5423 ('', 'rev', [], _('show difference from revision'), _('REV')),
5424 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5424 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5425 ] + walkopts + subrepoopts,
5425 ] + walkopts + subrepoopts,
5426 _('[OPTION]... [FILE]...'),
5426 _('[OPTION]... [FILE]...'),
5427 inferrepo=True)
5427 inferrepo=True)
5428 def status(ui, repo, *pats, **opts):
5428 def status(ui, repo, *pats, **opts):
5429 """show changed files in the working directory
5429 """show changed files in the working directory
5430
5430
5431 Show status of files in the repository. If names are given, only
5431 Show status of files in the repository. If names are given, only
5432 files that match are shown. Files that are clean or ignored or
5432 files that match are shown. Files that are clean or ignored or
5433 the source of a copy/move operation, are not listed unless
5433 the source of a copy/move operation, are not listed unless
5434 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5434 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5435 Unless options described with "show only ..." are given, the
5435 Unless options described with "show only ..." are given, the
5436 options -mardu are used.
5436 options -mardu are used.
5437
5437
5438 Option -q/--quiet hides untracked (unknown and ignored) files
5438 Option -q/--quiet hides untracked (unknown and ignored) files
5439 unless explicitly requested with -u/--unknown or -i/--ignored.
5439 unless explicitly requested with -u/--unknown or -i/--ignored.
5440
5440
5441 .. note::
5441 .. note::
5442
5442
5443 status may appear to disagree with diff if permissions have
5443 status may appear to disagree with diff if permissions have
5444 changed or a merge has occurred. The standard diff format does
5444 changed or a merge has occurred. The standard diff format does
5445 not report permission changes and diff only reports changes
5445 not report permission changes and diff only reports changes
5446 relative to one merge parent.
5446 relative to one merge parent.
5447
5447
5448 If one revision is given, it is used as the base revision.
5448 If one revision is given, it is used as the base revision.
5449 If two revisions are given, the differences between them are
5449 If two revisions are given, the differences between them are
5450 shown. The --change option can also be used as a shortcut to list
5450 shown. The --change option can also be used as a shortcut to list
5451 the changed files of a revision from its first parent.
5451 the changed files of a revision from its first parent.
5452
5452
5453 The codes used to show the status of files are::
5453 The codes used to show the status of files are::
5454
5454
5455 M = modified
5455 M = modified
5456 A = added
5456 A = added
5457 R = removed
5457 R = removed
5458 C = clean
5458 C = clean
5459 ! = missing (deleted by non-hg command, but still tracked)
5459 ! = missing (deleted by non-hg command, but still tracked)
5460 ? = not tracked
5460 ? = not tracked
5461 I = ignored
5461 I = ignored
5462 = origin of the previous file (with --copies)
5462 = origin of the previous file (with --copies)
5463
5463
5464 .. container:: verbose
5464 .. container:: verbose
5465
5465
5466 Examples:
5466 Examples:
5467
5467
5468 - show changes in the working directory relative to a
5468 - show changes in the working directory relative to a
5469 changeset::
5469 changeset::
5470
5470
5471 hg status --rev 9353
5471 hg status --rev 9353
5472
5472
5473 - show all changes including copies in an existing changeset::
5473 - show all changes including copies in an existing changeset::
5474
5474
5475 hg status --copies --change 9353
5475 hg status --copies --change 9353
5476
5476
5477 - get a NUL separated list of added files, suitable for xargs::
5477 - get a NUL separated list of added files, suitable for xargs::
5478
5478
5479 hg status -an0
5479 hg status -an0
5480
5480
5481 Returns 0 on success.
5481 Returns 0 on success.
5482 """
5482 """
5483
5483
5484 revs = opts.get('rev')
5484 revs = opts.get('rev')
5485 change = opts.get('change')
5485 change = opts.get('change')
5486
5486
5487 if revs and change:
5487 if revs and change:
5488 msg = _('cannot specify --rev and --change at the same time')
5488 msg = _('cannot specify --rev and --change at the same time')
5489 raise util.Abort(msg)
5489 raise util.Abort(msg)
5490 elif change:
5490 elif change:
5491 node2 = scmutil.revsingle(repo, change, None).node()
5491 node2 = scmutil.revsingle(repo, change, None).node()
5492 node1 = repo[node2].p1().node()
5492 node1 = repo[node2].p1().node()
5493 else:
5493 else:
5494 node1, node2 = scmutil.revpair(repo, revs)
5494 node1, node2 = scmutil.revpair(repo, revs)
5495
5495
5496 cwd = (pats and repo.getcwd()) or ''
5496 cwd = (pats and repo.getcwd()) or ''
5497 end = opts.get('print0') and '\0' or '\n'
5497 end = opts.get('print0') and '\0' or '\n'
5498 copy = {}
5498 copy = {}
5499 states = 'modified added removed deleted unknown ignored clean'.split()
5499 states = 'modified added removed deleted unknown ignored clean'.split()
5500 show = [k for k in states if opts.get(k)]
5500 show = [k for k in states if opts.get(k)]
5501 if opts.get('all'):
5501 if opts.get('all'):
5502 show += ui.quiet and (states[:4] + ['clean']) or states
5502 show += ui.quiet and (states[:4] + ['clean']) or states
5503 if not show:
5503 if not show:
5504 show = ui.quiet and states[:4] or states[:5]
5504 show = ui.quiet and states[:4] or states[:5]
5505
5505
5506 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5506 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5507 'ignored' in show, 'clean' in show, 'unknown' in show,
5507 'ignored' in show, 'clean' in show, 'unknown' in show,
5508 opts.get('subrepos'))
5508 opts.get('subrepos'))
5509 changestates = zip(states, 'MAR!?IC', stat)
5509 changestates = zip(states, 'MAR!?IC', stat)
5510
5510
5511 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5511 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5512 copy = copies.pathcopies(repo[node1], repo[node2])
5512 copy = copies.pathcopies(repo[node1], repo[node2])
5513
5513
5514 fm = ui.formatter('status', opts)
5514 fm = ui.formatter('status', opts)
5515 fmt = '%s' + end
5515 fmt = '%s' + end
5516 showchar = not opts.get('no_status')
5516 showchar = not opts.get('no_status')
5517
5517
5518 for state, char, files in changestates:
5518 for state, char, files in changestates:
5519 if state in show:
5519 if state in show:
5520 label = 'status.' + state
5520 label = 'status.' + state
5521 for f in files:
5521 for f in files:
5522 fm.startitem()
5522 fm.startitem()
5523 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5523 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5524 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5524 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5525 if f in copy:
5525 if f in copy:
5526 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5526 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5527 label='status.copied')
5527 label='status.copied')
5528 fm.end()
5528 fm.end()
5529
5529
5530 @command('^summary|sum',
5530 @command('^summary|sum',
5531 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5531 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5532 def summary(ui, repo, **opts):
5532 def summary(ui, repo, **opts):
5533 """summarize working directory state
5533 """summarize working directory state
5534
5534
5535 This generates a brief summary of the working directory state,
5535 This generates a brief summary of the working directory state,
5536 including parents, branch, commit status, and available updates.
5536 including parents, branch, commit status, and available updates.
5537
5537
5538 With the --remote option, this will check the default paths for
5538 With the --remote option, this will check the default paths for
5539 incoming and outgoing changes. This can be time-consuming.
5539 incoming and outgoing changes. This can be time-consuming.
5540
5540
5541 Returns 0 on success.
5541 Returns 0 on success.
5542 """
5542 """
5543
5543
5544 ctx = repo[None]
5544 ctx = repo[None]
5545 parents = ctx.parents()
5545 parents = ctx.parents()
5546 pnode = parents[0].node()
5546 pnode = parents[0].node()
5547 marks = []
5547 marks = []
5548
5548
5549 for p in parents:
5549 for p in parents:
5550 # label with log.changeset (instead of log.parent) since this
5550 # label with log.changeset (instead of log.parent) since this
5551 # shows a working directory parent *changeset*:
5551 # shows a working directory parent *changeset*:
5552 # i18n: column positioning for "hg summary"
5552 # i18n: column positioning for "hg summary"
5553 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5553 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5554 label='log.changeset changeset.%s' % p.phasestr())
5554 label='log.changeset changeset.%s' % p.phasestr())
5555 ui.write(' '.join(p.tags()), label='log.tag')
5555 ui.write(' '.join(p.tags()), label='log.tag')
5556 if p.bookmarks():
5556 if p.bookmarks():
5557 marks.extend(p.bookmarks())
5557 marks.extend(p.bookmarks())
5558 if p.rev() == -1:
5558 if p.rev() == -1:
5559 if not len(repo):
5559 if not len(repo):
5560 ui.write(_(' (empty repository)'))
5560 ui.write(_(' (empty repository)'))
5561 else:
5561 else:
5562 ui.write(_(' (no revision checked out)'))
5562 ui.write(_(' (no revision checked out)'))
5563 ui.write('\n')
5563 ui.write('\n')
5564 if p.description():
5564 if p.description():
5565 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5565 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5566 label='log.summary')
5566 label='log.summary')
5567
5567
5568 branch = ctx.branch()
5568 branch = ctx.branch()
5569 bheads = repo.branchheads(branch)
5569 bheads = repo.branchheads(branch)
5570 # i18n: column positioning for "hg summary"
5570 # i18n: column positioning for "hg summary"
5571 m = _('branch: %s\n') % branch
5571 m = _('branch: %s\n') % branch
5572 if branch != 'default':
5572 if branch != 'default':
5573 ui.write(m, label='log.branch')
5573 ui.write(m, label='log.branch')
5574 else:
5574 else:
5575 ui.status(m, label='log.branch')
5575 ui.status(m, label='log.branch')
5576
5576
5577 if marks:
5577 if marks:
5578 current = repo._bookmarkcurrent
5578 current = repo._bookmarkcurrent
5579 # i18n: column positioning for "hg summary"
5579 # i18n: column positioning for "hg summary"
5580 ui.write(_('bookmarks:'), label='log.bookmark')
5580 ui.write(_('bookmarks:'), label='log.bookmark')
5581 if current is not None:
5581 if current is not None:
5582 if current in marks:
5582 if current in marks:
5583 ui.write(' *' + current, label='bookmarks.current')
5583 ui.write(' *' + current, label='bookmarks.current')
5584 marks.remove(current)
5584 marks.remove(current)
5585 else:
5585 else:
5586 ui.write(' [%s]' % current, label='bookmarks.current')
5586 ui.write(' [%s]' % current, label='bookmarks.current')
5587 for m in marks:
5587 for m in marks:
5588 ui.write(' ' + m, label='log.bookmark')
5588 ui.write(' ' + m, label='log.bookmark')
5589 ui.write('\n', label='log.bookmark')
5589 ui.write('\n', label='log.bookmark')
5590
5590
5591 st = list(repo.status(unknown=True))[:6]
5591 st = list(repo.status(unknown=True))[:6]
5592
5592
5593 c = repo.dirstate.copies()
5593 c = repo.dirstate.copies()
5594 copied, renamed = [], []
5594 copied, renamed = [], []
5595 for d, s in c.iteritems():
5595 for d, s in c.iteritems():
5596 if s in st[2]:
5596 if s in st[2]:
5597 st[2].remove(s)
5597 st[2].remove(s)
5598 renamed.append(d)
5598 renamed.append(d)
5599 else:
5599 else:
5600 copied.append(d)
5600 copied.append(d)
5601 if d in st[1]:
5601 if d in st[1]:
5602 st[1].remove(d)
5602 st[1].remove(d)
5603 st.insert(3, renamed)
5603 st.insert(3, renamed)
5604 st.insert(4, copied)
5604 st.insert(4, copied)
5605
5605
5606 ms = mergemod.mergestate(repo)
5606 ms = mergemod.mergestate(repo)
5607 st.append([f for f in ms if ms[f] == 'u'])
5607 st.append([f for f in ms if ms[f] == 'u'])
5608
5608
5609 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5609 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5610 st.append(subs)
5610 st.append(subs)
5611
5611
5612 labels = [ui.label(_('%d modified'), 'status.modified'),
5612 labels = [ui.label(_('%d modified'), 'status.modified'),
5613 ui.label(_('%d added'), 'status.added'),
5613 ui.label(_('%d added'), 'status.added'),
5614 ui.label(_('%d removed'), 'status.removed'),
5614 ui.label(_('%d removed'), 'status.removed'),
5615 ui.label(_('%d renamed'), 'status.copied'),
5615 ui.label(_('%d renamed'), 'status.copied'),
5616 ui.label(_('%d copied'), 'status.copied'),
5616 ui.label(_('%d copied'), 'status.copied'),
5617 ui.label(_('%d deleted'), 'status.deleted'),
5617 ui.label(_('%d deleted'), 'status.deleted'),
5618 ui.label(_('%d unknown'), 'status.unknown'),
5618 ui.label(_('%d unknown'), 'status.unknown'),
5619 ui.label(_('%d ignored'), 'status.ignored'),
5619 ui.label(_('%d ignored'), 'status.ignored'),
5620 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5620 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5621 ui.label(_('%d subrepos'), 'status.modified')]
5621 ui.label(_('%d subrepos'), 'status.modified')]
5622 t = []
5622 t = []
5623 for s, l in zip(st, labels):
5623 for s, l in zip(st, labels):
5624 if s:
5624 if s:
5625 t.append(l % len(s))
5625 t.append(l % len(s))
5626
5626
5627 t = ', '.join(t)
5627 t = ', '.join(t)
5628 cleanworkdir = False
5628 cleanworkdir = False
5629
5629
5630 if repo.vfs.exists('updatestate'):
5630 if repo.vfs.exists('updatestate'):
5631 t += _(' (interrupted update)')
5631 t += _(' (interrupted update)')
5632 elif len(parents) > 1:
5632 elif len(parents) > 1:
5633 t += _(' (merge)')
5633 t += _(' (merge)')
5634 elif branch != parents[0].branch():
5634 elif branch != parents[0].branch():
5635 t += _(' (new branch)')
5635 t += _(' (new branch)')
5636 elif (parents[0].closesbranch() and
5636 elif (parents[0].closesbranch() and
5637 pnode in repo.branchheads(branch, closed=True)):
5637 pnode in repo.branchheads(branch, closed=True)):
5638 t += _(' (head closed)')
5638 t += _(' (head closed)')
5639 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5639 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5640 t += _(' (clean)')
5640 t += _(' (clean)')
5641 cleanworkdir = True
5641 cleanworkdir = True
5642 elif pnode not in bheads:
5642 elif pnode not in bheads:
5643 t += _(' (new branch head)')
5643 t += _(' (new branch head)')
5644
5644
5645 if cleanworkdir:
5645 if cleanworkdir:
5646 # i18n: column positioning for "hg summary"
5646 # i18n: column positioning for "hg summary"
5647 ui.status(_('commit: %s\n') % t.strip())
5647 ui.status(_('commit: %s\n') % t.strip())
5648 else:
5648 else:
5649 # i18n: column positioning for "hg summary"
5649 # i18n: column positioning for "hg summary"
5650 ui.write(_('commit: %s\n') % t.strip())
5650 ui.write(_('commit: %s\n') % t.strip())
5651
5651
5652 # all ancestors of branch heads - all ancestors of parent = new csets
5652 # all ancestors of branch heads - all ancestors of parent = new csets
5653 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5653 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5654 bheads))
5654 bheads))
5655
5655
5656 if new == 0:
5656 if new == 0:
5657 # i18n: column positioning for "hg summary"
5657 # i18n: column positioning for "hg summary"
5658 ui.status(_('update: (current)\n'))
5658 ui.status(_('update: (current)\n'))
5659 elif pnode not in bheads:
5659 elif pnode not in bheads:
5660 # i18n: column positioning for "hg summary"
5660 # i18n: column positioning for "hg summary"
5661 ui.write(_('update: %d new changesets (update)\n') % new)
5661 ui.write(_('update: %d new changesets (update)\n') % new)
5662 else:
5662 else:
5663 # i18n: column positioning for "hg summary"
5663 # i18n: column positioning for "hg summary"
5664 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5664 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5665 (new, len(bheads)))
5665 (new, len(bheads)))
5666
5666
5667 cmdutil.summaryhooks(ui, repo)
5667 cmdutil.summaryhooks(ui, repo)
5668
5668
5669 if opts.get('remote'):
5669 if opts.get('remote'):
5670 needsincoming, needsoutgoing = True, True
5670 needsincoming, needsoutgoing = True, True
5671 else:
5671 else:
5672 needsincoming, needsoutgoing = False, False
5672 needsincoming, needsoutgoing = False, False
5673 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5673 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5674 if i:
5674 if i:
5675 needsincoming = True
5675 needsincoming = True
5676 if o:
5676 if o:
5677 needsoutgoing = True
5677 needsoutgoing = True
5678 if not needsincoming and not needsoutgoing:
5678 if not needsincoming and not needsoutgoing:
5679 return
5679 return
5680
5680
5681 def getincoming():
5681 def getincoming():
5682 source, branches = hg.parseurl(ui.expandpath('default'))
5682 source, branches = hg.parseurl(ui.expandpath('default'))
5683 sbranch = branches[0]
5683 sbranch = branches[0]
5684 try:
5684 try:
5685 other = hg.peer(repo, {}, source)
5685 other = hg.peer(repo, {}, source)
5686 except error.RepoError:
5686 except error.RepoError:
5687 if opts.get('remote'):
5687 if opts.get('remote'):
5688 raise
5688 raise
5689 return source, sbranch, None, None, None
5689 return source, sbranch, None, None, None
5690 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5690 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5691 if revs:
5691 if revs:
5692 revs = [other.lookup(rev) for rev in revs]
5692 revs = [other.lookup(rev) for rev in revs]
5693 ui.debug('comparing with %s\n' % util.hidepassword(source))
5693 ui.debug('comparing with %s\n' % util.hidepassword(source))
5694 repo.ui.pushbuffer()
5694 repo.ui.pushbuffer()
5695 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5695 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5696 repo.ui.popbuffer()
5696 repo.ui.popbuffer()
5697 return source, sbranch, other, commoninc, commoninc[1]
5697 return source, sbranch, other, commoninc, commoninc[1]
5698
5698
5699 if needsincoming:
5699 if needsincoming:
5700 source, sbranch, sother, commoninc, incoming = getincoming()
5700 source, sbranch, sother, commoninc, incoming = getincoming()
5701 else:
5701 else:
5702 source = sbranch = sother = commoninc = incoming = None
5702 source = sbranch = sother = commoninc = incoming = None
5703
5703
5704 def getoutgoing():
5704 def getoutgoing():
5705 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5705 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5706 dbranch = branches[0]
5706 dbranch = branches[0]
5707 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5707 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5708 if source != dest:
5708 if source != dest:
5709 try:
5709 try:
5710 dother = hg.peer(repo, {}, dest)
5710 dother = hg.peer(repo, {}, dest)
5711 except error.RepoError:
5711 except error.RepoError:
5712 if opts.get('remote'):
5712 if opts.get('remote'):
5713 raise
5713 raise
5714 return dest, dbranch, None, None
5714 return dest, dbranch, None, None
5715 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5715 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5716 elif sother is None:
5716 elif sother is None:
5717 # there is no explicit destination peer, but source one is invalid
5717 # there is no explicit destination peer, but source one is invalid
5718 return dest, dbranch, None, None
5718 return dest, dbranch, None, None
5719 else:
5719 else:
5720 dother = sother
5720 dother = sother
5721 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5721 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5722 common = None
5722 common = None
5723 else:
5723 else:
5724 common = commoninc
5724 common = commoninc
5725 if revs:
5725 if revs:
5726 revs = [repo.lookup(rev) for rev in revs]
5726 revs = [repo.lookup(rev) for rev in revs]
5727 repo.ui.pushbuffer()
5727 repo.ui.pushbuffer()
5728 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5728 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5729 commoninc=common)
5729 commoninc=common)
5730 repo.ui.popbuffer()
5730 repo.ui.popbuffer()
5731 return dest, dbranch, dother, outgoing
5731 return dest, dbranch, dother, outgoing
5732
5732
5733 if needsoutgoing:
5733 if needsoutgoing:
5734 dest, dbranch, dother, outgoing = getoutgoing()
5734 dest, dbranch, dother, outgoing = getoutgoing()
5735 else:
5735 else:
5736 dest = dbranch = dother = outgoing = None
5736 dest = dbranch = dother = outgoing = None
5737
5737
5738 if opts.get('remote'):
5738 if opts.get('remote'):
5739 t = []
5739 t = []
5740 if incoming:
5740 if incoming:
5741 t.append(_('1 or more incoming'))
5741 t.append(_('1 or more incoming'))
5742 o = outgoing.missing
5742 o = outgoing.missing
5743 if o:
5743 if o:
5744 t.append(_('%d outgoing') % len(o))
5744 t.append(_('%d outgoing') % len(o))
5745 other = dother or sother
5745 other = dother or sother
5746 if 'bookmarks' in other.listkeys('namespaces'):
5746 if 'bookmarks' in other.listkeys('namespaces'):
5747 lmarks = repo.listkeys('bookmarks')
5747 lmarks = repo.listkeys('bookmarks')
5748 rmarks = other.listkeys('bookmarks')
5748 rmarks = other.listkeys('bookmarks')
5749 diff = set(rmarks) - set(lmarks)
5749 diff = set(rmarks) - set(lmarks)
5750 if len(diff) > 0:
5750 if len(diff) > 0:
5751 t.append(_('%d incoming bookmarks') % len(diff))
5751 t.append(_('%d incoming bookmarks') % len(diff))
5752 diff = set(lmarks) - set(rmarks)
5752 diff = set(lmarks) - set(rmarks)
5753 if len(diff) > 0:
5753 if len(diff) > 0:
5754 t.append(_('%d outgoing bookmarks') % len(diff))
5754 t.append(_('%d outgoing bookmarks') % len(diff))
5755
5755
5756 if t:
5756 if t:
5757 # i18n: column positioning for "hg summary"
5757 # i18n: column positioning for "hg summary"
5758 ui.write(_('remote: %s\n') % (', '.join(t)))
5758 ui.write(_('remote: %s\n') % (', '.join(t)))
5759 else:
5759 else:
5760 # i18n: column positioning for "hg summary"
5760 # i18n: column positioning for "hg summary"
5761 ui.status(_('remote: (synced)\n'))
5761 ui.status(_('remote: (synced)\n'))
5762
5762
5763 cmdutil.summaryremotehooks(ui, repo, opts,
5763 cmdutil.summaryremotehooks(ui, repo, opts,
5764 ((source, sbranch, sother, commoninc),
5764 ((source, sbranch, sother, commoninc),
5765 (dest, dbranch, dother, outgoing)))
5765 (dest, dbranch, dother, outgoing)))
5766
5766
5767 @command('tag',
5767 @command('tag',
5768 [('f', 'force', None, _('force tag')),
5768 [('f', 'force', None, _('force tag')),
5769 ('l', 'local', None, _('make the tag local')),
5769 ('l', 'local', None, _('make the tag local')),
5770 ('r', 'rev', '', _('revision to tag'), _('REV')),
5770 ('r', 'rev', '', _('revision to tag'), _('REV')),
5771 ('', 'remove', None, _('remove a tag')),
5771 ('', 'remove', None, _('remove a tag')),
5772 # -l/--local is already there, commitopts cannot be used
5772 # -l/--local is already there, commitopts cannot be used
5773 ('e', 'edit', None, _('invoke editor on commit messages')),
5773 ('e', 'edit', None, _('invoke editor on commit messages')),
5774 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5774 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5775 ] + commitopts2,
5775 ] + commitopts2,
5776 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5776 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5777 def tag(ui, repo, name1, *names, **opts):
5777 def tag(ui, repo, name1, *names, **opts):
5778 """add one or more tags for the current or given revision
5778 """add one or more tags for the current or given revision
5779
5779
5780 Name a particular revision using <name>.
5780 Name a particular revision using <name>.
5781
5781
5782 Tags are used to name particular revisions of the repository and are
5782 Tags are used to name particular revisions of the repository and are
5783 very useful to compare different revisions, to go back to significant
5783 very useful to compare different revisions, to go back to significant
5784 earlier versions or to mark branch points as releases, etc. Changing
5784 earlier versions or to mark branch points as releases, etc. Changing
5785 an existing tag is normally disallowed; use -f/--force to override.
5785 an existing tag is normally disallowed; use -f/--force to override.
5786
5786
5787 If no revision is given, the parent of the working directory is
5787 If no revision is given, the parent of the working directory is
5788 used.
5788 used.
5789
5789
5790 To facilitate version control, distribution, and merging of tags,
5790 To facilitate version control, distribution, and merging of tags,
5791 they are stored as a file named ".hgtags" which is managed similarly
5791 they are stored as a file named ".hgtags" which is managed similarly
5792 to other project files and can be hand-edited if necessary. This
5792 to other project files and can be hand-edited if necessary. This
5793 also means that tagging creates a new commit. The file
5793 also means that tagging creates a new commit. The file
5794 ".hg/localtags" is used for local tags (not shared among
5794 ".hg/localtags" is used for local tags (not shared among
5795 repositories).
5795 repositories).
5796
5796
5797 Tag commits are usually made at the head of a branch. If the parent
5797 Tag commits are usually made at the head of a branch. If the parent
5798 of the working directory is not a branch head, :hg:`tag` aborts; use
5798 of the working directory is not a branch head, :hg:`tag` aborts; use
5799 -f/--force to force the tag commit to be based on a non-head
5799 -f/--force to force the tag commit to be based on a non-head
5800 changeset.
5800 changeset.
5801
5801
5802 See :hg:`help dates` for a list of formats valid for -d/--date.
5802 See :hg:`help dates` for a list of formats valid for -d/--date.
5803
5803
5804 Since tag names have priority over branch names during revision
5804 Since tag names have priority over branch names during revision
5805 lookup, using an existing branch name as a tag name is discouraged.
5805 lookup, using an existing branch name as a tag name is discouraged.
5806
5806
5807 Returns 0 on success.
5807 Returns 0 on success.
5808 """
5808 """
5809 wlock = lock = None
5809 wlock = lock = None
5810 try:
5810 try:
5811 wlock = repo.wlock()
5811 wlock = repo.wlock()
5812 lock = repo.lock()
5812 lock = repo.lock()
5813 rev_ = "."
5813 rev_ = "."
5814 names = [t.strip() for t in (name1,) + names]
5814 names = [t.strip() for t in (name1,) + names]
5815 if len(names) != len(set(names)):
5815 if len(names) != len(set(names)):
5816 raise util.Abort(_('tag names must be unique'))
5816 raise util.Abort(_('tag names must be unique'))
5817 for n in names:
5817 for n in names:
5818 scmutil.checknewlabel(repo, n, 'tag')
5818 scmutil.checknewlabel(repo, n, 'tag')
5819 if not n:
5819 if not n:
5820 raise util.Abort(_('tag names cannot consist entirely of '
5820 raise util.Abort(_('tag names cannot consist entirely of '
5821 'whitespace'))
5821 'whitespace'))
5822 if opts.get('rev') and opts.get('remove'):
5822 if opts.get('rev') and opts.get('remove'):
5823 raise util.Abort(_("--rev and --remove are incompatible"))
5823 raise util.Abort(_("--rev and --remove are incompatible"))
5824 if opts.get('rev'):
5824 if opts.get('rev'):
5825 rev_ = opts['rev']
5825 rev_ = opts['rev']
5826 message = opts.get('message')
5826 message = opts.get('message')
5827 if opts.get('remove'):
5827 if opts.get('remove'):
5828 expectedtype = opts.get('local') and 'local' or 'global'
5828 expectedtype = opts.get('local') and 'local' or 'global'
5829 for n in names:
5829 for n in names:
5830 if not repo.tagtype(n):
5830 if not repo.tagtype(n):
5831 raise util.Abort(_("tag '%s' does not exist") % n)
5831 raise util.Abort(_("tag '%s' does not exist") % n)
5832 if repo.tagtype(n) != expectedtype:
5832 if repo.tagtype(n) != expectedtype:
5833 if expectedtype == 'global':
5833 if expectedtype == 'global':
5834 raise util.Abort(_("tag '%s' is not a global tag") % n)
5834 raise util.Abort(_("tag '%s' is not a global tag") % n)
5835 else:
5835 else:
5836 raise util.Abort(_("tag '%s' is not a local tag") % n)
5836 raise util.Abort(_("tag '%s' is not a local tag") % n)
5837 rev_ = nullid
5837 rev_ = nullid
5838 if not message:
5838 if not message:
5839 # we don't translate commit messages
5839 # we don't translate commit messages
5840 message = 'Removed tag %s' % ', '.join(names)
5840 message = 'Removed tag %s' % ', '.join(names)
5841 elif not opts.get('force'):
5841 elif not opts.get('force'):
5842 for n in names:
5842 for n in names:
5843 if n in repo.tags():
5843 if n in repo.tags():
5844 raise util.Abort(_("tag '%s' already exists "
5844 raise util.Abort(_("tag '%s' already exists "
5845 "(use -f to force)") % n)
5845 "(use -f to force)") % n)
5846 if not opts.get('local'):
5846 if not opts.get('local'):
5847 p1, p2 = repo.dirstate.parents()
5847 p1, p2 = repo.dirstate.parents()
5848 if p2 != nullid:
5848 if p2 != nullid:
5849 raise util.Abort(_('uncommitted merge'))
5849 raise util.Abort(_('uncommitted merge'))
5850 bheads = repo.branchheads()
5850 bheads = repo.branchheads()
5851 if not opts.get('force') and bheads and p1 not in bheads:
5851 if not opts.get('force') and bheads and p1 not in bheads:
5852 raise util.Abort(_('not at a branch head (use -f to force)'))
5852 raise util.Abort(_('not at a branch head (use -f to force)'))
5853 r = scmutil.revsingle(repo, rev_).node()
5853 r = scmutil.revsingle(repo, rev_).node()
5854
5854
5855 if not message:
5855 if not message:
5856 # we don't translate commit messages
5856 # we don't translate commit messages
5857 message = ('Added tag %s for changeset %s' %
5857 message = ('Added tag %s for changeset %s' %
5858 (', '.join(names), short(r)))
5858 (', '.join(names), short(r)))
5859
5859
5860 date = opts.get('date')
5860 date = opts.get('date')
5861 if date:
5861 if date:
5862 date = util.parsedate(date)
5862 date = util.parsedate(date)
5863
5863
5864 if opts.get('remove'):
5864 if opts.get('remove'):
5865 editform = 'tag.remove'
5865 editform = 'tag.remove'
5866 else:
5866 else:
5867 editform = 'tag.add'
5867 editform = 'tag.add'
5868 editor = cmdutil.getcommiteditor(editform=editform, **opts)
5868 editor = cmdutil.getcommiteditor(editform=editform, **opts)
5869
5869
5870 # don't allow tagging the null rev
5870 # don't allow tagging the null rev
5871 if (not opts.get('remove') and
5871 if (not opts.get('remove') and
5872 scmutil.revsingle(repo, rev_).rev() == nullrev):
5872 scmutil.revsingle(repo, rev_).rev() == nullrev):
5873 raise util.Abort(_("cannot tag null revision"))
5873 raise util.Abort(_("cannot tag null revision"))
5874
5874
5875 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
5875 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
5876 editor=editor)
5876 editor=editor)
5877 finally:
5877 finally:
5878 release(lock, wlock)
5878 release(lock, wlock)
5879
5879
5880 @command('tags', [], '')
5880 @command('tags', [], '')
5881 def tags(ui, repo, **opts):
5881 def tags(ui, repo, **opts):
5882 """list repository tags
5882 """list repository tags
5883
5883
5884 This lists both regular and local tags. When the -v/--verbose
5884 This lists both regular and local tags. When the -v/--verbose
5885 switch is used, a third column "local" is printed for local tags.
5885 switch is used, a third column "local" is printed for local tags.
5886
5886
5887 Returns 0 on success.
5887 Returns 0 on success.
5888 """
5888 """
5889
5889
5890 fm = ui.formatter('tags', opts)
5890 fm = ui.formatter('tags', opts)
5891 hexfunc = ui.debugflag and hex or short
5891 hexfunc = ui.debugflag and hex or short
5892 tagtype = ""
5892 tagtype = ""
5893
5893
5894 for t, n in reversed(repo.tagslist()):
5894 for t, n in reversed(repo.tagslist()):
5895 hn = hexfunc(n)
5895 hn = hexfunc(n)
5896 label = 'tags.normal'
5896 label = 'tags.normal'
5897 tagtype = ''
5897 tagtype = ''
5898 if repo.tagtype(t) == 'local':
5898 if repo.tagtype(t) == 'local':
5899 label = 'tags.local'
5899 label = 'tags.local'
5900 tagtype = 'local'
5900 tagtype = 'local'
5901
5901
5902 fm.startitem()
5902 fm.startitem()
5903 fm.write('tag', '%s', t, label=label)
5903 fm.write('tag', '%s', t, label=label)
5904 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5904 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5905 fm.condwrite(not ui.quiet, 'rev id', fmt,
5905 fm.condwrite(not ui.quiet, 'rev id', fmt,
5906 repo.changelog.rev(n), hn, label=label)
5906 repo.changelog.rev(n), hn, label=label)
5907 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5907 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5908 tagtype, label=label)
5908 tagtype, label=label)
5909 fm.plain('\n')
5909 fm.plain('\n')
5910 fm.end()
5910 fm.end()
5911
5911
5912 @command('tip',
5912 @command('tip',
5913 [('p', 'patch', None, _('show patch')),
5913 [('p', 'patch', None, _('show patch')),
5914 ('g', 'git', None, _('use git extended diff format')),
5914 ('g', 'git', None, _('use git extended diff format')),
5915 ] + templateopts,
5915 ] + templateopts,
5916 _('[-p] [-g]'))
5916 _('[-p] [-g]'))
5917 def tip(ui, repo, **opts):
5917 def tip(ui, repo, **opts):
5918 """show the tip revision (DEPRECATED)
5918 """show the tip revision (DEPRECATED)
5919
5919
5920 The tip revision (usually just called the tip) is the changeset
5920 The tip revision (usually just called the tip) is the changeset
5921 most recently added to the repository (and therefore the most
5921 most recently added to the repository (and therefore the most
5922 recently changed head).
5922 recently changed head).
5923
5923
5924 If you have just made a commit, that commit will be the tip. If
5924 If you have just made a commit, that commit will be the tip. If
5925 you have just pulled changes from another repository, the tip of
5925 you have just pulled changes from another repository, the tip of
5926 that repository becomes the current tip. The "tip" tag is special
5926 that repository becomes the current tip. The "tip" tag is special
5927 and cannot be renamed or assigned to a different changeset.
5927 and cannot be renamed or assigned to a different changeset.
5928
5928
5929 This command is deprecated, please use :hg:`heads` instead.
5929 This command is deprecated, please use :hg:`heads` instead.
5930
5930
5931 Returns 0 on success.
5931 Returns 0 on success.
5932 """
5932 """
5933 displayer = cmdutil.show_changeset(ui, repo, opts)
5933 displayer = cmdutil.show_changeset(ui, repo, opts)
5934 displayer.show(repo['tip'])
5934 displayer.show(repo['tip'])
5935 displayer.close()
5935 displayer.close()
5936
5936
5937 @command('unbundle',
5937 @command('unbundle',
5938 [('u', 'update', None,
5938 [('u', 'update', None,
5939 _('update to new branch head if changesets were unbundled'))],
5939 _('update to new branch head if changesets were unbundled'))],
5940 _('[-u] FILE...'))
5940 _('[-u] FILE...'))
5941 def unbundle(ui, repo, fname1, *fnames, **opts):
5941 def unbundle(ui, repo, fname1, *fnames, **opts):
5942 """apply one or more changegroup files
5942 """apply one or more changegroup files
5943
5943
5944 Apply one or more compressed changegroup files generated by the
5944 Apply one or more compressed changegroup files generated by the
5945 bundle command.
5945 bundle command.
5946
5946
5947 Returns 0 on success, 1 if an update has unresolved files.
5947 Returns 0 on success, 1 if an update has unresolved files.
5948 """
5948 """
5949 fnames = (fname1,) + fnames
5949 fnames = (fname1,) + fnames
5950
5950
5951 lock = repo.lock()
5951 lock = repo.lock()
5952 try:
5952 try:
5953 for fname in fnames:
5953 for fname in fnames:
5954 f = hg.openpath(ui, fname)
5954 f = hg.openpath(ui, fname)
5955 gen = exchange.readbundle(ui, f, fname)
5955 gen = exchange.readbundle(ui, f, fname)
5956 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
5956 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
5957 'bundle:' + fname)
5957 'bundle:' + fname)
5958 finally:
5958 finally:
5959 lock.release()
5959 lock.release()
5960
5960
5961 return postincoming(ui, repo, modheads, opts.get('update'), None)
5961 return postincoming(ui, repo, modheads, opts.get('update'), None)
5962
5962
5963 @command('^update|up|checkout|co',
5963 @command('^update|up|checkout|co',
5964 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5964 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5965 ('c', 'check', None,
5965 ('c', 'check', None,
5966 _('update across branches if no uncommitted changes')),
5966 _('update across branches if no uncommitted changes')),
5967 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5967 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5968 ('r', 'rev', '', _('revision'), _('REV'))
5968 ('r', 'rev', '', _('revision'), _('REV'))
5969 ] + mergetoolopts,
5969 ] + mergetoolopts,
5970 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5970 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5971 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5971 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5972 tool=None):
5972 tool=None):
5973 """update working directory (or switch revisions)
5973 """update working directory (or switch revisions)
5974
5974
5975 Update the repository's working directory to the specified
5975 Update the repository's working directory to the specified
5976 changeset. If no changeset is specified, update to the tip of the
5976 changeset. If no changeset is specified, update to the tip of the
5977 current named branch and move the current bookmark (see :hg:`help
5977 current named branch and move the current bookmark (see :hg:`help
5978 bookmarks`).
5978 bookmarks`).
5979
5979
5980 Update sets the working directory's parent revision to the specified
5980 Update sets the working directory's parent revision to the specified
5981 changeset (see :hg:`help parents`).
5981 changeset (see :hg:`help parents`).
5982
5982
5983 If the changeset is not a descendant or ancestor of the working
5983 If the changeset is not a descendant or ancestor of the working
5984 directory's parent, the update is aborted. With the -c/--check
5984 directory's parent, the update is aborted. With the -c/--check
5985 option, the working directory is checked for uncommitted changes; if
5985 option, the working directory is checked for uncommitted changes; if
5986 none are found, the working directory is updated to the specified
5986 none are found, the working directory is updated to the specified
5987 changeset.
5987 changeset.
5988
5988
5989 .. container:: verbose
5989 .. container:: verbose
5990
5990
5991 The following rules apply when the working directory contains
5991 The following rules apply when the working directory contains
5992 uncommitted changes:
5992 uncommitted changes:
5993
5993
5994 1. If neither -c/--check nor -C/--clean is specified, and if
5994 1. If neither -c/--check nor -C/--clean is specified, and if
5995 the requested changeset is an ancestor or descendant of
5995 the requested changeset is an ancestor or descendant of
5996 the working directory's parent, the uncommitted changes
5996 the working directory's parent, the uncommitted changes
5997 are merged into the requested changeset and the merged
5997 are merged into the requested changeset and the merged
5998 result is left uncommitted. If the requested changeset is
5998 result is left uncommitted. If the requested changeset is
5999 not an ancestor or descendant (that is, it is on another
5999 not an ancestor or descendant (that is, it is on another
6000 branch), the update is aborted and the uncommitted changes
6000 branch), the update is aborted and the uncommitted changes
6001 are preserved.
6001 are preserved.
6002
6002
6003 2. With the -c/--check option, the update is aborted and the
6003 2. With the -c/--check option, the update is aborted and the
6004 uncommitted changes are preserved.
6004 uncommitted changes are preserved.
6005
6005
6006 3. With the -C/--clean option, uncommitted changes are discarded and
6006 3. With the -C/--clean option, uncommitted changes are discarded and
6007 the working directory is updated to the requested changeset.
6007 the working directory is updated to the requested changeset.
6008
6008
6009 To cancel an uncommitted merge (and lose your changes), use
6009 To cancel an uncommitted merge (and lose your changes), use
6010 :hg:`update --clean .`.
6010 :hg:`update --clean .`.
6011
6011
6012 Use null as the changeset to remove the working directory (like
6012 Use null as the changeset to remove the working directory (like
6013 :hg:`clone -U`).
6013 :hg:`clone -U`).
6014
6014
6015 If you want to revert just one file to an older revision, use
6015 If you want to revert just one file to an older revision, use
6016 :hg:`revert [-r REV] NAME`.
6016 :hg:`revert [-r REV] NAME`.
6017
6017
6018 See :hg:`help dates` for a list of formats valid for -d/--date.
6018 See :hg:`help dates` for a list of formats valid for -d/--date.
6019
6019
6020 Returns 0 on success, 1 if there are unresolved files.
6020 Returns 0 on success, 1 if there are unresolved files.
6021 """
6021 """
6022 if rev and node:
6022 if rev and node:
6023 raise util.Abort(_("please specify just one revision"))
6023 raise util.Abort(_("please specify just one revision"))
6024
6024
6025 if rev is None or rev == '':
6025 if rev is None or rev == '':
6026 rev = node
6026 rev = node
6027
6027
6028 cmdutil.clearunfinished(repo)
6028 cmdutil.clearunfinished(repo)
6029
6029
6030 # with no argument, we also move the current bookmark, if any
6030 # with no argument, we also move the current bookmark, if any
6031 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6031 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6032
6032
6033 # if we defined a bookmark, we have to remember the original bookmark name
6033 # if we defined a bookmark, we have to remember the original bookmark name
6034 brev = rev
6034 brev = rev
6035 rev = scmutil.revsingle(repo, rev, rev).rev()
6035 rev = scmutil.revsingle(repo, rev, rev).rev()
6036
6036
6037 if check and clean:
6037 if check and clean:
6038 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6038 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6039
6039
6040 if date:
6040 if date:
6041 if rev is not None:
6041 if rev is not None:
6042 raise util.Abort(_("you can't specify a revision and a date"))
6042 raise util.Abort(_("you can't specify a revision and a date"))
6043 rev = cmdutil.finddate(ui, repo, date)
6043 rev = cmdutil.finddate(ui, repo, date)
6044
6044
6045 if check:
6045 if check:
6046 c = repo[None]
6046 c = repo[None]
6047 if c.dirty(merge=False, branch=False, missing=True):
6047 if c.dirty(merge=False, branch=False, missing=True):
6048 raise util.Abort(_("uncommitted changes"))
6048 raise util.Abort(_("uncommitted changes"))
6049 if rev is None:
6049 if rev is None:
6050 rev = repo[repo[None].branch()].rev()
6050 rev = repo[repo[None].branch()].rev()
6051 mergemod._checkunknown(repo, repo[None], repo[rev])
6051 mergemod._checkunknown(repo, repo[None], repo[rev])
6052
6052
6053 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6053 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6054
6054
6055 if clean:
6055 if clean:
6056 ret = hg.clean(repo, rev)
6056 ret = hg.clean(repo, rev)
6057 else:
6057 else:
6058 ret = hg.update(repo, rev)
6058 ret = hg.update(repo, rev)
6059
6059
6060 if not ret and movemarkfrom:
6060 if not ret and movemarkfrom:
6061 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6061 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6062 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6062 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6063 elif brev in repo._bookmarks:
6063 elif brev in repo._bookmarks:
6064 bookmarks.setcurrent(repo, brev)
6064 bookmarks.setcurrent(repo, brev)
6065 ui.status(_("(activating bookmark %s)\n") % brev)
6065 ui.status(_("(activating bookmark %s)\n") % brev)
6066 elif brev:
6066 elif brev:
6067 if repo._bookmarkcurrent:
6067 if repo._bookmarkcurrent:
6068 ui.status(_("(leaving bookmark %s)\n") %
6068 ui.status(_("(leaving bookmark %s)\n") %
6069 repo._bookmarkcurrent)
6069 repo._bookmarkcurrent)
6070 bookmarks.unsetcurrent(repo)
6070 bookmarks.unsetcurrent(repo)
6071
6071
6072 return ret
6072 return ret
6073
6073
6074 @command('verify', [])
6074 @command('verify', [])
6075 def verify(ui, repo):
6075 def verify(ui, repo):
6076 """verify the integrity of the repository
6076 """verify the integrity of the repository
6077
6077
6078 Verify the integrity of the current repository.
6078 Verify the integrity of the current repository.
6079
6079
6080 This will perform an extensive check of the repository's
6080 This will perform an extensive check of the repository's
6081 integrity, validating the hashes and checksums of each entry in
6081 integrity, validating the hashes and checksums of each entry in
6082 the changelog, manifest, and tracked files, as well as the
6082 the changelog, manifest, and tracked files, as well as the
6083 integrity of their crosslinks and indices.
6083 integrity of their crosslinks and indices.
6084
6084
6085 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6085 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6086 for more information about recovery from corruption of the
6086 for more information about recovery from corruption of the
6087 repository.
6087 repository.
6088
6088
6089 Returns 0 on success, 1 if errors are encountered.
6089 Returns 0 on success, 1 if errors are encountered.
6090 """
6090 """
6091 return hg.verify(repo)
6091 return hg.verify(repo)
6092
6092
6093 @command('version', [], norepo=True)
6093 @command('version', [], norepo=True)
6094 def version_(ui):
6094 def version_(ui):
6095 """output version and copyright information"""
6095 """output version and copyright information"""
6096 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6096 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6097 % util.version())
6097 % util.version())
6098 ui.status(_(
6098 ui.status(_(
6099 "(see http://mercurial.selenic.com for more information)\n"
6099 "(see http://mercurial.selenic.com for more information)\n"
6100 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6100 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6101 "This is free software; see the source for copying conditions. "
6101 "This is free software; see the source for copying conditions. "
6102 "There is NO\nwarranty; "
6102 "There is NO\nwarranty; "
6103 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6103 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6104 ))
6104 ))
6105
6105
6106 ui.note(_("\nEnabled extensions:\n\n"))
6106 ui.note(_("\nEnabled extensions:\n\n"))
6107 if ui.verbose:
6107 if ui.verbose:
6108 # format names and versions into columns
6108 # format names and versions into columns
6109 names = []
6109 names = []
6110 vers = []
6110 vers = []
6111 for name, module in extensions.extensions():
6111 for name, module in extensions.extensions():
6112 names.append(name)
6112 names.append(name)
6113 vers.append(extensions.moduleversion(module))
6113 vers.append(extensions.moduleversion(module))
6114 if names:
6114 if names:
6115 maxnamelen = max(len(n) for n in names)
6115 maxnamelen = max(len(n) for n in names)
6116 for i, name in enumerate(names):
6116 for i, name in enumerate(names):
6117 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6117 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,1450 +1,1457 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end 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 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 # import stuff from node for others to import from revlog
14 # import stuff from node for others to import from revlog
15 from node import bin, hex, nullid, nullrev
15 from node import bin, hex, nullid, nullrev
16 from i18n import _
16 from i18n import _
17 import ancestor, mdiff, parsers, error, util, templatefilters
17 import ancestor, mdiff, parsers, error, util, templatefilters
18 import struct, zlib, errno
18 import struct, zlib, errno
19
19
20 _pack = struct.pack
20 _pack = struct.pack
21 _unpack = struct.unpack
21 _unpack = struct.unpack
22 _compress = zlib.compress
22 _compress = zlib.compress
23 _decompress = zlib.decompress
23 _decompress = zlib.decompress
24 _sha = util.sha1
24 _sha = util.sha1
25
25
26 # revlog header flags
26 # revlog header flags
27 REVLOGV0 = 0
27 REVLOGV0 = 0
28 REVLOGNG = 1
28 REVLOGNG = 1
29 REVLOGNGINLINEDATA = (1 << 16)
29 REVLOGNGINLINEDATA = (1 << 16)
30 REVLOGGENERALDELTA = (1 << 17)
30 REVLOGGENERALDELTA = (1 << 17)
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
35
35
36 # revlog index flags
36 # revlog index flags
37 REVIDX_KNOWN_FLAGS = 0
37 REVIDX_KNOWN_FLAGS = 0
38
38
39 # max size of revlog with inline data
39 # max size of revlog with inline data
40 _maxinline = 131072
40 _maxinline = 131072
41 _chunksize = 1048576
41 _chunksize = 1048576
42
42
43 RevlogError = error.RevlogError
43 RevlogError = error.RevlogError
44 LookupError = error.LookupError
44 LookupError = error.LookupError
45
45
46 def getoffset(q):
46 def getoffset(q):
47 return int(q >> 16)
47 return int(q >> 16)
48
48
49 def gettype(q):
49 def gettype(q):
50 return int(q & 0xFFFF)
50 return int(q & 0xFFFF)
51
51
52 def offset_type(offset, type):
52 def offset_type(offset, type):
53 return long(long(offset) << 16 | type)
53 return long(long(offset) << 16 | type)
54
54
55 nullhash = _sha(nullid)
55 nullhash = _sha(nullid)
56
56
57 def hash(text, p1, p2):
57 def hash(text, p1, p2):
58 """generate a hash from the given text and its parent hashes
58 """generate a hash from the given text and its parent hashes
59
59
60 This hash combines both the current file contents and its history
60 This hash combines both the current file contents and its history
61 in a manner that makes it easy to distinguish nodes with the same
61 in a manner that makes it easy to distinguish nodes with the same
62 content in the revision graph.
62 content in the revision graph.
63 """
63 """
64 # As of now, if one of the parent node is null, p2 is null
64 # As of now, if one of the parent node is null, p2 is null
65 if p2 == nullid:
65 if p2 == nullid:
66 # deep copy of a hash is faster than creating one
66 # deep copy of a hash is faster than creating one
67 s = nullhash.copy()
67 s = nullhash.copy()
68 s.update(p1)
68 s.update(p1)
69 else:
69 else:
70 # none of the parent nodes are nullid
70 # none of the parent nodes are nullid
71 l = [p1, p2]
71 l = [p1, p2]
72 l.sort()
72 l.sort()
73 s = _sha(l[0])
73 s = _sha(l[0])
74 s.update(l[1])
74 s.update(l[1])
75 s.update(text)
75 s.update(text)
76 return s.digest()
76 return s.digest()
77
77
78 def decompress(bin):
78 def decompress(bin):
79 """ decompress the given input """
79 """ decompress the given input """
80 if not bin:
80 if not bin:
81 return bin
81 return bin
82 t = bin[0]
82 t = bin[0]
83 if t == '\0':
83 if t == '\0':
84 return bin
84 return bin
85 if t == 'x':
85 if t == 'x':
86 try:
86 try:
87 return _decompress(bin)
87 return _decompress(bin)
88 except zlib.error, e:
88 except zlib.error, e:
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
90 if t == 'u':
90 if t == 'u':
91 return bin[1:]
91 return bin[1:]
92 raise RevlogError(_("unknown compression type %r") % t)
92 raise RevlogError(_("unknown compression type %r") % t)
93
93
94 # index v0:
94 # index v0:
95 # 4 bytes: offset
95 # 4 bytes: offset
96 # 4 bytes: compressed length
96 # 4 bytes: compressed length
97 # 4 bytes: base rev
97 # 4 bytes: base rev
98 # 4 bytes: link rev
98 # 4 bytes: link rev
99 # 32 bytes: parent 1 nodeid
99 # 32 bytes: parent 1 nodeid
100 # 32 bytes: parent 2 nodeid
100 # 32 bytes: parent 2 nodeid
101 # 32 bytes: nodeid
101 # 32 bytes: nodeid
102 indexformatv0 = ">4l20s20s20s"
102 indexformatv0 = ">4l20s20s20s"
103 v0shaoffset = 56
103 v0shaoffset = 56
104
104
105 class revlogoldio(object):
105 class revlogoldio(object):
106 def __init__(self):
106 def __init__(self):
107 self.size = struct.calcsize(indexformatv0)
107 self.size = struct.calcsize(indexformatv0)
108
108
109 def parseindex(self, data, inline):
109 def parseindex(self, data, inline):
110 s = self.size
110 s = self.size
111 index = []
111 index = []
112 nodemap = {nullid: nullrev}
112 nodemap = {nullid: nullrev}
113 n = off = 0
113 n = off = 0
114 l = len(data)
114 l = len(data)
115 while off + s <= l:
115 while off + s <= l:
116 cur = data[off:off + s]
116 cur = data[off:off + s]
117 off += s
117 off += s
118 e = _unpack(indexformatv0, cur)
118 e = _unpack(indexformatv0, cur)
119 # transform to revlogv1 format
119 # transform to revlogv1 format
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
122 index.append(e2)
122 index.append(e2)
123 nodemap[e[6]] = n
123 nodemap[e[6]] = n
124 n += 1
124 n += 1
125
125
126 # add the magic null revision at -1
126 # add the magic null revision at -1
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
128
128
129 return index, nodemap, None
129 return index, nodemap, None
130
130
131 def packentry(self, entry, node, version, rev):
131 def packentry(self, entry, node, version, rev):
132 if gettype(entry[0]):
132 if gettype(entry[0]):
133 raise RevlogError(_("index entry flags need RevlogNG"))
133 raise RevlogError(_("index entry flags need RevlogNG"))
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
135 node(entry[5]), node(entry[6]), entry[7])
135 node(entry[5]), node(entry[6]), entry[7])
136 return _pack(indexformatv0, *e2)
136 return _pack(indexformatv0, *e2)
137
137
138 # index ng:
138 # index ng:
139 # 6 bytes: offset
139 # 6 bytes: offset
140 # 2 bytes: flags
140 # 2 bytes: flags
141 # 4 bytes: compressed length
141 # 4 bytes: compressed length
142 # 4 bytes: uncompressed length
142 # 4 bytes: uncompressed length
143 # 4 bytes: base rev
143 # 4 bytes: base rev
144 # 4 bytes: link rev
144 # 4 bytes: link rev
145 # 4 bytes: parent 1 rev
145 # 4 bytes: parent 1 rev
146 # 4 bytes: parent 2 rev
146 # 4 bytes: parent 2 rev
147 # 32 bytes: nodeid
147 # 32 bytes: nodeid
148 indexformatng = ">Qiiiiii20s12x"
148 indexformatng = ">Qiiiiii20s12x"
149 ngshaoffset = 32
149 ngshaoffset = 32
150 versionformat = ">I"
150 versionformat = ">I"
151
151
152 class revlogio(object):
152 class revlogio(object):
153 def __init__(self):
153 def __init__(self):
154 self.size = struct.calcsize(indexformatng)
154 self.size = struct.calcsize(indexformatng)
155
155
156 def parseindex(self, data, inline):
156 def parseindex(self, data, inline):
157 # call the C implementation to parse the index data
157 # call the C implementation to parse the index data
158 index, cache = parsers.parse_index2(data, inline)
158 index, cache = parsers.parse_index2(data, inline)
159 return index, getattr(index, 'nodemap', None), cache
159 return index, getattr(index, 'nodemap', None), cache
160
160
161 def packentry(self, entry, node, version, rev):
161 def packentry(self, entry, node, version, rev):
162 p = _pack(indexformatng, *entry)
162 p = _pack(indexformatng, *entry)
163 if rev == 0:
163 if rev == 0:
164 p = _pack(versionformat, version) + p[4:]
164 p = _pack(versionformat, version) + p[4:]
165 return p
165 return p
166
166
167 class revlog(object):
167 class revlog(object):
168 """
168 """
169 the underlying revision storage object
169 the underlying revision storage object
170
170
171 A revlog consists of two parts, an index and the revision data.
171 A revlog consists of two parts, an index and the revision data.
172
172
173 The index is a file with a fixed record size containing
173 The index is a file with a fixed record size containing
174 information on each revision, including its nodeid (hash), the
174 information on each revision, including its nodeid (hash), the
175 nodeids of its parents, the position and offset of its data within
175 nodeids of its parents, the position and offset of its data within
176 the data file, and the revision it's based on. Finally, each entry
176 the data file, and the revision it's based on. Finally, each entry
177 contains a linkrev entry that can serve as a pointer to external
177 contains a linkrev entry that can serve as a pointer to external
178 data.
178 data.
179
179
180 The revision data itself is a linear collection of data chunks.
180 The revision data itself is a linear collection of data chunks.
181 Each chunk represents a revision and is usually represented as a
181 Each chunk represents a revision and is usually represented as a
182 delta against the previous chunk. To bound lookup time, runs of
182 delta against the previous chunk. To bound lookup time, runs of
183 deltas are limited to about 2 times the length of the original
183 deltas are limited to about 2 times the length of the original
184 version data. This makes retrieval of a version proportional to
184 version data. This makes retrieval of a version proportional to
185 its size, or O(1) relative to the number of revisions.
185 its size, or O(1) relative to the number of revisions.
186
186
187 Both pieces of the revlog are written to in an append-only
187 Both pieces of the revlog are written to in an append-only
188 fashion, which means we never need to rewrite a file to insert or
188 fashion, which means we never need to rewrite a file to insert or
189 remove data, and can use some simple techniques to avoid the need
189 remove data, and can use some simple techniques to avoid the need
190 for locking while reading.
190 for locking while reading.
191 """
191 """
192 def __init__(self, opener, indexfile):
192 def __init__(self, opener, indexfile):
193 """
193 """
194 create a revlog object
194 create a revlog object
195
195
196 opener is a function that abstracts the file opening operation
196 opener is a function that abstracts the file opening operation
197 and can be used to implement COW semantics or the like.
197 and can be used to implement COW semantics or the like.
198 """
198 """
199 self.indexfile = indexfile
199 self.indexfile = indexfile
200 self.datafile = indexfile[:-2] + ".d"
200 self.datafile = indexfile[:-2] + ".d"
201 self.opener = opener
201 self.opener = opener
202 self._cache = None
202 self._cache = None
203 self._basecache = None
203 self._basecache = None
204 self._chunkcache = (0, '')
204 self._chunkcache = (0, '')
205 self._chunkcachesize = 65536
205 self._chunkcachesize = 65536
206 self.index = []
206 self.index = []
207 self._pcache = {}
207 self._pcache = {}
208 self._nodecache = {nullid: nullrev}
208 self._nodecache = {nullid: nullrev}
209 self._nodepos = None
209 self._nodepos = None
210
210
211 v = REVLOG_DEFAULT_VERSION
211 v = REVLOG_DEFAULT_VERSION
212 opts = getattr(opener, 'options', None)
212 opts = getattr(opener, 'options', None)
213 if opts is not None:
213 if opts is not None:
214 if 'revlogv1' in opts:
214 if 'revlogv1' in opts:
215 if 'generaldelta' in opts:
215 if 'generaldelta' in opts:
216 v |= REVLOGGENERALDELTA
216 v |= REVLOGGENERALDELTA
217 else:
217 else:
218 v = 0
218 v = 0
219 if 'chunkcachesize' in opts:
219 if 'chunkcachesize' in opts:
220 self._chunkcachesize = opts['chunkcachesize']
220 self._chunkcachesize = opts['chunkcachesize']
221
221
222 if self._chunkcachesize <= 0:
222 if self._chunkcachesize <= 0:
223 raise RevlogError(_('revlog chunk cache size %r is not greater '
223 raise RevlogError(_('revlog chunk cache size %r is not greater '
224 'than 0') % self._chunkcachesize)
224 'than 0') % self._chunkcachesize)
225 elif self._chunkcachesize & (self._chunkcachesize - 1):
225 elif self._chunkcachesize & (self._chunkcachesize - 1):
226 raise RevlogError(_('revlog chunk cache size %r is not a power '
226 raise RevlogError(_('revlog chunk cache size %r is not a power '
227 'of 2') % self._chunkcachesize)
227 'of 2') % self._chunkcachesize)
228
228
229 i = ''
229 i = ''
230 self._initempty = True
230 self._initempty = True
231 try:
231 try:
232 f = self.opener(self.indexfile)
232 f = self.opener(self.indexfile)
233 i = f.read()
233 i = f.read()
234 f.close()
234 f.close()
235 if len(i) > 0:
235 if len(i) > 0:
236 v = struct.unpack(versionformat, i[:4])[0]
236 v = struct.unpack(versionformat, i[:4])[0]
237 self._initempty = False
237 self._initempty = False
238 except IOError, inst:
238 except IOError, inst:
239 if inst.errno != errno.ENOENT:
239 if inst.errno != errno.ENOENT:
240 raise
240 raise
241
241
242 self.version = v
242 self.version = v
243 self._inline = v & REVLOGNGINLINEDATA
243 self._inline = v & REVLOGNGINLINEDATA
244 self._generaldelta = v & REVLOGGENERALDELTA
244 self._generaldelta = v & REVLOGGENERALDELTA
245 flags = v & ~0xFFFF
245 flags = v & ~0xFFFF
246 fmt = v & 0xFFFF
246 fmt = v & 0xFFFF
247 if fmt == REVLOGV0 and flags:
247 if fmt == REVLOGV0 and flags:
248 raise RevlogError(_("index %s unknown flags %#04x for format v0")
248 raise RevlogError(_("index %s unknown flags %#04x for format v0")
249 % (self.indexfile, flags >> 16))
249 % (self.indexfile, flags >> 16))
250 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
250 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
251 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
251 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
252 % (self.indexfile, flags >> 16))
252 % (self.indexfile, flags >> 16))
253 elif fmt > REVLOGNG:
253 elif fmt > REVLOGNG:
254 raise RevlogError(_("index %s unknown format %d")
254 raise RevlogError(_("index %s unknown format %d")
255 % (self.indexfile, fmt))
255 % (self.indexfile, fmt))
256
256
257 self._io = revlogio()
257 self._io = revlogio()
258 if self.version == REVLOGV0:
258 if self.version == REVLOGV0:
259 self._io = revlogoldio()
259 self._io = revlogoldio()
260 try:
260 try:
261 d = self._io.parseindex(i, self._inline)
261 d = self._io.parseindex(i, self._inline)
262 except (ValueError, IndexError):
262 except (ValueError, IndexError):
263 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
263 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
264 self.index, nodemap, self._chunkcache = d
264 self.index, nodemap, self._chunkcache = d
265 if nodemap is not None:
265 if nodemap is not None:
266 self.nodemap = self._nodecache = nodemap
266 self.nodemap = self._nodecache = nodemap
267 if not self._chunkcache:
267 if not self._chunkcache:
268 self._chunkclear()
268 self._chunkclear()
269
269
270 def tip(self):
270 def tip(self):
271 return self.node(len(self.index) - 2)
271 return self.node(len(self.index) - 2)
272 def __len__(self):
272 def __len__(self):
273 return len(self.index) - 1
273 return len(self.index) - 1
274 def __iter__(self):
274 def __iter__(self):
275 return iter(xrange(len(self)))
275 return iter(xrange(len(self)))
276 def revs(self, start=0, stop=None):
276 def revs(self, start=0, stop=None):
277 """iterate over all rev in this revlog (from start to stop)"""
277 """iterate over all rev in this revlog (from start to stop)"""
278 step = 1
278 step = 1
279 if stop is not None:
279 if stop is not None:
280 if start > stop:
280 if start > stop:
281 step = -1
281 step = -1
282 stop += step
282 stop += step
283 else:
283 else:
284 stop = len(self)
284 stop = len(self)
285 return xrange(start, stop, step)
285 return xrange(start, stop, step)
286
286
287 @util.propertycache
287 @util.propertycache
288 def nodemap(self):
288 def nodemap(self):
289 self.rev(self.node(0))
289 self.rev(self.node(0))
290 return self._nodecache
290 return self._nodecache
291
291
292 def hasnode(self, node):
292 def hasnode(self, node):
293 try:
293 try:
294 self.rev(node)
294 self.rev(node)
295 return True
295 return True
296 except KeyError:
296 except KeyError:
297 return False
297 return False
298
298
299 def clearcaches(self):
299 def clearcaches(self):
300 try:
300 try:
301 self._nodecache.clearcaches()
301 self._nodecache.clearcaches()
302 except AttributeError:
302 except AttributeError:
303 self._nodecache = {nullid: nullrev}
303 self._nodecache = {nullid: nullrev}
304 self._nodepos = None
304 self._nodepos = None
305
305
306 def rev(self, node):
306 def rev(self, node):
307 try:
307 try:
308 return self._nodecache[node]
308 return self._nodecache[node]
309 except TypeError:
309 except TypeError:
310 raise
310 raise
311 except RevlogError:
311 except RevlogError:
312 # parsers.c radix tree lookup failed
312 # parsers.c radix tree lookup failed
313 raise LookupError(node, self.indexfile, _('no node'))
313 raise LookupError(node, self.indexfile, _('no node'))
314 except KeyError:
314 except KeyError:
315 # pure python cache lookup failed
315 # pure python cache lookup failed
316 n = self._nodecache
316 n = self._nodecache
317 i = self.index
317 i = self.index
318 p = self._nodepos
318 p = self._nodepos
319 if p is None:
319 if p is None:
320 p = len(i) - 2
320 p = len(i) - 2
321 for r in xrange(p, -1, -1):
321 for r in xrange(p, -1, -1):
322 v = i[r][7]
322 v = i[r][7]
323 n[v] = r
323 n[v] = r
324 if v == node:
324 if v == node:
325 self._nodepos = r - 1
325 self._nodepos = r - 1
326 return r
326 return r
327 raise LookupError(node, self.indexfile, _('no node'))
327 raise LookupError(node, self.indexfile, _('no node'))
328
328
329 def node(self, rev):
329 def node(self, rev):
330 return self.index[rev][7]
330 return self.index[rev][7]
331 def linkrev(self, rev):
331 def linkrev(self, rev):
332 return self.index[rev][4]
332 return self.index[rev][4]
333 def parents(self, node):
333 def parents(self, node):
334 i = self.index
334 i = self.index
335 d = i[self.rev(node)]
335 d = i[self.rev(node)]
336 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
336 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
337 def parentrevs(self, rev):
337 def parentrevs(self, rev):
338 return self.index[rev][5:7]
338 return self.index[rev][5:7]
339 def start(self, rev):
339 def start(self, rev):
340 return int(self.index[rev][0] >> 16)
340 return int(self.index[rev][0] >> 16)
341 def end(self, rev):
341 def end(self, rev):
342 return self.start(rev) + self.length(rev)
342 return self.start(rev) + self.length(rev)
343 def length(self, rev):
343 def length(self, rev):
344 return self.index[rev][1]
344 return self.index[rev][1]
345 def chainbase(self, rev):
345 def chainbase(self, rev):
346 index = self.index
346 index = self.index
347 base = index[rev][3]
347 base = index[rev][3]
348 while base != rev:
348 while base != rev:
349 rev = base
349 rev = base
350 base = index[rev][3]
350 base = index[rev][3]
351 return base
351 return base
352 def flags(self, rev):
352 def flags(self, rev):
353 return self.index[rev][0] & 0xFFFF
353 return self.index[rev][0] & 0xFFFF
354 def rawsize(self, rev):
354 def rawsize(self, rev):
355 """return the length of the uncompressed text for a given revision"""
355 """return the length of the uncompressed text for a given revision"""
356 l = self.index[rev][2]
356 l = self.index[rev][2]
357 if l >= 0:
357 if l >= 0:
358 return l
358 return l
359
359
360 t = self.revision(self.node(rev))
360 t = self.revision(self.node(rev))
361 return len(t)
361 return len(t)
362 size = rawsize
362 size = rawsize
363
363
364 def ancestors(self, revs, stoprev=0, inclusive=False):
364 def ancestors(self, revs, stoprev=0, inclusive=False):
365 """Generate the ancestors of 'revs' in reverse topological order.
365 """Generate the ancestors of 'revs' in reverse topological order.
366 Does not generate revs lower than stoprev.
366 Does not generate revs lower than stoprev.
367
367
368 See the documentation for ancestor.lazyancestors for more details."""
368 See the documentation for ancestor.lazyancestors for more details."""
369
369
370 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
370 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
371 inclusive=inclusive)
371 inclusive=inclusive)
372
372
373 def descendants(self, revs):
373 def descendants(self, revs):
374 """Generate the descendants of 'revs' in revision order.
374 """Generate the descendants of 'revs' in revision order.
375
375
376 Yield a sequence of revision numbers starting with a child of
376 Yield a sequence of revision numbers starting with a child of
377 some rev in revs, i.e., each revision is *not* considered a
377 some rev in revs, i.e., each revision is *not* considered a
378 descendant of itself. Results are ordered by revision number (a
378 descendant of itself. Results are ordered by revision number (a
379 topological sort)."""
379 topological sort)."""
380 first = min(revs)
380 first = min(revs)
381 if first == nullrev:
381 if first == nullrev:
382 for i in self:
382 for i in self:
383 yield i
383 yield i
384 return
384 return
385
385
386 seen = set(revs)
386 seen = set(revs)
387 for i in self.revs(start=first + 1):
387 for i in self.revs(start=first + 1):
388 for x in self.parentrevs(i):
388 for x in self.parentrevs(i):
389 if x != nullrev and x in seen:
389 if x != nullrev and x in seen:
390 seen.add(i)
390 seen.add(i)
391 yield i
391 yield i
392 break
392 break
393
393
394 def findcommonmissing(self, common=None, heads=None):
394 def findcommonmissing(self, common=None, heads=None):
395 """Return a tuple of the ancestors of common and the ancestors of heads
395 """Return a tuple of the ancestors of common and the ancestors of heads
396 that are not ancestors of common. In revset terminology, we return the
396 that are not ancestors of common. In revset terminology, we return the
397 tuple:
397 tuple:
398
398
399 ::common, (::heads) - (::common)
399 ::common, (::heads) - (::common)
400
400
401 The list is sorted by revision number, meaning it is
401 The list is sorted by revision number, meaning it is
402 topologically sorted.
402 topologically sorted.
403
403
404 'heads' and 'common' are both lists of node IDs. If heads is
404 'heads' and 'common' are both lists of node IDs. If heads is
405 not supplied, uses all of the revlog's heads. If common is not
405 not supplied, uses all of the revlog's heads. If common is not
406 supplied, uses nullid."""
406 supplied, uses nullid."""
407 if common is None:
407 if common is None:
408 common = [nullid]
408 common = [nullid]
409 if heads is None:
409 if heads is None:
410 heads = self.heads()
410 heads = self.heads()
411
411
412 common = [self.rev(n) for n in common]
412 common = [self.rev(n) for n in common]
413 heads = [self.rev(n) for n in heads]
413 heads = [self.rev(n) for n in heads]
414
414
415 # we want the ancestors, but inclusive
415 # we want the ancestors, but inclusive
416 class lazyset(object):
416 class lazyset(object):
417 def __init__(self, lazyvalues):
417 def __init__(self, lazyvalues):
418 self.addedvalues = set()
418 self.addedvalues = set()
419 self.lazyvalues = lazyvalues
419 self.lazyvalues = lazyvalues
420
420
421 def __contains__(self, value):
421 def __contains__(self, value):
422 return value in self.addedvalues or value in self.lazyvalues
422 return value in self.addedvalues or value in self.lazyvalues
423
423
424 def __iter__(self):
424 def __iter__(self):
425 added = self.addedvalues
425 added = self.addedvalues
426 for r in added:
426 for r in added:
427 yield r
427 yield r
428 for r in self.lazyvalues:
428 for r in self.lazyvalues:
429 if not r in added:
429 if not r in added:
430 yield r
430 yield r
431
431
432 def add(self, value):
432 def add(self, value):
433 self.addedvalues.add(value)
433 self.addedvalues.add(value)
434
434
435 def update(self, values):
435 def update(self, values):
436 self.addedvalues.update(values)
436 self.addedvalues.update(values)
437
437
438 has = lazyset(self.ancestors(common))
438 has = lazyset(self.ancestors(common))
439 has.add(nullrev)
439 has.add(nullrev)
440 has.update(common)
440 has.update(common)
441
441
442 # take all ancestors from heads that aren't in has
442 # take all ancestors from heads that aren't in has
443 missing = set()
443 missing = set()
444 visit = util.deque(r for r in heads if r not in has)
444 visit = util.deque(r for r in heads if r not in has)
445 while visit:
445 while visit:
446 r = visit.popleft()
446 r = visit.popleft()
447 if r in missing:
447 if r in missing:
448 continue
448 continue
449 else:
449 else:
450 missing.add(r)
450 missing.add(r)
451 for p in self.parentrevs(r):
451 for p in self.parentrevs(r):
452 if p not in has:
452 if p not in has:
453 visit.append(p)
453 visit.append(p)
454 missing = list(missing)
454 missing = list(missing)
455 missing.sort()
455 missing.sort()
456 return has, [self.node(r) for r in missing]
456 return has, [self.node(r) for r in missing]
457
457
458 def findmissingrevs(self, common=None, heads=None):
458 def findmissingrevs(self, common=None, heads=None):
459 """Return the revision numbers of the ancestors of heads that
459 """Return the revision numbers of the ancestors of heads that
460 are not ancestors of common.
460 are not ancestors of common.
461
461
462 More specifically, return a list of revision numbers corresponding to
462 More specifically, return a list of revision numbers corresponding to
463 nodes N such that every N satisfies the following constraints:
463 nodes N such that every N satisfies the following constraints:
464
464
465 1. N is an ancestor of some node in 'heads'
465 1. N is an ancestor of some node in 'heads'
466 2. N is not an ancestor of any node in 'common'
466 2. N is not an ancestor of any node in 'common'
467
467
468 The list is sorted by revision number, meaning it is
468 The list is sorted by revision number, meaning it is
469 topologically sorted.
469 topologically sorted.
470
470
471 'heads' and 'common' are both lists of revision numbers. If heads is
471 'heads' and 'common' are both lists of revision numbers. If heads is
472 not supplied, uses all of the revlog's heads. If common is not
472 not supplied, uses all of the revlog's heads. If common is not
473 supplied, uses nullid."""
473 supplied, uses nullid."""
474 if common is None:
474 if common is None:
475 common = [nullrev]
475 common = [nullrev]
476 if heads is None:
476 if heads is None:
477 heads = self.headrevs()
477 heads = self.headrevs()
478
478
479 return ancestor.missingancestors(heads, common, self.parentrevs)
479 return ancestor.missingancestors(heads, common, self.parentrevs)
480
480
481 def findmissing(self, common=None, heads=None):
481 def findmissing(self, common=None, heads=None):
482 """Return the ancestors of heads that are not ancestors of common.
482 """Return the ancestors of heads that are not ancestors of common.
483
483
484 More specifically, return a list of nodes N such that every N
484 More specifically, return a list of nodes N such that every N
485 satisfies the following constraints:
485 satisfies the following constraints:
486
486
487 1. N is an ancestor of some node in 'heads'
487 1. N is an ancestor of some node in 'heads'
488 2. N is not an ancestor of any node in 'common'
488 2. N is not an ancestor of any node in 'common'
489
489
490 The list is sorted by revision number, meaning it is
490 The list is sorted by revision number, meaning it is
491 topologically sorted.
491 topologically sorted.
492
492
493 'heads' and 'common' are both lists of node IDs. If heads is
493 'heads' and 'common' are both lists of node IDs. If heads is
494 not supplied, uses all of the revlog's heads. If common is not
494 not supplied, uses all of the revlog's heads. If common is not
495 supplied, uses nullid."""
495 supplied, uses nullid."""
496 if common is None:
496 if common is None:
497 common = [nullid]
497 common = [nullid]
498 if heads is None:
498 if heads is None:
499 heads = self.heads()
499 heads = self.heads()
500
500
501 common = [self.rev(n) for n in common]
501 common = [self.rev(n) for n in common]
502 heads = [self.rev(n) for n in heads]
502 heads = [self.rev(n) for n in heads]
503
503
504 return [self.node(r) for r in
504 return [self.node(r) for r in
505 ancestor.missingancestors(heads, common, self.parentrevs)]
505 ancestor.missingancestors(heads, common, self.parentrevs)]
506
506
507 def nodesbetween(self, roots=None, heads=None):
507 def nodesbetween(self, roots=None, heads=None):
508 """Return a topological path from 'roots' to 'heads'.
508 """Return a topological path from 'roots' to 'heads'.
509
509
510 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
510 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
511 topologically sorted list of all nodes N that satisfy both of
511 topologically sorted list of all nodes N that satisfy both of
512 these constraints:
512 these constraints:
513
513
514 1. N is a descendant of some node in 'roots'
514 1. N is a descendant of some node in 'roots'
515 2. N is an ancestor of some node in 'heads'
515 2. N is an ancestor of some node in 'heads'
516
516
517 Every node is considered to be both a descendant and an ancestor
517 Every node is considered to be both a descendant and an ancestor
518 of itself, so every reachable node in 'roots' and 'heads' will be
518 of itself, so every reachable node in 'roots' and 'heads' will be
519 included in 'nodes'.
519 included in 'nodes'.
520
520
521 'outroots' is the list of reachable nodes in 'roots', i.e., the
521 'outroots' is the list of reachable nodes in 'roots', i.e., the
522 subset of 'roots' that is returned in 'nodes'. Likewise,
522 subset of 'roots' that is returned in 'nodes'. Likewise,
523 'outheads' is the subset of 'heads' that is also in 'nodes'.
523 'outheads' is the subset of 'heads' that is also in 'nodes'.
524
524
525 'roots' and 'heads' are both lists of node IDs. If 'roots' is
525 'roots' and 'heads' are both lists of node IDs. If 'roots' is
526 unspecified, uses nullid as the only root. If 'heads' is
526 unspecified, uses nullid as the only root. If 'heads' is
527 unspecified, uses list of all of the revlog's heads."""
527 unspecified, uses list of all of the revlog's heads."""
528 nonodes = ([], [], [])
528 nonodes = ([], [], [])
529 if roots is not None:
529 if roots is not None:
530 roots = list(roots)
530 roots = list(roots)
531 if not roots:
531 if not roots:
532 return nonodes
532 return nonodes
533 lowestrev = min([self.rev(n) for n in roots])
533 lowestrev = min([self.rev(n) for n in roots])
534 else:
534 else:
535 roots = [nullid] # Everybody's a descendant of nullid
535 roots = [nullid] # Everybody's a descendant of nullid
536 lowestrev = nullrev
536 lowestrev = nullrev
537 if (lowestrev == nullrev) and (heads is None):
537 if (lowestrev == nullrev) and (heads is None):
538 # We want _all_ the nodes!
538 # We want _all_ the nodes!
539 return ([self.node(r) for r in self], [nullid], list(self.heads()))
539 return ([self.node(r) for r in self], [nullid], list(self.heads()))
540 if heads is None:
540 if heads is None:
541 # All nodes are ancestors, so the latest ancestor is the last
541 # All nodes are ancestors, so the latest ancestor is the last
542 # node.
542 # node.
543 highestrev = len(self) - 1
543 highestrev = len(self) - 1
544 # Set ancestors to None to signal that every node is an ancestor.
544 # Set ancestors to None to signal that every node is an ancestor.
545 ancestors = None
545 ancestors = None
546 # Set heads to an empty dictionary for later discovery of heads
546 # Set heads to an empty dictionary for later discovery of heads
547 heads = {}
547 heads = {}
548 else:
548 else:
549 heads = list(heads)
549 heads = list(heads)
550 if not heads:
550 if not heads:
551 return nonodes
551 return nonodes
552 ancestors = set()
552 ancestors = set()
553 # Turn heads into a dictionary so we can remove 'fake' heads.
553 # Turn heads into a dictionary so we can remove 'fake' heads.
554 # Also, later we will be using it to filter out the heads we can't
554 # Also, later we will be using it to filter out the heads we can't
555 # find from roots.
555 # find from roots.
556 heads = dict.fromkeys(heads, False)
556 heads = dict.fromkeys(heads, False)
557 # Start at the top and keep marking parents until we're done.
557 # Start at the top and keep marking parents until we're done.
558 nodestotag = set(heads)
558 nodestotag = set(heads)
559 # Remember where the top was so we can use it as a limit later.
559 # Remember where the top was so we can use it as a limit later.
560 highestrev = max([self.rev(n) for n in nodestotag])
560 highestrev = max([self.rev(n) for n in nodestotag])
561 while nodestotag:
561 while nodestotag:
562 # grab a node to tag
562 # grab a node to tag
563 n = nodestotag.pop()
563 n = nodestotag.pop()
564 # Never tag nullid
564 # Never tag nullid
565 if n == nullid:
565 if n == nullid:
566 continue
566 continue
567 # A node's revision number represents its place in a
567 # A node's revision number represents its place in a
568 # topologically sorted list of nodes.
568 # topologically sorted list of nodes.
569 r = self.rev(n)
569 r = self.rev(n)
570 if r >= lowestrev:
570 if r >= lowestrev:
571 if n not in ancestors:
571 if n not in ancestors:
572 # If we are possibly a descendant of one of the roots
572 # If we are possibly a descendant of one of the roots
573 # and we haven't already been marked as an ancestor
573 # and we haven't already been marked as an ancestor
574 ancestors.add(n) # Mark as ancestor
574 ancestors.add(n) # Mark as ancestor
575 # Add non-nullid parents to list of nodes to tag.
575 # Add non-nullid parents to list of nodes to tag.
576 nodestotag.update([p for p in self.parents(n) if
576 nodestotag.update([p for p in self.parents(n) if
577 p != nullid])
577 p != nullid])
578 elif n in heads: # We've seen it before, is it a fake head?
578 elif n in heads: # We've seen it before, is it a fake head?
579 # So it is, real heads should not be the ancestors of
579 # So it is, real heads should not be the ancestors of
580 # any other heads.
580 # any other heads.
581 heads.pop(n)
581 heads.pop(n)
582 if not ancestors:
582 if not ancestors:
583 return nonodes
583 return nonodes
584 # Now that we have our set of ancestors, we want to remove any
584 # Now that we have our set of ancestors, we want to remove any
585 # roots that are not ancestors.
585 # roots that are not ancestors.
586
586
587 # If one of the roots was nullid, everything is included anyway.
587 # If one of the roots was nullid, everything is included anyway.
588 if lowestrev > nullrev:
588 if lowestrev > nullrev:
589 # But, since we weren't, let's recompute the lowest rev to not
589 # But, since we weren't, let's recompute the lowest rev to not
590 # include roots that aren't ancestors.
590 # include roots that aren't ancestors.
591
591
592 # Filter out roots that aren't ancestors of heads
592 # Filter out roots that aren't ancestors of heads
593 roots = [n for n in roots if n in ancestors]
593 roots = [n for n in roots if n in ancestors]
594 # Recompute the lowest revision
594 # Recompute the lowest revision
595 if roots:
595 if roots:
596 lowestrev = min([self.rev(n) for n in roots])
596 lowestrev = min([self.rev(n) for n in roots])
597 else:
597 else:
598 # No more roots? Return empty list
598 # No more roots? Return empty list
599 return nonodes
599 return nonodes
600 else:
600 else:
601 # We are descending from nullid, and don't need to care about
601 # We are descending from nullid, and don't need to care about
602 # any other roots.
602 # any other roots.
603 lowestrev = nullrev
603 lowestrev = nullrev
604 roots = [nullid]
604 roots = [nullid]
605 # Transform our roots list into a set.
605 # Transform our roots list into a set.
606 descendants = set(roots)
606 descendants = set(roots)
607 # Also, keep the original roots so we can filter out roots that aren't
607 # Also, keep the original roots so we can filter out roots that aren't
608 # 'real' roots (i.e. are descended from other roots).
608 # 'real' roots (i.e. are descended from other roots).
609 roots = descendants.copy()
609 roots = descendants.copy()
610 # Our topologically sorted list of output nodes.
610 # Our topologically sorted list of output nodes.
611 orderedout = []
611 orderedout = []
612 # Don't start at nullid since we don't want nullid in our output list,
612 # Don't start at nullid since we don't want nullid in our output list,
613 # and if nullid shows up in descendants, empty parents will look like
613 # and if nullid shows up in descendants, empty parents will look like
614 # they're descendants.
614 # they're descendants.
615 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
615 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
616 n = self.node(r)
616 n = self.node(r)
617 isdescendant = False
617 isdescendant = False
618 if lowestrev == nullrev: # Everybody is a descendant of nullid
618 if lowestrev == nullrev: # Everybody is a descendant of nullid
619 isdescendant = True
619 isdescendant = True
620 elif n in descendants:
620 elif n in descendants:
621 # n is already a descendant
621 # n is already a descendant
622 isdescendant = True
622 isdescendant = True
623 # This check only needs to be done here because all the roots
623 # This check only needs to be done here because all the roots
624 # will start being marked is descendants before the loop.
624 # will start being marked is descendants before the loop.
625 if n in roots:
625 if n in roots:
626 # If n was a root, check if it's a 'real' root.
626 # If n was a root, check if it's a 'real' root.
627 p = tuple(self.parents(n))
627 p = tuple(self.parents(n))
628 # If any of its parents are descendants, it's not a root.
628 # If any of its parents are descendants, it's not a root.
629 if (p[0] in descendants) or (p[1] in descendants):
629 if (p[0] in descendants) or (p[1] in descendants):
630 roots.remove(n)
630 roots.remove(n)
631 else:
631 else:
632 p = tuple(self.parents(n))
632 p = tuple(self.parents(n))
633 # A node is a descendant if either of its parents are
633 # A node is a descendant if either of its parents are
634 # descendants. (We seeded the dependents list with the roots
634 # descendants. (We seeded the dependents list with the roots
635 # up there, remember?)
635 # up there, remember?)
636 if (p[0] in descendants) or (p[1] in descendants):
636 if (p[0] in descendants) or (p[1] in descendants):
637 descendants.add(n)
637 descendants.add(n)
638 isdescendant = True
638 isdescendant = True
639 if isdescendant and ((ancestors is None) or (n in ancestors)):
639 if isdescendant and ((ancestors is None) or (n in ancestors)):
640 # Only include nodes that are both descendants and ancestors.
640 # Only include nodes that are both descendants and ancestors.
641 orderedout.append(n)
641 orderedout.append(n)
642 if (ancestors is not None) and (n in heads):
642 if (ancestors is not None) and (n in heads):
643 # We're trying to figure out which heads are reachable
643 # We're trying to figure out which heads are reachable
644 # from roots.
644 # from roots.
645 # Mark this head as having been reached
645 # Mark this head as having been reached
646 heads[n] = True
646 heads[n] = True
647 elif ancestors is None:
647 elif ancestors is None:
648 # Otherwise, we're trying to discover the heads.
648 # Otherwise, we're trying to discover the heads.
649 # Assume this is a head because if it isn't, the next step
649 # Assume this is a head because if it isn't, the next step
650 # will eventually remove it.
650 # will eventually remove it.
651 heads[n] = True
651 heads[n] = True
652 # But, obviously its parents aren't.
652 # But, obviously its parents aren't.
653 for p in self.parents(n):
653 for p in self.parents(n):
654 heads.pop(p, None)
654 heads.pop(p, None)
655 heads = [n for n, flag in heads.iteritems() if flag]
655 heads = [n for n, flag in heads.iteritems() if flag]
656 roots = list(roots)
656 roots = list(roots)
657 assert orderedout
657 assert orderedout
658 assert roots
658 assert roots
659 assert heads
659 assert heads
660 return (orderedout, roots, heads)
660 return (orderedout, roots, heads)
661
661
662 def headrevs(self):
662 def headrevs(self):
663 try:
663 try:
664 return self.index.headrevs()
664 return self.index.headrevs()
665 except AttributeError:
665 except AttributeError:
666 return self._headrevs()
666 return self._headrevs()
667
667
668 def _headrevs(self):
668 def _headrevs(self):
669 count = len(self)
669 count = len(self)
670 if not count:
670 if not count:
671 return [nullrev]
671 return [nullrev]
672 # we won't iter over filtered rev so nobody is a head at start
672 # we won't iter over filtered rev so nobody is a head at start
673 ishead = [0] * (count + 1)
673 ishead = [0] * (count + 1)
674 index = self.index
674 index = self.index
675 for r in self:
675 for r in self:
676 ishead[r] = 1 # I may be an head
676 ishead[r] = 1 # I may be an head
677 e = index[r]
677 e = index[r]
678 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
678 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
679 return [r for r, val in enumerate(ishead) if val]
679 return [r for r, val in enumerate(ishead) if val]
680
680
681 def heads(self, start=None, stop=None):
681 def heads(self, start=None, stop=None):
682 """return the list of all nodes that have no children
682 """return the list of all nodes that have no children
683
683
684 if start is specified, only heads that are descendants of
684 if start is specified, only heads that are descendants of
685 start will be returned
685 start will be returned
686 if stop is specified, it will consider all the revs from stop
686 if stop is specified, it will consider all the revs from stop
687 as if they had no children
687 as if they had no children
688 """
688 """
689 if start is None and stop is None:
689 if start is None and stop is None:
690 if not len(self):
690 if not len(self):
691 return [nullid]
691 return [nullid]
692 return [self.node(r) for r in self.headrevs()]
692 return [self.node(r) for r in self.headrevs()]
693
693
694 if start is None:
694 if start is None:
695 start = nullid
695 start = nullid
696 if stop is None:
696 if stop is None:
697 stop = []
697 stop = []
698 stoprevs = set([self.rev(n) for n in stop])
698 stoprevs = set([self.rev(n) for n in stop])
699 startrev = self.rev(start)
699 startrev = self.rev(start)
700 reachable = set((startrev,))
700 reachable = set((startrev,))
701 heads = set((startrev,))
701 heads = set((startrev,))
702
702
703 parentrevs = self.parentrevs
703 parentrevs = self.parentrevs
704 for r in self.revs(start=startrev + 1):
704 for r in self.revs(start=startrev + 1):
705 for p in parentrevs(r):
705 for p in parentrevs(r):
706 if p in reachable:
706 if p in reachable:
707 if r not in stoprevs:
707 if r not in stoprevs:
708 reachable.add(r)
708 reachable.add(r)
709 heads.add(r)
709 heads.add(r)
710 if p in heads and p not in stoprevs:
710 if p in heads and p not in stoprevs:
711 heads.remove(p)
711 heads.remove(p)
712
712
713 return [self.node(r) for r in heads]
713 return [self.node(r) for r in heads]
714
714
715 def children(self, node):
715 def children(self, node):
716 """find the children of a given node"""
716 """find the children of a given node"""
717 c = []
717 c = []
718 p = self.rev(node)
718 p = self.rev(node)
719 for r in self.revs(start=p + 1):
719 for r in self.revs(start=p + 1):
720 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
720 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
721 if prevs:
721 if prevs:
722 for pr in prevs:
722 for pr in prevs:
723 if pr == p:
723 if pr == p:
724 c.append(self.node(r))
724 c.append(self.node(r))
725 elif p == nullrev:
725 elif p == nullrev:
726 c.append(self.node(r))
726 c.append(self.node(r))
727 return c
727 return c
728
728
729 def descendant(self, start, end):
729 def descendant(self, start, end):
730 if start == nullrev:
730 if start == nullrev:
731 return True
731 return True
732 for i in self.descendants([start]):
732 for i in self.descendants([start]):
733 if i == end:
733 if i == end:
734 return True
734 return True
735 elif i > end:
735 elif i > end:
736 break
736 break
737 return False
737 return False
738
738
739 def commonancestorsheads(self, a, b):
739 def commonancestorsheads(self, a, b):
740 """calculate all the heads of the common ancestors of nodes a and b"""
740 """calculate all the heads of the common ancestors of nodes a and b"""
741 a, b = self.rev(a), self.rev(b)
741 a, b = self.rev(a), self.rev(b)
742 try:
742 try:
743 ancs = self.index.commonancestorsheads(a, b)
743 ancs = self.index.commonancestorsheads(a, b)
744 except (AttributeError, OverflowError): # C implementation failed
744 except (AttributeError, OverflowError): # C implementation failed
745 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
745 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
746 return map(self.node, ancs)
746 return map(self.node, ancs)
747
747
748 def isancestor(self, a, b):
749 """return True if node a is an ancestor of node b
750
751 The implementation of this is trivial but the use of
752 commonancestorsheads is not."""
753 return a in self.commonancestorsheads(a, b)
754
748 def ancestor(self, a, b):
755 def ancestor(self, a, b):
749 """calculate the least common ancestor of nodes a and b"""
756 """calculate the least common ancestor of nodes a and b"""
750
757
751 a, b = self.rev(a), self.rev(b)
758 a, b = self.rev(a), self.rev(b)
752 try:
759 try:
753 ancs = self.index.ancestors(a, b)
760 ancs = self.index.ancestors(a, b)
754 except (AttributeError, OverflowError):
761 except (AttributeError, OverflowError):
755 ancs = ancestor.ancestors(self.parentrevs, a, b)
762 ancs = ancestor.ancestors(self.parentrevs, a, b)
756 if ancs:
763 if ancs:
757 # choose a consistent winner when there's a tie
764 # choose a consistent winner when there's a tie
758 return min(map(self.node, ancs))
765 return min(map(self.node, ancs))
759 return nullid
766 return nullid
760
767
761 def _match(self, id):
768 def _match(self, id):
762 if isinstance(id, int):
769 if isinstance(id, int):
763 # rev
770 # rev
764 return self.node(id)
771 return self.node(id)
765 if len(id) == 20:
772 if len(id) == 20:
766 # possibly a binary node
773 # possibly a binary node
767 # odds of a binary node being all hex in ASCII are 1 in 10**25
774 # odds of a binary node being all hex in ASCII are 1 in 10**25
768 try:
775 try:
769 node = id
776 node = id
770 self.rev(node) # quick search the index
777 self.rev(node) # quick search the index
771 return node
778 return node
772 except LookupError:
779 except LookupError:
773 pass # may be partial hex id
780 pass # may be partial hex id
774 try:
781 try:
775 # str(rev)
782 # str(rev)
776 rev = int(id)
783 rev = int(id)
777 if str(rev) != id:
784 if str(rev) != id:
778 raise ValueError
785 raise ValueError
779 if rev < 0:
786 if rev < 0:
780 rev = len(self) + rev
787 rev = len(self) + rev
781 if rev < 0 or rev >= len(self):
788 if rev < 0 or rev >= len(self):
782 raise ValueError
789 raise ValueError
783 return self.node(rev)
790 return self.node(rev)
784 except (ValueError, OverflowError):
791 except (ValueError, OverflowError):
785 pass
792 pass
786 if len(id) == 40:
793 if len(id) == 40:
787 try:
794 try:
788 # a full hex nodeid?
795 # a full hex nodeid?
789 node = bin(id)
796 node = bin(id)
790 self.rev(node)
797 self.rev(node)
791 return node
798 return node
792 except (TypeError, LookupError):
799 except (TypeError, LookupError):
793 pass
800 pass
794
801
795 def _partialmatch(self, id):
802 def _partialmatch(self, id):
796 try:
803 try:
797 n = self.index.partialmatch(id)
804 n = self.index.partialmatch(id)
798 if n and self.hasnode(n):
805 if n and self.hasnode(n):
799 return n
806 return n
800 return None
807 return None
801 except RevlogError:
808 except RevlogError:
802 # parsers.c radix tree lookup gave multiple matches
809 # parsers.c radix tree lookup gave multiple matches
803 # fall through to slow path that filters hidden revisions
810 # fall through to slow path that filters hidden revisions
804 pass
811 pass
805 except (AttributeError, ValueError):
812 except (AttributeError, ValueError):
806 # we are pure python, or key was too short to search radix tree
813 # we are pure python, or key was too short to search radix tree
807 pass
814 pass
808
815
809 if id in self._pcache:
816 if id in self._pcache:
810 return self._pcache[id]
817 return self._pcache[id]
811
818
812 if len(id) < 40:
819 if len(id) < 40:
813 try:
820 try:
814 # hex(node)[:...]
821 # hex(node)[:...]
815 l = len(id) // 2 # grab an even number of digits
822 l = len(id) // 2 # grab an even number of digits
816 prefix = bin(id[:l * 2])
823 prefix = bin(id[:l * 2])
817 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
824 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
818 nl = [n for n in nl if hex(n).startswith(id) and
825 nl = [n for n in nl if hex(n).startswith(id) and
819 self.hasnode(n)]
826 self.hasnode(n)]
820 if len(nl) > 0:
827 if len(nl) > 0:
821 if len(nl) == 1:
828 if len(nl) == 1:
822 self._pcache[id] = nl[0]
829 self._pcache[id] = nl[0]
823 return nl[0]
830 return nl[0]
824 raise LookupError(id, self.indexfile,
831 raise LookupError(id, self.indexfile,
825 _('ambiguous identifier'))
832 _('ambiguous identifier'))
826 return None
833 return None
827 except TypeError:
834 except TypeError:
828 pass
835 pass
829
836
830 def lookup(self, id):
837 def lookup(self, id):
831 """locate a node based on:
838 """locate a node based on:
832 - revision number or str(revision number)
839 - revision number or str(revision number)
833 - nodeid or subset of hex nodeid
840 - nodeid or subset of hex nodeid
834 """
841 """
835 n = self._match(id)
842 n = self._match(id)
836 if n is not None:
843 if n is not None:
837 return n
844 return n
838 n = self._partialmatch(id)
845 n = self._partialmatch(id)
839 if n:
846 if n:
840 return n
847 return n
841
848
842 raise LookupError(id, self.indexfile, _('no match found'))
849 raise LookupError(id, self.indexfile, _('no match found'))
843
850
844 def cmp(self, node, text):
851 def cmp(self, node, text):
845 """compare text with a given file revision
852 """compare text with a given file revision
846
853
847 returns True if text is different than what is stored.
854 returns True if text is different than what is stored.
848 """
855 """
849 p1, p2 = self.parents(node)
856 p1, p2 = self.parents(node)
850 return hash(text, p1, p2) != node
857 return hash(text, p1, p2) != node
851
858
852 def _addchunk(self, offset, data):
859 def _addchunk(self, offset, data):
853 o, d = self._chunkcache
860 o, d = self._chunkcache
854 # try to add to existing cache
861 # try to add to existing cache
855 if o + len(d) == offset and len(d) + len(data) < _chunksize:
862 if o + len(d) == offset and len(d) + len(data) < _chunksize:
856 self._chunkcache = o, d + data
863 self._chunkcache = o, d + data
857 else:
864 else:
858 self._chunkcache = offset, data
865 self._chunkcache = offset, data
859
866
860 def _loadchunk(self, offset, length):
867 def _loadchunk(self, offset, length):
861 if self._inline:
868 if self._inline:
862 df = self.opener(self.indexfile)
869 df = self.opener(self.indexfile)
863 else:
870 else:
864 df = self.opener(self.datafile)
871 df = self.opener(self.datafile)
865
872
866 # Cache data both forward and backward around the requested
873 # Cache data both forward and backward around the requested
867 # data, in a fixed size window. This helps speed up operations
874 # data, in a fixed size window. This helps speed up operations
868 # involving reading the revlog backwards.
875 # involving reading the revlog backwards.
869 cachesize = self._chunkcachesize
876 cachesize = self._chunkcachesize
870 realoffset = offset & ~(cachesize - 1)
877 realoffset = offset & ~(cachesize - 1)
871 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
878 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
872 - realoffset)
879 - realoffset)
873 df.seek(realoffset)
880 df.seek(realoffset)
874 d = df.read(reallength)
881 d = df.read(reallength)
875 df.close()
882 df.close()
876 self._addchunk(realoffset, d)
883 self._addchunk(realoffset, d)
877 if offset != realoffset or reallength != length:
884 if offset != realoffset or reallength != length:
878 return util.buffer(d, offset - realoffset, length)
885 return util.buffer(d, offset - realoffset, length)
879 return d
886 return d
880
887
881 def _getchunk(self, offset, length):
888 def _getchunk(self, offset, length):
882 o, d = self._chunkcache
889 o, d = self._chunkcache
883 l = len(d)
890 l = len(d)
884
891
885 # is it in the cache?
892 # is it in the cache?
886 cachestart = offset - o
893 cachestart = offset - o
887 cacheend = cachestart + length
894 cacheend = cachestart + length
888 if cachestart >= 0 and cacheend <= l:
895 if cachestart >= 0 and cacheend <= l:
889 if cachestart == 0 and cacheend == l:
896 if cachestart == 0 and cacheend == l:
890 return d # avoid a copy
897 return d # avoid a copy
891 return util.buffer(d, cachestart, cacheend - cachestart)
898 return util.buffer(d, cachestart, cacheend - cachestart)
892
899
893 return self._loadchunk(offset, length)
900 return self._loadchunk(offset, length)
894
901
895 def _chunkraw(self, startrev, endrev):
902 def _chunkraw(self, startrev, endrev):
896 start = self.start(startrev)
903 start = self.start(startrev)
897 end = self.end(endrev)
904 end = self.end(endrev)
898 if self._inline:
905 if self._inline:
899 start += (startrev + 1) * self._io.size
906 start += (startrev + 1) * self._io.size
900 end += (endrev + 1) * self._io.size
907 end += (endrev + 1) * self._io.size
901 length = end - start
908 length = end - start
902 return self._getchunk(start, length)
909 return self._getchunk(start, length)
903
910
904 def _chunk(self, rev):
911 def _chunk(self, rev):
905 return decompress(self._chunkraw(rev, rev))
912 return decompress(self._chunkraw(rev, rev))
906
913
907 def _chunks(self, revs):
914 def _chunks(self, revs):
908 '''faster version of [self._chunk(rev) for rev in revs]
915 '''faster version of [self._chunk(rev) for rev in revs]
909
916
910 Assumes that revs is in ascending order.'''
917 Assumes that revs is in ascending order.'''
911 if not revs:
918 if not revs:
912 return []
919 return []
913 start = self.start
920 start = self.start
914 length = self.length
921 length = self.length
915 inline = self._inline
922 inline = self._inline
916 iosize = self._io.size
923 iosize = self._io.size
917 buffer = util.buffer
924 buffer = util.buffer
918
925
919 l = []
926 l = []
920 ladd = l.append
927 ladd = l.append
921
928
922 # preload the cache
929 # preload the cache
923 try:
930 try:
924 while True:
931 while True:
925 # ensure that the cache doesn't change out from under us
932 # ensure that the cache doesn't change out from under us
926 _cache = self._chunkcache
933 _cache = self._chunkcache
927 self._chunkraw(revs[0], revs[-1])
934 self._chunkraw(revs[0], revs[-1])
928 if _cache == self._chunkcache:
935 if _cache == self._chunkcache:
929 break
936 break
930 offset, data = _cache
937 offset, data = _cache
931 except OverflowError:
938 except OverflowError:
932 # issue4215 - we can't cache a run of chunks greater than
939 # issue4215 - we can't cache a run of chunks greater than
933 # 2G on Windows
940 # 2G on Windows
934 return [self._chunk(rev) for rev in revs]
941 return [self._chunk(rev) for rev in revs]
935
942
936 for rev in revs:
943 for rev in revs:
937 chunkstart = start(rev)
944 chunkstart = start(rev)
938 if inline:
945 if inline:
939 chunkstart += (rev + 1) * iosize
946 chunkstart += (rev + 1) * iosize
940 chunklength = length(rev)
947 chunklength = length(rev)
941 ladd(decompress(buffer(data, chunkstart - offset, chunklength)))
948 ladd(decompress(buffer(data, chunkstart - offset, chunklength)))
942
949
943 return l
950 return l
944
951
945 def _chunkclear(self):
952 def _chunkclear(self):
946 self._chunkcache = (0, '')
953 self._chunkcache = (0, '')
947
954
948 def deltaparent(self, rev):
955 def deltaparent(self, rev):
949 """return deltaparent of the given revision"""
956 """return deltaparent of the given revision"""
950 base = self.index[rev][3]
957 base = self.index[rev][3]
951 if base == rev:
958 if base == rev:
952 return nullrev
959 return nullrev
953 elif self._generaldelta:
960 elif self._generaldelta:
954 return base
961 return base
955 else:
962 else:
956 return rev - 1
963 return rev - 1
957
964
958 def revdiff(self, rev1, rev2):
965 def revdiff(self, rev1, rev2):
959 """return or calculate a delta between two revisions"""
966 """return or calculate a delta between two revisions"""
960 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
967 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
961 return str(self._chunk(rev2))
968 return str(self._chunk(rev2))
962
969
963 return mdiff.textdiff(self.revision(rev1),
970 return mdiff.textdiff(self.revision(rev1),
964 self.revision(rev2))
971 self.revision(rev2))
965
972
966 def revision(self, nodeorrev):
973 def revision(self, nodeorrev):
967 """return an uncompressed revision of a given node or revision
974 """return an uncompressed revision of a given node or revision
968 number.
975 number.
969 """
976 """
970 if isinstance(nodeorrev, int):
977 if isinstance(nodeorrev, int):
971 rev = nodeorrev
978 rev = nodeorrev
972 node = self.node(rev)
979 node = self.node(rev)
973 else:
980 else:
974 node = nodeorrev
981 node = nodeorrev
975 rev = None
982 rev = None
976
983
977 _cache = self._cache # grab local copy of cache to avoid thread race
984 _cache = self._cache # grab local copy of cache to avoid thread race
978 cachedrev = None
985 cachedrev = None
979 if node == nullid:
986 if node == nullid:
980 return ""
987 return ""
981 if _cache:
988 if _cache:
982 if _cache[0] == node:
989 if _cache[0] == node:
983 return _cache[2]
990 return _cache[2]
984 cachedrev = _cache[1]
991 cachedrev = _cache[1]
985
992
986 # look up what we need to read
993 # look up what we need to read
987 text = None
994 text = None
988 if rev is None:
995 if rev is None:
989 rev = self.rev(node)
996 rev = self.rev(node)
990
997
991 # check rev flags
998 # check rev flags
992 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
999 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
993 raise RevlogError(_('incompatible revision flag %x') %
1000 raise RevlogError(_('incompatible revision flag %x') %
994 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
1001 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
995
1002
996 # build delta chain
1003 # build delta chain
997 chain = []
1004 chain = []
998 index = self.index # for performance
1005 index = self.index # for performance
999 generaldelta = self._generaldelta
1006 generaldelta = self._generaldelta
1000 iterrev = rev
1007 iterrev = rev
1001 e = index[iterrev]
1008 e = index[iterrev]
1002 while iterrev != e[3] and iterrev != cachedrev:
1009 while iterrev != e[3] and iterrev != cachedrev:
1003 chain.append(iterrev)
1010 chain.append(iterrev)
1004 if generaldelta:
1011 if generaldelta:
1005 iterrev = e[3]
1012 iterrev = e[3]
1006 else:
1013 else:
1007 iterrev -= 1
1014 iterrev -= 1
1008 e = index[iterrev]
1015 e = index[iterrev]
1009
1016
1010 if iterrev == cachedrev:
1017 if iterrev == cachedrev:
1011 # cache hit
1018 # cache hit
1012 text = _cache[2]
1019 text = _cache[2]
1013 else:
1020 else:
1014 chain.append(iterrev)
1021 chain.append(iterrev)
1015 chain.reverse()
1022 chain.reverse()
1016
1023
1017 # drop cache to save memory
1024 # drop cache to save memory
1018 self._cache = None
1025 self._cache = None
1019
1026
1020 bins = self._chunks(chain)
1027 bins = self._chunks(chain)
1021 if text is None:
1028 if text is None:
1022 text = str(bins[0])
1029 text = str(bins[0])
1023 bins = bins[1:]
1030 bins = bins[1:]
1024
1031
1025 text = mdiff.patches(text, bins)
1032 text = mdiff.patches(text, bins)
1026
1033
1027 text = self._checkhash(text, node, rev)
1034 text = self._checkhash(text, node, rev)
1028
1035
1029 self._cache = (node, rev, text)
1036 self._cache = (node, rev, text)
1030 return text
1037 return text
1031
1038
1032 def _checkhash(self, text, node, rev):
1039 def _checkhash(self, text, node, rev):
1033 p1, p2 = self.parents(node)
1040 p1, p2 = self.parents(node)
1034 self.checkhash(text, p1, p2, node, rev)
1041 self.checkhash(text, p1, p2, node, rev)
1035 return text
1042 return text
1036
1043
1037 def checkhash(self, text, p1, p2, node, rev=None):
1044 def checkhash(self, text, p1, p2, node, rev=None):
1038 if node != hash(text, p1, p2):
1045 if node != hash(text, p1, p2):
1039 revornode = rev
1046 revornode = rev
1040 if revornode is None:
1047 if revornode is None:
1041 revornode = templatefilters.short(hex(node))
1048 revornode = templatefilters.short(hex(node))
1042 raise RevlogError(_("integrity check failed on %s:%s")
1049 raise RevlogError(_("integrity check failed on %s:%s")
1043 % (self.indexfile, revornode))
1050 % (self.indexfile, revornode))
1044
1051
1045 def checkinlinesize(self, tr, fp=None):
1052 def checkinlinesize(self, tr, fp=None):
1046 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1053 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1047 return
1054 return
1048
1055
1049 trinfo = tr.find(self.indexfile)
1056 trinfo = tr.find(self.indexfile)
1050 if trinfo is None:
1057 if trinfo is None:
1051 raise RevlogError(_("%s not found in the transaction")
1058 raise RevlogError(_("%s not found in the transaction")
1052 % self.indexfile)
1059 % self.indexfile)
1053
1060
1054 trindex = trinfo[2]
1061 trindex = trinfo[2]
1055 dataoff = self.start(trindex)
1062 dataoff = self.start(trindex)
1056
1063
1057 tr.add(self.datafile, dataoff)
1064 tr.add(self.datafile, dataoff)
1058
1065
1059 if fp:
1066 if fp:
1060 fp.flush()
1067 fp.flush()
1061 fp.close()
1068 fp.close()
1062
1069
1063 df = self.opener(self.datafile, 'w')
1070 df = self.opener(self.datafile, 'w')
1064 try:
1071 try:
1065 for r in self:
1072 for r in self:
1066 df.write(self._chunkraw(r, r))
1073 df.write(self._chunkraw(r, r))
1067 finally:
1074 finally:
1068 df.close()
1075 df.close()
1069
1076
1070 fp = self.opener(self.indexfile, 'w', atomictemp=True)
1077 fp = self.opener(self.indexfile, 'w', atomictemp=True)
1071 self.version &= ~(REVLOGNGINLINEDATA)
1078 self.version &= ~(REVLOGNGINLINEDATA)
1072 self._inline = False
1079 self._inline = False
1073 for i in self:
1080 for i in self:
1074 e = self._io.packentry(self.index[i], self.node, self.version, i)
1081 e = self._io.packentry(self.index[i], self.node, self.version, i)
1075 fp.write(e)
1082 fp.write(e)
1076
1083
1077 # if we don't call close, the temp file will never replace the
1084 # if we don't call close, the temp file will never replace the
1078 # real index
1085 # real index
1079 fp.close()
1086 fp.close()
1080
1087
1081 tr.replace(self.indexfile, trindex * self._io.size)
1088 tr.replace(self.indexfile, trindex * self._io.size)
1082 self._chunkclear()
1089 self._chunkclear()
1083
1090
1084 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1091 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1085 node=None):
1092 node=None):
1086 """add a revision to the log
1093 """add a revision to the log
1087
1094
1088 text - the revision data to add
1095 text - the revision data to add
1089 transaction - the transaction object used for rollback
1096 transaction - the transaction object used for rollback
1090 link - the linkrev data to add
1097 link - the linkrev data to add
1091 p1, p2 - the parent nodeids of the revision
1098 p1, p2 - the parent nodeids of the revision
1092 cachedelta - an optional precomputed delta
1099 cachedelta - an optional precomputed delta
1093 node - nodeid of revision; typically node is not specified, and it is
1100 node - nodeid of revision; typically node is not specified, and it is
1094 computed by default as hash(text, p1, p2), however subclasses might
1101 computed by default as hash(text, p1, p2), however subclasses might
1095 use different hashing method (and override checkhash() in such case)
1102 use different hashing method (and override checkhash() in such case)
1096 """
1103 """
1097 if link == nullrev:
1104 if link == nullrev:
1098 raise RevlogError(_("attempted to add linkrev -1 to %s")
1105 raise RevlogError(_("attempted to add linkrev -1 to %s")
1099 % self.indexfile)
1106 % self.indexfile)
1100 node = node or hash(text, p1, p2)
1107 node = node or hash(text, p1, p2)
1101 if node in self.nodemap:
1108 if node in self.nodemap:
1102 return node
1109 return node
1103
1110
1104 dfh = None
1111 dfh = None
1105 if not self._inline:
1112 if not self._inline:
1106 dfh = self.opener(self.datafile, "a")
1113 dfh = self.opener(self.datafile, "a")
1107 ifh = self.opener(self.indexfile, "a+")
1114 ifh = self.opener(self.indexfile, "a+")
1108 try:
1115 try:
1109 return self._addrevision(node, text, transaction, link, p1, p2,
1116 return self._addrevision(node, text, transaction, link, p1, p2,
1110 cachedelta, ifh, dfh)
1117 cachedelta, ifh, dfh)
1111 finally:
1118 finally:
1112 if dfh:
1119 if dfh:
1113 dfh.close()
1120 dfh.close()
1114 ifh.close()
1121 ifh.close()
1115
1122
1116 def compress(self, text):
1123 def compress(self, text):
1117 """ generate a possibly-compressed representation of text """
1124 """ generate a possibly-compressed representation of text """
1118 if not text:
1125 if not text:
1119 return ("", text)
1126 return ("", text)
1120 l = len(text)
1127 l = len(text)
1121 bin = None
1128 bin = None
1122 if l < 44:
1129 if l < 44:
1123 pass
1130 pass
1124 elif l > 1000000:
1131 elif l > 1000000:
1125 # zlib makes an internal copy, thus doubling memory usage for
1132 # zlib makes an internal copy, thus doubling memory usage for
1126 # large files, so lets do this in pieces
1133 # large files, so lets do this in pieces
1127 z = zlib.compressobj()
1134 z = zlib.compressobj()
1128 p = []
1135 p = []
1129 pos = 0
1136 pos = 0
1130 while pos < l:
1137 while pos < l:
1131 pos2 = pos + 2**20
1138 pos2 = pos + 2**20
1132 p.append(z.compress(text[pos:pos2]))
1139 p.append(z.compress(text[pos:pos2]))
1133 pos = pos2
1140 pos = pos2
1134 p.append(z.flush())
1141 p.append(z.flush())
1135 if sum(map(len, p)) < l:
1142 if sum(map(len, p)) < l:
1136 bin = "".join(p)
1143 bin = "".join(p)
1137 else:
1144 else:
1138 bin = _compress(text)
1145 bin = _compress(text)
1139 if bin is None or len(bin) > l:
1146 if bin is None or len(bin) > l:
1140 if text[0] == '\0':
1147 if text[0] == '\0':
1141 return ("", text)
1148 return ("", text)
1142 return ('u', text)
1149 return ('u', text)
1143 return ("", bin)
1150 return ("", bin)
1144
1151
1145 def _addrevision(self, node, text, transaction, link, p1, p2,
1152 def _addrevision(self, node, text, transaction, link, p1, p2,
1146 cachedelta, ifh, dfh):
1153 cachedelta, ifh, dfh):
1147 """internal function to add revisions to the log
1154 """internal function to add revisions to the log
1148
1155
1149 see addrevision for argument descriptions.
1156 see addrevision for argument descriptions.
1150 invariants:
1157 invariants:
1151 - text is optional (can be None); if not set, cachedelta must be set.
1158 - text is optional (can be None); if not set, cachedelta must be set.
1152 if both are set, they must correspond to each other.
1159 if both are set, they must correspond to each other.
1153 """
1160 """
1154 btext = [text]
1161 btext = [text]
1155 def buildtext():
1162 def buildtext():
1156 if btext[0] is not None:
1163 if btext[0] is not None:
1157 return btext[0]
1164 return btext[0]
1158 # flush any pending writes here so we can read it in revision
1165 # flush any pending writes here so we can read it in revision
1159 if dfh:
1166 if dfh:
1160 dfh.flush()
1167 dfh.flush()
1161 ifh.flush()
1168 ifh.flush()
1162 basetext = self.revision(self.node(cachedelta[0]))
1169 basetext = self.revision(self.node(cachedelta[0]))
1163 btext[0] = mdiff.patch(basetext, cachedelta[1])
1170 btext[0] = mdiff.patch(basetext, cachedelta[1])
1164 self.checkhash(btext[0], p1, p2, node)
1171 self.checkhash(btext[0], p1, p2, node)
1165 return btext[0]
1172 return btext[0]
1166
1173
1167 def builddelta(rev):
1174 def builddelta(rev):
1168 # can we use the cached delta?
1175 # can we use the cached delta?
1169 if cachedelta and cachedelta[0] == rev:
1176 if cachedelta and cachedelta[0] == rev:
1170 delta = cachedelta[1]
1177 delta = cachedelta[1]
1171 else:
1178 else:
1172 t = buildtext()
1179 t = buildtext()
1173 ptext = self.revision(self.node(rev))
1180 ptext = self.revision(self.node(rev))
1174 delta = mdiff.textdiff(ptext, t)
1181 delta = mdiff.textdiff(ptext, t)
1175 data = self.compress(delta)
1182 data = self.compress(delta)
1176 l = len(data[1]) + len(data[0])
1183 l = len(data[1]) + len(data[0])
1177 if basecache[0] == rev:
1184 if basecache[0] == rev:
1178 chainbase = basecache[1]
1185 chainbase = basecache[1]
1179 else:
1186 else:
1180 chainbase = self.chainbase(rev)
1187 chainbase = self.chainbase(rev)
1181 dist = l + offset - self.start(chainbase)
1188 dist = l + offset - self.start(chainbase)
1182 if self._generaldelta:
1189 if self._generaldelta:
1183 base = rev
1190 base = rev
1184 else:
1191 else:
1185 base = chainbase
1192 base = chainbase
1186 return dist, l, data, base, chainbase
1193 return dist, l, data, base, chainbase
1187
1194
1188 curr = len(self)
1195 curr = len(self)
1189 prev = curr - 1
1196 prev = curr - 1
1190 base = chainbase = curr
1197 base = chainbase = curr
1191 offset = self.end(prev)
1198 offset = self.end(prev)
1192 flags = 0
1199 flags = 0
1193 d = None
1200 d = None
1194 if self._basecache is None:
1201 if self._basecache is None:
1195 self._basecache = (prev, self.chainbase(prev))
1202 self._basecache = (prev, self.chainbase(prev))
1196 basecache = self._basecache
1203 basecache = self._basecache
1197 p1r, p2r = self.rev(p1), self.rev(p2)
1204 p1r, p2r = self.rev(p1), self.rev(p2)
1198
1205
1199 # should we try to build a delta?
1206 # should we try to build a delta?
1200 if prev != nullrev:
1207 if prev != nullrev:
1201 if self._generaldelta:
1208 if self._generaldelta:
1202 if p1r >= basecache[1]:
1209 if p1r >= basecache[1]:
1203 d = builddelta(p1r)
1210 d = builddelta(p1r)
1204 elif p2r >= basecache[1]:
1211 elif p2r >= basecache[1]:
1205 d = builddelta(p2r)
1212 d = builddelta(p2r)
1206 else:
1213 else:
1207 d = builddelta(prev)
1214 d = builddelta(prev)
1208 else:
1215 else:
1209 d = builddelta(prev)
1216 d = builddelta(prev)
1210 dist, l, data, base, chainbase = d
1217 dist, l, data, base, chainbase = d
1211
1218
1212 # full versions are inserted when the needed deltas
1219 # full versions are inserted when the needed deltas
1213 # become comparable to the uncompressed text
1220 # become comparable to the uncompressed text
1214 if text is None:
1221 if text is None:
1215 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1222 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1216 cachedelta[1])
1223 cachedelta[1])
1217 else:
1224 else:
1218 textlen = len(text)
1225 textlen = len(text)
1219 if d is None or dist > textlen * 2:
1226 if d is None or dist > textlen * 2:
1220 text = buildtext()
1227 text = buildtext()
1221 data = self.compress(text)
1228 data = self.compress(text)
1222 l = len(data[1]) + len(data[0])
1229 l = len(data[1]) + len(data[0])
1223 base = chainbase = curr
1230 base = chainbase = curr
1224
1231
1225 e = (offset_type(offset, flags), l, textlen,
1232 e = (offset_type(offset, flags), l, textlen,
1226 base, link, p1r, p2r, node)
1233 base, link, p1r, p2r, node)
1227 self.index.insert(-1, e)
1234 self.index.insert(-1, e)
1228 self.nodemap[node] = curr
1235 self.nodemap[node] = curr
1229
1236
1230 entry = self._io.packentry(e, self.node, self.version, curr)
1237 entry = self._io.packentry(e, self.node, self.version, curr)
1231 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1238 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1232
1239
1233 if type(text) == str: # only accept immutable objects
1240 if type(text) == str: # only accept immutable objects
1234 self._cache = (node, curr, text)
1241 self._cache = (node, curr, text)
1235 self._basecache = (curr, chainbase)
1242 self._basecache = (curr, chainbase)
1236 return node
1243 return node
1237
1244
1238 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1245 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1239 curr = len(self) - 1
1246 curr = len(self) - 1
1240 if not self._inline:
1247 if not self._inline:
1241 transaction.add(self.datafile, offset)
1248 transaction.add(self.datafile, offset)
1242 transaction.add(self.indexfile, curr * len(entry))
1249 transaction.add(self.indexfile, curr * len(entry))
1243 if data[0]:
1250 if data[0]:
1244 dfh.write(data[0])
1251 dfh.write(data[0])
1245 dfh.write(data[1])
1252 dfh.write(data[1])
1246 dfh.flush()
1253 dfh.flush()
1247 ifh.write(entry)
1254 ifh.write(entry)
1248 else:
1255 else:
1249 offset += curr * self._io.size
1256 offset += curr * self._io.size
1250 transaction.add(self.indexfile, offset, curr)
1257 transaction.add(self.indexfile, offset, curr)
1251 ifh.write(entry)
1258 ifh.write(entry)
1252 ifh.write(data[0])
1259 ifh.write(data[0])
1253 ifh.write(data[1])
1260 ifh.write(data[1])
1254 self.checkinlinesize(transaction, ifh)
1261 self.checkinlinesize(transaction, ifh)
1255
1262
1256 def addgroup(self, bundle, linkmapper, transaction):
1263 def addgroup(self, bundle, linkmapper, transaction):
1257 """
1264 """
1258 add a delta group
1265 add a delta group
1259
1266
1260 given a set of deltas, add them to the revision log. the
1267 given a set of deltas, add them to the revision log. the
1261 first delta is against its parent, which should be in our
1268 first delta is against its parent, which should be in our
1262 log, the rest are against the previous delta.
1269 log, the rest are against the previous delta.
1263 """
1270 """
1264
1271
1265 # track the base of the current delta log
1272 # track the base of the current delta log
1266 content = []
1273 content = []
1267 node = None
1274 node = None
1268
1275
1269 r = len(self)
1276 r = len(self)
1270 end = 0
1277 end = 0
1271 if r:
1278 if r:
1272 end = self.end(r - 1)
1279 end = self.end(r - 1)
1273 ifh = self.opener(self.indexfile, "a+")
1280 ifh = self.opener(self.indexfile, "a+")
1274 isize = r * self._io.size
1281 isize = r * self._io.size
1275 if self._inline:
1282 if self._inline:
1276 transaction.add(self.indexfile, end + isize, r)
1283 transaction.add(self.indexfile, end + isize, r)
1277 dfh = None
1284 dfh = None
1278 else:
1285 else:
1279 transaction.add(self.indexfile, isize, r)
1286 transaction.add(self.indexfile, isize, r)
1280 transaction.add(self.datafile, end)
1287 transaction.add(self.datafile, end)
1281 dfh = self.opener(self.datafile, "a")
1288 dfh = self.opener(self.datafile, "a")
1282
1289
1283 try:
1290 try:
1284 # loop through our set of deltas
1291 # loop through our set of deltas
1285 chain = None
1292 chain = None
1286 while True:
1293 while True:
1287 chunkdata = bundle.deltachunk(chain)
1294 chunkdata = bundle.deltachunk(chain)
1288 if not chunkdata:
1295 if not chunkdata:
1289 break
1296 break
1290 node = chunkdata['node']
1297 node = chunkdata['node']
1291 p1 = chunkdata['p1']
1298 p1 = chunkdata['p1']
1292 p2 = chunkdata['p2']
1299 p2 = chunkdata['p2']
1293 cs = chunkdata['cs']
1300 cs = chunkdata['cs']
1294 deltabase = chunkdata['deltabase']
1301 deltabase = chunkdata['deltabase']
1295 delta = chunkdata['delta']
1302 delta = chunkdata['delta']
1296
1303
1297 content.append(node)
1304 content.append(node)
1298
1305
1299 link = linkmapper(cs)
1306 link = linkmapper(cs)
1300 if node in self.nodemap:
1307 if node in self.nodemap:
1301 # this can happen if two branches make the same change
1308 # this can happen if two branches make the same change
1302 chain = node
1309 chain = node
1303 continue
1310 continue
1304
1311
1305 for p in (p1, p2):
1312 for p in (p1, p2):
1306 if p not in self.nodemap:
1313 if p not in self.nodemap:
1307 raise LookupError(p, self.indexfile,
1314 raise LookupError(p, self.indexfile,
1308 _('unknown parent'))
1315 _('unknown parent'))
1309
1316
1310 if deltabase not in self.nodemap:
1317 if deltabase not in self.nodemap:
1311 raise LookupError(deltabase, self.indexfile,
1318 raise LookupError(deltabase, self.indexfile,
1312 _('unknown delta base'))
1319 _('unknown delta base'))
1313
1320
1314 baserev = self.rev(deltabase)
1321 baserev = self.rev(deltabase)
1315 chain = self._addrevision(node, None, transaction, link,
1322 chain = self._addrevision(node, None, transaction, link,
1316 p1, p2, (baserev, delta), ifh, dfh)
1323 p1, p2, (baserev, delta), ifh, dfh)
1317 if not dfh and not self._inline:
1324 if not dfh and not self._inline:
1318 # addrevision switched from inline to conventional
1325 # addrevision switched from inline to conventional
1319 # reopen the index
1326 # reopen the index
1320 ifh.close()
1327 ifh.close()
1321 dfh = self.opener(self.datafile, "a")
1328 dfh = self.opener(self.datafile, "a")
1322 ifh = self.opener(self.indexfile, "a")
1329 ifh = self.opener(self.indexfile, "a")
1323 finally:
1330 finally:
1324 if dfh:
1331 if dfh:
1325 dfh.close()
1332 dfh.close()
1326 ifh.close()
1333 ifh.close()
1327
1334
1328 return content
1335 return content
1329
1336
1330 def getstrippoint(self, minlink):
1337 def getstrippoint(self, minlink):
1331 """find the minimum rev that must be stripped to strip the linkrev
1338 """find the minimum rev that must be stripped to strip the linkrev
1332
1339
1333 Returns a tuple containing the minimum rev and a set of all revs that
1340 Returns a tuple containing the minimum rev and a set of all revs that
1334 have linkrevs that will be broken by this strip.
1341 have linkrevs that will be broken by this strip.
1335 """
1342 """
1336 brokenrevs = set()
1343 brokenrevs = set()
1337 strippoint = len(self)
1344 strippoint = len(self)
1338
1345
1339 heads = {}
1346 heads = {}
1340 futurelargelinkrevs = set()
1347 futurelargelinkrevs = set()
1341 for head in self.headrevs():
1348 for head in self.headrevs():
1342 headlinkrev = self.linkrev(head)
1349 headlinkrev = self.linkrev(head)
1343 heads[head] = headlinkrev
1350 heads[head] = headlinkrev
1344 if headlinkrev >= minlink:
1351 if headlinkrev >= minlink:
1345 futurelargelinkrevs.add(headlinkrev)
1352 futurelargelinkrevs.add(headlinkrev)
1346
1353
1347 # This algorithm involves walking down the rev graph, starting at the
1354 # This algorithm involves walking down the rev graph, starting at the
1348 # heads. Since the revs are topologically sorted according to linkrev,
1355 # heads. Since the revs are topologically sorted according to linkrev,
1349 # once all head linkrevs are below the minlink, we know there are
1356 # once all head linkrevs are below the minlink, we know there are
1350 # no more revs that could have a linkrev greater than minlink.
1357 # no more revs that could have a linkrev greater than minlink.
1351 # So we can stop walking.
1358 # So we can stop walking.
1352 while futurelargelinkrevs:
1359 while futurelargelinkrevs:
1353 strippoint -= 1
1360 strippoint -= 1
1354 linkrev = heads.pop(strippoint)
1361 linkrev = heads.pop(strippoint)
1355
1362
1356 if linkrev < minlink:
1363 if linkrev < minlink:
1357 brokenrevs.add(strippoint)
1364 brokenrevs.add(strippoint)
1358 else:
1365 else:
1359 futurelargelinkrevs.remove(linkrev)
1366 futurelargelinkrevs.remove(linkrev)
1360
1367
1361 for p in self.parentrevs(strippoint):
1368 for p in self.parentrevs(strippoint):
1362 if p != nullrev:
1369 if p != nullrev:
1363 plinkrev = self.linkrev(p)
1370 plinkrev = self.linkrev(p)
1364 heads[p] = plinkrev
1371 heads[p] = plinkrev
1365 if plinkrev >= minlink:
1372 if plinkrev >= minlink:
1366 futurelargelinkrevs.add(plinkrev)
1373 futurelargelinkrevs.add(plinkrev)
1367
1374
1368 return strippoint, brokenrevs
1375 return strippoint, brokenrevs
1369
1376
1370 def strip(self, minlink, transaction):
1377 def strip(self, minlink, transaction):
1371 """truncate the revlog on the first revision with a linkrev >= minlink
1378 """truncate the revlog on the first revision with a linkrev >= minlink
1372
1379
1373 This function is called when we're stripping revision minlink and
1380 This function is called when we're stripping revision minlink and
1374 its descendants from the repository.
1381 its descendants from the repository.
1375
1382
1376 We have to remove all revisions with linkrev >= minlink, because
1383 We have to remove all revisions with linkrev >= minlink, because
1377 the equivalent changelog revisions will be renumbered after the
1384 the equivalent changelog revisions will be renumbered after the
1378 strip.
1385 strip.
1379
1386
1380 So we truncate the revlog on the first of these revisions, and
1387 So we truncate the revlog on the first of these revisions, and
1381 trust that the caller has saved the revisions that shouldn't be
1388 trust that the caller has saved the revisions that shouldn't be
1382 removed and that it'll re-add them after this truncation.
1389 removed and that it'll re-add them after this truncation.
1383 """
1390 """
1384 if len(self) == 0:
1391 if len(self) == 0:
1385 return
1392 return
1386
1393
1387 rev, _ = self.getstrippoint(minlink)
1394 rev, _ = self.getstrippoint(minlink)
1388 if rev == len(self):
1395 if rev == len(self):
1389 return
1396 return
1390
1397
1391 # first truncate the files on disk
1398 # first truncate the files on disk
1392 end = self.start(rev)
1399 end = self.start(rev)
1393 if not self._inline:
1400 if not self._inline:
1394 transaction.add(self.datafile, end)
1401 transaction.add(self.datafile, end)
1395 end = rev * self._io.size
1402 end = rev * self._io.size
1396 else:
1403 else:
1397 end += rev * self._io.size
1404 end += rev * self._io.size
1398
1405
1399 transaction.add(self.indexfile, end)
1406 transaction.add(self.indexfile, end)
1400
1407
1401 # then reset internal state in memory to forget those revisions
1408 # then reset internal state in memory to forget those revisions
1402 self._cache = None
1409 self._cache = None
1403 self._chunkclear()
1410 self._chunkclear()
1404 for x in xrange(rev, len(self)):
1411 for x in xrange(rev, len(self)):
1405 del self.nodemap[self.node(x)]
1412 del self.nodemap[self.node(x)]
1406
1413
1407 del self.index[rev:-1]
1414 del self.index[rev:-1]
1408
1415
1409 def checksize(self):
1416 def checksize(self):
1410 expected = 0
1417 expected = 0
1411 if len(self):
1418 if len(self):
1412 expected = max(0, self.end(len(self) - 1))
1419 expected = max(0, self.end(len(self) - 1))
1413
1420
1414 try:
1421 try:
1415 f = self.opener(self.datafile)
1422 f = self.opener(self.datafile)
1416 f.seek(0, 2)
1423 f.seek(0, 2)
1417 actual = f.tell()
1424 actual = f.tell()
1418 f.close()
1425 f.close()
1419 dd = actual - expected
1426 dd = actual - expected
1420 except IOError, inst:
1427 except IOError, inst:
1421 if inst.errno != errno.ENOENT:
1428 if inst.errno != errno.ENOENT:
1422 raise
1429 raise
1423 dd = 0
1430 dd = 0
1424
1431
1425 try:
1432 try:
1426 f = self.opener(self.indexfile)
1433 f = self.opener(self.indexfile)
1427 f.seek(0, 2)
1434 f.seek(0, 2)
1428 actual = f.tell()
1435 actual = f.tell()
1429 f.close()
1436 f.close()
1430 s = self._io.size
1437 s = self._io.size
1431 i = max(0, actual // s)
1438 i = max(0, actual // s)
1432 di = actual - (i * s)
1439 di = actual - (i * s)
1433 if self._inline:
1440 if self._inline:
1434 databytes = 0
1441 databytes = 0
1435 for r in self:
1442 for r in self:
1436 databytes += max(0, self.length(r))
1443 databytes += max(0, self.length(r))
1437 dd = 0
1444 dd = 0
1438 di = actual - len(self) * s - databytes
1445 di = actual - len(self) * s - databytes
1439 except IOError, inst:
1446 except IOError, inst:
1440 if inst.errno != errno.ENOENT:
1447 if inst.errno != errno.ENOENT:
1441 raise
1448 raise
1442 di = 0
1449 di = 0
1443
1450
1444 return (dd, di)
1451 return (dd, di)
1445
1452
1446 def files(self):
1453 def files(self):
1447 res = [self.indexfile]
1454 res = [self.indexfile]
1448 if not self._inline:
1455 if not self._inline:
1449 res.append(self.datafile)
1456 res.append(self.datafile)
1450 return res
1457 return res
General Comments 0
You need to be logged in to leave comments. Login now