##// END OF EJS Templates
discovery: introduce outgoing object for result of findcommonoutgoing...
Pierre-Yves David -
r15837:cd956049 default
parent child Browse files
Show More
@@ -1,5757 +1,5757 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 _, gettext
10 from i18n import _, gettext
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import match as matchmod
16 import match as matchmod
17 import merge as mergemod
17 import merge as mergemod
18 import minirst, revset, fileset
18 import minirst, revset, fileset
19 import dagparser, context, simplemerge
19 import dagparser, context, simplemerge
20 import random, setdiscovery, treediscovery, dagutil
20 import random, setdiscovery, treediscovery, dagutil
21 import phases as phasesmod
21 import phases as phasesmod
22
22
23 table = {}
23 table = {}
24
24
25 command = cmdutil.command(table)
25 command = cmdutil.command(table)
26
26
27 # common command options
27 # common command options
28
28
29 globalopts = [
29 globalopts = [
30 ('R', 'repository', '',
30 ('R', 'repository', '',
31 _('repository root directory or name of overlay bundle file'),
31 _('repository root directory or name of overlay bundle file'),
32 _('REPO')),
32 _('REPO')),
33 ('', 'cwd', '',
33 ('', 'cwd', '',
34 _('change working directory'), _('DIR')),
34 _('change working directory'), _('DIR')),
35 ('y', 'noninteractive', None,
35 ('y', 'noninteractive', None,
36 _('do not prompt, automatically pick the first choice for all prompts')),
36 _('do not prompt, automatically pick the first choice for all prompts')),
37 ('q', 'quiet', None, _('suppress output')),
37 ('q', 'quiet', None, _('suppress output')),
38 ('v', 'verbose', None, _('enable additional output')),
38 ('v', 'verbose', None, _('enable additional output')),
39 ('', 'config', [],
39 ('', 'config', [],
40 _('set/override config option (use \'section.name=value\')'),
40 _('set/override config option (use \'section.name=value\')'),
41 _('CONFIG')),
41 _('CONFIG')),
42 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debug', None, _('enable debugging output')),
43 ('', 'debugger', None, _('start debugger')),
43 ('', 'debugger', None, _('start debugger')),
44 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
45 _('ENCODE')),
45 _('ENCODE')),
46 ('', 'encodingmode', encoding.encodingmode,
46 ('', 'encodingmode', encoding.encodingmode,
47 _('set the charset encoding mode'), _('MODE')),
47 _('set the charset encoding mode'), _('MODE')),
48 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'traceback', None, _('always print a traceback on exception')),
49 ('', 'time', None, _('time how long the command takes')),
49 ('', 'time', None, _('time how long the command takes')),
50 ('', 'profile', None, _('print command execution profile')),
50 ('', 'profile', None, _('print command execution profile')),
51 ('', 'version', None, _('output version information and exit')),
51 ('', 'version', None, _('output version information and exit')),
52 ('h', 'help', None, _('display help and exit')),
52 ('h', 'help', None, _('display help and exit')),
53 ]
53 ]
54
54
55 dryrunopts = [('n', 'dry-run', None,
55 dryrunopts = [('n', 'dry-run', None,
56 _('do not perform actions, just print output'))]
56 _('do not perform actions, just print output'))]
57
57
58 remoteopts = [
58 remoteopts = [
59 ('e', 'ssh', '',
59 ('e', 'ssh', '',
60 _('specify ssh command to use'), _('CMD')),
60 _('specify ssh command to use'), _('CMD')),
61 ('', 'remotecmd', '',
61 ('', 'remotecmd', '',
62 _('specify hg command to run on the remote side'), _('CMD')),
62 _('specify hg command to run on the remote side'), _('CMD')),
63 ('', 'insecure', None,
63 ('', 'insecure', None,
64 _('do not verify server certificate (ignoring web.cacerts config)')),
64 _('do not verify server certificate (ignoring web.cacerts config)')),
65 ]
65 ]
66
66
67 walkopts = [
67 walkopts = [
68 ('I', 'include', [],
68 ('I', 'include', [],
69 _('include names matching the given patterns'), _('PATTERN')),
69 _('include names matching the given patterns'), _('PATTERN')),
70 ('X', 'exclude', [],
70 ('X', 'exclude', [],
71 _('exclude names matching the given patterns'), _('PATTERN')),
71 _('exclude names matching the given patterns'), _('PATTERN')),
72 ]
72 ]
73
73
74 commitopts = [
74 commitopts = [
75 ('m', 'message', '',
75 ('m', 'message', '',
76 _('use text as commit message'), _('TEXT')),
76 _('use text as commit message'), _('TEXT')),
77 ('l', 'logfile', '',
77 ('l', 'logfile', '',
78 _('read commit message from file'), _('FILE')),
78 _('read commit message from file'), _('FILE')),
79 ]
79 ]
80
80
81 commitopts2 = [
81 commitopts2 = [
82 ('d', 'date', '',
82 ('d', 'date', '',
83 _('record the specified date as commit date'), _('DATE')),
83 _('record the specified date as commit date'), _('DATE')),
84 ('u', 'user', '',
84 ('u', 'user', '',
85 _('record the specified user as committer'), _('USER')),
85 _('record the specified user as committer'), _('USER')),
86 ]
86 ]
87
87
88 templateopts = [
88 templateopts = [
89 ('', 'style', '',
89 ('', 'style', '',
90 _('display using template map file'), _('STYLE')),
90 _('display using template map file'), _('STYLE')),
91 ('', 'template', '',
91 ('', 'template', '',
92 _('display with template'), _('TEMPLATE')),
92 _('display with template'), _('TEMPLATE')),
93 ]
93 ]
94
94
95 logopts = [
95 logopts = [
96 ('p', 'patch', None, _('show patch')),
96 ('p', 'patch', None, _('show patch')),
97 ('g', 'git', None, _('use git extended diff format')),
97 ('g', 'git', None, _('use git extended diff format')),
98 ('l', 'limit', '',
98 ('l', 'limit', '',
99 _('limit number of changes displayed'), _('NUM')),
99 _('limit number of changes displayed'), _('NUM')),
100 ('M', 'no-merges', None, _('do not show merges')),
100 ('M', 'no-merges', None, _('do not show merges')),
101 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ('', 'stat', None, _('output diffstat-style summary of changes')),
102 ] + templateopts
102 ] + templateopts
103
103
104 diffopts = [
104 diffopts = [
105 ('a', 'text', None, _('treat all files as text')),
105 ('a', 'text', None, _('treat all files as text')),
106 ('g', 'git', None, _('use git extended diff format')),
106 ('g', 'git', None, _('use git extended diff format')),
107 ('', 'nodates', None, _('omit dates from diff headers'))
107 ('', 'nodates', None, _('omit dates from diff headers'))
108 ]
108 ]
109
109
110 diffwsopts = [
110 diffwsopts = [
111 ('w', 'ignore-all-space', None,
111 ('w', 'ignore-all-space', None,
112 _('ignore white space when comparing lines')),
112 _('ignore white space when comparing lines')),
113 ('b', 'ignore-space-change', None,
113 ('b', 'ignore-space-change', None,
114 _('ignore changes in the amount of white space')),
114 _('ignore changes in the amount of white space')),
115 ('B', 'ignore-blank-lines', None,
115 ('B', 'ignore-blank-lines', None,
116 _('ignore changes whose lines are all blank')),
116 _('ignore changes whose lines are all blank')),
117 ]
117 ]
118
118
119 diffopts2 = [
119 diffopts2 = [
120 ('p', 'show-function', None, _('show which function each change is in')),
120 ('p', 'show-function', None, _('show which function each change is in')),
121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 ] + diffwsopts + [
122 ] + diffwsopts + [
123 ('U', 'unified', '',
123 ('U', 'unified', '',
124 _('number of lines of context to show'), _('NUM')),
124 _('number of lines of context to show'), _('NUM')),
125 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 ]
126 ]
127
127
128 mergetoolopts = [
128 mergetoolopts = [
129 ('t', 'tool', '', _('specify merge tool')),
129 ('t', 'tool', '', _('specify merge tool')),
130 ]
130 ]
131
131
132 similarityopts = [
132 similarityopts = [
133 ('s', 'similarity', '',
133 ('s', 'similarity', '',
134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 ]
135 ]
136
136
137 subrepoopts = [
137 subrepoopts = [
138 ('S', 'subrepos', None,
138 ('S', 'subrepos', None,
139 _('recurse into subrepositories'))
139 _('recurse into subrepositories'))
140 ]
140 ]
141
141
142 # Commands start here, listed alphabetically
142 # Commands start here, listed alphabetically
143
143
144 @command('^add',
144 @command('^add',
145 walkopts + subrepoopts + dryrunopts,
145 walkopts + subrepoopts + dryrunopts,
146 _('[OPTION]... [FILE]...'))
146 _('[OPTION]... [FILE]...'))
147 def add(ui, repo, *pats, **opts):
147 def add(ui, repo, *pats, **opts):
148 """add the specified files on the next commit
148 """add the specified files on the next commit
149
149
150 Schedule files to be version controlled and added to the
150 Schedule files to be version controlled and added to the
151 repository.
151 repository.
152
152
153 The files will be added to the repository at the next commit. To
153 The files will be added to the repository at the next commit. To
154 undo an add before that, see :hg:`forget`.
154 undo an add before that, see :hg:`forget`.
155
155
156 If no names are given, add all files to the repository.
156 If no names are given, add all files to the repository.
157
157
158 .. container:: verbose
158 .. container:: verbose
159
159
160 An example showing how new (unknown) files are added
160 An example showing how new (unknown) files are added
161 automatically by :hg:`add`::
161 automatically by :hg:`add`::
162
162
163 $ ls
163 $ ls
164 foo.c
164 foo.c
165 $ hg status
165 $ hg status
166 ? foo.c
166 ? foo.c
167 $ hg add
167 $ hg add
168 adding foo.c
168 adding foo.c
169 $ hg status
169 $ hg status
170 A foo.c
170 A foo.c
171
171
172 Returns 0 if all files are successfully added.
172 Returns 0 if all files are successfully added.
173 """
173 """
174
174
175 m = scmutil.match(repo[None], pats, opts)
175 m = scmutil.match(repo[None], pats, opts)
176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 opts.get('subrepos'), prefix="")
177 opts.get('subrepos'), prefix="")
178 return rejected and 1 or 0
178 return rejected and 1 or 0
179
179
180 @command('addremove',
180 @command('addremove',
181 similarityopts + walkopts + dryrunopts,
181 similarityopts + walkopts + dryrunopts,
182 _('[OPTION]... [FILE]...'))
182 _('[OPTION]... [FILE]...'))
183 def addremove(ui, repo, *pats, **opts):
183 def addremove(ui, repo, *pats, **opts):
184 """add all new files, delete all missing files
184 """add all new files, delete all missing files
185
185
186 Add all new files and remove all missing files from the
186 Add all new files and remove all missing files from the
187 repository.
187 repository.
188
188
189 New files are ignored if they match any of the patterns in
189 New files are ignored if they match any of the patterns in
190 ``.hgignore``. As with add, these changes take effect at the next
190 ``.hgignore``. As with add, these changes take effect at the next
191 commit.
191 commit.
192
192
193 Use the -s/--similarity option to detect renamed files. With a
193 Use the -s/--similarity option to detect renamed files. With a
194 parameter greater than 0, this compares every removed file with
194 parameter greater than 0, this compares every removed file with
195 every added file and records those similar enough as renames. This
195 every added file and records those similar enough as renames. This
196 option takes a percentage between 0 (disabled) and 100 (files must
196 option takes a percentage between 0 (disabled) and 100 (files must
197 be identical) as its parameter. Detecting renamed files this way
197 be identical) as its parameter. Detecting renamed files this way
198 can be expensive. After using this option, :hg:`status -C` can be
198 can be expensive. After using this option, :hg:`status -C` can be
199 used to check which files were identified as moved or renamed.
199 used to check which files were identified as moved or renamed.
200
200
201 Returns 0 if all files are successfully added.
201 Returns 0 if all files are successfully added.
202 """
202 """
203 try:
203 try:
204 sim = float(opts.get('similarity') or 100)
204 sim = float(opts.get('similarity') or 100)
205 except ValueError:
205 except ValueError:
206 raise util.Abort(_('similarity must be a number'))
206 raise util.Abort(_('similarity must be a number'))
207 if sim < 0 or sim > 100:
207 if sim < 0 or sim > 100:
208 raise util.Abort(_('similarity must be between 0 and 100'))
208 raise util.Abort(_('similarity must be between 0 and 100'))
209 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
209 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
210
210
211 @command('^annotate|blame',
211 @command('^annotate|blame',
212 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
212 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
213 ('', 'follow', None,
213 ('', 'follow', None,
214 _('follow copies/renames and list the filename (DEPRECATED)')),
214 _('follow copies/renames and list the filename (DEPRECATED)')),
215 ('', 'no-follow', None, _("don't follow copies and renames")),
215 ('', 'no-follow', None, _("don't follow copies and renames")),
216 ('a', 'text', None, _('treat all files as text')),
216 ('a', 'text', None, _('treat all files as text')),
217 ('u', 'user', None, _('list the author (long with -v)')),
217 ('u', 'user', None, _('list the author (long with -v)')),
218 ('f', 'file', None, _('list the filename')),
218 ('f', 'file', None, _('list the filename')),
219 ('d', 'date', None, _('list the date (short with -q)')),
219 ('d', 'date', None, _('list the date (short with -q)')),
220 ('n', 'number', None, _('list the revision number (default)')),
220 ('n', 'number', None, _('list the revision number (default)')),
221 ('c', 'changeset', None, _('list the changeset')),
221 ('c', 'changeset', None, _('list the changeset')),
222 ('l', 'line-number', None, _('show line number at the first appearance'))
222 ('l', 'line-number', None, _('show line number at the first appearance'))
223 ] + diffwsopts + walkopts,
223 ] + diffwsopts + walkopts,
224 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
224 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
225 def annotate(ui, repo, *pats, **opts):
225 def annotate(ui, repo, *pats, **opts):
226 """show changeset information by line for each file
226 """show changeset information by line for each file
227
227
228 List changes in files, showing the revision id responsible for
228 List changes in files, showing the revision id responsible for
229 each line
229 each line
230
230
231 This command is useful for discovering when a change was made and
231 This command is useful for discovering when a change was made and
232 by whom.
232 by whom.
233
233
234 Without the -a/--text option, annotate will avoid processing files
234 Without the -a/--text option, annotate will avoid processing files
235 it detects as binary. With -a, annotate will annotate the file
235 it detects as binary. With -a, annotate will annotate the file
236 anyway, although the results will probably be neither useful
236 anyway, although the results will probably be neither useful
237 nor desirable.
237 nor desirable.
238
238
239 Returns 0 on success.
239 Returns 0 on success.
240 """
240 """
241 if opts.get('follow'):
241 if opts.get('follow'):
242 # --follow is deprecated and now just an alias for -f/--file
242 # --follow is deprecated and now just an alias for -f/--file
243 # to mimic the behavior of Mercurial before version 1.5
243 # to mimic the behavior of Mercurial before version 1.5
244 opts['file'] = True
244 opts['file'] = True
245
245
246 datefunc = ui.quiet and util.shortdate or util.datestr
246 datefunc = ui.quiet and util.shortdate or util.datestr
247 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
248
248
249 if not pats:
249 if not pats:
250 raise util.Abort(_('at least one filename or pattern is required'))
250 raise util.Abort(_('at least one filename or pattern is required'))
251
251
252 hexfn = ui.debugflag and hex or short
252 hexfn = ui.debugflag and hex or short
253
253
254 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
254 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
255 ('number', ' ', lambda x: str(x[0].rev())),
255 ('number', ' ', lambda x: str(x[0].rev())),
256 ('changeset', ' ', lambda x: hexfn(x[0].node())),
256 ('changeset', ' ', lambda x: hexfn(x[0].node())),
257 ('date', ' ', getdate),
257 ('date', ' ', getdate),
258 ('file', ' ', lambda x: x[0].path()),
258 ('file', ' ', lambda x: x[0].path()),
259 ('line_number', ':', lambda x: str(x[1])),
259 ('line_number', ':', lambda x: str(x[1])),
260 ]
260 ]
261
261
262 if (not opts.get('user') and not opts.get('changeset')
262 if (not opts.get('user') and not opts.get('changeset')
263 and not opts.get('date') and not opts.get('file')):
263 and not opts.get('date') and not opts.get('file')):
264 opts['number'] = True
264 opts['number'] = True
265
265
266 linenumber = opts.get('line_number') is not None
266 linenumber = opts.get('line_number') is not None
267 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
267 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
268 raise util.Abort(_('at least one of -n/-c is required for -l'))
268 raise util.Abort(_('at least one of -n/-c is required for -l'))
269
269
270 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
270 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
271 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
271 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
272
272
273 def bad(x, y):
273 def bad(x, y):
274 raise util.Abort("%s: %s" % (x, y))
274 raise util.Abort("%s: %s" % (x, y))
275
275
276 ctx = scmutil.revsingle(repo, opts.get('rev'))
276 ctx = scmutil.revsingle(repo, opts.get('rev'))
277 m = scmutil.match(ctx, pats, opts)
277 m = scmutil.match(ctx, pats, opts)
278 m.bad = bad
278 m.bad = bad
279 follow = not opts.get('no_follow')
279 follow = not opts.get('no_follow')
280 diffopts = patch.diffopts(ui, opts, section='annotate')
280 diffopts = patch.diffopts(ui, opts, section='annotate')
281 for abs in ctx.walk(m):
281 for abs in ctx.walk(m):
282 fctx = ctx[abs]
282 fctx = ctx[abs]
283 if not opts.get('text') and util.binary(fctx.data()):
283 if not opts.get('text') and util.binary(fctx.data()):
284 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
284 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
285 continue
285 continue
286
286
287 lines = fctx.annotate(follow=follow, linenumber=linenumber,
287 lines = fctx.annotate(follow=follow, linenumber=linenumber,
288 diffopts=diffopts)
288 diffopts=diffopts)
289 pieces = []
289 pieces = []
290
290
291 for f, sep in funcmap:
291 for f, sep in funcmap:
292 l = [f(n) for n, dummy in lines]
292 l = [f(n) for n, dummy in lines]
293 if l:
293 if l:
294 sized = [(x, encoding.colwidth(x)) for x in l]
294 sized = [(x, encoding.colwidth(x)) for x in l]
295 ml = max([w for x, w in sized])
295 ml = max([w for x, w in sized])
296 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
296 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
297 for x, w in sized])
297 for x, w in sized])
298
298
299 if pieces:
299 if pieces:
300 for p, l in zip(zip(*pieces), lines):
300 for p, l in zip(zip(*pieces), lines):
301 ui.write("%s: %s" % ("".join(p), l[1]))
301 ui.write("%s: %s" % ("".join(p), l[1]))
302
302
303 if lines and not lines[-1][1].endswith('\n'):
303 if lines and not lines[-1][1].endswith('\n'):
304 ui.write('\n')
304 ui.write('\n')
305
305
306 @command('archive',
306 @command('archive',
307 [('', 'no-decode', None, _('do not pass files through decoders')),
307 [('', 'no-decode', None, _('do not pass files through decoders')),
308 ('p', 'prefix', '', _('directory prefix for files in archive'),
308 ('p', 'prefix', '', _('directory prefix for files in archive'),
309 _('PREFIX')),
309 _('PREFIX')),
310 ('r', 'rev', '', _('revision to distribute'), _('REV')),
310 ('r', 'rev', '', _('revision to distribute'), _('REV')),
311 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
311 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
312 ] + subrepoopts + walkopts,
312 ] + subrepoopts + walkopts,
313 _('[OPTION]... DEST'))
313 _('[OPTION]... DEST'))
314 def archive(ui, repo, dest, **opts):
314 def archive(ui, repo, dest, **opts):
315 '''create an unversioned archive of a repository revision
315 '''create an unversioned archive of a repository revision
316
316
317 By default, the revision used is the parent of the working
317 By default, the revision used is the parent of the working
318 directory; use -r/--rev to specify a different revision.
318 directory; use -r/--rev to specify a different revision.
319
319
320 The archive type is automatically detected based on file
320 The archive type is automatically detected based on file
321 extension (or override using -t/--type).
321 extension (or override using -t/--type).
322
322
323 .. container:: verbose
323 .. container:: verbose
324
324
325 Examples:
325 Examples:
326
326
327 - create a zip file containing the 1.0 release::
327 - create a zip file containing the 1.0 release::
328
328
329 hg archive -r 1.0 project-1.0.zip
329 hg archive -r 1.0 project-1.0.zip
330
330
331 - create a tarball excluding .hg files::
331 - create a tarball excluding .hg files::
332
332
333 hg archive project.tar.gz -X ".hg*"
333 hg archive project.tar.gz -X ".hg*"
334
334
335 Valid types are:
335 Valid types are:
336
336
337 :``files``: a directory full of files (default)
337 :``files``: a directory full of files (default)
338 :``tar``: tar archive, uncompressed
338 :``tar``: tar archive, uncompressed
339 :``tbz2``: tar archive, compressed using bzip2
339 :``tbz2``: tar archive, compressed using bzip2
340 :``tgz``: tar archive, compressed using gzip
340 :``tgz``: tar archive, compressed using gzip
341 :``uzip``: zip archive, uncompressed
341 :``uzip``: zip archive, uncompressed
342 :``zip``: zip archive, compressed using deflate
342 :``zip``: zip archive, compressed using deflate
343
343
344 The exact name of the destination archive or directory is given
344 The exact name of the destination archive or directory is given
345 using a format string; see :hg:`help export` for details.
345 using a format string; see :hg:`help export` for details.
346
346
347 Each member added to an archive file has a directory prefix
347 Each member added to an archive file has a directory prefix
348 prepended. Use -p/--prefix to specify a format string for the
348 prepended. Use -p/--prefix to specify a format string for the
349 prefix. The default is the basename of the archive, with suffixes
349 prefix. The default is the basename of the archive, with suffixes
350 removed.
350 removed.
351
351
352 Returns 0 on success.
352 Returns 0 on success.
353 '''
353 '''
354
354
355 ctx = scmutil.revsingle(repo, opts.get('rev'))
355 ctx = scmutil.revsingle(repo, opts.get('rev'))
356 if not ctx:
356 if not ctx:
357 raise util.Abort(_('no working directory: please specify a revision'))
357 raise util.Abort(_('no working directory: please specify a revision'))
358 node = ctx.node()
358 node = ctx.node()
359 dest = cmdutil.makefilename(repo, dest, node)
359 dest = cmdutil.makefilename(repo, dest, node)
360 if os.path.realpath(dest) == repo.root:
360 if os.path.realpath(dest) == repo.root:
361 raise util.Abort(_('repository root cannot be destination'))
361 raise util.Abort(_('repository root cannot be destination'))
362
362
363 kind = opts.get('type') or archival.guesskind(dest) or 'files'
363 kind = opts.get('type') or archival.guesskind(dest) or 'files'
364 prefix = opts.get('prefix')
364 prefix = opts.get('prefix')
365
365
366 if dest == '-':
366 if dest == '-':
367 if kind == 'files':
367 if kind == 'files':
368 raise util.Abort(_('cannot archive plain files to stdout'))
368 raise util.Abort(_('cannot archive plain files to stdout'))
369 dest = cmdutil.makefileobj(repo, dest)
369 dest = cmdutil.makefileobj(repo, dest)
370 if not prefix:
370 if not prefix:
371 prefix = os.path.basename(repo.root) + '-%h'
371 prefix = os.path.basename(repo.root) + '-%h'
372
372
373 prefix = cmdutil.makefilename(repo, prefix, node)
373 prefix = cmdutil.makefilename(repo, prefix, node)
374 matchfn = scmutil.match(ctx, [], opts)
374 matchfn = scmutil.match(ctx, [], opts)
375 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
375 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
376 matchfn, prefix, subrepos=opts.get('subrepos'))
376 matchfn, prefix, subrepos=opts.get('subrepos'))
377
377
378 @command('backout',
378 @command('backout',
379 [('', 'merge', None, _('merge with old dirstate parent after backout')),
379 [('', 'merge', None, _('merge with old dirstate parent after backout')),
380 ('', 'parent', '',
380 ('', 'parent', '',
381 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
381 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
382 ('r', 'rev', '', _('revision to backout'), _('REV')),
382 ('r', 'rev', '', _('revision to backout'), _('REV')),
383 ] + mergetoolopts + walkopts + commitopts + commitopts2,
383 ] + mergetoolopts + walkopts + commitopts + commitopts2,
384 _('[OPTION]... [-r] REV'))
384 _('[OPTION]... [-r] REV'))
385 def backout(ui, repo, node=None, rev=None, **opts):
385 def backout(ui, repo, node=None, rev=None, **opts):
386 '''reverse effect of earlier changeset
386 '''reverse effect of earlier changeset
387
387
388 Prepare a new changeset with the effect of REV undone in the
388 Prepare a new changeset with the effect of REV undone in the
389 current working directory.
389 current working directory.
390
390
391 If REV is the parent of the working directory, then this new changeset
391 If REV is the parent of the working directory, then this new changeset
392 is committed automatically. Otherwise, hg needs to merge the
392 is committed automatically. Otherwise, hg needs to merge the
393 changes and the merged result is left uncommitted.
393 changes and the merged result is left uncommitted.
394
394
395 .. note::
395 .. note::
396 backout cannot be used to fix either an unwanted or
396 backout cannot be used to fix either an unwanted or
397 incorrect merge.
397 incorrect merge.
398
398
399 .. container:: verbose
399 .. container:: verbose
400
400
401 By default, the pending changeset will have one parent,
401 By default, the pending changeset will have one parent,
402 maintaining a linear history. With --merge, the pending
402 maintaining a linear history. With --merge, the pending
403 changeset will instead have two parents: the old parent of the
403 changeset will instead have two parents: the old parent of the
404 working directory and a new child of REV that simply undoes REV.
404 working directory and a new child of REV that simply undoes REV.
405
405
406 Before version 1.7, the behavior without --merge was equivalent
406 Before version 1.7, the behavior without --merge was equivalent
407 to specifying --merge followed by :hg:`update --clean .` to
407 to specifying --merge followed by :hg:`update --clean .` to
408 cancel the merge and leave the child of REV as a head to be
408 cancel the merge and leave the child of REV as a head to be
409 merged separately.
409 merged separately.
410
410
411 See :hg:`help dates` for a list of formats valid for -d/--date.
411 See :hg:`help dates` for a list of formats valid for -d/--date.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 '''
414 '''
415 if rev and node:
415 if rev and node:
416 raise util.Abort(_("please specify just one revision"))
416 raise util.Abort(_("please specify just one revision"))
417
417
418 if not rev:
418 if not rev:
419 rev = node
419 rev = node
420
420
421 if not rev:
421 if not rev:
422 raise util.Abort(_("please specify a revision to backout"))
422 raise util.Abort(_("please specify a revision to backout"))
423
423
424 date = opts.get('date')
424 date = opts.get('date')
425 if date:
425 if date:
426 opts['date'] = util.parsedate(date)
426 opts['date'] = util.parsedate(date)
427
427
428 cmdutil.bailifchanged(repo)
428 cmdutil.bailifchanged(repo)
429 node = scmutil.revsingle(repo, rev).node()
429 node = scmutil.revsingle(repo, rev).node()
430
430
431 op1, op2 = repo.dirstate.parents()
431 op1, op2 = repo.dirstate.parents()
432 a = repo.changelog.ancestor(op1, node)
432 a = repo.changelog.ancestor(op1, node)
433 if a != node:
433 if a != node:
434 raise util.Abort(_('cannot backout change on a different branch'))
434 raise util.Abort(_('cannot backout change on a different branch'))
435
435
436 p1, p2 = repo.changelog.parents(node)
436 p1, p2 = repo.changelog.parents(node)
437 if p1 == nullid:
437 if p1 == nullid:
438 raise util.Abort(_('cannot backout a change with no parents'))
438 raise util.Abort(_('cannot backout a change with no parents'))
439 if p2 != nullid:
439 if p2 != nullid:
440 if not opts.get('parent'):
440 if not opts.get('parent'):
441 raise util.Abort(_('cannot backout a merge changeset'))
441 raise util.Abort(_('cannot backout a merge changeset'))
442 p = repo.lookup(opts['parent'])
442 p = repo.lookup(opts['parent'])
443 if p not in (p1, p2):
443 if p not in (p1, p2):
444 raise util.Abort(_('%s is not a parent of %s') %
444 raise util.Abort(_('%s is not a parent of %s') %
445 (short(p), short(node)))
445 (short(p), short(node)))
446 parent = p
446 parent = p
447 else:
447 else:
448 if opts.get('parent'):
448 if opts.get('parent'):
449 raise util.Abort(_('cannot use --parent on non-merge changeset'))
449 raise util.Abort(_('cannot use --parent on non-merge changeset'))
450 parent = p1
450 parent = p1
451
451
452 # the backout should appear on the same branch
452 # the backout should appear on the same branch
453 branch = repo.dirstate.branch()
453 branch = repo.dirstate.branch()
454 hg.clean(repo, node, show_stats=False)
454 hg.clean(repo, node, show_stats=False)
455 repo.dirstate.setbranch(branch)
455 repo.dirstate.setbranch(branch)
456 revert_opts = opts.copy()
456 revert_opts = opts.copy()
457 revert_opts['date'] = None
457 revert_opts['date'] = None
458 revert_opts['all'] = True
458 revert_opts['all'] = True
459 revert_opts['rev'] = hex(parent)
459 revert_opts['rev'] = hex(parent)
460 revert_opts['no_backup'] = None
460 revert_opts['no_backup'] = None
461 revert(ui, repo, **revert_opts)
461 revert(ui, repo, **revert_opts)
462 if not opts.get('merge') and op1 != node:
462 if not opts.get('merge') and op1 != node:
463 try:
463 try:
464 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
464 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
465 return hg.update(repo, op1)
465 return hg.update(repo, op1)
466 finally:
466 finally:
467 ui.setconfig('ui', 'forcemerge', '')
467 ui.setconfig('ui', 'forcemerge', '')
468
468
469 commit_opts = opts.copy()
469 commit_opts = opts.copy()
470 commit_opts['addremove'] = False
470 commit_opts['addremove'] = False
471 if not commit_opts['message'] and not commit_opts['logfile']:
471 if not commit_opts['message'] and not commit_opts['logfile']:
472 # we don't translate commit messages
472 # we don't translate commit messages
473 commit_opts['message'] = "Backed out changeset %s" % short(node)
473 commit_opts['message'] = "Backed out changeset %s" % short(node)
474 commit_opts['force_editor'] = True
474 commit_opts['force_editor'] = True
475 commit(ui, repo, **commit_opts)
475 commit(ui, repo, **commit_opts)
476 def nice(node):
476 def nice(node):
477 return '%d:%s' % (repo.changelog.rev(node), short(node))
477 return '%d:%s' % (repo.changelog.rev(node), short(node))
478 ui.status(_('changeset %s backs out changeset %s\n') %
478 ui.status(_('changeset %s backs out changeset %s\n') %
479 (nice(repo.changelog.tip()), nice(node)))
479 (nice(repo.changelog.tip()), nice(node)))
480 if opts.get('merge') and op1 != node:
480 if opts.get('merge') and op1 != node:
481 hg.clean(repo, op1, show_stats=False)
481 hg.clean(repo, op1, show_stats=False)
482 ui.status(_('merging with changeset %s\n')
482 ui.status(_('merging with changeset %s\n')
483 % nice(repo.changelog.tip()))
483 % nice(repo.changelog.tip()))
484 try:
484 try:
485 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
485 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
486 return hg.merge(repo, hex(repo.changelog.tip()))
486 return hg.merge(repo, hex(repo.changelog.tip()))
487 finally:
487 finally:
488 ui.setconfig('ui', 'forcemerge', '')
488 ui.setconfig('ui', 'forcemerge', '')
489 return 0
489 return 0
490
490
491 @command('bisect',
491 @command('bisect',
492 [('r', 'reset', False, _('reset bisect state')),
492 [('r', 'reset', False, _('reset bisect state')),
493 ('g', 'good', False, _('mark changeset good')),
493 ('g', 'good', False, _('mark changeset good')),
494 ('b', 'bad', False, _('mark changeset bad')),
494 ('b', 'bad', False, _('mark changeset bad')),
495 ('s', 'skip', False, _('skip testing changeset')),
495 ('s', 'skip', False, _('skip testing changeset')),
496 ('e', 'extend', False, _('extend the bisect range')),
496 ('e', 'extend', False, _('extend the bisect range')),
497 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
497 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
498 ('U', 'noupdate', False, _('do not update to target'))],
498 ('U', 'noupdate', False, _('do not update to target'))],
499 _("[-gbsr] [-U] [-c CMD] [REV]"))
499 _("[-gbsr] [-U] [-c CMD] [REV]"))
500 def bisect(ui, repo, rev=None, extra=None, command=None,
500 def bisect(ui, repo, rev=None, extra=None, command=None,
501 reset=None, good=None, bad=None, skip=None, extend=None,
501 reset=None, good=None, bad=None, skip=None, extend=None,
502 noupdate=None):
502 noupdate=None):
503 """subdivision search of changesets
503 """subdivision search of changesets
504
504
505 This command helps to find changesets which introduce problems. To
505 This command helps to find changesets which introduce problems. To
506 use, mark the earliest changeset you know exhibits the problem as
506 use, mark the earliest changeset you know exhibits the problem as
507 bad, then mark the latest changeset which is free from the problem
507 bad, then mark the latest changeset which is free from the problem
508 as good. Bisect will update your working directory to a revision
508 as good. Bisect will update your working directory to a revision
509 for testing (unless the -U/--noupdate option is specified). Once
509 for testing (unless the -U/--noupdate option is specified). Once
510 you have performed tests, mark the working directory as good or
510 you have performed tests, mark the working directory as good or
511 bad, and bisect will either update to another candidate changeset
511 bad, and bisect will either update to another candidate changeset
512 or announce that it has found the bad revision.
512 or announce that it has found the bad revision.
513
513
514 As a shortcut, you can also use the revision argument to mark a
514 As a shortcut, you can also use the revision argument to mark a
515 revision as good or bad without checking it out first.
515 revision as good or bad without checking it out first.
516
516
517 If you supply a command, it will be used for automatic bisection.
517 If you supply a command, it will be used for automatic bisection.
518 Its exit status will be used to mark revisions as good or bad:
518 Its exit status will be used to mark revisions as good or bad:
519 status 0 means good, 125 means to skip the revision, 127
519 status 0 means good, 125 means to skip the revision, 127
520 (command not found) will abort the bisection, and any other
520 (command not found) will abort the bisection, and any other
521 non-zero exit status means the revision is bad.
521 non-zero exit status means the revision is bad.
522
522
523 .. container:: verbose
523 .. container:: verbose
524
524
525 Some examples:
525 Some examples:
526
526
527 - start a bisection with known bad revision 12, and good revision 34::
527 - start a bisection with known bad revision 12, and good revision 34::
528
528
529 hg bisect --bad 34
529 hg bisect --bad 34
530 hg bisect --good 12
530 hg bisect --good 12
531
531
532 - advance the current bisection by marking current revision as good or
532 - advance the current bisection by marking current revision as good or
533 bad::
533 bad::
534
534
535 hg bisect --good
535 hg bisect --good
536 hg bisect --bad
536 hg bisect --bad
537
537
538 - mark the current revision, or a known revision, to be skipped (eg. if
538 - mark the current revision, or a known revision, to be skipped (eg. if
539 that revision is not usable because of another issue)::
539 that revision is not usable because of another issue)::
540
540
541 hg bisect --skip
541 hg bisect --skip
542 hg bisect --skip 23
542 hg bisect --skip 23
543
543
544 - forget the current bisection::
544 - forget the current bisection::
545
545
546 hg bisect --reset
546 hg bisect --reset
547
547
548 - use 'make && make tests' to automatically find the first broken
548 - use 'make && make tests' to automatically find the first broken
549 revision::
549 revision::
550
550
551 hg bisect --reset
551 hg bisect --reset
552 hg bisect --bad 34
552 hg bisect --bad 34
553 hg bisect --good 12
553 hg bisect --good 12
554 hg bisect --command 'make && make tests'
554 hg bisect --command 'make && make tests'
555
555
556 - see all changesets whose states are already known in the current
556 - see all changesets whose states are already known in the current
557 bisection::
557 bisection::
558
558
559 hg log -r "bisect(pruned)"
559 hg log -r "bisect(pruned)"
560
560
561 - see all changesets that took part in the current bisection::
561 - see all changesets that took part in the current bisection::
562
562
563 hg log -r "bisect(range)"
563 hg log -r "bisect(range)"
564
564
565 - with the graphlog extension, you can even get a nice graph::
565 - with the graphlog extension, you can even get a nice graph::
566
566
567 hg log --graph -r "bisect(range)"
567 hg log --graph -r "bisect(range)"
568
568
569 See :hg:`help revsets` for more about the `bisect()` keyword.
569 See :hg:`help revsets` for more about the `bisect()` keyword.
570
570
571 Returns 0 on success.
571 Returns 0 on success.
572 """
572 """
573 def extendbisectrange(nodes, good):
573 def extendbisectrange(nodes, good):
574 # bisect is incomplete when it ends on a merge node and
574 # bisect is incomplete when it ends on a merge node and
575 # one of the parent was not checked.
575 # one of the parent was not checked.
576 parents = repo[nodes[0]].parents()
576 parents = repo[nodes[0]].parents()
577 if len(parents) > 1:
577 if len(parents) > 1:
578 side = good and state['bad'] or state['good']
578 side = good and state['bad'] or state['good']
579 num = len(set(i.node() for i in parents) & set(side))
579 num = len(set(i.node() for i in parents) & set(side))
580 if num == 1:
580 if num == 1:
581 return parents[0].ancestor(parents[1])
581 return parents[0].ancestor(parents[1])
582 return None
582 return None
583
583
584 def print_result(nodes, good):
584 def print_result(nodes, good):
585 displayer = cmdutil.show_changeset(ui, repo, {})
585 displayer = cmdutil.show_changeset(ui, repo, {})
586 if len(nodes) == 1:
586 if len(nodes) == 1:
587 # narrowed it down to a single revision
587 # narrowed it down to a single revision
588 if good:
588 if good:
589 ui.write(_("The first good revision is:\n"))
589 ui.write(_("The first good revision is:\n"))
590 else:
590 else:
591 ui.write(_("The first bad revision is:\n"))
591 ui.write(_("The first bad revision is:\n"))
592 displayer.show(repo[nodes[0]])
592 displayer.show(repo[nodes[0]])
593 extendnode = extendbisectrange(nodes, good)
593 extendnode = extendbisectrange(nodes, good)
594 if extendnode is not None:
594 if extendnode is not None:
595 ui.write(_('Not all ancestors of this changeset have been'
595 ui.write(_('Not all ancestors of this changeset have been'
596 ' checked.\nUse bisect --extend to continue the '
596 ' checked.\nUse bisect --extend to continue the '
597 'bisection from\nthe common ancestor, %s.\n')
597 'bisection from\nthe common ancestor, %s.\n')
598 % extendnode)
598 % extendnode)
599 else:
599 else:
600 # multiple possible revisions
600 # multiple possible revisions
601 if good:
601 if good:
602 ui.write(_("Due to skipped revisions, the first "
602 ui.write(_("Due to skipped revisions, the first "
603 "good revision could be any of:\n"))
603 "good revision could be any of:\n"))
604 else:
604 else:
605 ui.write(_("Due to skipped revisions, the first "
605 ui.write(_("Due to skipped revisions, the first "
606 "bad revision could be any of:\n"))
606 "bad revision could be any of:\n"))
607 for n in nodes:
607 for n in nodes:
608 displayer.show(repo[n])
608 displayer.show(repo[n])
609 displayer.close()
609 displayer.close()
610
610
611 def check_state(state, interactive=True):
611 def check_state(state, interactive=True):
612 if not state['good'] or not state['bad']:
612 if not state['good'] or not state['bad']:
613 if (good or bad or skip or reset) and interactive:
613 if (good or bad or skip or reset) and interactive:
614 return
614 return
615 if not state['good']:
615 if not state['good']:
616 raise util.Abort(_('cannot bisect (no known good revisions)'))
616 raise util.Abort(_('cannot bisect (no known good revisions)'))
617 else:
617 else:
618 raise util.Abort(_('cannot bisect (no known bad revisions)'))
618 raise util.Abort(_('cannot bisect (no known bad revisions)'))
619 return True
619 return True
620
620
621 # backward compatibility
621 # backward compatibility
622 if rev in "good bad reset init".split():
622 if rev in "good bad reset init".split():
623 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
623 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
624 cmd, rev, extra = rev, extra, None
624 cmd, rev, extra = rev, extra, None
625 if cmd == "good":
625 if cmd == "good":
626 good = True
626 good = True
627 elif cmd == "bad":
627 elif cmd == "bad":
628 bad = True
628 bad = True
629 else:
629 else:
630 reset = True
630 reset = True
631 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
631 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
632 raise util.Abort(_('incompatible arguments'))
632 raise util.Abort(_('incompatible arguments'))
633
633
634 if reset:
634 if reset:
635 p = repo.join("bisect.state")
635 p = repo.join("bisect.state")
636 if os.path.exists(p):
636 if os.path.exists(p):
637 os.unlink(p)
637 os.unlink(p)
638 return
638 return
639
639
640 state = hbisect.load_state(repo)
640 state = hbisect.load_state(repo)
641
641
642 if command:
642 if command:
643 changesets = 1
643 changesets = 1
644 try:
644 try:
645 while changesets:
645 while changesets:
646 # update state
646 # update state
647 status = util.system(command, out=ui.fout)
647 status = util.system(command, out=ui.fout)
648 if status == 125:
648 if status == 125:
649 transition = "skip"
649 transition = "skip"
650 elif status == 0:
650 elif status == 0:
651 transition = "good"
651 transition = "good"
652 # status < 0 means process was killed
652 # status < 0 means process was killed
653 elif status == 127:
653 elif status == 127:
654 raise util.Abort(_("failed to execute %s") % command)
654 raise util.Abort(_("failed to execute %s") % command)
655 elif status < 0:
655 elif status < 0:
656 raise util.Abort(_("%s killed") % command)
656 raise util.Abort(_("%s killed") % command)
657 else:
657 else:
658 transition = "bad"
658 transition = "bad"
659 ctx = scmutil.revsingle(repo, rev)
659 ctx = scmutil.revsingle(repo, rev)
660 rev = None # clear for future iterations
660 rev = None # clear for future iterations
661 state[transition].append(ctx.node())
661 state[transition].append(ctx.node())
662 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
662 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
663 check_state(state, interactive=False)
663 check_state(state, interactive=False)
664 # bisect
664 # bisect
665 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
665 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
666 # update to next check
666 # update to next check
667 cmdutil.bailifchanged(repo)
667 cmdutil.bailifchanged(repo)
668 hg.clean(repo, nodes[0], show_stats=False)
668 hg.clean(repo, nodes[0], show_stats=False)
669 finally:
669 finally:
670 hbisect.save_state(repo, state)
670 hbisect.save_state(repo, state)
671 print_result(nodes, good)
671 print_result(nodes, good)
672 return
672 return
673
673
674 # update state
674 # update state
675
675
676 if rev:
676 if rev:
677 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
677 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
678 else:
678 else:
679 nodes = [repo.lookup('.')]
679 nodes = [repo.lookup('.')]
680
680
681 if good or bad or skip:
681 if good or bad or skip:
682 if good:
682 if good:
683 state['good'] += nodes
683 state['good'] += nodes
684 elif bad:
684 elif bad:
685 state['bad'] += nodes
685 state['bad'] += nodes
686 elif skip:
686 elif skip:
687 state['skip'] += nodes
687 state['skip'] += nodes
688 hbisect.save_state(repo, state)
688 hbisect.save_state(repo, state)
689
689
690 if not check_state(state):
690 if not check_state(state):
691 return
691 return
692
692
693 # actually bisect
693 # actually bisect
694 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
694 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
695 if extend:
695 if extend:
696 if not changesets:
696 if not changesets:
697 extendnode = extendbisectrange(nodes, good)
697 extendnode = extendbisectrange(nodes, good)
698 if extendnode is not None:
698 if extendnode is not None:
699 ui.write(_("Extending search to changeset %d:%s\n"
699 ui.write(_("Extending search to changeset %d:%s\n"
700 % (extendnode.rev(), extendnode)))
700 % (extendnode.rev(), extendnode)))
701 if noupdate:
701 if noupdate:
702 return
702 return
703 cmdutil.bailifchanged(repo)
703 cmdutil.bailifchanged(repo)
704 return hg.clean(repo, extendnode.node())
704 return hg.clean(repo, extendnode.node())
705 raise util.Abort(_("nothing to extend"))
705 raise util.Abort(_("nothing to extend"))
706
706
707 if changesets == 0:
707 if changesets == 0:
708 print_result(nodes, good)
708 print_result(nodes, good)
709 else:
709 else:
710 assert len(nodes) == 1 # only a single node can be tested next
710 assert len(nodes) == 1 # only a single node can be tested next
711 node = nodes[0]
711 node = nodes[0]
712 # compute the approximate number of remaining tests
712 # compute the approximate number of remaining tests
713 tests, size = 0, 2
713 tests, size = 0, 2
714 while size <= changesets:
714 while size <= changesets:
715 tests, size = tests + 1, size * 2
715 tests, size = tests + 1, size * 2
716 rev = repo.changelog.rev(node)
716 rev = repo.changelog.rev(node)
717 ui.write(_("Testing changeset %d:%s "
717 ui.write(_("Testing changeset %d:%s "
718 "(%d changesets remaining, ~%d tests)\n")
718 "(%d changesets remaining, ~%d tests)\n")
719 % (rev, short(node), changesets, tests))
719 % (rev, short(node), changesets, tests))
720 if not noupdate:
720 if not noupdate:
721 cmdutil.bailifchanged(repo)
721 cmdutil.bailifchanged(repo)
722 return hg.clean(repo, node)
722 return hg.clean(repo, node)
723
723
724 @command('bookmarks',
724 @command('bookmarks',
725 [('f', 'force', False, _('force')),
725 [('f', 'force', False, _('force')),
726 ('r', 'rev', '', _('revision'), _('REV')),
726 ('r', 'rev', '', _('revision'), _('REV')),
727 ('d', 'delete', False, _('delete a given bookmark')),
727 ('d', 'delete', False, _('delete a given bookmark')),
728 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
728 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
729 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
729 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
730 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
730 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
731 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
731 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
732 rename=None, inactive=False):
732 rename=None, inactive=False):
733 '''track a line of development with movable markers
733 '''track a line of development with movable markers
734
734
735 Bookmarks are pointers to certain commits that move when committing.
735 Bookmarks are pointers to certain commits that move when committing.
736 Bookmarks are local. They can be renamed, copied and deleted. It is
736 Bookmarks are local. They can be renamed, copied and deleted. It is
737 possible to use :hg:`merge NAME` to merge from a given bookmark, and
737 possible to use :hg:`merge NAME` to merge from a given bookmark, and
738 :hg:`update NAME` to update to a given bookmark.
738 :hg:`update NAME` to update to a given bookmark.
739
739
740 You can use :hg:`bookmark NAME` to set a bookmark on the working
740 You can use :hg:`bookmark NAME` to set a bookmark on the working
741 directory's parent revision with the given name. If you specify
741 directory's parent revision with the given name. If you specify
742 a revision using -r REV (where REV may be an existing bookmark),
742 a revision using -r REV (where REV may be an existing bookmark),
743 the bookmark is assigned to that revision.
743 the bookmark is assigned to that revision.
744
744
745 Bookmarks can be pushed and pulled between repositories (see :hg:`help
745 Bookmarks can be pushed and pulled between repositories (see :hg:`help
746 push` and :hg:`help pull`). This requires both the local and remote
746 push` and :hg:`help pull`). This requires both the local and remote
747 repositories to support bookmarks. For versions prior to 1.8, this means
747 repositories to support bookmarks. For versions prior to 1.8, this means
748 the bookmarks extension must be enabled.
748 the bookmarks extension must be enabled.
749 '''
749 '''
750 hexfn = ui.debugflag and hex or short
750 hexfn = ui.debugflag and hex or short
751 marks = repo._bookmarks
751 marks = repo._bookmarks
752 cur = repo.changectx('.').node()
752 cur = repo.changectx('.').node()
753
753
754 if delete:
754 if delete:
755 if mark is None:
755 if mark is None:
756 raise util.Abort(_("bookmark name required"))
756 raise util.Abort(_("bookmark name required"))
757 if mark not in marks:
757 if mark not in marks:
758 raise util.Abort(_("bookmark '%s' does not exist") % mark)
758 raise util.Abort(_("bookmark '%s' does not exist") % mark)
759 if mark == repo._bookmarkcurrent:
759 if mark == repo._bookmarkcurrent:
760 bookmarks.setcurrent(repo, None)
760 bookmarks.setcurrent(repo, None)
761 del marks[mark]
761 del marks[mark]
762 bookmarks.write(repo)
762 bookmarks.write(repo)
763 return
763 return
764
764
765 if rename:
765 if rename:
766 if rename not in marks:
766 if rename not in marks:
767 raise util.Abort(_("bookmark '%s' does not exist") % rename)
767 raise util.Abort(_("bookmark '%s' does not exist") % rename)
768 if mark in marks and not force:
768 if mark in marks and not force:
769 raise util.Abort(_("bookmark '%s' already exists "
769 raise util.Abort(_("bookmark '%s' already exists "
770 "(use -f to force)") % mark)
770 "(use -f to force)") % mark)
771 if mark is None:
771 if mark is None:
772 raise util.Abort(_("new bookmark name required"))
772 raise util.Abort(_("new bookmark name required"))
773 marks[mark] = marks[rename]
773 marks[mark] = marks[rename]
774 if repo._bookmarkcurrent == rename and not inactive:
774 if repo._bookmarkcurrent == rename and not inactive:
775 bookmarks.setcurrent(repo, mark)
775 bookmarks.setcurrent(repo, mark)
776 del marks[rename]
776 del marks[rename]
777 bookmarks.write(repo)
777 bookmarks.write(repo)
778 return
778 return
779
779
780 if mark is not None:
780 if mark is not None:
781 if "\n" in mark:
781 if "\n" in mark:
782 raise util.Abort(_("bookmark name cannot contain newlines"))
782 raise util.Abort(_("bookmark name cannot contain newlines"))
783 mark = mark.strip()
783 mark = mark.strip()
784 if not mark:
784 if not mark:
785 raise util.Abort(_("bookmark names cannot consist entirely of "
785 raise util.Abort(_("bookmark names cannot consist entirely of "
786 "whitespace"))
786 "whitespace"))
787 if inactive and mark == repo._bookmarkcurrent:
787 if inactive and mark == repo._bookmarkcurrent:
788 bookmarks.setcurrent(repo, None)
788 bookmarks.setcurrent(repo, None)
789 return
789 return
790 if mark in marks and not force:
790 if mark in marks and not force:
791 raise util.Abort(_("bookmark '%s' already exists "
791 raise util.Abort(_("bookmark '%s' already exists "
792 "(use -f to force)") % mark)
792 "(use -f to force)") % mark)
793 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
793 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
794 and not force):
794 and not force):
795 raise util.Abort(
795 raise util.Abort(
796 _("a bookmark cannot have the name of an existing branch"))
796 _("a bookmark cannot have the name of an existing branch"))
797 if rev:
797 if rev:
798 marks[mark] = repo.lookup(rev)
798 marks[mark] = repo.lookup(rev)
799 else:
799 else:
800 marks[mark] = cur
800 marks[mark] = cur
801 if not inactive and cur == marks[mark]:
801 if not inactive and cur == marks[mark]:
802 bookmarks.setcurrent(repo, mark)
802 bookmarks.setcurrent(repo, mark)
803 bookmarks.write(repo)
803 bookmarks.write(repo)
804 return
804 return
805
805
806 if mark is None:
806 if mark is None:
807 if rev:
807 if rev:
808 raise util.Abort(_("bookmark name required"))
808 raise util.Abort(_("bookmark name required"))
809 if len(marks) == 0:
809 if len(marks) == 0:
810 ui.status(_("no bookmarks set\n"))
810 ui.status(_("no bookmarks set\n"))
811 else:
811 else:
812 for bmark, n in sorted(marks.iteritems()):
812 for bmark, n in sorted(marks.iteritems()):
813 current = repo._bookmarkcurrent
813 current = repo._bookmarkcurrent
814 if bmark == current and n == cur:
814 if bmark == current and n == cur:
815 prefix, label = '*', 'bookmarks.current'
815 prefix, label = '*', 'bookmarks.current'
816 else:
816 else:
817 prefix, label = ' ', ''
817 prefix, label = ' ', ''
818
818
819 if ui.quiet:
819 if ui.quiet:
820 ui.write("%s\n" % bmark, label=label)
820 ui.write("%s\n" % bmark, label=label)
821 else:
821 else:
822 ui.write(" %s %-25s %d:%s\n" % (
822 ui.write(" %s %-25s %d:%s\n" % (
823 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
823 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
824 label=label)
824 label=label)
825 return
825 return
826
826
827 @command('branch',
827 @command('branch',
828 [('f', 'force', None,
828 [('f', 'force', None,
829 _('set branch name even if it shadows an existing branch')),
829 _('set branch name even if it shadows an existing branch')),
830 ('C', 'clean', None, _('reset branch name to parent branch name'))],
830 ('C', 'clean', None, _('reset branch name to parent branch name'))],
831 _('[-fC] [NAME]'))
831 _('[-fC] [NAME]'))
832 def branch(ui, repo, label=None, **opts):
832 def branch(ui, repo, label=None, **opts):
833 """set or show the current branch name
833 """set or show the current branch name
834
834
835 .. note::
835 .. note::
836 Branch names are permanent and global. Use :hg:`bookmark` to create a
836 Branch names are permanent and global. Use :hg:`bookmark` to create a
837 light-weight bookmark instead. See :hg:`help glossary` for more
837 light-weight bookmark instead. See :hg:`help glossary` for more
838 information about named branches and bookmarks.
838 information about named branches and bookmarks.
839
839
840 With no argument, show the current branch name. With one argument,
840 With no argument, show the current branch name. With one argument,
841 set the working directory branch name (the branch will not exist
841 set the working directory branch name (the branch will not exist
842 in the repository until the next commit). Standard practice
842 in the repository until the next commit). Standard practice
843 recommends that primary development take place on the 'default'
843 recommends that primary development take place on the 'default'
844 branch.
844 branch.
845
845
846 Unless -f/--force is specified, branch will not let you set a
846 Unless -f/--force is specified, branch will not let you set a
847 branch name that already exists, even if it's inactive.
847 branch name that already exists, even if it's inactive.
848
848
849 Use -C/--clean to reset the working directory branch to that of
849 Use -C/--clean to reset the working directory branch to that of
850 the parent of the working directory, negating a previous branch
850 the parent of the working directory, negating a previous branch
851 change.
851 change.
852
852
853 Use the command :hg:`update` to switch to an existing branch. Use
853 Use the command :hg:`update` to switch to an existing branch. Use
854 :hg:`commit --close-branch` to mark this branch as closed.
854 :hg:`commit --close-branch` to mark this branch as closed.
855
855
856 Returns 0 on success.
856 Returns 0 on success.
857 """
857 """
858
858
859 if opts.get('clean'):
859 if opts.get('clean'):
860 label = repo[None].p1().branch()
860 label = repo[None].p1().branch()
861 repo.dirstate.setbranch(label)
861 repo.dirstate.setbranch(label)
862 ui.status(_('reset working directory to branch %s\n') % label)
862 ui.status(_('reset working directory to branch %s\n') % label)
863 elif label:
863 elif label:
864 if not opts.get('force') and label in repo.branchtags():
864 if not opts.get('force') and label in repo.branchtags():
865 if label not in [p.branch() for p in repo.parents()]:
865 if label not in [p.branch() for p in repo.parents()]:
866 raise util.Abort(_('a branch of the same name already exists'),
866 raise util.Abort(_('a branch of the same name already exists'),
867 # i18n: "it" refers to an existing branch
867 # i18n: "it" refers to an existing branch
868 hint=_("use 'hg update' to switch to it"))
868 hint=_("use 'hg update' to switch to it"))
869 repo.dirstate.setbranch(label)
869 repo.dirstate.setbranch(label)
870 ui.status(_('marked working directory as branch %s\n') % label)
870 ui.status(_('marked working directory as branch %s\n') % label)
871 ui.status(_('(branches are permanent and global, '
871 ui.status(_('(branches are permanent and global, '
872 'did you want a bookmark?)\n'))
872 'did you want a bookmark?)\n'))
873 else:
873 else:
874 ui.write("%s\n" % repo.dirstate.branch())
874 ui.write("%s\n" % repo.dirstate.branch())
875
875
876 @command('branches',
876 @command('branches',
877 [('a', 'active', False, _('show only branches that have unmerged heads')),
877 [('a', 'active', False, _('show only branches that have unmerged heads')),
878 ('c', 'closed', False, _('show normal and closed branches'))],
878 ('c', 'closed', False, _('show normal and closed branches'))],
879 _('[-ac]'))
879 _('[-ac]'))
880 def branches(ui, repo, active=False, closed=False):
880 def branches(ui, repo, active=False, closed=False):
881 """list repository named branches
881 """list repository named branches
882
882
883 List the repository's named branches, indicating which ones are
883 List the repository's named branches, indicating which ones are
884 inactive. If -c/--closed is specified, also list branches which have
884 inactive. If -c/--closed is specified, also list branches which have
885 been marked closed (see :hg:`commit --close-branch`).
885 been marked closed (see :hg:`commit --close-branch`).
886
886
887 If -a/--active is specified, only show active branches. A branch
887 If -a/--active is specified, only show active branches. A branch
888 is considered active if it contains repository heads.
888 is considered active if it contains repository heads.
889
889
890 Use the command :hg:`update` to switch to an existing branch.
890 Use the command :hg:`update` to switch to an existing branch.
891
891
892 Returns 0.
892 Returns 0.
893 """
893 """
894
894
895 hexfunc = ui.debugflag and hex or short
895 hexfunc = ui.debugflag and hex or short
896 activebranches = [repo[n].branch() for n in repo.heads()]
896 activebranches = [repo[n].branch() for n in repo.heads()]
897 def testactive(tag, node):
897 def testactive(tag, node):
898 realhead = tag in activebranches
898 realhead = tag in activebranches
899 open = node in repo.branchheads(tag, closed=False)
899 open = node in repo.branchheads(tag, closed=False)
900 return realhead and open
900 return realhead and open
901 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
901 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
902 for tag, node in repo.branchtags().items()],
902 for tag, node in repo.branchtags().items()],
903 reverse=True)
903 reverse=True)
904
904
905 for isactive, node, tag in branches:
905 for isactive, node, tag in branches:
906 if (not active) or isactive:
906 if (not active) or isactive:
907 if ui.quiet:
907 if ui.quiet:
908 ui.write("%s\n" % tag)
908 ui.write("%s\n" % tag)
909 else:
909 else:
910 hn = repo.lookup(node)
910 hn = repo.lookup(node)
911 if isactive:
911 if isactive:
912 label = 'branches.active'
912 label = 'branches.active'
913 notice = ''
913 notice = ''
914 elif hn not in repo.branchheads(tag, closed=False):
914 elif hn not in repo.branchheads(tag, closed=False):
915 if not closed:
915 if not closed:
916 continue
916 continue
917 label = 'branches.closed'
917 label = 'branches.closed'
918 notice = _(' (closed)')
918 notice = _(' (closed)')
919 else:
919 else:
920 label = 'branches.inactive'
920 label = 'branches.inactive'
921 notice = _(' (inactive)')
921 notice = _(' (inactive)')
922 if tag == repo.dirstate.branch():
922 if tag == repo.dirstate.branch():
923 label = 'branches.current'
923 label = 'branches.current'
924 rev = str(node).rjust(31 - encoding.colwidth(tag))
924 rev = str(node).rjust(31 - encoding.colwidth(tag))
925 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
925 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
926 tag = ui.label(tag, label)
926 tag = ui.label(tag, label)
927 ui.write("%s %s%s\n" % (tag, rev, notice))
927 ui.write("%s %s%s\n" % (tag, rev, notice))
928
928
929 @command('bundle',
929 @command('bundle',
930 [('f', 'force', None, _('run even when the destination is unrelated')),
930 [('f', 'force', None, _('run even when the destination is unrelated')),
931 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
931 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
932 _('REV')),
932 _('REV')),
933 ('b', 'branch', [], _('a specific branch you would like to bundle'),
933 ('b', 'branch', [], _('a specific branch you would like to bundle'),
934 _('BRANCH')),
934 _('BRANCH')),
935 ('', 'base', [],
935 ('', 'base', [],
936 _('a base changeset assumed to be available at the destination'),
936 _('a base changeset assumed to be available at the destination'),
937 _('REV')),
937 _('REV')),
938 ('a', 'all', None, _('bundle all changesets in the repository')),
938 ('a', 'all', None, _('bundle all changesets in the repository')),
939 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
939 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
940 ] + remoteopts,
940 ] + remoteopts,
941 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
941 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
942 def bundle(ui, repo, fname, dest=None, **opts):
942 def bundle(ui, repo, fname, dest=None, **opts):
943 """create a changegroup file
943 """create a changegroup file
944
944
945 Generate a compressed changegroup file collecting changesets not
945 Generate a compressed changegroup file collecting changesets not
946 known to be in another repository.
946 known to be in another repository.
947
947
948 If you omit the destination repository, then hg assumes the
948 If you omit the destination repository, then hg assumes the
949 destination will have all the nodes you specify with --base
949 destination will have all the nodes you specify with --base
950 parameters. To create a bundle containing all changesets, use
950 parameters. To create a bundle containing all changesets, use
951 -a/--all (or --base null).
951 -a/--all (or --base null).
952
952
953 You can change compression method with the -t/--type option.
953 You can change compression method with the -t/--type option.
954 The available compression methods are: none, bzip2, and
954 The available compression methods are: none, bzip2, and
955 gzip (by default, bundles are compressed using bzip2).
955 gzip (by default, bundles are compressed using bzip2).
956
956
957 The bundle file can then be transferred using conventional means
957 The bundle file can then be transferred using conventional means
958 and applied to another repository with the unbundle or pull
958 and applied to another repository with the unbundle or pull
959 command. This is useful when direct push and pull are not
959 command. This is useful when direct push and pull are not
960 available or when exporting an entire repository is undesirable.
960 available or when exporting an entire repository is undesirable.
961
961
962 Applying bundles preserves all changeset contents including
962 Applying bundles preserves all changeset contents including
963 permissions, copy/rename information, and revision history.
963 permissions, copy/rename information, and revision history.
964
964
965 Returns 0 on success, 1 if no changes found.
965 Returns 0 on success, 1 if no changes found.
966 """
966 """
967 revs = None
967 revs = None
968 if 'rev' in opts:
968 if 'rev' in opts:
969 revs = scmutil.revrange(repo, opts['rev'])
969 revs = scmutil.revrange(repo, opts['rev'])
970
970
971 if opts.get('all'):
971 if opts.get('all'):
972 base = ['null']
972 base = ['null']
973 else:
973 else:
974 base = scmutil.revrange(repo, opts.get('base'))
974 base = scmutil.revrange(repo, opts.get('base'))
975 if base:
975 if base:
976 if dest:
976 if dest:
977 raise util.Abort(_("--base is incompatible with specifying "
977 raise util.Abort(_("--base is incompatible with specifying "
978 "a destination"))
978 "a destination"))
979 common = [repo.lookup(rev) for rev in base]
979 common = [repo.lookup(rev) for rev in base]
980 outheads = revs and map(repo.lookup, revs) or revs
980 heads = revs and map(repo.lookup, revs) or revs
981 cg = repo.getbundle('bundle', heads=heads, common=common)
981 else:
982 else:
982 dest = ui.expandpath(dest or 'default-push', dest or 'default')
983 dest = ui.expandpath(dest or 'default-push', dest or 'default')
983 dest, branches = hg.parseurl(dest, opts.get('branch'))
984 dest, branches = hg.parseurl(dest, opts.get('branch'))
984 other = hg.peer(repo, opts, dest)
985 other = hg.peer(repo, opts, dest)
985 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
986 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
986 heads = revs and map(repo.lookup, revs) or revs
987 heads = revs and map(repo.lookup, revs) or revs
987 common, outheads = discovery.findcommonoutgoing(repo, other,
988 outgoing = discovery.findcommonoutgoing(repo, other,
988 onlyheads=heads,
989 onlyheads=heads,
989 force=opts.get('force'))
990 force=opts.get('force'))
990
991 cg = repo.getlocalbundle('bundle', outgoing)
991 cg = repo.getbundle('bundle', common=common, heads=outheads)
992 if not cg:
992 if not cg:
993 ui.status(_("no changes found\n"))
993 ui.status(_("no changes found\n"))
994 return 1
994 return 1
995
995
996 bundletype = opts.get('type', 'bzip2').lower()
996 bundletype = opts.get('type', 'bzip2').lower()
997 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
997 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
998 bundletype = btypes.get(bundletype)
998 bundletype = btypes.get(bundletype)
999 if bundletype not in changegroup.bundletypes:
999 if bundletype not in changegroup.bundletypes:
1000 raise util.Abort(_('unknown bundle type specified with --type'))
1000 raise util.Abort(_('unknown bundle type specified with --type'))
1001
1001
1002 changegroup.writebundle(cg, fname, bundletype)
1002 changegroup.writebundle(cg, fname, bundletype)
1003
1003
1004 @command('cat',
1004 @command('cat',
1005 [('o', 'output', '',
1005 [('o', 'output', '',
1006 _('print output to file with formatted name'), _('FORMAT')),
1006 _('print output to file with formatted name'), _('FORMAT')),
1007 ('r', 'rev', '', _('print the given revision'), _('REV')),
1007 ('r', 'rev', '', _('print the given revision'), _('REV')),
1008 ('', 'decode', None, _('apply any matching decode filter')),
1008 ('', 'decode', None, _('apply any matching decode filter')),
1009 ] + walkopts,
1009 ] + walkopts,
1010 _('[OPTION]... FILE...'))
1010 _('[OPTION]... FILE...'))
1011 def cat(ui, repo, file1, *pats, **opts):
1011 def cat(ui, repo, file1, *pats, **opts):
1012 """output the current or given revision of files
1012 """output the current or given revision of files
1013
1013
1014 Print the specified files as they were at the given revision. If
1014 Print the specified files as they were at the given revision. If
1015 no revision is given, the parent of the working directory is used,
1015 no revision is given, the parent of the working directory is used,
1016 or tip if no revision is checked out.
1016 or tip if no revision is checked out.
1017
1017
1018 Output may be to a file, in which case the name of the file is
1018 Output may be to a file, in which case the name of the file is
1019 given using a format string. The formatting rules are the same as
1019 given using a format string. The formatting rules are the same as
1020 for the export command, with the following additions:
1020 for the export command, with the following additions:
1021
1021
1022 :``%s``: basename of file being printed
1022 :``%s``: basename of file being printed
1023 :``%d``: dirname of file being printed, or '.' if in repository root
1023 :``%d``: dirname of file being printed, or '.' if in repository root
1024 :``%p``: root-relative path name of file being printed
1024 :``%p``: root-relative path name of file being printed
1025
1025
1026 Returns 0 on success.
1026 Returns 0 on success.
1027 """
1027 """
1028 ctx = scmutil.revsingle(repo, opts.get('rev'))
1028 ctx = scmutil.revsingle(repo, opts.get('rev'))
1029 err = 1
1029 err = 1
1030 m = scmutil.match(ctx, (file1,) + pats, opts)
1030 m = scmutil.match(ctx, (file1,) + pats, opts)
1031 for abs in ctx.walk(m):
1031 for abs in ctx.walk(m):
1032 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1032 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1033 pathname=abs)
1033 pathname=abs)
1034 data = ctx[abs].data()
1034 data = ctx[abs].data()
1035 if opts.get('decode'):
1035 if opts.get('decode'):
1036 data = repo.wwritedata(abs, data)
1036 data = repo.wwritedata(abs, data)
1037 fp.write(data)
1037 fp.write(data)
1038 fp.close()
1038 fp.close()
1039 err = 0
1039 err = 0
1040 return err
1040 return err
1041
1041
1042 @command('^clone',
1042 @command('^clone',
1043 [('U', 'noupdate', None,
1043 [('U', 'noupdate', None,
1044 _('the clone will include an empty working copy (only a repository)')),
1044 _('the clone will include an empty working copy (only a repository)')),
1045 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1045 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1046 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1046 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1047 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1047 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1048 ('', 'pull', None, _('use pull protocol to copy metadata')),
1048 ('', 'pull', None, _('use pull protocol to copy metadata')),
1049 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1049 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1050 ] + remoteopts,
1050 ] + remoteopts,
1051 _('[OPTION]... SOURCE [DEST]'))
1051 _('[OPTION]... SOURCE [DEST]'))
1052 def clone(ui, source, dest=None, **opts):
1052 def clone(ui, source, dest=None, **opts):
1053 """make a copy of an existing repository
1053 """make a copy of an existing repository
1054
1054
1055 Create a copy of an existing repository in a new directory.
1055 Create a copy of an existing repository in a new directory.
1056
1056
1057 If no destination directory name is specified, it defaults to the
1057 If no destination directory name is specified, it defaults to the
1058 basename of the source.
1058 basename of the source.
1059
1059
1060 The location of the source is added to the new repository's
1060 The location of the source is added to the new repository's
1061 ``.hg/hgrc`` file, as the default to be used for future pulls.
1061 ``.hg/hgrc`` file, as the default to be used for future pulls.
1062
1062
1063 Only local paths and ``ssh://`` URLs are supported as
1063 Only local paths and ``ssh://`` URLs are supported as
1064 destinations. For ``ssh://`` destinations, no working directory or
1064 destinations. For ``ssh://`` destinations, no working directory or
1065 ``.hg/hgrc`` will be created on the remote side.
1065 ``.hg/hgrc`` will be created on the remote side.
1066
1066
1067 To pull only a subset of changesets, specify one or more revisions
1067 To pull only a subset of changesets, specify one or more revisions
1068 identifiers with -r/--rev or branches with -b/--branch. The
1068 identifiers with -r/--rev or branches with -b/--branch. The
1069 resulting clone will contain only the specified changesets and
1069 resulting clone will contain only the specified changesets and
1070 their ancestors. These options (or 'clone src#rev dest') imply
1070 their ancestors. These options (or 'clone src#rev dest') imply
1071 --pull, even for local source repositories. Note that specifying a
1071 --pull, even for local source repositories. Note that specifying a
1072 tag will include the tagged changeset but not the changeset
1072 tag will include the tagged changeset but not the changeset
1073 containing the tag.
1073 containing the tag.
1074
1074
1075 To check out a particular version, use -u/--update, or
1075 To check out a particular version, use -u/--update, or
1076 -U/--noupdate to create a clone with no working directory.
1076 -U/--noupdate to create a clone with no working directory.
1077
1077
1078 .. container:: verbose
1078 .. container:: verbose
1079
1079
1080 For efficiency, hardlinks are used for cloning whenever the
1080 For efficiency, hardlinks are used for cloning whenever the
1081 source and destination are on the same filesystem (note this
1081 source and destination are on the same filesystem (note this
1082 applies only to the repository data, not to the working
1082 applies only to the repository data, not to the working
1083 directory). Some filesystems, such as AFS, implement hardlinking
1083 directory). Some filesystems, such as AFS, implement hardlinking
1084 incorrectly, but do not report errors. In these cases, use the
1084 incorrectly, but do not report errors. In these cases, use the
1085 --pull option to avoid hardlinking.
1085 --pull option to avoid hardlinking.
1086
1086
1087 In some cases, you can clone repositories and the working
1087 In some cases, you can clone repositories and the working
1088 directory using full hardlinks with ::
1088 directory using full hardlinks with ::
1089
1089
1090 $ cp -al REPO REPOCLONE
1090 $ cp -al REPO REPOCLONE
1091
1091
1092 This is the fastest way to clone, but it is not always safe. The
1092 This is the fastest way to clone, but it is not always safe. The
1093 operation is not atomic (making sure REPO is not modified during
1093 operation is not atomic (making sure REPO is not modified during
1094 the operation is up to you) and you have to make sure your
1094 the operation is up to you) and you have to make sure your
1095 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1095 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1096 so). Also, this is not compatible with certain extensions that
1096 so). Also, this is not compatible with certain extensions that
1097 place their metadata under the .hg directory, such as mq.
1097 place their metadata under the .hg directory, such as mq.
1098
1098
1099 Mercurial will update the working directory to the first applicable
1099 Mercurial will update the working directory to the first applicable
1100 revision from this list:
1100 revision from this list:
1101
1101
1102 a) null if -U or the source repository has no changesets
1102 a) null if -U or the source repository has no changesets
1103 b) if -u . and the source repository is local, the first parent of
1103 b) if -u . and the source repository is local, the first parent of
1104 the source repository's working directory
1104 the source repository's working directory
1105 c) the changeset specified with -u (if a branch name, this means the
1105 c) the changeset specified with -u (if a branch name, this means the
1106 latest head of that branch)
1106 latest head of that branch)
1107 d) the changeset specified with -r
1107 d) the changeset specified with -r
1108 e) the tipmost head specified with -b
1108 e) the tipmost head specified with -b
1109 f) the tipmost head specified with the url#branch source syntax
1109 f) the tipmost head specified with the url#branch source syntax
1110 g) the tipmost head of the default branch
1110 g) the tipmost head of the default branch
1111 h) tip
1111 h) tip
1112
1112
1113 Examples:
1113 Examples:
1114
1114
1115 - clone a remote repository to a new directory named hg/::
1115 - clone a remote repository to a new directory named hg/::
1116
1116
1117 hg clone http://selenic.com/hg
1117 hg clone http://selenic.com/hg
1118
1118
1119 - create a lightweight local clone::
1119 - create a lightweight local clone::
1120
1120
1121 hg clone project/ project-feature/
1121 hg clone project/ project-feature/
1122
1122
1123 - clone from an absolute path on an ssh server (note double-slash)::
1123 - clone from an absolute path on an ssh server (note double-slash)::
1124
1124
1125 hg clone ssh://user@server//home/projects/alpha/
1125 hg clone ssh://user@server//home/projects/alpha/
1126
1126
1127 - do a high-speed clone over a LAN while checking out a
1127 - do a high-speed clone over a LAN while checking out a
1128 specified version::
1128 specified version::
1129
1129
1130 hg clone --uncompressed http://server/repo -u 1.5
1130 hg clone --uncompressed http://server/repo -u 1.5
1131
1131
1132 - create a repository without changesets after a particular revision::
1132 - create a repository without changesets after a particular revision::
1133
1133
1134 hg clone -r 04e544 experimental/ good/
1134 hg clone -r 04e544 experimental/ good/
1135
1135
1136 - clone (and track) a particular named branch::
1136 - clone (and track) a particular named branch::
1137
1137
1138 hg clone http://selenic.com/hg#stable
1138 hg clone http://selenic.com/hg#stable
1139
1139
1140 See :hg:`help urls` for details on specifying URLs.
1140 See :hg:`help urls` for details on specifying URLs.
1141
1141
1142 Returns 0 on success.
1142 Returns 0 on success.
1143 """
1143 """
1144 if opts.get('noupdate') and opts.get('updaterev'):
1144 if opts.get('noupdate') and opts.get('updaterev'):
1145 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1145 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1146
1146
1147 r = hg.clone(ui, opts, source, dest,
1147 r = hg.clone(ui, opts, source, dest,
1148 pull=opts.get('pull'),
1148 pull=opts.get('pull'),
1149 stream=opts.get('uncompressed'),
1149 stream=opts.get('uncompressed'),
1150 rev=opts.get('rev'),
1150 rev=opts.get('rev'),
1151 update=opts.get('updaterev') or not opts.get('noupdate'),
1151 update=opts.get('updaterev') or not opts.get('noupdate'),
1152 branch=opts.get('branch'))
1152 branch=opts.get('branch'))
1153
1153
1154 return r is None
1154 return r is None
1155
1155
1156 @command('^commit|ci',
1156 @command('^commit|ci',
1157 [('A', 'addremove', None,
1157 [('A', 'addremove', None,
1158 _('mark new/missing files as added/removed before committing')),
1158 _('mark new/missing files as added/removed before committing')),
1159 ('', 'close-branch', None,
1159 ('', 'close-branch', None,
1160 _('mark a branch as closed, hiding it from the branch list')),
1160 _('mark a branch as closed, hiding it from the branch list')),
1161 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1161 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1162 _('[OPTION]... [FILE]...'))
1162 _('[OPTION]... [FILE]...'))
1163 def commit(ui, repo, *pats, **opts):
1163 def commit(ui, repo, *pats, **opts):
1164 """commit the specified files or all outstanding changes
1164 """commit the specified files or all outstanding changes
1165
1165
1166 Commit changes to the given files into the repository. Unlike a
1166 Commit changes to the given files into the repository. Unlike a
1167 centralized SCM, this operation is a local operation. See
1167 centralized SCM, this operation is a local operation. See
1168 :hg:`push` for a way to actively distribute your changes.
1168 :hg:`push` for a way to actively distribute your changes.
1169
1169
1170 If a list of files is omitted, all changes reported by :hg:`status`
1170 If a list of files is omitted, all changes reported by :hg:`status`
1171 will be committed.
1171 will be committed.
1172
1172
1173 If you are committing the result of a merge, do not provide any
1173 If you are committing the result of a merge, do not provide any
1174 filenames or -I/-X filters.
1174 filenames or -I/-X filters.
1175
1175
1176 If no commit message is specified, Mercurial starts your
1176 If no commit message is specified, Mercurial starts your
1177 configured editor where you can enter a message. In case your
1177 configured editor where you can enter a message. In case your
1178 commit fails, you will find a backup of your message in
1178 commit fails, you will find a backup of your message in
1179 ``.hg/last-message.txt``.
1179 ``.hg/last-message.txt``.
1180
1180
1181 See :hg:`help dates` for a list of formats valid for -d/--date.
1181 See :hg:`help dates` for a list of formats valid for -d/--date.
1182
1182
1183 Returns 0 on success, 1 if nothing changed.
1183 Returns 0 on success, 1 if nothing changed.
1184 """
1184 """
1185 if opts.get('subrepos'):
1185 if opts.get('subrepos'):
1186 # Let --subrepos on the command line overide config setting.
1186 # Let --subrepos on the command line overide config setting.
1187 ui.setconfig('ui', 'commitsubrepos', True)
1187 ui.setconfig('ui', 'commitsubrepos', True)
1188
1188
1189 extra = {}
1189 extra = {}
1190 if opts.get('close_branch'):
1190 if opts.get('close_branch'):
1191 if repo['.'].node() not in repo.branchheads():
1191 if repo['.'].node() not in repo.branchheads():
1192 # The topo heads set is included in the branch heads set of the
1192 # The topo heads set is included in the branch heads set of the
1193 # current branch, so it's sufficient to test branchheads
1193 # current branch, so it's sufficient to test branchheads
1194 raise util.Abort(_('can only close branch heads'))
1194 raise util.Abort(_('can only close branch heads'))
1195 extra['close'] = 1
1195 extra['close'] = 1
1196 e = cmdutil.commiteditor
1196 e = cmdutil.commiteditor
1197 if opts.get('force_editor'):
1197 if opts.get('force_editor'):
1198 e = cmdutil.commitforceeditor
1198 e = cmdutil.commitforceeditor
1199
1199
1200 def commitfunc(ui, repo, message, match, opts):
1200 def commitfunc(ui, repo, message, match, opts):
1201 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1201 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1202 editor=e, extra=extra)
1202 editor=e, extra=extra)
1203
1203
1204 branch = repo[None].branch()
1204 branch = repo[None].branch()
1205 bheads = repo.branchheads(branch)
1205 bheads = repo.branchheads(branch)
1206
1206
1207 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1207 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1208 if not node:
1208 if not node:
1209 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1209 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1210 if stat[3]:
1210 if stat[3]:
1211 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1211 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1212 % len(stat[3]))
1212 % len(stat[3]))
1213 else:
1213 else:
1214 ui.status(_("nothing changed\n"))
1214 ui.status(_("nothing changed\n"))
1215 return 1
1215 return 1
1216
1216
1217 ctx = repo[node]
1217 ctx = repo[node]
1218 parents = ctx.parents()
1218 parents = ctx.parents()
1219
1219
1220 if (bheads and node not in bheads and not
1220 if (bheads and node not in bheads and not
1221 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1221 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1222 ui.status(_('created new head\n'))
1222 ui.status(_('created new head\n'))
1223 # The message is not printed for initial roots. For the other
1223 # The message is not printed for initial roots. For the other
1224 # changesets, it is printed in the following situations:
1224 # changesets, it is printed in the following situations:
1225 #
1225 #
1226 # Par column: for the 2 parents with ...
1226 # Par column: for the 2 parents with ...
1227 # N: null or no parent
1227 # N: null or no parent
1228 # B: parent is on another named branch
1228 # B: parent is on another named branch
1229 # C: parent is a regular non head changeset
1229 # C: parent is a regular non head changeset
1230 # H: parent was a branch head of the current branch
1230 # H: parent was a branch head of the current branch
1231 # Msg column: whether we print "created new head" message
1231 # Msg column: whether we print "created new head" message
1232 # In the following, it is assumed that there already exists some
1232 # In the following, it is assumed that there already exists some
1233 # initial branch heads of the current branch, otherwise nothing is
1233 # initial branch heads of the current branch, otherwise nothing is
1234 # printed anyway.
1234 # printed anyway.
1235 #
1235 #
1236 # Par Msg Comment
1236 # Par Msg Comment
1237 # NN y additional topo root
1237 # NN y additional topo root
1238 #
1238 #
1239 # BN y additional branch root
1239 # BN y additional branch root
1240 # CN y additional topo head
1240 # CN y additional topo head
1241 # HN n usual case
1241 # HN n usual case
1242 #
1242 #
1243 # BB y weird additional branch root
1243 # BB y weird additional branch root
1244 # CB y branch merge
1244 # CB y branch merge
1245 # HB n merge with named branch
1245 # HB n merge with named branch
1246 #
1246 #
1247 # CC y additional head from merge
1247 # CC y additional head from merge
1248 # CH n merge with a head
1248 # CH n merge with a head
1249 #
1249 #
1250 # HH n head merge: head count decreases
1250 # HH n head merge: head count decreases
1251
1251
1252 if not opts.get('close_branch'):
1252 if not opts.get('close_branch'):
1253 for r in parents:
1253 for r in parents:
1254 if r.extra().get('close') and r.branch() == branch:
1254 if r.extra().get('close') and r.branch() == branch:
1255 ui.status(_('reopening closed branch head %d\n') % r)
1255 ui.status(_('reopening closed branch head %d\n') % r)
1256
1256
1257 if ui.debugflag:
1257 if ui.debugflag:
1258 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1258 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1259 elif ui.verbose:
1259 elif ui.verbose:
1260 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1260 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1261
1261
1262 @command('copy|cp',
1262 @command('copy|cp',
1263 [('A', 'after', None, _('record a copy that has already occurred')),
1263 [('A', 'after', None, _('record a copy that has already occurred')),
1264 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1264 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1265 ] + walkopts + dryrunopts,
1265 ] + walkopts + dryrunopts,
1266 _('[OPTION]... [SOURCE]... DEST'))
1266 _('[OPTION]... [SOURCE]... DEST'))
1267 def copy(ui, repo, *pats, **opts):
1267 def copy(ui, repo, *pats, **opts):
1268 """mark files as copied for the next commit
1268 """mark files as copied for the next commit
1269
1269
1270 Mark dest as having copies of source files. If dest is a
1270 Mark dest as having copies of source files. If dest is a
1271 directory, copies are put in that directory. If dest is a file,
1271 directory, copies are put in that directory. If dest is a file,
1272 the source must be a single file.
1272 the source must be a single file.
1273
1273
1274 By default, this command copies the contents of files as they
1274 By default, this command copies the contents of files as they
1275 exist in the working directory. If invoked with -A/--after, the
1275 exist in the working directory. If invoked with -A/--after, the
1276 operation is recorded, but no copying is performed.
1276 operation is recorded, but no copying is performed.
1277
1277
1278 This command takes effect with the next commit. To undo a copy
1278 This command takes effect with the next commit. To undo a copy
1279 before that, see :hg:`revert`.
1279 before that, see :hg:`revert`.
1280
1280
1281 Returns 0 on success, 1 if errors are encountered.
1281 Returns 0 on success, 1 if errors are encountered.
1282 """
1282 """
1283 wlock = repo.wlock(False)
1283 wlock = repo.wlock(False)
1284 try:
1284 try:
1285 return cmdutil.copy(ui, repo, pats, opts)
1285 return cmdutil.copy(ui, repo, pats, opts)
1286 finally:
1286 finally:
1287 wlock.release()
1287 wlock.release()
1288
1288
1289 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1289 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1290 def debugancestor(ui, repo, *args):
1290 def debugancestor(ui, repo, *args):
1291 """find the ancestor revision of two revisions in a given index"""
1291 """find the ancestor revision of two revisions in a given index"""
1292 if len(args) == 3:
1292 if len(args) == 3:
1293 index, rev1, rev2 = args
1293 index, rev1, rev2 = args
1294 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1294 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1295 lookup = r.lookup
1295 lookup = r.lookup
1296 elif len(args) == 2:
1296 elif len(args) == 2:
1297 if not repo:
1297 if not repo:
1298 raise util.Abort(_("there is no Mercurial repository here "
1298 raise util.Abort(_("there is no Mercurial repository here "
1299 "(.hg not found)"))
1299 "(.hg not found)"))
1300 rev1, rev2 = args
1300 rev1, rev2 = args
1301 r = repo.changelog
1301 r = repo.changelog
1302 lookup = repo.lookup
1302 lookup = repo.lookup
1303 else:
1303 else:
1304 raise util.Abort(_('either two or three arguments required'))
1304 raise util.Abort(_('either two or three arguments required'))
1305 a = r.ancestor(lookup(rev1), lookup(rev2))
1305 a = r.ancestor(lookup(rev1), lookup(rev2))
1306 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1306 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1307
1307
1308 @command('debugbuilddag',
1308 @command('debugbuilddag',
1309 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1309 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1310 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1310 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1311 ('n', 'new-file', None, _('add new file at each rev'))],
1311 ('n', 'new-file', None, _('add new file at each rev'))],
1312 _('[OPTION]... [TEXT]'))
1312 _('[OPTION]... [TEXT]'))
1313 def debugbuilddag(ui, repo, text=None,
1313 def debugbuilddag(ui, repo, text=None,
1314 mergeable_file=False,
1314 mergeable_file=False,
1315 overwritten_file=False,
1315 overwritten_file=False,
1316 new_file=False):
1316 new_file=False):
1317 """builds a repo with a given DAG from scratch in the current empty repo
1317 """builds a repo with a given DAG from scratch in the current empty repo
1318
1318
1319 The description of the DAG is read from stdin if not given on the
1319 The description of the DAG is read from stdin if not given on the
1320 command line.
1320 command line.
1321
1321
1322 Elements:
1322 Elements:
1323
1323
1324 - "+n" is a linear run of n nodes based on the current default parent
1324 - "+n" is a linear run of n nodes based on the current default parent
1325 - "." is a single node based on the current default parent
1325 - "." is a single node based on the current default parent
1326 - "$" resets the default parent to null (implied at the start);
1326 - "$" resets the default parent to null (implied at the start);
1327 otherwise the default parent is always the last node created
1327 otherwise the default parent is always the last node created
1328 - "<p" sets the default parent to the backref p
1328 - "<p" sets the default parent to the backref p
1329 - "*p" is a fork at parent p, which is a backref
1329 - "*p" is a fork at parent p, which is a backref
1330 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1330 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1331 - "/p2" is a merge of the preceding node and p2
1331 - "/p2" is a merge of the preceding node and p2
1332 - ":tag" defines a local tag for the preceding node
1332 - ":tag" defines a local tag for the preceding node
1333 - "@branch" sets the named branch for subsequent nodes
1333 - "@branch" sets the named branch for subsequent nodes
1334 - "#...\\n" is a comment up to the end of the line
1334 - "#...\\n" is a comment up to the end of the line
1335
1335
1336 Whitespace between the above elements is ignored.
1336 Whitespace between the above elements is ignored.
1337
1337
1338 A backref is either
1338 A backref is either
1339
1339
1340 - a number n, which references the node curr-n, where curr is the current
1340 - a number n, which references the node curr-n, where curr is the current
1341 node, or
1341 node, or
1342 - the name of a local tag you placed earlier using ":tag", or
1342 - the name of a local tag you placed earlier using ":tag", or
1343 - empty to denote the default parent.
1343 - empty to denote the default parent.
1344
1344
1345 All string valued-elements are either strictly alphanumeric, or must
1345 All string valued-elements are either strictly alphanumeric, or must
1346 be enclosed in double quotes ("..."), with "\\" as escape character.
1346 be enclosed in double quotes ("..."), with "\\" as escape character.
1347 """
1347 """
1348
1348
1349 if text is None:
1349 if text is None:
1350 ui.status(_("reading DAG from stdin\n"))
1350 ui.status(_("reading DAG from stdin\n"))
1351 text = ui.fin.read()
1351 text = ui.fin.read()
1352
1352
1353 cl = repo.changelog
1353 cl = repo.changelog
1354 if len(cl) > 0:
1354 if len(cl) > 0:
1355 raise util.Abort(_('repository is not empty'))
1355 raise util.Abort(_('repository is not empty'))
1356
1356
1357 # determine number of revs in DAG
1357 # determine number of revs in DAG
1358 total = 0
1358 total = 0
1359 for type, data in dagparser.parsedag(text):
1359 for type, data in dagparser.parsedag(text):
1360 if type == 'n':
1360 if type == 'n':
1361 total += 1
1361 total += 1
1362
1362
1363 if mergeable_file:
1363 if mergeable_file:
1364 linesperrev = 2
1364 linesperrev = 2
1365 # make a file with k lines per rev
1365 # make a file with k lines per rev
1366 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1366 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1367 initialmergedlines.append("")
1367 initialmergedlines.append("")
1368
1368
1369 tags = []
1369 tags = []
1370
1370
1371 tr = repo.transaction("builddag")
1371 tr = repo.transaction("builddag")
1372 try:
1372 try:
1373
1373
1374 at = -1
1374 at = -1
1375 atbranch = 'default'
1375 atbranch = 'default'
1376 nodeids = []
1376 nodeids = []
1377 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1377 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1378 for type, data in dagparser.parsedag(text):
1378 for type, data in dagparser.parsedag(text):
1379 if type == 'n':
1379 if type == 'n':
1380 ui.note('node %s\n' % str(data))
1380 ui.note('node %s\n' % str(data))
1381 id, ps = data
1381 id, ps = data
1382
1382
1383 files = []
1383 files = []
1384 fctxs = {}
1384 fctxs = {}
1385
1385
1386 p2 = None
1386 p2 = None
1387 if mergeable_file:
1387 if mergeable_file:
1388 fn = "mf"
1388 fn = "mf"
1389 p1 = repo[ps[0]]
1389 p1 = repo[ps[0]]
1390 if len(ps) > 1:
1390 if len(ps) > 1:
1391 p2 = repo[ps[1]]
1391 p2 = repo[ps[1]]
1392 pa = p1.ancestor(p2)
1392 pa = p1.ancestor(p2)
1393 base, local, other = [x[fn].data() for x in pa, p1, p2]
1393 base, local, other = [x[fn].data() for x in pa, p1, p2]
1394 m3 = simplemerge.Merge3Text(base, local, other)
1394 m3 = simplemerge.Merge3Text(base, local, other)
1395 ml = [l.strip() for l in m3.merge_lines()]
1395 ml = [l.strip() for l in m3.merge_lines()]
1396 ml.append("")
1396 ml.append("")
1397 elif at > 0:
1397 elif at > 0:
1398 ml = p1[fn].data().split("\n")
1398 ml = p1[fn].data().split("\n")
1399 else:
1399 else:
1400 ml = initialmergedlines
1400 ml = initialmergedlines
1401 ml[id * linesperrev] += " r%i" % id
1401 ml[id * linesperrev] += " r%i" % id
1402 mergedtext = "\n".join(ml)
1402 mergedtext = "\n".join(ml)
1403 files.append(fn)
1403 files.append(fn)
1404 fctxs[fn] = context.memfilectx(fn, mergedtext)
1404 fctxs[fn] = context.memfilectx(fn, mergedtext)
1405
1405
1406 if overwritten_file:
1406 if overwritten_file:
1407 fn = "of"
1407 fn = "of"
1408 files.append(fn)
1408 files.append(fn)
1409 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1409 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1410
1410
1411 if new_file:
1411 if new_file:
1412 fn = "nf%i" % id
1412 fn = "nf%i" % id
1413 files.append(fn)
1413 files.append(fn)
1414 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1414 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1415 if len(ps) > 1:
1415 if len(ps) > 1:
1416 if not p2:
1416 if not p2:
1417 p2 = repo[ps[1]]
1417 p2 = repo[ps[1]]
1418 for fn in p2:
1418 for fn in p2:
1419 if fn.startswith("nf"):
1419 if fn.startswith("nf"):
1420 files.append(fn)
1420 files.append(fn)
1421 fctxs[fn] = p2[fn]
1421 fctxs[fn] = p2[fn]
1422
1422
1423 def fctxfn(repo, cx, path):
1423 def fctxfn(repo, cx, path):
1424 return fctxs.get(path)
1424 return fctxs.get(path)
1425
1425
1426 if len(ps) == 0 or ps[0] < 0:
1426 if len(ps) == 0 or ps[0] < 0:
1427 pars = [None, None]
1427 pars = [None, None]
1428 elif len(ps) == 1:
1428 elif len(ps) == 1:
1429 pars = [nodeids[ps[0]], None]
1429 pars = [nodeids[ps[0]], None]
1430 else:
1430 else:
1431 pars = [nodeids[p] for p in ps]
1431 pars = [nodeids[p] for p in ps]
1432 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1432 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1433 date=(id, 0),
1433 date=(id, 0),
1434 user="debugbuilddag",
1434 user="debugbuilddag",
1435 extra={'branch': atbranch})
1435 extra={'branch': atbranch})
1436 nodeid = repo.commitctx(cx)
1436 nodeid = repo.commitctx(cx)
1437 nodeids.append(nodeid)
1437 nodeids.append(nodeid)
1438 at = id
1438 at = id
1439 elif type == 'l':
1439 elif type == 'l':
1440 id, name = data
1440 id, name = data
1441 ui.note('tag %s\n' % name)
1441 ui.note('tag %s\n' % name)
1442 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1442 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1443 elif type == 'a':
1443 elif type == 'a':
1444 ui.note('branch %s\n' % data)
1444 ui.note('branch %s\n' % data)
1445 atbranch = data
1445 atbranch = data
1446 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1446 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1447 tr.close()
1447 tr.close()
1448 finally:
1448 finally:
1449 ui.progress(_('building'), None)
1449 ui.progress(_('building'), None)
1450 tr.release()
1450 tr.release()
1451
1451
1452 if tags:
1452 if tags:
1453 repo.opener.write("localtags", "".join(tags))
1453 repo.opener.write("localtags", "".join(tags))
1454
1454
1455 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1455 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1456 def debugbundle(ui, bundlepath, all=None, **opts):
1456 def debugbundle(ui, bundlepath, all=None, **opts):
1457 """lists the contents of a bundle"""
1457 """lists the contents of a bundle"""
1458 f = url.open(ui, bundlepath)
1458 f = url.open(ui, bundlepath)
1459 try:
1459 try:
1460 gen = changegroup.readbundle(f, bundlepath)
1460 gen = changegroup.readbundle(f, bundlepath)
1461 if all:
1461 if all:
1462 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1462 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1463
1463
1464 def showchunks(named):
1464 def showchunks(named):
1465 ui.write("\n%s\n" % named)
1465 ui.write("\n%s\n" % named)
1466 chain = None
1466 chain = None
1467 while True:
1467 while True:
1468 chunkdata = gen.deltachunk(chain)
1468 chunkdata = gen.deltachunk(chain)
1469 if not chunkdata:
1469 if not chunkdata:
1470 break
1470 break
1471 node = chunkdata['node']
1471 node = chunkdata['node']
1472 p1 = chunkdata['p1']
1472 p1 = chunkdata['p1']
1473 p2 = chunkdata['p2']
1473 p2 = chunkdata['p2']
1474 cs = chunkdata['cs']
1474 cs = chunkdata['cs']
1475 deltabase = chunkdata['deltabase']
1475 deltabase = chunkdata['deltabase']
1476 delta = chunkdata['delta']
1476 delta = chunkdata['delta']
1477 ui.write("%s %s %s %s %s %s\n" %
1477 ui.write("%s %s %s %s %s %s\n" %
1478 (hex(node), hex(p1), hex(p2),
1478 (hex(node), hex(p1), hex(p2),
1479 hex(cs), hex(deltabase), len(delta)))
1479 hex(cs), hex(deltabase), len(delta)))
1480 chain = node
1480 chain = node
1481
1481
1482 chunkdata = gen.changelogheader()
1482 chunkdata = gen.changelogheader()
1483 showchunks("changelog")
1483 showchunks("changelog")
1484 chunkdata = gen.manifestheader()
1484 chunkdata = gen.manifestheader()
1485 showchunks("manifest")
1485 showchunks("manifest")
1486 while True:
1486 while True:
1487 chunkdata = gen.filelogheader()
1487 chunkdata = gen.filelogheader()
1488 if not chunkdata:
1488 if not chunkdata:
1489 break
1489 break
1490 fname = chunkdata['filename']
1490 fname = chunkdata['filename']
1491 showchunks(fname)
1491 showchunks(fname)
1492 else:
1492 else:
1493 chunkdata = gen.changelogheader()
1493 chunkdata = gen.changelogheader()
1494 chain = None
1494 chain = None
1495 while True:
1495 while True:
1496 chunkdata = gen.deltachunk(chain)
1496 chunkdata = gen.deltachunk(chain)
1497 if not chunkdata:
1497 if not chunkdata:
1498 break
1498 break
1499 node = chunkdata['node']
1499 node = chunkdata['node']
1500 ui.write("%s\n" % hex(node))
1500 ui.write("%s\n" % hex(node))
1501 chain = node
1501 chain = node
1502 finally:
1502 finally:
1503 f.close()
1503 f.close()
1504
1504
1505 @command('debugcheckstate', [], '')
1505 @command('debugcheckstate', [], '')
1506 def debugcheckstate(ui, repo):
1506 def debugcheckstate(ui, repo):
1507 """validate the correctness of the current dirstate"""
1507 """validate the correctness of the current dirstate"""
1508 parent1, parent2 = repo.dirstate.parents()
1508 parent1, parent2 = repo.dirstate.parents()
1509 m1 = repo[parent1].manifest()
1509 m1 = repo[parent1].manifest()
1510 m2 = repo[parent2].manifest()
1510 m2 = repo[parent2].manifest()
1511 errors = 0
1511 errors = 0
1512 for f in repo.dirstate:
1512 for f in repo.dirstate:
1513 state = repo.dirstate[f]
1513 state = repo.dirstate[f]
1514 if state in "nr" and f not in m1:
1514 if state in "nr" and f not in m1:
1515 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1515 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1516 errors += 1
1516 errors += 1
1517 if state in "a" and f in m1:
1517 if state in "a" and f in m1:
1518 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1518 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1519 errors += 1
1519 errors += 1
1520 if state in "m" and f not in m1 and f not in m2:
1520 if state in "m" and f not in m1 and f not in m2:
1521 ui.warn(_("%s in state %s, but not in either manifest\n") %
1521 ui.warn(_("%s in state %s, but not in either manifest\n") %
1522 (f, state))
1522 (f, state))
1523 errors += 1
1523 errors += 1
1524 for f in m1:
1524 for f in m1:
1525 state = repo.dirstate[f]
1525 state = repo.dirstate[f]
1526 if state not in "nrm":
1526 if state not in "nrm":
1527 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1527 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1528 errors += 1
1528 errors += 1
1529 if errors:
1529 if errors:
1530 error = _(".hg/dirstate inconsistent with current parent's manifest")
1530 error = _(".hg/dirstate inconsistent with current parent's manifest")
1531 raise util.Abort(error)
1531 raise util.Abort(error)
1532
1532
1533 @command('debugcommands', [], _('[COMMAND]'))
1533 @command('debugcommands', [], _('[COMMAND]'))
1534 def debugcommands(ui, cmd='', *args):
1534 def debugcommands(ui, cmd='', *args):
1535 """list all available commands and options"""
1535 """list all available commands and options"""
1536 for cmd, vals in sorted(table.iteritems()):
1536 for cmd, vals in sorted(table.iteritems()):
1537 cmd = cmd.split('|')[0].strip('^')
1537 cmd = cmd.split('|')[0].strip('^')
1538 opts = ', '.join([i[1] for i in vals[1]])
1538 opts = ', '.join([i[1] for i in vals[1]])
1539 ui.write('%s: %s\n' % (cmd, opts))
1539 ui.write('%s: %s\n' % (cmd, opts))
1540
1540
1541 @command('debugcomplete',
1541 @command('debugcomplete',
1542 [('o', 'options', None, _('show the command options'))],
1542 [('o', 'options', None, _('show the command options'))],
1543 _('[-o] CMD'))
1543 _('[-o] CMD'))
1544 def debugcomplete(ui, cmd='', **opts):
1544 def debugcomplete(ui, cmd='', **opts):
1545 """returns the completion list associated with the given command"""
1545 """returns the completion list associated with the given command"""
1546
1546
1547 if opts.get('options'):
1547 if opts.get('options'):
1548 options = []
1548 options = []
1549 otables = [globalopts]
1549 otables = [globalopts]
1550 if cmd:
1550 if cmd:
1551 aliases, entry = cmdutil.findcmd(cmd, table, False)
1551 aliases, entry = cmdutil.findcmd(cmd, table, False)
1552 otables.append(entry[1])
1552 otables.append(entry[1])
1553 for t in otables:
1553 for t in otables:
1554 for o in t:
1554 for o in t:
1555 if "(DEPRECATED)" in o[3]:
1555 if "(DEPRECATED)" in o[3]:
1556 continue
1556 continue
1557 if o[0]:
1557 if o[0]:
1558 options.append('-%s' % o[0])
1558 options.append('-%s' % o[0])
1559 options.append('--%s' % o[1])
1559 options.append('--%s' % o[1])
1560 ui.write("%s\n" % "\n".join(options))
1560 ui.write("%s\n" % "\n".join(options))
1561 return
1561 return
1562
1562
1563 cmdlist = cmdutil.findpossible(cmd, table)
1563 cmdlist = cmdutil.findpossible(cmd, table)
1564 if ui.verbose:
1564 if ui.verbose:
1565 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1565 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1566 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1566 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1567
1567
1568 @command('debugdag',
1568 @command('debugdag',
1569 [('t', 'tags', None, _('use tags as labels')),
1569 [('t', 'tags', None, _('use tags as labels')),
1570 ('b', 'branches', None, _('annotate with branch names')),
1570 ('b', 'branches', None, _('annotate with branch names')),
1571 ('', 'dots', None, _('use dots for runs')),
1571 ('', 'dots', None, _('use dots for runs')),
1572 ('s', 'spaces', None, _('separate elements by spaces'))],
1572 ('s', 'spaces', None, _('separate elements by spaces'))],
1573 _('[OPTION]... [FILE [REV]...]'))
1573 _('[OPTION]... [FILE [REV]...]'))
1574 def debugdag(ui, repo, file_=None, *revs, **opts):
1574 def debugdag(ui, repo, file_=None, *revs, **opts):
1575 """format the changelog or an index DAG as a concise textual description
1575 """format the changelog or an index DAG as a concise textual description
1576
1576
1577 If you pass a revlog index, the revlog's DAG is emitted. If you list
1577 If you pass a revlog index, the revlog's DAG is emitted. If you list
1578 revision numbers, they get labelled in the output as rN.
1578 revision numbers, they get labelled in the output as rN.
1579
1579
1580 Otherwise, the changelog DAG of the current repo is emitted.
1580 Otherwise, the changelog DAG of the current repo is emitted.
1581 """
1581 """
1582 spaces = opts.get('spaces')
1582 spaces = opts.get('spaces')
1583 dots = opts.get('dots')
1583 dots = opts.get('dots')
1584 if file_:
1584 if file_:
1585 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1585 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1586 revs = set((int(r) for r in revs))
1586 revs = set((int(r) for r in revs))
1587 def events():
1587 def events():
1588 for r in rlog:
1588 for r in rlog:
1589 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1589 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1590 if r in revs:
1590 if r in revs:
1591 yield 'l', (r, "r%i" % r)
1591 yield 'l', (r, "r%i" % r)
1592 elif repo:
1592 elif repo:
1593 cl = repo.changelog
1593 cl = repo.changelog
1594 tags = opts.get('tags')
1594 tags = opts.get('tags')
1595 branches = opts.get('branches')
1595 branches = opts.get('branches')
1596 if tags:
1596 if tags:
1597 labels = {}
1597 labels = {}
1598 for l, n in repo.tags().items():
1598 for l, n in repo.tags().items():
1599 labels.setdefault(cl.rev(n), []).append(l)
1599 labels.setdefault(cl.rev(n), []).append(l)
1600 def events():
1600 def events():
1601 b = "default"
1601 b = "default"
1602 for r in cl:
1602 for r in cl:
1603 if branches:
1603 if branches:
1604 newb = cl.read(cl.node(r))[5]['branch']
1604 newb = cl.read(cl.node(r))[5]['branch']
1605 if newb != b:
1605 if newb != b:
1606 yield 'a', newb
1606 yield 'a', newb
1607 b = newb
1607 b = newb
1608 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1608 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1609 if tags:
1609 if tags:
1610 ls = labels.get(r)
1610 ls = labels.get(r)
1611 if ls:
1611 if ls:
1612 for l in ls:
1612 for l in ls:
1613 yield 'l', (r, l)
1613 yield 'l', (r, l)
1614 else:
1614 else:
1615 raise util.Abort(_('need repo for changelog dag'))
1615 raise util.Abort(_('need repo for changelog dag'))
1616
1616
1617 for line in dagparser.dagtextlines(events(),
1617 for line in dagparser.dagtextlines(events(),
1618 addspaces=spaces,
1618 addspaces=spaces,
1619 wraplabels=True,
1619 wraplabels=True,
1620 wrapannotations=True,
1620 wrapannotations=True,
1621 wrapnonlinear=dots,
1621 wrapnonlinear=dots,
1622 usedots=dots,
1622 usedots=dots,
1623 maxlinewidth=70):
1623 maxlinewidth=70):
1624 ui.write(line)
1624 ui.write(line)
1625 ui.write("\n")
1625 ui.write("\n")
1626
1626
1627 @command('debugdata',
1627 @command('debugdata',
1628 [('c', 'changelog', False, _('open changelog')),
1628 [('c', 'changelog', False, _('open changelog')),
1629 ('m', 'manifest', False, _('open manifest'))],
1629 ('m', 'manifest', False, _('open manifest'))],
1630 _('-c|-m|FILE REV'))
1630 _('-c|-m|FILE REV'))
1631 def debugdata(ui, repo, file_, rev = None, **opts):
1631 def debugdata(ui, repo, file_, rev = None, **opts):
1632 """dump the contents of a data file revision"""
1632 """dump the contents of a data file revision"""
1633 if opts.get('changelog') or opts.get('manifest'):
1633 if opts.get('changelog') or opts.get('manifest'):
1634 file_, rev = None, file_
1634 file_, rev = None, file_
1635 elif rev is None:
1635 elif rev is None:
1636 raise error.CommandError('debugdata', _('invalid arguments'))
1636 raise error.CommandError('debugdata', _('invalid arguments'))
1637 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1637 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1638 try:
1638 try:
1639 ui.write(r.revision(r.lookup(rev)))
1639 ui.write(r.revision(r.lookup(rev)))
1640 except KeyError:
1640 except KeyError:
1641 raise util.Abort(_('invalid revision identifier %s') % rev)
1641 raise util.Abort(_('invalid revision identifier %s') % rev)
1642
1642
1643 @command('debugdate',
1643 @command('debugdate',
1644 [('e', 'extended', None, _('try extended date formats'))],
1644 [('e', 'extended', None, _('try extended date formats'))],
1645 _('[-e] DATE [RANGE]'))
1645 _('[-e] DATE [RANGE]'))
1646 def debugdate(ui, date, range=None, **opts):
1646 def debugdate(ui, date, range=None, **opts):
1647 """parse and display a date"""
1647 """parse and display a date"""
1648 if opts["extended"]:
1648 if opts["extended"]:
1649 d = util.parsedate(date, util.extendeddateformats)
1649 d = util.parsedate(date, util.extendeddateformats)
1650 else:
1650 else:
1651 d = util.parsedate(date)
1651 d = util.parsedate(date)
1652 ui.write("internal: %s %s\n" % d)
1652 ui.write("internal: %s %s\n" % d)
1653 ui.write("standard: %s\n" % util.datestr(d))
1653 ui.write("standard: %s\n" % util.datestr(d))
1654 if range:
1654 if range:
1655 m = util.matchdate(range)
1655 m = util.matchdate(range)
1656 ui.write("match: %s\n" % m(d[0]))
1656 ui.write("match: %s\n" % m(d[0]))
1657
1657
1658 @command('debugdiscovery',
1658 @command('debugdiscovery',
1659 [('', 'old', None, _('use old-style discovery')),
1659 [('', 'old', None, _('use old-style discovery')),
1660 ('', 'nonheads', None,
1660 ('', 'nonheads', None,
1661 _('use old-style discovery with non-heads included')),
1661 _('use old-style discovery with non-heads included')),
1662 ] + remoteopts,
1662 ] + remoteopts,
1663 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1663 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1664 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1664 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1665 """runs the changeset discovery protocol in isolation"""
1665 """runs the changeset discovery protocol in isolation"""
1666 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1666 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1667 remote = hg.peer(repo, opts, remoteurl)
1667 remote = hg.peer(repo, opts, remoteurl)
1668 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1668 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1669
1669
1670 # make sure tests are repeatable
1670 # make sure tests are repeatable
1671 random.seed(12323)
1671 random.seed(12323)
1672
1672
1673 def doit(localheads, remoteheads):
1673 def doit(localheads, remoteheads):
1674 if opts.get('old'):
1674 if opts.get('old'):
1675 if localheads:
1675 if localheads:
1676 raise util.Abort('cannot use localheads with old style discovery')
1676 raise util.Abort('cannot use localheads with old style discovery')
1677 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1677 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1678 force=True)
1678 force=True)
1679 common = set(common)
1679 common = set(common)
1680 if not opts.get('nonheads'):
1680 if not opts.get('nonheads'):
1681 ui.write("unpruned common: %s\n" % " ".join([short(n)
1681 ui.write("unpruned common: %s\n" % " ".join([short(n)
1682 for n in common]))
1682 for n in common]))
1683 dag = dagutil.revlogdag(repo.changelog)
1683 dag = dagutil.revlogdag(repo.changelog)
1684 all = dag.ancestorset(dag.internalizeall(common))
1684 all = dag.ancestorset(dag.internalizeall(common))
1685 common = dag.externalizeall(dag.headsetofconnecteds(all))
1685 common = dag.externalizeall(dag.headsetofconnecteds(all))
1686 else:
1686 else:
1687 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1687 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1688 common = set(common)
1688 common = set(common)
1689 rheads = set(hds)
1689 rheads = set(hds)
1690 lheads = set(repo.heads())
1690 lheads = set(repo.heads())
1691 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1691 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1692 if lheads <= common:
1692 if lheads <= common:
1693 ui.write("local is subset\n")
1693 ui.write("local is subset\n")
1694 elif rheads <= common:
1694 elif rheads <= common:
1695 ui.write("remote is subset\n")
1695 ui.write("remote is subset\n")
1696
1696
1697 serverlogs = opts.get('serverlog')
1697 serverlogs = opts.get('serverlog')
1698 if serverlogs:
1698 if serverlogs:
1699 for filename in serverlogs:
1699 for filename in serverlogs:
1700 logfile = open(filename, 'r')
1700 logfile = open(filename, 'r')
1701 try:
1701 try:
1702 line = logfile.readline()
1702 line = logfile.readline()
1703 while line:
1703 while line:
1704 parts = line.strip().split(';')
1704 parts = line.strip().split(';')
1705 op = parts[1]
1705 op = parts[1]
1706 if op == 'cg':
1706 if op == 'cg':
1707 pass
1707 pass
1708 elif op == 'cgss':
1708 elif op == 'cgss':
1709 doit(parts[2].split(' '), parts[3].split(' '))
1709 doit(parts[2].split(' '), parts[3].split(' '))
1710 elif op == 'unb':
1710 elif op == 'unb':
1711 doit(parts[3].split(' '), parts[2].split(' '))
1711 doit(parts[3].split(' '), parts[2].split(' '))
1712 line = logfile.readline()
1712 line = logfile.readline()
1713 finally:
1713 finally:
1714 logfile.close()
1714 logfile.close()
1715
1715
1716 else:
1716 else:
1717 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1717 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1718 opts.get('remote_head'))
1718 opts.get('remote_head'))
1719 localrevs = opts.get('local_head')
1719 localrevs = opts.get('local_head')
1720 doit(localrevs, remoterevs)
1720 doit(localrevs, remoterevs)
1721
1721
1722 @command('debugfileset', [], ('REVSPEC'))
1722 @command('debugfileset', [], ('REVSPEC'))
1723 def debugfileset(ui, repo, expr):
1723 def debugfileset(ui, repo, expr):
1724 '''parse and apply a fileset specification'''
1724 '''parse and apply a fileset specification'''
1725 if ui.verbose:
1725 if ui.verbose:
1726 tree = fileset.parse(expr)[0]
1726 tree = fileset.parse(expr)[0]
1727 ui.note(tree, "\n")
1727 ui.note(tree, "\n")
1728
1728
1729 for f in fileset.getfileset(repo[None], expr):
1729 for f in fileset.getfileset(repo[None], expr):
1730 ui.write("%s\n" % f)
1730 ui.write("%s\n" % f)
1731
1731
1732 @command('debugfsinfo', [], _('[PATH]'))
1732 @command('debugfsinfo', [], _('[PATH]'))
1733 def debugfsinfo(ui, path = "."):
1733 def debugfsinfo(ui, path = "."):
1734 """show information detected about current filesystem"""
1734 """show information detected about current filesystem"""
1735 util.writefile('.debugfsinfo', '')
1735 util.writefile('.debugfsinfo', '')
1736 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1736 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1737 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1737 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1738 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1738 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1739 and 'yes' or 'no'))
1739 and 'yes' or 'no'))
1740 os.unlink('.debugfsinfo')
1740 os.unlink('.debugfsinfo')
1741
1741
1742 @command('debuggetbundle',
1742 @command('debuggetbundle',
1743 [('H', 'head', [], _('id of head node'), _('ID')),
1743 [('H', 'head', [], _('id of head node'), _('ID')),
1744 ('C', 'common', [], _('id of common node'), _('ID')),
1744 ('C', 'common', [], _('id of common node'), _('ID')),
1745 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1745 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1746 _('REPO FILE [-H|-C ID]...'))
1746 _('REPO FILE [-H|-C ID]...'))
1747 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1747 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1748 """retrieves a bundle from a repo
1748 """retrieves a bundle from a repo
1749
1749
1750 Every ID must be a full-length hex node id string. Saves the bundle to the
1750 Every ID must be a full-length hex node id string. Saves the bundle to the
1751 given file.
1751 given file.
1752 """
1752 """
1753 repo = hg.peer(ui, opts, repopath)
1753 repo = hg.peer(ui, opts, repopath)
1754 if not repo.capable('getbundle'):
1754 if not repo.capable('getbundle'):
1755 raise util.Abort("getbundle() not supported by target repository")
1755 raise util.Abort("getbundle() not supported by target repository")
1756 args = {}
1756 args = {}
1757 if common:
1757 if common:
1758 args['common'] = [bin(s) for s in common]
1758 args['common'] = [bin(s) for s in common]
1759 if head:
1759 if head:
1760 args['heads'] = [bin(s) for s in head]
1760 args['heads'] = [bin(s) for s in head]
1761 bundle = repo.getbundle('debug', **args)
1761 bundle = repo.getbundle('debug', **args)
1762
1762
1763 bundletype = opts.get('type', 'bzip2').lower()
1763 bundletype = opts.get('type', 'bzip2').lower()
1764 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1764 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1765 bundletype = btypes.get(bundletype)
1765 bundletype = btypes.get(bundletype)
1766 if bundletype not in changegroup.bundletypes:
1766 if bundletype not in changegroup.bundletypes:
1767 raise util.Abort(_('unknown bundle type specified with --type'))
1767 raise util.Abort(_('unknown bundle type specified with --type'))
1768 changegroup.writebundle(bundle, bundlepath, bundletype)
1768 changegroup.writebundle(bundle, bundlepath, bundletype)
1769
1769
1770 @command('debugignore', [], '')
1770 @command('debugignore', [], '')
1771 def debugignore(ui, repo, *values, **opts):
1771 def debugignore(ui, repo, *values, **opts):
1772 """display the combined ignore pattern"""
1772 """display the combined ignore pattern"""
1773 ignore = repo.dirstate._ignore
1773 ignore = repo.dirstate._ignore
1774 includepat = getattr(ignore, 'includepat', None)
1774 includepat = getattr(ignore, 'includepat', None)
1775 if includepat is not None:
1775 if includepat is not None:
1776 ui.write("%s\n" % includepat)
1776 ui.write("%s\n" % includepat)
1777 else:
1777 else:
1778 raise util.Abort(_("no ignore patterns found"))
1778 raise util.Abort(_("no ignore patterns found"))
1779
1779
1780 @command('debugindex',
1780 @command('debugindex',
1781 [('c', 'changelog', False, _('open changelog')),
1781 [('c', 'changelog', False, _('open changelog')),
1782 ('m', 'manifest', False, _('open manifest')),
1782 ('m', 'manifest', False, _('open manifest')),
1783 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1783 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1784 _('[-f FORMAT] -c|-m|FILE'))
1784 _('[-f FORMAT] -c|-m|FILE'))
1785 def debugindex(ui, repo, file_ = None, **opts):
1785 def debugindex(ui, repo, file_ = None, **opts):
1786 """dump the contents of an index file"""
1786 """dump the contents of an index file"""
1787 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1787 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1788 format = opts.get('format', 0)
1788 format = opts.get('format', 0)
1789 if format not in (0, 1):
1789 if format not in (0, 1):
1790 raise util.Abort(_("unknown format %d") % format)
1790 raise util.Abort(_("unknown format %d") % format)
1791
1791
1792 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1792 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1793 if generaldelta:
1793 if generaldelta:
1794 basehdr = ' delta'
1794 basehdr = ' delta'
1795 else:
1795 else:
1796 basehdr = ' base'
1796 basehdr = ' base'
1797
1797
1798 if format == 0:
1798 if format == 0:
1799 ui.write(" rev offset length " + basehdr + " linkrev"
1799 ui.write(" rev offset length " + basehdr + " linkrev"
1800 " nodeid p1 p2\n")
1800 " nodeid p1 p2\n")
1801 elif format == 1:
1801 elif format == 1:
1802 ui.write(" rev flag offset length"
1802 ui.write(" rev flag offset length"
1803 " size " + basehdr + " link p1 p2 nodeid\n")
1803 " size " + basehdr + " link p1 p2 nodeid\n")
1804
1804
1805 for i in r:
1805 for i in r:
1806 node = r.node(i)
1806 node = r.node(i)
1807 if generaldelta:
1807 if generaldelta:
1808 base = r.deltaparent(i)
1808 base = r.deltaparent(i)
1809 else:
1809 else:
1810 base = r.chainbase(i)
1810 base = r.chainbase(i)
1811 if format == 0:
1811 if format == 0:
1812 try:
1812 try:
1813 pp = r.parents(node)
1813 pp = r.parents(node)
1814 except:
1814 except:
1815 pp = [nullid, nullid]
1815 pp = [nullid, nullid]
1816 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1816 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1817 i, r.start(i), r.length(i), base, r.linkrev(i),
1817 i, r.start(i), r.length(i), base, r.linkrev(i),
1818 short(node), short(pp[0]), short(pp[1])))
1818 short(node), short(pp[0]), short(pp[1])))
1819 elif format == 1:
1819 elif format == 1:
1820 pr = r.parentrevs(i)
1820 pr = r.parentrevs(i)
1821 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1821 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1822 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1822 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1823 base, r.linkrev(i), pr[0], pr[1], short(node)))
1823 base, r.linkrev(i), pr[0], pr[1], short(node)))
1824
1824
1825 @command('debugindexdot', [], _('FILE'))
1825 @command('debugindexdot', [], _('FILE'))
1826 def debugindexdot(ui, repo, file_):
1826 def debugindexdot(ui, repo, file_):
1827 """dump an index DAG as a graphviz dot file"""
1827 """dump an index DAG as a graphviz dot file"""
1828 r = None
1828 r = None
1829 if repo:
1829 if repo:
1830 filelog = repo.file(file_)
1830 filelog = repo.file(file_)
1831 if len(filelog):
1831 if len(filelog):
1832 r = filelog
1832 r = filelog
1833 if not r:
1833 if not r:
1834 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1834 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1835 ui.write("digraph G {\n")
1835 ui.write("digraph G {\n")
1836 for i in r:
1836 for i in r:
1837 node = r.node(i)
1837 node = r.node(i)
1838 pp = r.parents(node)
1838 pp = r.parents(node)
1839 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1839 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1840 if pp[1] != nullid:
1840 if pp[1] != nullid:
1841 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1841 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1842 ui.write("}\n")
1842 ui.write("}\n")
1843
1843
1844 @command('debuginstall', [], '')
1844 @command('debuginstall', [], '')
1845 def debuginstall(ui):
1845 def debuginstall(ui):
1846 '''test Mercurial installation
1846 '''test Mercurial installation
1847
1847
1848 Returns 0 on success.
1848 Returns 0 on success.
1849 '''
1849 '''
1850
1850
1851 def writetemp(contents):
1851 def writetemp(contents):
1852 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1852 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1853 f = os.fdopen(fd, "wb")
1853 f = os.fdopen(fd, "wb")
1854 f.write(contents)
1854 f.write(contents)
1855 f.close()
1855 f.close()
1856 return name
1856 return name
1857
1857
1858 problems = 0
1858 problems = 0
1859
1859
1860 # encoding
1860 # encoding
1861 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1861 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1862 try:
1862 try:
1863 encoding.fromlocal("test")
1863 encoding.fromlocal("test")
1864 except util.Abort, inst:
1864 except util.Abort, inst:
1865 ui.write(" %s\n" % inst)
1865 ui.write(" %s\n" % inst)
1866 ui.write(_(" (check that your locale is properly set)\n"))
1866 ui.write(_(" (check that your locale is properly set)\n"))
1867 problems += 1
1867 problems += 1
1868
1868
1869 # compiled modules
1869 # compiled modules
1870 ui.status(_("Checking installed modules (%s)...\n")
1870 ui.status(_("Checking installed modules (%s)...\n")
1871 % os.path.dirname(__file__))
1871 % os.path.dirname(__file__))
1872 try:
1872 try:
1873 import bdiff, mpatch, base85, osutil
1873 import bdiff, mpatch, base85, osutil
1874 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1874 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1875 except Exception, inst:
1875 except Exception, inst:
1876 ui.write(" %s\n" % inst)
1876 ui.write(" %s\n" % inst)
1877 ui.write(_(" One or more extensions could not be found"))
1877 ui.write(_(" One or more extensions could not be found"))
1878 ui.write(_(" (check that you compiled the extensions)\n"))
1878 ui.write(_(" (check that you compiled the extensions)\n"))
1879 problems += 1
1879 problems += 1
1880
1880
1881 # templates
1881 # templates
1882 import templater
1882 import templater
1883 p = templater.templatepath()
1883 p = templater.templatepath()
1884 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1884 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1885 try:
1885 try:
1886 templater.templater(templater.templatepath("map-cmdline.default"))
1886 templater.templater(templater.templatepath("map-cmdline.default"))
1887 except Exception, inst:
1887 except Exception, inst:
1888 ui.write(" %s\n" % inst)
1888 ui.write(" %s\n" % inst)
1889 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1889 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1890 problems += 1
1890 problems += 1
1891
1891
1892 # editor
1892 # editor
1893 ui.status(_("Checking commit editor...\n"))
1893 ui.status(_("Checking commit editor...\n"))
1894 editor = ui.geteditor()
1894 editor = ui.geteditor()
1895 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1895 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1896 if not cmdpath:
1896 if not cmdpath:
1897 if editor == 'vi':
1897 if editor == 'vi':
1898 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1898 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1899 ui.write(_(" (specify a commit editor in your configuration"
1899 ui.write(_(" (specify a commit editor in your configuration"
1900 " file)\n"))
1900 " file)\n"))
1901 else:
1901 else:
1902 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1902 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1903 ui.write(_(" (specify a commit editor in your configuration"
1903 ui.write(_(" (specify a commit editor in your configuration"
1904 " file)\n"))
1904 " file)\n"))
1905 problems += 1
1905 problems += 1
1906
1906
1907 # check username
1907 # check username
1908 ui.status(_("Checking username...\n"))
1908 ui.status(_("Checking username...\n"))
1909 try:
1909 try:
1910 ui.username()
1910 ui.username()
1911 except util.Abort, e:
1911 except util.Abort, e:
1912 ui.write(" %s\n" % e)
1912 ui.write(" %s\n" % e)
1913 ui.write(_(" (specify a username in your configuration file)\n"))
1913 ui.write(_(" (specify a username in your configuration file)\n"))
1914 problems += 1
1914 problems += 1
1915
1915
1916 if not problems:
1916 if not problems:
1917 ui.status(_("No problems detected\n"))
1917 ui.status(_("No problems detected\n"))
1918 else:
1918 else:
1919 ui.write(_("%s problems detected,"
1919 ui.write(_("%s problems detected,"
1920 " please check your install!\n") % problems)
1920 " please check your install!\n") % problems)
1921
1921
1922 return problems
1922 return problems
1923
1923
1924 @command('debugknown', [], _('REPO ID...'))
1924 @command('debugknown', [], _('REPO ID...'))
1925 def debugknown(ui, repopath, *ids, **opts):
1925 def debugknown(ui, repopath, *ids, **opts):
1926 """test whether node ids are known to a repo
1926 """test whether node ids are known to a repo
1927
1927
1928 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1928 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1929 indicating unknown/known.
1929 indicating unknown/known.
1930 """
1930 """
1931 repo = hg.peer(ui, opts, repopath)
1931 repo = hg.peer(ui, opts, repopath)
1932 if not repo.capable('known'):
1932 if not repo.capable('known'):
1933 raise util.Abort("known() not supported by target repository")
1933 raise util.Abort("known() not supported by target repository")
1934 flags = repo.known([bin(s) for s in ids])
1934 flags = repo.known([bin(s) for s in ids])
1935 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1935 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1936
1936
1937 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1937 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1938 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1938 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1939 '''access the pushkey key/value protocol
1939 '''access the pushkey key/value protocol
1940
1940
1941 With two args, list the keys in the given namespace.
1941 With two args, list the keys in the given namespace.
1942
1942
1943 With five args, set a key to new if it currently is set to old.
1943 With five args, set a key to new if it currently is set to old.
1944 Reports success or failure.
1944 Reports success or failure.
1945 '''
1945 '''
1946
1946
1947 target = hg.peer(ui, {}, repopath)
1947 target = hg.peer(ui, {}, repopath)
1948 if keyinfo:
1948 if keyinfo:
1949 key, old, new = keyinfo
1949 key, old, new = keyinfo
1950 r = target.pushkey(namespace, key, old, new)
1950 r = target.pushkey(namespace, key, old, new)
1951 ui.status(str(r) + '\n')
1951 ui.status(str(r) + '\n')
1952 return not r
1952 return not r
1953 else:
1953 else:
1954 for k, v in target.listkeys(namespace).iteritems():
1954 for k, v in target.listkeys(namespace).iteritems():
1955 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1955 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1956 v.encode('string-escape')))
1956 v.encode('string-escape')))
1957
1957
1958 @command('debugrebuildstate',
1958 @command('debugrebuildstate',
1959 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1959 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1960 _('[-r REV] [REV]'))
1960 _('[-r REV] [REV]'))
1961 def debugrebuildstate(ui, repo, rev="tip"):
1961 def debugrebuildstate(ui, repo, rev="tip"):
1962 """rebuild the dirstate as it would look like for the given revision"""
1962 """rebuild the dirstate as it would look like for the given revision"""
1963 ctx = scmutil.revsingle(repo, rev)
1963 ctx = scmutil.revsingle(repo, rev)
1964 wlock = repo.wlock()
1964 wlock = repo.wlock()
1965 try:
1965 try:
1966 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1966 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1967 finally:
1967 finally:
1968 wlock.release()
1968 wlock.release()
1969
1969
1970 @command('debugrename',
1970 @command('debugrename',
1971 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1971 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1972 _('[-r REV] FILE'))
1972 _('[-r REV] FILE'))
1973 def debugrename(ui, repo, file1, *pats, **opts):
1973 def debugrename(ui, repo, file1, *pats, **opts):
1974 """dump rename information"""
1974 """dump rename information"""
1975
1975
1976 ctx = scmutil.revsingle(repo, opts.get('rev'))
1976 ctx = scmutil.revsingle(repo, opts.get('rev'))
1977 m = scmutil.match(ctx, (file1,) + pats, opts)
1977 m = scmutil.match(ctx, (file1,) + pats, opts)
1978 for abs in ctx.walk(m):
1978 for abs in ctx.walk(m):
1979 fctx = ctx[abs]
1979 fctx = ctx[abs]
1980 o = fctx.filelog().renamed(fctx.filenode())
1980 o = fctx.filelog().renamed(fctx.filenode())
1981 rel = m.rel(abs)
1981 rel = m.rel(abs)
1982 if o:
1982 if o:
1983 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1983 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1984 else:
1984 else:
1985 ui.write(_("%s not renamed\n") % rel)
1985 ui.write(_("%s not renamed\n") % rel)
1986
1986
1987 @command('debugrevlog',
1987 @command('debugrevlog',
1988 [('c', 'changelog', False, _('open changelog')),
1988 [('c', 'changelog', False, _('open changelog')),
1989 ('m', 'manifest', False, _('open manifest')),
1989 ('m', 'manifest', False, _('open manifest')),
1990 ('d', 'dump', False, _('dump index data'))],
1990 ('d', 'dump', False, _('dump index data'))],
1991 _('-c|-m|FILE'))
1991 _('-c|-m|FILE'))
1992 def debugrevlog(ui, repo, file_ = None, **opts):
1992 def debugrevlog(ui, repo, file_ = None, **opts):
1993 """show data and statistics about a revlog"""
1993 """show data and statistics about a revlog"""
1994 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1994 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1995
1995
1996 if opts.get("dump"):
1996 if opts.get("dump"):
1997 numrevs = len(r)
1997 numrevs = len(r)
1998 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1998 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1999 " rawsize totalsize compression heads\n")
1999 " rawsize totalsize compression heads\n")
2000 ts = 0
2000 ts = 0
2001 heads = set()
2001 heads = set()
2002 for rev in xrange(numrevs):
2002 for rev in xrange(numrevs):
2003 dbase = r.deltaparent(rev)
2003 dbase = r.deltaparent(rev)
2004 if dbase == -1:
2004 if dbase == -1:
2005 dbase = rev
2005 dbase = rev
2006 cbase = r.chainbase(rev)
2006 cbase = r.chainbase(rev)
2007 p1, p2 = r.parentrevs(rev)
2007 p1, p2 = r.parentrevs(rev)
2008 rs = r.rawsize(rev)
2008 rs = r.rawsize(rev)
2009 ts = ts + rs
2009 ts = ts + rs
2010 heads -= set(r.parentrevs(rev))
2010 heads -= set(r.parentrevs(rev))
2011 heads.add(rev)
2011 heads.add(rev)
2012 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2012 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2013 (rev, p1, p2, r.start(rev), r.end(rev),
2013 (rev, p1, p2, r.start(rev), r.end(rev),
2014 r.start(dbase), r.start(cbase),
2014 r.start(dbase), r.start(cbase),
2015 r.start(p1), r.start(p2),
2015 r.start(p1), r.start(p2),
2016 rs, ts, ts / r.end(rev), len(heads)))
2016 rs, ts, ts / r.end(rev), len(heads)))
2017 return 0
2017 return 0
2018
2018
2019 v = r.version
2019 v = r.version
2020 format = v & 0xFFFF
2020 format = v & 0xFFFF
2021 flags = []
2021 flags = []
2022 gdelta = False
2022 gdelta = False
2023 if v & revlog.REVLOGNGINLINEDATA:
2023 if v & revlog.REVLOGNGINLINEDATA:
2024 flags.append('inline')
2024 flags.append('inline')
2025 if v & revlog.REVLOGGENERALDELTA:
2025 if v & revlog.REVLOGGENERALDELTA:
2026 gdelta = True
2026 gdelta = True
2027 flags.append('generaldelta')
2027 flags.append('generaldelta')
2028 if not flags:
2028 if not flags:
2029 flags = ['(none)']
2029 flags = ['(none)']
2030
2030
2031 nummerges = 0
2031 nummerges = 0
2032 numfull = 0
2032 numfull = 0
2033 numprev = 0
2033 numprev = 0
2034 nump1 = 0
2034 nump1 = 0
2035 nump2 = 0
2035 nump2 = 0
2036 numother = 0
2036 numother = 0
2037 nump1prev = 0
2037 nump1prev = 0
2038 nump2prev = 0
2038 nump2prev = 0
2039 chainlengths = []
2039 chainlengths = []
2040
2040
2041 datasize = [None, 0, 0L]
2041 datasize = [None, 0, 0L]
2042 fullsize = [None, 0, 0L]
2042 fullsize = [None, 0, 0L]
2043 deltasize = [None, 0, 0L]
2043 deltasize = [None, 0, 0L]
2044
2044
2045 def addsize(size, l):
2045 def addsize(size, l):
2046 if l[0] is None or size < l[0]:
2046 if l[0] is None or size < l[0]:
2047 l[0] = size
2047 l[0] = size
2048 if size > l[1]:
2048 if size > l[1]:
2049 l[1] = size
2049 l[1] = size
2050 l[2] += size
2050 l[2] += size
2051
2051
2052 numrevs = len(r)
2052 numrevs = len(r)
2053 for rev in xrange(numrevs):
2053 for rev in xrange(numrevs):
2054 p1, p2 = r.parentrevs(rev)
2054 p1, p2 = r.parentrevs(rev)
2055 delta = r.deltaparent(rev)
2055 delta = r.deltaparent(rev)
2056 if format > 0:
2056 if format > 0:
2057 addsize(r.rawsize(rev), datasize)
2057 addsize(r.rawsize(rev), datasize)
2058 if p2 != nullrev:
2058 if p2 != nullrev:
2059 nummerges += 1
2059 nummerges += 1
2060 size = r.length(rev)
2060 size = r.length(rev)
2061 if delta == nullrev:
2061 if delta == nullrev:
2062 chainlengths.append(0)
2062 chainlengths.append(0)
2063 numfull += 1
2063 numfull += 1
2064 addsize(size, fullsize)
2064 addsize(size, fullsize)
2065 else:
2065 else:
2066 chainlengths.append(chainlengths[delta] + 1)
2066 chainlengths.append(chainlengths[delta] + 1)
2067 addsize(size, deltasize)
2067 addsize(size, deltasize)
2068 if delta == rev - 1:
2068 if delta == rev - 1:
2069 numprev += 1
2069 numprev += 1
2070 if delta == p1:
2070 if delta == p1:
2071 nump1prev += 1
2071 nump1prev += 1
2072 elif delta == p2:
2072 elif delta == p2:
2073 nump2prev += 1
2073 nump2prev += 1
2074 elif delta == p1:
2074 elif delta == p1:
2075 nump1 += 1
2075 nump1 += 1
2076 elif delta == p2:
2076 elif delta == p2:
2077 nump2 += 1
2077 nump2 += 1
2078 elif delta != nullrev:
2078 elif delta != nullrev:
2079 numother += 1
2079 numother += 1
2080
2080
2081 numdeltas = numrevs - numfull
2081 numdeltas = numrevs - numfull
2082 numoprev = numprev - nump1prev - nump2prev
2082 numoprev = numprev - nump1prev - nump2prev
2083 totalrawsize = datasize[2]
2083 totalrawsize = datasize[2]
2084 datasize[2] /= numrevs
2084 datasize[2] /= numrevs
2085 fulltotal = fullsize[2]
2085 fulltotal = fullsize[2]
2086 fullsize[2] /= numfull
2086 fullsize[2] /= numfull
2087 deltatotal = deltasize[2]
2087 deltatotal = deltasize[2]
2088 deltasize[2] /= numrevs - numfull
2088 deltasize[2] /= numrevs - numfull
2089 totalsize = fulltotal + deltatotal
2089 totalsize = fulltotal + deltatotal
2090 avgchainlen = sum(chainlengths) / numrevs
2090 avgchainlen = sum(chainlengths) / numrevs
2091 compratio = totalrawsize / totalsize
2091 compratio = totalrawsize / totalsize
2092
2092
2093 basedfmtstr = '%%%dd\n'
2093 basedfmtstr = '%%%dd\n'
2094 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2094 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2095
2095
2096 def dfmtstr(max):
2096 def dfmtstr(max):
2097 return basedfmtstr % len(str(max))
2097 return basedfmtstr % len(str(max))
2098 def pcfmtstr(max, padding=0):
2098 def pcfmtstr(max, padding=0):
2099 return basepcfmtstr % (len(str(max)), ' ' * padding)
2099 return basepcfmtstr % (len(str(max)), ' ' * padding)
2100
2100
2101 def pcfmt(value, total):
2101 def pcfmt(value, total):
2102 return (value, 100 * float(value) / total)
2102 return (value, 100 * float(value) / total)
2103
2103
2104 ui.write('format : %d\n' % format)
2104 ui.write('format : %d\n' % format)
2105 ui.write('flags : %s\n' % ', '.join(flags))
2105 ui.write('flags : %s\n' % ', '.join(flags))
2106
2106
2107 ui.write('\n')
2107 ui.write('\n')
2108 fmt = pcfmtstr(totalsize)
2108 fmt = pcfmtstr(totalsize)
2109 fmt2 = dfmtstr(totalsize)
2109 fmt2 = dfmtstr(totalsize)
2110 ui.write('revisions : ' + fmt2 % numrevs)
2110 ui.write('revisions : ' + fmt2 % numrevs)
2111 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2111 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2112 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2112 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2113 ui.write('revisions : ' + fmt2 % numrevs)
2113 ui.write('revisions : ' + fmt2 % numrevs)
2114 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2114 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2115 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2115 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2116 ui.write('revision size : ' + fmt2 % totalsize)
2116 ui.write('revision size : ' + fmt2 % totalsize)
2117 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2117 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2118 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2118 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2119
2119
2120 ui.write('\n')
2120 ui.write('\n')
2121 fmt = dfmtstr(max(avgchainlen, compratio))
2121 fmt = dfmtstr(max(avgchainlen, compratio))
2122 ui.write('avg chain length : ' + fmt % avgchainlen)
2122 ui.write('avg chain length : ' + fmt % avgchainlen)
2123 ui.write('compression ratio : ' + fmt % compratio)
2123 ui.write('compression ratio : ' + fmt % compratio)
2124
2124
2125 if format > 0:
2125 if format > 0:
2126 ui.write('\n')
2126 ui.write('\n')
2127 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2127 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2128 % tuple(datasize))
2128 % tuple(datasize))
2129 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2129 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2130 % tuple(fullsize))
2130 % tuple(fullsize))
2131 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2131 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2132 % tuple(deltasize))
2132 % tuple(deltasize))
2133
2133
2134 if numdeltas > 0:
2134 if numdeltas > 0:
2135 ui.write('\n')
2135 ui.write('\n')
2136 fmt = pcfmtstr(numdeltas)
2136 fmt = pcfmtstr(numdeltas)
2137 fmt2 = pcfmtstr(numdeltas, 4)
2137 fmt2 = pcfmtstr(numdeltas, 4)
2138 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2138 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2139 if numprev > 0:
2139 if numprev > 0:
2140 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2140 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2141 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2141 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2142 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2142 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2143 if gdelta:
2143 if gdelta:
2144 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2144 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2145 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2145 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2146 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2146 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2147
2147
2148 @command('debugrevspec', [], ('REVSPEC'))
2148 @command('debugrevspec', [], ('REVSPEC'))
2149 def debugrevspec(ui, repo, expr):
2149 def debugrevspec(ui, repo, expr):
2150 '''parse and apply a revision specification'''
2150 '''parse and apply a revision specification'''
2151 if ui.verbose:
2151 if ui.verbose:
2152 tree = revset.parse(expr)[0]
2152 tree = revset.parse(expr)[0]
2153 ui.note(tree, "\n")
2153 ui.note(tree, "\n")
2154 newtree = revset.findaliases(ui, tree)
2154 newtree = revset.findaliases(ui, tree)
2155 if newtree != tree:
2155 if newtree != tree:
2156 ui.note(newtree, "\n")
2156 ui.note(newtree, "\n")
2157 func = revset.match(ui, expr)
2157 func = revset.match(ui, expr)
2158 for c in func(repo, range(len(repo))):
2158 for c in func(repo, range(len(repo))):
2159 ui.write("%s\n" % c)
2159 ui.write("%s\n" % c)
2160
2160
2161 @command('debugsetparents', [], _('REV1 [REV2]'))
2161 @command('debugsetparents', [], _('REV1 [REV2]'))
2162 def debugsetparents(ui, repo, rev1, rev2=None):
2162 def debugsetparents(ui, repo, rev1, rev2=None):
2163 """manually set the parents of the current working directory
2163 """manually set the parents of the current working directory
2164
2164
2165 This is useful for writing repository conversion tools, but should
2165 This is useful for writing repository conversion tools, but should
2166 be used with care.
2166 be used with care.
2167
2167
2168 Returns 0 on success.
2168 Returns 0 on success.
2169 """
2169 """
2170
2170
2171 r1 = scmutil.revsingle(repo, rev1).node()
2171 r1 = scmutil.revsingle(repo, rev1).node()
2172 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2172 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2173
2173
2174 wlock = repo.wlock()
2174 wlock = repo.wlock()
2175 try:
2175 try:
2176 repo.dirstate.setparents(r1, r2)
2176 repo.dirstate.setparents(r1, r2)
2177 finally:
2177 finally:
2178 wlock.release()
2178 wlock.release()
2179
2179
2180 @command('debugstate',
2180 @command('debugstate',
2181 [('', 'nodates', None, _('do not display the saved mtime')),
2181 [('', 'nodates', None, _('do not display the saved mtime')),
2182 ('', 'datesort', None, _('sort by saved mtime'))],
2182 ('', 'datesort', None, _('sort by saved mtime'))],
2183 _('[OPTION]...'))
2183 _('[OPTION]...'))
2184 def debugstate(ui, repo, nodates=None, datesort=None):
2184 def debugstate(ui, repo, nodates=None, datesort=None):
2185 """show the contents of the current dirstate"""
2185 """show the contents of the current dirstate"""
2186 timestr = ""
2186 timestr = ""
2187 showdate = not nodates
2187 showdate = not nodates
2188 if datesort:
2188 if datesort:
2189 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2189 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2190 else:
2190 else:
2191 keyfunc = None # sort by filename
2191 keyfunc = None # sort by filename
2192 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2192 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2193 if showdate:
2193 if showdate:
2194 if ent[3] == -1:
2194 if ent[3] == -1:
2195 # Pad or slice to locale representation
2195 # Pad or slice to locale representation
2196 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2196 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2197 time.localtime(0)))
2197 time.localtime(0)))
2198 timestr = 'unset'
2198 timestr = 'unset'
2199 timestr = (timestr[:locale_len] +
2199 timestr = (timestr[:locale_len] +
2200 ' ' * (locale_len - len(timestr)))
2200 ' ' * (locale_len - len(timestr)))
2201 else:
2201 else:
2202 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2202 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2203 time.localtime(ent[3]))
2203 time.localtime(ent[3]))
2204 if ent[1] & 020000:
2204 if ent[1] & 020000:
2205 mode = 'lnk'
2205 mode = 'lnk'
2206 else:
2206 else:
2207 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2207 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2208 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2208 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2209 for f in repo.dirstate.copies():
2209 for f in repo.dirstate.copies():
2210 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2210 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2211
2211
2212 @command('debugsub',
2212 @command('debugsub',
2213 [('r', 'rev', '',
2213 [('r', 'rev', '',
2214 _('revision to check'), _('REV'))],
2214 _('revision to check'), _('REV'))],
2215 _('[-r REV] [REV]'))
2215 _('[-r REV] [REV]'))
2216 def debugsub(ui, repo, rev=None):
2216 def debugsub(ui, repo, rev=None):
2217 ctx = scmutil.revsingle(repo, rev, None)
2217 ctx = scmutil.revsingle(repo, rev, None)
2218 for k, v in sorted(ctx.substate.items()):
2218 for k, v in sorted(ctx.substate.items()):
2219 ui.write('path %s\n' % k)
2219 ui.write('path %s\n' % k)
2220 ui.write(' source %s\n' % v[0])
2220 ui.write(' source %s\n' % v[0])
2221 ui.write(' revision %s\n' % v[1])
2221 ui.write(' revision %s\n' % v[1])
2222
2222
2223 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2223 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2224 def debugwalk(ui, repo, *pats, **opts):
2224 def debugwalk(ui, repo, *pats, **opts):
2225 """show how files match on given patterns"""
2225 """show how files match on given patterns"""
2226 m = scmutil.match(repo[None], pats, opts)
2226 m = scmutil.match(repo[None], pats, opts)
2227 items = list(repo.walk(m))
2227 items = list(repo.walk(m))
2228 if not items:
2228 if not items:
2229 return
2229 return
2230 fmt = 'f %%-%ds %%-%ds %%s' % (
2230 fmt = 'f %%-%ds %%-%ds %%s' % (
2231 max([len(abs) for abs in items]),
2231 max([len(abs) for abs in items]),
2232 max([len(m.rel(abs)) for abs in items]))
2232 max([len(m.rel(abs)) for abs in items]))
2233 for abs in items:
2233 for abs in items:
2234 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2234 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2235 ui.write("%s\n" % line.rstrip())
2235 ui.write("%s\n" % line.rstrip())
2236
2236
2237 @command('debugwireargs',
2237 @command('debugwireargs',
2238 [('', 'three', '', 'three'),
2238 [('', 'three', '', 'three'),
2239 ('', 'four', '', 'four'),
2239 ('', 'four', '', 'four'),
2240 ('', 'five', '', 'five'),
2240 ('', 'five', '', 'five'),
2241 ] + remoteopts,
2241 ] + remoteopts,
2242 _('REPO [OPTIONS]... [ONE [TWO]]'))
2242 _('REPO [OPTIONS]... [ONE [TWO]]'))
2243 def debugwireargs(ui, repopath, *vals, **opts):
2243 def debugwireargs(ui, repopath, *vals, **opts):
2244 repo = hg.peer(ui, opts, repopath)
2244 repo = hg.peer(ui, opts, repopath)
2245 for opt in remoteopts:
2245 for opt in remoteopts:
2246 del opts[opt[1]]
2246 del opts[opt[1]]
2247 args = {}
2247 args = {}
2248 for k, v in opts.iteritems():
2248 for k, v in opts.iteritems():
2249 if v:
2249 if v:
2250 args[k] = v
2250 args[k] = v
2251 # run twice to check that we don't mess up the stream for the next command
2251 # run twice to check that we don't mess up the stream for the next command
2252 res1 = repo.debugwireargs(*vals, **args)
2252 res1 = repo.debugwireargs(*vals, **args)
2253 res2 = repo.debugwireargs(*vals, **args)
2253 res2 = repo.debugwireargs(*vals, **args)
2254 ui.write("%s\n" % res1)
2254 ui.write("%s\n" % res1)
2255 if res1 != res2:
2255 if res1 != res2:
2256 ui.warn("%s\n" % res2)
2256 ui.warn("%s\n" % res2)
2257
2257
2258 @command('^diff',
2258 @command('^diff',
2259 [('r', 'rev', [], _('revision'), _('REV')),
2259 [('r', 'rev', [], _('revision'), _('REV')),
2260 ('c', 'change', '', _('change made by revision'), _('REV'))
2260 ('c', 'change', '', _('change made by revision'), _('REV'))
2261 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2261 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2262 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2262 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2263 def diff(ui, repo, *pats, **opts):
2263 def diff(ui, repo, *pats, **opts):
2264 """diff repository (or selected files)
2264 """diff repository (or selected files)
2265
2265
2266 Show differences between revisions for the specified files.
2266 Show differences between revisions for the specified files.
2267
2267
2268 Differences between files are shown using the unified diff format.
2268 Differences between files are shown using the unified diff format.
2269
2269
2270 .. note::
2270 .. note::
2271 diff may generate unexpected results for merges, as it will
2271 diff may generate unexpected results for merges, as it will
2272 default to comparing against the working directory's first
2272 default to comparing against the working directory's first
2273 parent changeset if no revisions are specified.
2273 parent changeset if no revisions are specified.
2274
2274
2275 When two revision arguments are given, then changes are shown
2275 When two revision arguments are given, then changes are shown
2276 between those revisions. If only one revision is specified then
2276 between those revisions. If only one revision is specified then
2277 that revision is compared to the working directory, and, when no
2277 that revision is compared to the working directory, and, when no
2278 revisions are specified, the working directory files are compared
2278 revisions are specified, the working directory files are compared
2279 to its parent.
2279 to its parent.
2280
2280
2281 Alternatively you can specify -c/--change with a revision to see
2281 Alternatively you can specify -c/--change with a revision to see
2282 the changes in that changeset relative to its first parent.
2282 the changes in that changeset relative to its first parent.
2283
2283
2284 Without the -a/--text option, diff will avoid generating diffs of
2284 Without the -a/--text option, diff will avoid generating diffs of
2285 files it detects as binary. With -a, diff will generate a diff
2285 files it detects as binary. With -a, diff will generate a diff
2286 anyway, probably with undesirable results.
2286 anyway, probably with undesirable results.
2287
2287
2288 Use the -g/--git option to generate diffs in the git extended diff
2288 Use the -g/--git option to generate diffs in the git extended diff
2289 format. For more information, read :hg:`help diffs`.
2289 format. For more information, read :hg:`help diffs`.
2290
2290
2291 .. container:: verbose
2291 .. container:: verbose
2292
2292
2293 Examples:
2293 Examples:
2294
2294
2295 - compare a file in the current working directory to its parent::
2295 - compare a file in the current working directory to its parent::
2296
2296
2297 hg diff foo.c
2297 hg diff foo.c
2298
2298
2299 - compare two historical versions of a directory, with rename info::
2299 - compare two historical versions of a directory, with rename info::
2300
2300
2301 hg diff --git -r 1.0:1.2 lib/
2301 hg diff --git -r 1.0:1.2 lib/
2302
2302
2303 - get change stats relative to the last change on some date::
2303 - get change stats relative to the last change on some date::
2304
2304
2305 hg diff --stat -r "date('may 2')"
2305 hg diff --stat -r "date('may 2')"
2306
2306
2307 - diff all newly-added files that contain a keyword::
2307 - diff all newly-added files that contain a keyword::
2308
2308
2309 hg diff "set:added() and grep(GNU)"
2309 hg diff "set:added() and grep(GNU)"
2310
2310
2311 - compare a revision and its parents::
2311 - compare a revision and its parents::
2312
2312
2313 hg diff -c 9353 # compare against first parent
2313 hg diff -c 9353 # compare against first parent
2314 hg diff -r 9353^:9353 # same using revset syntax
2314 hg diff -r 9353^:9353 # same using revset syntax
2315 hg diff -r 9353^2:9353 # compare against the second parent
2315 hg diff -r 9353^2:9353 # compare against the second parent
2316
2316
2317 Returns 0 on success.
2317 Returns 0 on success.
2318 """
2318 """
2319
2319
2320 revs = opts.get('rev')
2320 revs = opts.get('rev')
2321 change = opts.get('change')
2321 change = opts.get('change')
2322 stat = opts.get('stat')
2322 stat = opts.get('stat')
2323 reverse = opts.get('reverse')
2323 reverse = opts.get('reverse')
2324
2324
2325 if revs and change:
2325 if revs and change:
2326 msg = _('cannot specify --rev and --change at the same time')
2326 msg = _('cannot specify --rev and --change at the same time')
2327 raise util.Abort(msg)
2327 raise util.Abort(msg)
2328 elif change:
2328 elif change:
2329 node2 = scmutil.revsingle(repo, change, None).node()
2329 node2 = scmutil.revsingle(repo, change, None).node()
2330 node1 = repo[node2].p1().node()
2330 node1 = repo[node2].p1().node()
2331 else:
2331 else:
2332 node1, node2 = scmutil.revpair(repo, revs)
2332 node1, node2 = scmutil.revpair(repo, revs)
2333
2333
2334 if reverse:
2334 if reverse:
2335 node1, node2 = node2, node1
2335 node1, node2 = node2, node1
2336
2336
2337 diffopts = patch.diffopts(ui, opts)
2337 diffopts = patch.diffopts(ui, opts)
2338 m = scmutil.match(repo[node2], pats, opts)
2338 m = scmutil.match(repo[node2], pats, opts)
2339 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2339 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2340 listsubrepos=opts.get('subrepos'))
2340 listsubrepos=opts.get('subrepos'))
2341
2341
2342 @command('^export',
2342 @command('^export',
2343 [('o', 'output', '',
2343 [('o', 'output', '',
2344 _('print output to file with formatted name'), _('FORMAT')),
2344 _('print output to file with formatted name'), _('FORMAT')),
2345 ('', 'switch-parent', None, _('diff against the second parent')),
2345 ('', 'switch-parent', None, _('diff against the second parent')),
2346 ('r', 'rev', [], _('revisions to export'), _('REV')),
2346 ('r', 'rev', [], _('revisions to export'), _('REV')),
2347 ] + diffopts,
2347 ] + diffopts,
2348 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2348 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2349 def export(ui, repo, *changesets, **opts):
2349 def export(ui, repo, *changesets, **opts):
2350 """dump the header and diffs for one or more changesets
2350 """dump the header and diffs for one or more changesets
2351
2351
2352 Print the changeset header and diffs for one or more revisions.
2352 Print the changeset header and diffs for one or more revisions.
2353
2353
2354 The information shown in the changeset header is: author, date,
2354 The information shown in the changeset header is: author, date,
2355 branch name (if non-default), changeset hash, parent(s) and commit
2355 branch name (if non-default), changeset hash, parent(s) and commit
2356 comment.
2356 comment.
2357
2357
2358 .. note::
2358 .. note::
2359 export may generate unexpected diff output for merge
2359 export may generate unexpected diff output for merge
2360 changesets, as it will compare the merge changeset against its
2360 changesets, as it will compare the merge changeset against its
2361 first parent only.
2361 first parent only.
2362
2362
2363 Output may be to a file, in which case the name of the file is
2363 Output may be to a file, in which case the name of the file is
2364 given using a format string. The formatting rules are as follows:
2364 given using a format string. The formatting rules are as follows:
2365
2365
2366 :``%%``: literal "%" character
2366 :``%%``: literal "%" character
2367 :``%H``: changeset hash (40 hexadecimal digits)
2367 :``%H``: changeset hash (40 hexadecimal digits)
2368 :``%N``: number of patches being generated
2368 :``%N``: number of patches being generated
2369 :``%R``: changeset revision number
2369 :``%R``: changeset revision number
2370 :``%b``: basename of the exporting repository
2370 :``%b``: basename of the exporting repository
2371 :``%h``: short-form changeset hash (12 hexadecimal digits)
2371 :``%h``: short-form changeset hash (12 hexadecimal digits)
2372 :``%m``: first line of the commit message (only alphanumeric characters)
2372 :``%m``: first line of the commit message (only alphanumeric characters)
2373 :``%n``: zero-padded sequence number, starting at 1
2373 :``%n``: zero-padded sequence number, starting at 1
2374 :``%r``: zero-padded changeset revision number
2374 :``%r``: zero-padded changeset revision number
2375
2375
2376 Without the -a/--text option, export will avoid generating diffs
2376 Without the -a/--text option, export will avoid generating diffs
2377 of files it detects as binary. With -a, export will generate a
2377 of files it detects as binary. With -a, export will generate a
2378 diff anyway, probably with undesirable results.
2378 diff anyway, probably with undesirable results.
2379
2379
2380 Use the -g/--git option to generate diffs in the git extended diff
2380 Use the -g/--git option to generate diffs in the git extended diff
2381 format. See :hg:`help diffs` for more information.
2381 format. See :hg:`help diffs` for more information.
2382
2382
2383 With the --switch-parent option, the diff will be against the
2383 With the --switch-parent option, the diff will be against the
2384 second parent. It can be useful to review a merge.
2384 second parent. It can be useful to review a merge.
2385
2385
2386 .. container:: verbose
2386 .. container:: verbose
2387
2387
2388 Examples:
2388 Examples:
2389
2389
2390 - use export and import to transplant a bugfix to the current
2390 - use export and import to transplant a bugfix to the current
2391 branch::
2391 branch::
2392
2392
2393 hg export -r 9353 | hg import -
2393 hg export -r 9353 | hg import -
2394
2394
2395 - export all the changesets between two revisions to a file with
2395 - export all the changesets between two revisions to a file with
2396 rename information::
2396 rename information::
2397
2397
2398 hg export --git -r 123:150 > changes.txt
2398 hg export --git -r 123:150 > changes.txt
2399
2399
2400 - split outgoing changes into a series of patches with
2400 - split outgoing changes into a series of patches with
2401 descriptive names::
2401 descriptive names::
2402
2402
2403 hg export -r "outgoing()" -o "%n-%m.patch"
2403 hg export -r "outgoing()" -o "%n-%m.patch"
2404
2404
2405 Returns 0 on success.
2405 Returns 0 on success.
2406 """
2406 """
2407 changesets += tuple(opts.get('rev', []))
2407 changesets += tuple(opts.get('rev', []))
2408 if not changesets:
2408 if not changesets:
2409 raise util.Abort(_("export requires at least one changeset"))
2409 raise util.Abort(_("export requires at least one changeset"))
2410 revs = scmutil.revrange(repo, changesets)
2410 revs = scmutil.revrange(repo, changesets)
2411 if len(revs) > 1:
2411 if len(revs) > 1:
2412 ui.note(_('exporting patches:\n'))
2412 ui.note(_('exporting patches:\n'))
2413 else:
2413 else:
2414 ui.note(_('exporting patch:\n'))
2414 ui.note(_('exporting patch:\n'))
2415 cmdutil.export(repo, revs, template=opts.get('output'),
2415 cmdutil.export(repo, revs, template=opts.get('output'),
2416 switch_parent=opts.get('switch_parent'),
2416 switch_parent=opts.get('switch_parent'),
2417 opts=patch.diffopts(ui, opts))
2417 opts=patch.diffopts(ui, opts))
2418
2418
2419 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2419 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2420 def forget(ui, repo, *pats, **opts):
2420 def forget(ui, repo, *pats, **opts):
2421 """forget the specified files on the next commit
2421 """forget the specified files on the next commit
2422
2422
2423 Mark the specified files so they will no longer be tracked
2423 Mark the specified files so they will no longer be tracked
2424 after the next commit.
2424 after the next commit.
2425
2425
2426 This only removes files from the current branch, not from the
2426 This only removes files from the current branch, not from the
2427 entire project history, and it does not delete them from the
2427 entire project history, and it does not delete them from the
2428 working directory.
2428 working directory.
2429
2429
2430 To undo a forget before the next commit, see :hg:`add`.
2430 To undo a forget before the next commit, see :hg:`add`.
2431
2431
2432 .. container:: verbose
2432 .. container:: verbose
2433
2433
2434 Examples:
2434 Examples:
2435
2435
2436 - forget newly-added binary files::
2436 - forget newly-added binary files::
2437
2437
2438 hg forget "set:added() and binary()"
2438 hg forget "set:added() and binary()"
2439
2439
2440 - forget files that would be excluded by .hgignore::
2440 - forget files that would be excluded by .hgignore::
2441
2441
2442 hg forget "set:hgignore()"
2442 hg forget "set:hgignore()"
2443
2443
2444 Returns 0 on success.
2444 Returns 0 on success.
2445 """
2445 """
2446
2446
2447 if not pats:
2447 if not pats:
2448 raise util.Abort(_('no files specified'))
2448 raise util.Abort(_('no files specified'))
2449
2449
2450 wctx = repo[None]
2450 wctx = repo[None]
2451 m = scmutil.match(wctx, pats, opts)
2451 m = scmutil.match(wctx, pats, opts)
2452 s = repo.status(match=m, clean=True)
2452 s = repo.status(match=m, clean=True)
2453 forget = sorted(s[0] + s[1] + s[3] + s[6])
2453 forget = sorted(s[0] + s[1] + s[3] + s[6])
2454 subforget = {}
2454 subforget = {}
2455 errs = 0
2455 errs = 0
2456
2456
2457 for subpath in wctx.substate:
2457 for subpath in wctx.substate:
2458 sub = wctx.sub(subpath)
2458 sub = wctx.sub(subpath)
2459 try:
2459 try:
2460 submatch = matchmod.narrowmatcher(subpath, m)
2460 submatch = matchmod.narrowmatcher(subpath, m)
2461 for fsub in sub.walk(submatch):
2461 for fsub in sub.walk(submatch):
2462 if submatch.exact(fsub):
2462 if submatch.exact(fsub):
2463 subforget[subpath + '/' + fsub] = (fsub, sub)
2463 subforget[subpath + '/' + fsub] = (fsub, sub)
2464 except error.LookupError:
2464 except error.LookupError:
2465 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2465 ui.status(_("skipping missing subrepository: %s\n") % subpath)
2466
2466
2467 for f in m.files():
2467 for f in m.files():
2468 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2468 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2469 if f not in subforget:
2469 if f not in subforget:
2470 if os.path.exists(m.rel(f)):
2470 if os.path.exists(m.rel(f)):
2471 ui.warn(_('not removing %s: file is already untracked\n')
2471 ui.warn(_('not removing %s: file is already untracked\n')
2472 % m.rel(f))
2472 % m.rel(f))
2473 errs = 1
2473 errs = 1
2474
2474
2475 for f in forget:
2475 for f in forget:
2476 if ui.verbose or not m.exact(f):
2476 if ui.verbose or not m.exact(f):
2477 ui.status(_('removing %s\n') % m.rel(f))
2477 ui.status(_('removing %s\n') % m.rel(f))
2478
2478
2479 if ui.verbose:
2479 if ui.verbose:
2480 for f in sorted(subforget.keys()):
2480 for f in sorted(subforget.keys()):
2481 ui.status(_('removing %s\n') % m.rel(f))
2481 ui.status(_('removing %s\n') % m.rel(f))
2482
2482
2483 wctx.forget(forget)
2483 wctx.forget(forget)
2484
2484
2485 for f in sorted(subforget.keys()):
2485 for f in sorted(subforget.keys()):
2486 fsub, sub = subforget[f]
2486 fsub, sub = subforget[f]
2487 sub.forget([fsub])
2487 sub.forget([fsub])
2488
2488
2489 return errs
2489 return errs
2490
2490
2491 @command(
2491 @command(
2492 'graft',
2492 'graft',
2493 [('c', 'continue', False, _('resume interrupted graft')),
2493 [('c', 'continue', False, _('resume interrupted graft')),
2494 ('e', 'edit', False, _('invoke editor on commit messages')),
2494 ('e', 'edit', False, _('invoke editor on commit messages')),
2495 ('D', 'currentdate', False,
2495 ('D', 'currentdate', False,
2496 _('record the current date as commit date')),
2496 _('record the current date as commit date')),
2497 ('U', 'currentuser', False,
2497 ('U', 'currentuser', False,
2498 _('record the current user as committer'), _('DATE'))]
2498 _('record the current user as committer'), _('DATE'))]
2499 + commitopts2 + mergetoolopts,
2499 + commitopts2 + mergetoolopts,
2500 _('[OPTION]... REVISION...'))
2500 _('[OPTION]... REVISION...'))
2501 def graft(ui, repo, *revs, **opts):
2501 def graft(ui, repo, *revs, **opts):
2502 '''copy changes from other branches onto the current branch
2502 '''copy changes from other branches onto the current branch
2503
2503
2504 This command uses Mercurial's merge logic to copy individual
2504 This command uses Mercurial's merge logic to copy individual
2505 changes from other branches without merging branches in the
2505 changes from other branches without merging branches in the
2506 history graph. This is sometimes known as 'backporting' or
2506 history graph. This is sometimes known as 'backporting' or
2507 'cherry-picking'. By default, graft will copy user, date, and
2507 'cherry-picking'. By default, graft will copy user, date, and
2508 description from the source changesets.
2508 description from the source changesets.
2509
2509
2510 Changesets that are ancestors of the current revision, that have
2510 Changesets that are ancestors of the current revision, that have
2511 already been grafted, or that are merges will be skipped.
2511 already been grafted, or that are merges will be skipped.
2512
2512
2513 If a graft merge results in conflicts, the graft process is
2513 If a graft merge results in conflicts, the graft process is
2514 interrupted so that the current merge can be manually resolved.
2514 interrupted so that the current merge can be manually resolved.
2515 Once all conflicts are addressed, the graft process can be
2515 Once all conflicts are addressed, the graft process can be
2516 continued with the -c/--continue option.
2516 continued with the -c/--continue option.
2517
2517
2518 .. note::
2518 .. note::
2519 The -c/--continue option does not reapply earlier options.
2519 The -c/--continue option does not reapply earlier options.
2520
2520
2521 .. container:: verbose
2521 .. container:: verbose
2522
2522
2523 Examples:
2523 Examples:
2524
2524
2525 - copy a single change to the stable branch and edit its description::
2525 - copy a single change to the stable branch and edit its description::
2526
2526
2527 hg update stable
2527 hg update stable
2528 hg graft --edit 9393
2528 hg graft --edit 9393
2529
2529
2530 - graft a range of changesets with one exception, updating dates::
2530 - graft a range of changesets with one exception, updating dates::
2531
2531
2532 hg graft -D "2085::2093 and not 2091"
2532 hg graft -D "2085::2093 and not 2091"
2533
2533
2534 - continue a graft after resolving conflicts::
2534 - continue a graft after resolving conflicts::
2535
2535
2536 hg graft -c
2536 hg graft -c
2537
2537
2538 - show the source of a grafted changeset::
2538 - show the source of a grafted changeset::
2539
2539
2540 hg log --debug -r tip
2540 hg log --debug -r tip
2541
2541
2542 Returns 0 on successful completion.
2542 Returns 0 on successful completion.
2543 '''
2543 '''
2544
2544
2545 if not opts.get('user') and opts.get('currentuser'):
2545 if not opts.get('user') and opts.get('currentuser'):
2546 opts['user'] = ui.username()
2546 opts['user'] = ui.username()
2547 if not opts.get('date') and opts.get('currentdate'):
2547 if not opts.get('date') and opts.get('currentdate'):
2548 opts['date'] = "%d %d" % util.makedate()
2548 opts['date'] = "%d %d" % util.makedate()
2549
2549
2550 editor = None
2550 editor = None
2551 if opts.get('edit'):
2551 if opts.get('edit'):
2552 editor = cmdutil.commitforceeditor
2552 editor = cmdutil.commitforceeditor
2553
2553
2554 cont = False
2554 cont = False
2555 if opts['continue']:
2555 if opts['continue']:
2556 cont = True
2556 cont = True
2557 if revs:
2557 if revs:
2558 raise util.Abort(_("can't specify --continue and revisions"))
2558 raise util.Abort(_("can't specify --continue and revisions"))
2559 # read in unfinished revisions
2559 # read in unfinished revisions
2560 try:
2560 try:
2561 nodes = repo.opener.read('graftstate').splitlines()
2561 nodes = repo.opener.read('graftstate').splitlines()
2562 revs = [repo[node].rev() for node in nodes]
2562 revs = [repo[node].rev() for node in nodes]
2563 except IOError, inst:
2563 except IOError, inst:
2564 if inst.errno != errno.ENOENT:
2564 if inst.errno != errno.ENOENT:
2565 raise
2565 raise
2566 raise util.Abort(_("no graft state found, can't continue"))
2566 raise util.Abort(_("no graft state found, can't continue"))
2567 else:
2567 else:
2568 cmdutil.bailifchanged(repo)
2568 cmdutil.bailifchanged(repo)
2569 if not revs:
2569 if not revs:
2570 raise util.Abort(_('no revisions specified'))
2570 raise util.Abort(_('no revisions specified'))
2571 revs = scmutil.revrange(repo, revs)
2571 revs = scmutil.revrange(repo, revs)
2572
2572
2573 # check for merges
2573 # check for merges
2574 for rev in repo.revs('%ld and merge()', revs):
2574 for rev in repo.revs('%ld and merge()', revs):
2575 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2575 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2576 revs.remove(rev)
2576 revs.remove(rev)
2577 if not revs:
2577 if not revs:
2578 return -1
2578 return -1
2579
2579
2580 # check for ancestors of dest branch
2580 # check for ancestors of dest branch
2581 for rev in repo.revs('::. and %ld', revs):
2581 for rev in repo.revs('::. and %ld', revs):
2582 ui.warn(_('skipping ancestor revision %s\n') % rev)
2582 ui.warn(_('skipping ancestor revision %s\n') % rev)
2583 revs.remove(rev)
2583 revs.remove(rev)
2584 if not revs:
2584 if not revs:
2585 return -1
2585 return -1
2586
2586
2587 # analyze revs for earlier grafts
2587 # analyze revs for earlier grafts
2588 ids = {}
2588 ids = {}
2589 for ctx in repo.set("%ld", revs):
2589 for ctx in repo.set("%ld", revs):
2590 ids[ctx.hex()] = ctx.rev()
2590 ids[ctx.hex()] = ctx.rev()
2591 n = ctx.extra().get('source')
2591 n = ctx.extra().get('source')
2592 if n:
2592 if n:
2593 ids[n] = ctx.rev()
2593 ids[n] = ctx.rev()
2594
2594
2595 # check ancestors for earlier grafts
2595 # check ancestors for earlier grafts
2596 ui.debug('scanning for duplicate grafts\n')
2596 ui.debug('scanning for duplicate grafts\n')
2597 for ctx in repo.set("::. - ::%ld", revs):
2597 for ctx in repo.set("::. - ::%ld", revs):
2598 n = ctx.extra().get('source')
2598 n = ctx.extra().get('source')
2599 if n in ids:
2599 if n in ids:
2600 r = repo[n].rev()
2600 r = repo[n].rev()
2601 if r in revs:
2601 if r in revs:
2602 ui.warn(_('skipping already grafted revision %s\n') % r)
2602 ui.warn(_('skipping already grafted revision %s\n') % r)
2603 revs.remove(r)
2603 revs.remove(r)
2604 elif ids[n] in revs:
2604 elif ids[n] in revs:
2605 ui.warn(_('skipping already grafted revision %s '
2605 ui.warn(_('skipping already grafted revision %s '
2606 '(same origin %d)\n') % (ids[n], r))
2606 '(same origin %d)\n') % (ids[n], r))
2607 revs.remove(ids[n])
2607 revs.remove(ids[n])
2608 elif ctx.hex() in ids:
2608 elif ctx.hex() in ids:
2609 r = ids[ctx.hex()]
2609 r = ids[ctx.hex()]
2610 ui.warn(_('skipping already grafted revision %s '
2610 ui.warn(_('skipping already grafted revision %s '
2611 '(was grafted from %d)\n') % (r, ctx.rev()))
2611 '(was grafted from %d)\n') % (r, ctx.rev()))
2612 revs.remove(r)
2612 revs.remove(r)
2613 if not revs:
2613 if not revs:
2614 return -1
2614 return -1
2615
2615
2616 for pos, ctx in enumerate(repo.set("%ld", revs)):
2616 for pos, ctx in enumerate(repo.set("%ld", revs)):
2617 current = repo['.']
2617 current = repo['.']
2618 ui.status(_('grafting revision %s\n') % ctx.rev())
2618 ui.status(_('grafting revision %s\n') % ctx.rev())
2619
2619
2620 # we don't merge the first commit when continuing
2620 # we don't merge the first commit when continuing
2621 if not cont:
2621 if not cont:
2622 # perform the graft merge with p1(rev) as 'ancestor'
2622 # perform the graft merge with p1(rev) as 'ancestor'
2623 try:
2623 try:
2624 # ui.forcemerge is an internal variable, do not document
2624 # ui.forcemerge is an internal variable, do not document
2625 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2625 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2626 stats = mergemod.update(repo, ctx.node(), True, True, False,
2626 stats = mergemod.update(repo, ctx.node(), True, True, False,
2627 ctx.p1().node())
2627 ctx.p1().node())
2628 finally:
2628 finally:
2629 ui.setconfig('ui', 'forcemerge', '')
2629 ui.setconfig('ui', 'forcemerge', '')
2630 # drop the second merge parent
2630 # drop the second merge parent
2631 repo.dirstate.setparents(current.node(), nullid)
2631 repo.dirstate.setparents(current.node(), nullid)
2632 repo.dirstate.write()
2632 repo.dirstate.write()
2633 # fix up dirstate for copies and renames
2633 # fix up dirstate for copies and renames
2634 cmdutil.duplicatecopies(repo, ctx.rev(), current.node())
2634 cmdutil.duplicatecopies(repo, ctx.rev(), current.node())
2635 # report any conflicts
2635 # report any conflicts
2636 if stats and stats[3] > 0:
2636 if stats and stats[3] > 0:
2637 # write out state for --continue
2637 # write out state for --continue
2638 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2638 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2639 repo.opener.write('graftstate', ''.join(nodelines))
2639 repo.opener.write('graftstate', ''.join(nodelines))
2640 raise util.Abort(
2640 raise util.Abort(
2641 _("unresolved conflicts, can't continue"),
2641 _("unresolved conflicts, can't continue"),
2642 hint=_('use hg resolve and hg graft --continue'))
2642 hint=_('use hg resolve and hg graft --continue'))
2643 else:
2643 else:
2644 cont = False
2644 cont = False
2645
2645
2646 # commit
2646 # commit
2647 source = ctx.extra().get('source')
2647 source = ctx.extra().get('source')
2648 if not source:
2648 if not source:
2649 source = ctx.hex()
2649 source = ctx.hex()
2650 extra = {'source': source}
2650 extra = {'source': source}
2651 user = ctx.user()
2651 user = ctx.user()
2652 if opts.get('user'):
2652 if opts.get('user'):
2653 user = opts['user']
2653 user = opts['user']
2654 date = ctx.date()
2654 date = ctx.date()
2655 if opts.get('date'):
2655 if opts.get('date'):
2656 date = opts['date']
2656 date = opts['date']
2657 repo.commit(text=ctx.description(), user=user,
2657 repo.commit(text=ctx.description(), user=user,
2658 date=date, extra=extra, editor=editor)
2658 date=date, extra=extra, editor=editor)
2659
2659
2660 # remove state when we complete successfully
2660 # remove state when we complete successfully
2661 if os.path.exists(repo.join('graftstate')):
2661 if os.path.exists(repo.join('graftstate')):
2662 util.unlinkpath(repo.join('graftstate'))
2662 util.unlinkpath(repo.join('graftstate'))
2663
2663
2664 return 0
2664 return 0
2665
2665
2666 @command('grep',
2666 @command('grep',
2667 [('0', 'print0', None, _('end fields with NUL')),
2667 [('0', 'print0', None, _('end fields with NUL')),
2668 ('', 'all', None, _('print all revisions that match')),
2668 ('', 'all', None, _('print all revisions that match')),
2669 ('a', 'text', None, _('treat all files as text')),
2669 ('a', 'text', None, _('treat all files as text')),
2670 ('f', 'follow', None,
2670 ('f', 'follow', None,
2671 _('follow changeset history,'
2671 _('follow changeset history,'
2672 ' or file history across copies and renames')),
2672 ' or file history across copies and renames')),
2673 ('i', 'ignore-case', None, _('ignore case when matching')),
2673 ('i', 'ignore-case', None, _('ignore case when matching')),
2674 ('l', 'files-with-matches', None,
2674 ('l', 'files-with-matches', None,
2675 _('print only filenames and revisions that match')),
2675 _('print only filenames and revisions that match')),
2676 ('n', 'line-number', None, _('print matching line numbers')),
2676 ('n', 'line-number', None, _('print matching line numbers')),
2677 ('r', 'rev', [],
2677 ('r', 'rev', [],
2678 _('only search files changed within revision range'), _('REV')),
2678 _('only search files changed within revision range'), _('REV')),
2679 ('u', 'user', None, _('list the author (long with -v)')),
2679 ('u', 'user', None, _('list the author (long with -v)')),
2680 ('d', 'date', None, _('list the date (short with -q)')),
2680 ('d', 'date', None, _('list the date (short with -q)')),
2681 ] + walkopts,
2681 ] + walkopts,
2682 _('[OPTION]... PATTERN [FILE]...'))
2682 _('[OPTION]... PATTERN [FILE]...'))
2683 def grep(ui, repo, pattern, *pats, **opts):
2683 def grep(ui, repo, pattern, *pats, **opts):
2684 """search for a pattern in specified files and revisions
2684 """search for a pattern in specified files and revisions
2685
2685
2686 Search revisions of files for a regular expression.
2686 Search revisions of files for a regular expression.
2687
2687
2688 This command behaves differently than Unix grep. It only accepts
2688 This command behaves differently than Unix grep. It only accepts
2689 Python/Perl regexps. It searches repository history, not the
2689 Python/Perl regexps. It searches repository history, not the
2690 working directory. It always prints the revision number in which a
2690 working directory. It always prints the revision number in which a
2691 match appears.
2691 match appears.
2692
2692
2693 By default, grep only prints output for the first revision of a
2693 By default, grep only prints output for the first revision of a
2694 file in which it finds a match. To get it to print every revision
2694 file in which it finds a match. To get it to print every revision
2695 that contains a change in match status ("-" for a match that
2695 that contains a change in match status ("-" for a match that
2696 becomes a non-match, or "+" for a non-match that becomes a match),
2696 becomes a non-match, or "+" for a non-match that becomes a match),
2697 use the --all flag.
2697 use the --all flag.
2698
2698
2699 Returns 0 if a match is found, 1 otherwise.
2699 Returns 0 if a match is found, 1 otherwise.
2700 """
2700 """
2701 reflags = re.M
2701 reflags = re.M
2702 if opts.get('ignore_case'):
2702 if opts.get('ignore_case'):
2703 reflags |= re.I
2703 reflags |= re.I
2704 try:
2704 try:
2705 regexp = re.compile(pattern, reflags)
2705 regexp = re.compile(pattern, reflags)
2706 except re.error, inst:
2706 except re.error, inst:
2707 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2707 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2708 return 1
2708 return 1
2709 sep, eol = ':', '\n'
2709 sep, eol = ':', '\n'
2710 if opts.get('print0'):
2710 if opts.get('print0'):
2711 sep = eol = '\0'
2711 sep = eol = '\0'
2712
2712
2713 getfile = util.lrucachefunc(repo.file)
2713 getfile = util.lrucachefunc(repo.file)
2714
2714
2715 def matchlines(body):
2715 def matchlines(body):
2716 begin = 0
2716 begin = 0
2717 linenum = 0
2717 linenum = 0
2718 while True:
2718 while True:
2719 match = regexp.search(body, begin)
2719 match = regexp.search(body, begin)
2720 if not match:
2720 if not match:
2721 break
2721 break
2722 mstart, mend = match.span()
2722 mstart, mend = match.span()
2723 linenum += body.count('\n', begin, mstart) + 1
2723 linenum += body.count('\n', begin, mstart) + 1
2724 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2724 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2725 begin = body.find('\n', mend) + 1 or len(body) + 1
2725 begin = body.find('\n', mend) + 1 or len(body) + 1
2726 lend = begin - 1
2726 lend = begin - 1
2727 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2727 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2728
2728
2729 class linestate(object):
2729 class linestate(object):
2730 def __init__(self, line, linenum, colstart, colend):
2730 def __init__(self, line, linenum, colstart, colend):
2731 self.line = line
2731 self.line = line
2732 self.linenum = linenum
2732 self.linenum = linenum
2733 self.colstart = colstart
2733 self.colstart = colstart
2734 self.colend = colend
2734 self.colend = colend
2735
2735
2736 def __hash__(self):
2736 def __hash__(self):
2737 return hash((self.linenum, self.line))
2737 return hash((self.linenum, self.line))
2738
2738
2739 def __eq__(self, other):
2739 def __eq__(self, other):
2740 return self.line == other.line
2740 return self.line == other.line
2741
2741
2742 matches = {}
2742 matches = {}
2743 copies = {}
2743 copies = {}
2744 def grepbody(fn, rev, body):
2744 def grepbody(fn, rev, body):
2745 matches[rev].setdefault(fn, [])
2745 matches[rev].setdefault(fn, [])
2746 m = matches[rev][fn]
2746 m = matches[rev][fn]
2747 for lnum, cstart, cend, line in matchlines(body):
2747 for lnum, cstart, cend, line in matchlines(body):
2748 s = linestate(line, lnum, cstart, cend)
2748 s = linestate(line, lnum, cstart, cend)
2749 m.append(s)
2749 m.append(s)
2750
2750
2751 def difflinestates(a, b):
2751 def difflinestates(a, b):
2752 sm = difflib.SequenceMatcher(None, a, b)
2752 sm = difflib.SequenceMatcher(None, a, b)
2753 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2753 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2754 if tag == 'insert':
2754 if tag == 'insert':
2755 for i in xrange(blo, bhi):
2755 for i in xrange(blo, bhi):
2756 yield ('+', b[i])
2756 yield ('+', b[i])
2757 elif tag == 'delete':
2757 elif tag == 'delete':
2758 for i in xrange(alo, ahi):
2758 for i in xrange(alo, ahi):
2759 yield ('-', a[i])
2759 yield ('-', a[i])
2760 elif tag == 'replace':
2760 elif tag == 'replace':
2761 for i in xrange(alo, ahi):
2761 for i in xrange(alo, ahi):
2762 yield ('-', a[i])
2762 yield ('-', a[i])
2763 for i in xrange(blo, bhi):
2763 for i in xrange(blo, bhi):
2764 yield ('+', b[i])
2764 yield ('+', b[i])
2765
2765
2766 def display(fn, ctx, pstates, states):
2766 def display(fn, ctx, pstates, states):
2767 rev = ctx.rev()
2767 rev = ctx.rev()
2768 datefunc = ui.quiet and util.shortdate or util.datestr
2768 datefunc = ui.quiet and util.shortdate or util.datestr
2769 found = False
2769 found = False
2770 filerevmatches = {}
2770 filerevmatches = {}
2771 def binary():
2771 def binary():
2772 flog = getfile(fn)
2772 flog = getfile(fn)
2773 return util.binary(flog.read(ctx.filenode(fn)))
2773 return util.binary(flog.read(ctx.filenode(fn)))
2774
2774
2775 if opts.get('all'):
2775 if opts.get('all'):
2776 iter = difflinestates(pstates, states)
2776 iter = difflinestates(pstates, states)
2777 else:
2777 else:
2778 iter = [('', l) for l in states]
2778 iter = [('', l) for l in states]
2779 for change, l in iter:
2779 for change, l in iter:
2780 cols = [fn, str(rev)]
2780 cols = [fn, str(rev)]
2781 before, match, after = None, None, None
2781 before, match, after = None, None, None
2782 if opts.get('line_number'):
2782 if opts.get('line_number'):
2783 cols.append(str(l.linenum))
2783 cols.append(str(l.linenum))
2784 if opts.get('all'):
2784 if opts.get('all'):
2785 cols.append(change)
2785 cols.append(change)
2786 if opts.get('user'):
2786 if opts.get('user'):
2787 cols.append(ui.shortuser(ctx.user()))
2787 cols.append(ui.shortuser(ctx.user()))
2788 if opts.get('date'):
2788 if opts.get('date'):
2789 cols.append(datefunc(ctx.date()))
2789 cols.append(datefunc(ctx.date()))
2790 if opts.get('files_with_matches'):
2790 if opts.get('files_with_matches'):
2791 c = (fn, rev)
2791 c = (fn, rev)
2792 if c in filerevmatches:
2792 if c in filerevmatches:
2793 continue
2793 continue
2794 filerevmatches[c] = 1
2794 filerevmatches[c] = 1
2795 else:
2795 else:
2796 before = l.line[:l.colstart]
2796 before = l.line[:l.colstart]
2797 match = l.line[l.colstart:l.colend]
2797 match = l.line[l.colstart:l.colend]
2798 after = l.line[l.colend:]
2798 after = l.line[l.colend:]
2799 ui.write(sep.join(cols))
2799 ui.write(sep.join(cols))
2800 if before is not None:
2800 if before is not None:
2801 if not opts.get('text') and binary():
2801 if not opts.get('text') and binary():
2802 ui.write(sep + " Binary file matches")
2802 ui.write(sep + " Binary file matches")
2803 else:
2803 else:
2804 ui.write(sep + before)
2804 ui.write(sep + before)
2805 ui.write(match, label='grep.match')
2805 ui.write(match, label='grep.match')
2806 ui.write(after)
2806 ui.write(after)
2807 ui.write(eol)
2807 ui.write(eol)
2808 found = True
2808 found = True
2809 return found
2809 return found
2810
2810
2811 skip = {}
2811 skip = {}
2812 revfiles = {}
2812 revfiles = {}
2813 matchfn = scmutil.match(repo[None], pats, opts)
2813 matchfn = scmutil.match(repo[None], pats, opts)
2814 found = False
2814 found = False
2815 follow = opts.get('follow')
2815 follow = opts.get('follow')
2816
2816
2817 def prep(ctx, fns):
2817 def prep(ctx, fns):
2818 rev = ctx.rev()
2818 rev = ctx.rev()
2819 pctx = ctx.p1()
2819 pctx = ctx.p1()
2820 parent = pctx.rev()
2820 parent = pctx.rev()
2821 matches.setdefault(rev, {})
2821 matches.setdefault(rev, {})
2822 matches.setdefault(parent, {})
2822 matches.setdefault(parent, {})
2823 files = revfiles.setdefault(rev, [])
2823 files = revfiles.setdefault(rev, [])
2824 for fn in fns:
2824 for fn in fns:
2825 flog = getfile(fn)
2825 flog = getfile(fn)
2826 try:
2826 try:
2827 fnode = ctx.filenode(fn)
2827 fnode = ctx.filenode(fn)
2828 except error.LookupError:
2828 except error.LookupError:
2829 continue
2829 continue
2830
2830
2831 copied = flog.renamed(fnode)
2831 copied = flog.renamed(fnode)
2832 copy = follow and copied and copied[0]
2832 copy = follow and copied and copied[0]
2833 if copy:
2833 if copy:
2834 copies.setdefault(rev, {})[fn] = copy
2834 copies.setdefault(rev, {})[fn] = copy
2835 if fn in skip:
2835 if fn in skip:
2836 if copy:
2836 if copy:
2837 skip[copy] = True
2837 skip[copy] = True
2838 continue
2838 continue
2839 files.append(fn)
2839 files.append(fn)
2840
2840
2841 if fn not in matches[rev]:
2841 if fn not in matches[rev]:
2842 grepbody(fn, rev, flog.read(fnode))
2842 grepbody(fn, rev, flog.read(fnode))
2843
2843
2844 pfn = copy or fn
2844 pfn = copy or fn
2845 if pfn not in matches[parent]:
2845 if pfn not in matches[parent]:
2846 try:
2846 try:
2847 fnode = pctx.filenode(pfn)
2847 fnode = pctx.filenode(pfn)
2848 grepbody(pfn, parent, flog.read(fnode))
2848 grepbody(pfn, parent, flog.read(fnode))
2849 except error.LookupError:
2849 except error.LookupError:
2850 pass
2850 pass
2851
2851
2852 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2852 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2853 rev = ctx.rev()
2853 rev = ctx.rev()
2854 parent = ctx.p1().rev()
2854 parent = ctx.p1().rev()
2855 for fn in sorted(revfiles.get(rev, [])):
2855 for fn in sorted(revfiles.get(rev, [])):
2856 states = matches[rev][fn]
2856 states = matches[rev][fn]
2857 copy = copies.get(rev, {}).get(fn)
2857 copy = copies.get(rev, {}).get(fn)
2858 if fn in skip:
2858 if fn in skip:
2859 if copy:
2859 if copy:
2860 skip[copy] = True
2860 skip[copy] = True
2861 continue
2861 continue
2862 pstates = matches.get(parent, {}).get(copy or fn, [])
2862 pstates = matches.get(parent, {}).get(copy or fn, [])
2863 if pstates or states:
2863 if pstates or states:
2864 r = display(fn, ctx, pstates, states)
2864 r = display(fn, ctx, pstates, states)
2865 found = found or r
2865 found = found or r
2866 if r and not opts.get('all'):
2866 if r and not opts.get('all'):
2867 skip[fn] = True
2867 skip[fn] = True
2868 if copy:
2868 if copy:
2869 skip[copy] = True
2869 skip[copy] = True
2870 del matches[rev]
2870 del matches[rev]
2871 del revfiles[rev]
2871 del revfiles[rev]
2872
2872
2873 return not found
2873 return not found
2874
2874
2875 @command('heads',
2875 @command('heads',
2876 [('r', 'rev', '',
2876 [('r', 'rev', '',
2877 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2877 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2878 ('t', 'topo', False, _('show topological heads only')),
2878 ('t', 'topo', False, _('show topological heads only')),
2879 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2879 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2880 ('c', 'closed', False, _('show normal and closed branch heads')),
2880 ('c', 'closed', False, _('show normal and closed branch heads')),
2881 ] + templateopts,
2881 ] + templateopts,
2882 _('[-ac] [-r STARTREV] [REV]...'))
2882 _('[-ac] [-r STARTREV] [REV]...'))
2883 def heads(ui, repo, *branchrevs, **opts):
2883 def heads(ui, repo, *branchrevs, **opts):
2884 """show current repository heads or show branch heads
2884 """show current repository heads or show branch heads
2885
2885
2886 With no arguments, show all repository branch heads.
2886 With no arguments, show all repository branch heads.
2887
2887
2888 Repository "heads" are changesets with no child changesets. They are
2888 Repository "heads" are changesets with no child changesets. They are
2889 where development generally takes place and are the usual targets
2889 where development generally takes place and are the usual targets
2890 for update and merge operations. Branch heads are changesets that have
2890 for update and merge operations. Branch heads are changesets that have
2891 no child changeset on the same branch.
2891 no child changeset on the same branch.
2892
2892
2893 If one or more REVs are given, only branch heads on the branches
2893 If one or more REVs are given, only branch heads on the branches
2894 associated with the specified changesets are shown. This means
2894 associated with the specified changesets are shown. This means
2895 that you can use :hg:`heads foo` to see the heads on a branch
2895 that you can use :hg:`heads foo` to see the heads on a branch
2896 named ``foo``.
2896 named ``foo``.
2897
2897
2898 If -c/--closed is specified, also show branch heads marked closed
2898 If -c/--closed is specified, also show branch heads marked closed
2899 (see :hg:`commit --close-branch`).
2899 (see :hg:`commit --close-branch`).
2900
2900
2901 If STARTREV is specified, only those heads that are descendants of
2901 If STARTREV is specified, only those heads that are descendants of
2902 STARTREV will be displayed.
2902 STARTREV will be displayed.
2903
2903
2904 If -t/--topo is specified, named branch mechanics will be ignored and only
2904 If -t/--topo is specified, named branch mechanics will be ignored and only
2905 changesets without children will be shown.
2905 changesets without children will be shown.
2906
2906
2907 Returns 0 if matching heads are found, 1 if not.
2907 Returns 0 if matching heads are found, 1 if not.
2908 """
2908 """
2909
2909
2910 start = None
2910 start = None
2911 if 'rev' in opts:
2911 if 'rev' in opts:
2912 start = scmutil.revsingle(repo, opts['rev'], None).node()
2912 start = scmutil.revsingle(repo, opts['rev'], None).node()
2913
2913
2914 if opts.get('topo'):
2914 if opts.get('topo'):
2915 heads = [repo[h] for h in repo.heads(start)]
2915 heads = [repo[h] for h in repo.heads(start)]
2916 else:
2916 else:
2917 heads = []
2917 heads = []
2918 for branch in repo.branchmap():
2918 for branch in repo.branchmap():
2919 heads += repo.branchheads(branch, start, opts.get('closed'))
2919 heads += repo.branchheads(branch, start, opts.get('closed'))
2920 heads = [repo[h] for h in heads]
2920 heads = [repo[h] for h in heads]
2921
2921
2922 if branchrevs:
2922 if branchrevs:
2923 branches = set(repo[br].branch() for br in branchrevs)
2923 branches = set(repo[br].branch() for br in branchrevs)
2924 heads = [h for h in heads if h.branch() in branches]
2924 heads = [h for h in heads if h.branch() in branches]
2925
2925
2926 if opts.get('active') and branchrevs:
2926 if opts.get('active') and branchrevs:
2927 dagheads = repo.heads(start)
2927 dagheads = repo.heads(start)
2928 heads = [h for h in heads if h.node() in dagheads]
2928 heads = [h for h in heads if h.node() in dagheads]
2929
2929
2930 if branchrevs:
2930 if branchrevs:
2931 haveheads = set(h.branch() for h in heads)
2931 haveheads = set(h.branch() for h in heads)
2932 if branches - haveheads:
2932 if branches - haveheads:
2933 headless = ', '.join(b for b in branches - haveheads)
2933 headless = ', '.join(b for b in branches - haveheads)
2934 msg = _('no open branch heads found on branches %s')
2934 msg = _('no open branch heads found on branches %s')
2935 if opts.get('rev'):
2935 if opts.get('rev'):
2936 msg += _(' (started at %s)' % opts['rev'])
2936 msg += _(' (started at %s)' % opts['rev'])
2937 ui.warn((msg + '\n') % headless)
2937 ui.warn((msg + '\n') % headless)
2938
2938
2939 if not heads:
2939 if not heads:
2940 return 1
2940 return 1
2941
2941
2942 heads = sorted(heads, key=lambda x: -x.rev())
2942 heads = sorted(heads, key=lambda x: -x.rev())
2943 displayer = cmdutil.show_changeset(ui, repo, opts)
2943 displayer = cmdutil.show_changeset(ui, repo, opts)
2944 for ctx in heads:
2944 for ctx in heads:
2945 displayer.show(ctx)
2945 displayer.show(ctx)
2946 displayer.close()
2946 displayer.close()
2947
2947
2948 @command('help',
2948 @command('help',
2949 [('e', 'extension', None, _('show only help for extensions')),
2949 [('e', 'extension', None, _('show only help for extensions')),
2950 ('c', 'command', None, _('show only help for commands'))],
2950 ('c', 'command', None, _('show only help for commands'))],
2951 _('[-ec] [TOPIC]'))
2951 _('[-ec] [TOPIC]'))
2952 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2952 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2953 """show help for a given topic or a help overview
2953 """show help for a given topic or a help overview
2954
2954
2955 With no arguments, print a list of commands with short help messages.
2955 With no arguments, print a list of commands with short help messages.
2956
2956
2957 Given a topic, extension, or command name, print help for that
2957 Given a topic, extension, or command name, print help for that
2958 topic.
2958 topic.
2959
2959
2960 Returns 0 if successful.
2960 Returns 0 if successful.
2961 """
2961 """
2962
2962
2963 textwidth = min(ui.termwidth(), 80) - 2
2963 textwidth = min(ui.termwidth(), 80) - 2
2964
2964
2965 def optrst(options):
2965 def optrst(options):
2966 data = []
2966 data = []
2967 multioccur = False
2967 multioccur = False
2968 for option in options:
2968 for option in options:
2969 if len(option) == 5:
2969 if len(option) == 5:
2970 shortopt, longopt, default, desc, optlabel = option
2970 shortopt, longopt, default, desc, optlabel = option
2971 else:
2971 else:
2972 shortopt, longopt, default, desc = option
2972 shortopt, longopt, default, desc = option
2973 optlabel = _("VALUE") # default label
2973 optlabel = _("VALUE") # default label
2974
2974
2975 if _("DEPRECATED") in desc and not ui.verbose:
2975 if _("DEPRECATED") in desc and not ui.verbose:
2976 continue
2976 continue
2977
2977
2978 so = ''
2978 so = ''
2979 if shortopt:
2979 if shortopt:
2980 so = '-' + shortopt
2980 so = '-' + shortopt
2981 lo = '--' + longopt
2981 lo = '--' + longopt
2982 if default:
2982 if default:
2983 desc += _(" (default: %s)") % default
2983 desc += _(" (default: %s)") % default
2984
2984
2985 if isinstance(default, list):
2985 if isinstance(default, list):
2986 lo += " %s [+]" % optlabel
2986 lo += " %s [+]" % optlabel
2987 multioccur = True
2987 multioccur = True
2988 elif (default is not None) and not isinstance(default, bool):
2988 elif (default is not None) and not isinstance(default, bool):
2989 lo += " %s" % optlabel
2989 lo += " %s" % optlabel
2990
2990
2991 data.append((so, lo, desc))
2991 data.append((so, lo, desc))
2992
2992
2993 rst = minirst.maketable(data, 1)
2993 rst = minirst.maketable(data, 1)
2994
2994
2995 if multioccur:
2995 if multioccur:
2996 rst += _("\n[+] marked option can be specified multiple times\n")
2996 rst += _("\n[+] marked option can be specified multiple times\n")
2997
2997
2998 return rst
2998 return rst
2999
2999
3000 # list all option lists
3000 # list all option lists
3001 def opttext(optlist, width):
3001 def opttext(optlist, width):
3002 rst = ''
3002 rst = ''
3003 if not optlist:
3003 if not optlist:
3004 return ''
3004 return ''
3005
3005
3006 for title, options in optlist:
3006 for title, options in optlist:
3007 rst += '\n%s\n' % title
3007 rst += '\n%s\n' % title
3008 if options:
3008 if options:
3009 rst += "\n"
3009 rst += "\n"
3010 rst += optrst(options)
3010 rst += optrst(options)
3011 rst += '\n'
3011 rst += '\n'
3012
3012
3013 return '\n' + minirst.format(rst, width)
3013 return '\n' + minirst.format(rst, width)
3014
3014
3015 def addglobalopts(optlist, aliases):
3015 def addglobalopts(optlist, aliases):
3016 if ui.quiet:
3016 if ui.quiet:
3017 return []
3017 return []
3018
3018
3019 if ui.verbose:
3019 if ui.verbose:
3020 optlist.append((_("global options:"), globalopts))
3020 optlist.append((_("global options:"), globalopts))
3021 if name == 'shortlist':
3021 if name == 'shortlist':
3022 optlist.append((_('use "hg help" for the full list '
3022 optlist.append((_('use "hg help" for the full list '
3023 'of commands'), ()))
3023 'of commands'), ()))
3024 else:
3024 else:
3025 if name == 'shortlist':
3025 if name == 'shortlist':
3026 msg = _('use "hg help" for the full list of commands '
3026 msg = _('use "hg help" for the full list of commands '
3027 'or "hg -v" for details')
3027 'or "hg -v" for details')
3028 elif name and not full:
3028 elif name and not full:
3029 msg = _('use "hg help %s" to show the full help text' % name)
3029 msg = _('use "hg help %s" to show the full help text' % name)
3030 elif aliases:
3030 elif aliases:
3031 msg = _('use "hg -v help%s" to show builtin aliases and '
3031 msg = _('use "hg -v help%s" to show builtin aliases and '
3032 'global options') % (name and " " + name or "")
3032 'global options') % (name and " " + name or "")
3033 else:
3033 else:
3034 msg = _('use "hg -v help %s" to show more info') % name
3034 msg = _('use "hg -v help %s" to show more info') % name
3035 optlist.append((msg, ()))
3035 optlist.append((msg, ()))
3036
3036
3037 def helpcmd(name):
3037 def helpcmd(name):
3038 try:
3038 try:
3039 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3039 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3040 except error.AmbiguousCommand, inst:
3040 except error.AmbiguousCommand, inst:
3041 # py3k fix: except vars can't be used outside the scope of the
3041 # py3k fix: except vars can't be used outside the scope of the
3042 # except block, nor can be used inside a lambda. python issue4617
3042 # except block, nor can be used inside a lambda. python issue4617
3043 prefix = inst.args[0]
3043 prefix = inst.args[0]
3044 select = lambda c: c.lstrip('^').startswith(prefix)
3044 select = lambda c: c.lstrip('^').startswith(prefix)
3045 helplist(select)
3045 helplist(select)
3046 return
3046 return
3047
3047
3048 # check if it's an invalid alias and display its error if it is
3048 # check if it's an invalid alias and display its error if it is
3049 if getattr(entry[0], 'badalias', False):
3049 if getattr(entry[0], 'badalias', False):
3050 if not unknowncmd:
3050 if not unknowncmd:
3051 entry[0](ui)
3051 entry[0](ui)
3052 return
3052 return
3053
3053
3054 rst = ""
3054 rst = ""
3055
3055
3056 # synopsis
3056 # synopsis
3057 if len(entry) > 2:
3057 if len(entry) > 2:
3058 if entry[2].startswith('hg'):
3058 if entry[2].startswith('hg'):
3059 rst += "%s\n" % entry[2]
3059 rst += "%s\n" % entry[2]
3060 else:
3060 else:
3061 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3061 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3062 else:
3062 else:
3063 rst += 'hg %s\n' % aliases[0]
3063 rst += 'hg %s\n' % aliases[0]
3064
3064
3065 # aliases
3065 # aliases
3066 if full and not ui.quiet and len(aliases) > 1:
3066 if full and not ui.quiet and len(aliases) > 1:
3067 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3067 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3068
3068
3069 # description
3069 # description
3070 doc = gettext(entry[0].__doc__)
3070 doc = gettext(entry[0].__doc__)
3071 if not doc:
3071 if not doc:
3072 doc = _("(no help text available)")
3072 doc = _("(no help text available)")
3073 if util.safehasattr(entry[0], 'definition'): # aliased command
3073 if util.safehasattr(entry[0], 'definition'): # aliased command
3074 if entry[0].definition.startswith('!'): # shell alias
3074 if entry[0].definition.startswith('!'): # shell alias
3075 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3075 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3076 else:
3076 else:
3077 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3077 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3078 if ui.quiet or not full:
3078 if ui.quiet or not full:
3079 doc = doc.splitlines()[0]
3079 doc = doc.splitlines()[0]
3080 rst += "\n" + doc + "\n"
3080 rst += "\n" + doc + "\n"
3081
3081
3082 # check if this command shadows a non-trivial (multi-line)
3082 # check if this command shadows a non-trivial (multi-line)
3083 # extension help text
3083 # extension help text
3084 try:
3084 try:
3085 mod = extensions.find(name)
3085 mod = extensions.find(name)
3086 doc = gettext(mod.__doc__) or ''
3086 doc = gettext(mod.__doc__) or ''
3087 if '\n' in doc.strip():
3087 if '\n' in doc.strip():
3088 msg = _('use "hg help -e %s" to show help for '
3088 msg = _('use "hg help -e %s" to show help for '
3089 'the %s extension') % (name, name)
3089 'the %s extension') % (name, name)
3090 rst += '\n%s\n' % msg
3090 rst += '\n%s\n' % msg
3091 except KeyError:
3091 except KeyError:
3092 pass
3092 pass
3093
3093
3094 # options
3094 # options
3095 if not ui.quiet and entry[1]:
3095 if not ui.quiet and entry[1]:
3096 rst += '\noptions:\n\n'
3096 rst += '\noptions:\n\n'
3097 rst += optrst(entry[1])
3097 rst += optrst(entry[1])
3098
3098
3099 if ui.verbose:
3099 if ui.verbose:
3100 rst += '\nglobal options:\n\n'
3100 rst += '\nglobal options:\n\n'
3101 rst += optrst(globalopts)
3101 rst += optrst(globalopts)
3102
3102
3103 keep = ui.verbose and ['verbose'] or []
3103 keep = ui.verbose and ['verbose'] or []
3104 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3104 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3105 ui.write(formatted)
3105 ui.write(formatted)
3106
3106
3107 if not ui.verbose:
3107 if not ui.verbose:
3108 if not full:
3108 if not full:
3109 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3109 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3110 % name)
3110 % name)
3111 elif not ui.quiet:
3111 elif not ui.quiet:
3112 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3112 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3113
3113
3114
3114
3115 def helplist(select=None):
3115 def helplist(select=None):
3116 # list of commands
3116 # list of commands
3117 if name == "shortlist":
3117 if name == "shortlist":
3118 header = _('basic commands:\n\n')
3118 header = _('basic commands:\n\n')
3119 else:
3119 else:
3120 header = _('list of commands:\n\n')
3120 header = _('list of commands:\n\n')
3121
3121
3122 h = {}
3122 h = {}
3123 cmds = {}
3123 cmds = {}
3124 for c, e in table.iteritems():
3124 for c, e in table.iteritems():
3125 f = c.split("|", 1)[0]
3125 f = c.split("|", 1)[0]
3126 if select and not select(f):
3126 if select and not select(f):
3127 continue
3127 continue
3128 if (not select and name != 'shortlist' and
3128 if (not select and name != 'shortlist' and
3129 e[0].__module__ != __name__):
3129 e[0].__module__ != __name__):
3130 continue
3130 continue
3131 if name == "shortlist" and not f.startswith("^"):
3131 if name == "shortlist" and not f.startswith("^"):
3132 continue
3132 continue
3133 f = f.lstrip("^")
3133 f = f.lstrip("^")
3134 if not ui.debugflag and f.startswith("debug"):
3134 if not ui.debugflag and f.startswith("debug"):
3135 continue
3135 continue
3136 doc = e[0].__doc__
3136 doc = e[0].__doc__
3137 if doc and 'DEPRECATED' in doc and not ui.verbose:
3137 if doc and 'DEPRECATED' in doc and not ui.verbose:
3138 continue
3138 continue
3139 doc = gettext(doc)
3139 doc = gettext(doc)
3140 if not doc:
3140 if not doc:
3141 doc = _("(no help text available)")
3141 doc = _("(no help text available)")
3142 h[f] = doc.splitlines()[0].rstrip()
3142 h[f] = doc.splitlines()[0].rstrip()
3143 cmds[f] = c.lstrip("^")
3143 cmds[f] = c.lstrip("^")
3144
3144
3145 if not h:
3145 if not h:
3146 ui.status(_('no commands defined\n'))
3146 ui.status(_('no commands defined\n'))
3147 return
3147 return
3148
3148
3149 ui.status(header)
3149 ui.status(header)
3150 fns = sorted(h)
3150 fns = sorted(h)
3151 m = max(map(len, fns))
3151 m = max(map(len, fns))
3152 for f in fns:
3152 for f in fns:
3153 if ui.verbose:
3153 if ui.verbose:
3154 commands = cmds[f].replace("|",", ")
3154 commands = cmds[f].replace("|",", ")
3155 ui.write(" %s:\n %s\n"%(commands, h[f]))
3155 ui.write(" %s:\n %s\n"%(commands, h[f]))
3156 else:
3156 else:
3157 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3157 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3158 initindent=' %-*s ' % (m, f),
3158 initindent=' %-*s ' % (m, f),
3159 hangindent=' ' * (m + 4))))
3159 hangindent=' ' * (m + 4))))
3160
3160
3161 if not name:
3161 if not name:
3162 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3162 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3163 if text:
3163 if text:
3164 ui.write("\n%s" % minirst.format(text, textwidth))
3164 ui.write("\n%s" % minirst.format(text, textwidth))
3165
3165
3166 ui.write(_("\nadditional help topics:\n\n"))
3166 ui.write(_("\nadditional help topics:\n\n"))
3167 topics = []
3167 topics = []
3168 for names, header, doc in help.helptable:
3168 for names, header, doc in help.helptable:
3169 topics.append((sorted(names, key=len, reverse=True)[0], header))
3169 topics.append((sorted(names, key=len, reverse=True)[0], header))
3170 topics_len = max([len(s[0]) for s in topics])
3170 topics_len = max([len(s[0]) for s in topics])
3171 for t, desc in topics:
3171 for t, desc in topics:
3172 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3172 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3173
3173
3174 optlist = []
3174 optlist = []
3175 addglobalopts(optlist, True)
3175 addglobalopts(optlist, True)
3176 ui.write(opttext(optlist, textwidth))
3176 ui.write(opttext(optlist, textwidth))
3177
3177
3178 def helptopic(name):
3178 def helptopic(name):
3179 for names, header, doc in help.helptable:
3179 for names, header, doc in help.helptable:
3180 if name in names:
3180 if name in names:
3181 break
3181 break
3182 else:
3182 else:
3183 raise error.UnknownCommand(name)
3183 raise error.UnknownCommand(name)
3184
3184
3185 # description
3185 # description
3186 if not doc:
3186 if not doc:
3187 doc = _("(no help text available)")
3187 doc = _("(no help text available)")
3188 if util.safehasattr(doc, '__call__'):
3188 if util.safehasattr(doc, '__call__'):
3189 doc = doc()
3189 doc = doc()
3190
3190
3191 ui.write("%s\n\n" % header)
3191 ui.write("%s\n\n" % header)
3192 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3192 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3193 try:
3193 try:
3194 cmdutil.findcmd(name, table)
3194 cmdutil.findcmd(name, table)
3195 ui.write(_('\nuse "hg help -c %s" to see help for '
3195 ui.write(_('\nuse "hg help -c %s" to see help for '
3196 'the %s command\n') % (name, name))
3196 'the %s command\n') % (name, name))
3197 except error.UnknownCommand:
3197 except error.UnknownCommand:
3198 pass
3198 pass
3199
3199
3200 def helpext(name):
3200 def helpext(name):
3201 try:
3201 try:
3202 mod = extensions.find(name)
3202 mod = extensions.find(name)
3203 doc = gettext(mod.__doc__) or _('no help text available')
3203 doc = gettext(mod.__doc__) or _('no help text available')
3204 except KeyError:
3204 except KeyError:
3205 mod = None
3205 mod = None
3206 doc = extensions.disabledext(name)
3206 doc = extensions.disabledext(name)
3207 if not doc:
3207 if not doc:
3208 raise error.UnknownCommand(name)
3208 raise error.UnknownCommand(name)
3209
3209
3210 if '\n' not in doc:
3210 if '\n' not in doc:
3211 head, tail = doc, ""
3211 head, tail = doc, ""
3212 else:
3212 else:
3213 head, tail = doc.split('\n', 1)
3213 head, tail = doc.split('\n', 1)
3214 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3214 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3215 if tail:
3215 if tail:
3216 ui.write(minirst.format(tail, textwidth))
3216 ui.write(minirst.format(tail, textwidth))
3217 ui.status('\n')
3217 ui.status('\n')
3218
3218
3219 if mod:
3219 if mod:
3220 try:
3220 try:
3221 ct = mod.cmdtable
3221 ct = mod.cmdtable
3222 except AttributeError:
3222 except AttributeError:
3223 ct = {}
3223 ct = {}
3224 modcmds = set([c.split('|', 1)[0] for c in ct])
3224 modcmds = set([c.split('|', 1)[0] for c in ct])
3225 helplist(modcmds.__contains__)
3225 helplist(modcmds.__contains__)
3226 else:
3226 else:
3227 ui.write(_('use "hg help extensions" for information on enabling '
3227 ui.write(_('use "hg help extensions" for information on enabling '
3228 'extensions\n'))
3228 'extensions\n'))
3229
3229
3230 def helpextcmd(name):
3230 def helpextcmd(name):
3231 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3231 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3232 doc = gettext(mod.__doc__).splitlines()[0]
3232 doc = gettext(mod.__doc__).splitlines()[0]
3233
3233
3234 msg = help.listexts(_("'%s' is provided by the following "
3234 msg = help.listexts(_("'%s' is provided by the following "
3235 "extension:") % cmd, {ext: doc}, indent=4)
3235 "extension:") % cmd, {ext: doc}, indent=4)
3236 ui.write(minirst.format(msg, textwidth))
3236 ui.write(minirst.format(msg, textwidth))
3237 ui.write('\n')
3237 ui.write('\n')
3238 ui.write(_('use "hg help extensions" for information on enabling '
3238 ui.write(_('use "hg help extensions" for information on enabling '
3239 'extensions\n'))
3239 'extensions\n'))
3240
3240
3241 if name and name != 'shortlist':
3241 if name and name != 'shortlist':
3242 i = None
3242 i = None
3243 if unknowncmd:
3243 if unknowncmd:
3244 queries = (helpextcmd,)
3244 queries = (helpextcmd,)
3245 elif opts.get('extension'):
3245 elif opts.get('extension'):
3246 queries = (helpext,)
3246 queries = (helpext,)
3247 elif opts.get('command'):
3247 elif opts.get('command'):
3248 queries = (helpcmd,)
3248 queries = (helpcmd,)
3249 else:
3249 else:
3250 queries = (helptopic, helpcmd, helpext, helpextcmd)
3250 queries = (helptopic, helpcmd, helpext, helpextcmd)
3251 for f in queries:
3251 for f in queries:
3252 try:
3252 try:
3253 f(name)
3253 f(name)
3254 i = None
3254 i = None
3255 break
3255 break
3256 except error.UnknownCommand, inst:
3256 except error.UnknownCommand, inst:
3257 i = inst
3257 i = inst
3258 if i:
3258 if i:
3259 raise i
3259 raise i
3260 else:
3260 else:
3261 # program name
3261 # program name
3262 ui.status(_("Mercurial Distributed SCM\n"))
3262 ui.status(_("Mercurial Distributed SCM\n"))
3263 ui.status('\n')
3263 ui.status('\n')
3264 helplist()
3264 helplist()
3265
3265
3266
3266
3267 @command('identify|id',
3267 @command('identify|id',
3268 [('r', 'rev', '',
3268 [('r', 'rev', '',
3269 _('identify the specified revision'), _('REV')),
3269 _('identify the specified revision'), _('REV')),
3270 ('n', 'num', None, _('show local revision number')),
3270 ('n', 'num', None, _('show local revision number')),
3271 ('i', 'id', None, _('show global revision id')),
3271 ('i', 'id', None, _('show global revision id')),
3272 ('b', 'branch', None, _('show branch')),
3272 ('b', 'branch', None, _('show branch')),
3273 ('t', 'tags', None, _('show tags')),
3273 ('t', 'tags', None, _('show tags')),
3274 ('B', 'bookmarks', None, _('show bookmarks')),
3274 ('B', 'bookmarks', None, _('show bookmarks')),
3275 ] + remoteopts,
3275 ] + remoteopts,
3276 _('[-nibtB] [-r REV] [SOURCE]'))
3276 _('[-nibtB] [-r REV] [SOURCE]'))
3277 def identify(ui, repo, source=None, rev=None,
3277 def identify(ui, repo, source=None, rev=None,
3278 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3278 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3279 """identify the working copy or specified revision
3279 """identify the working copy or specified revision
3280
3280
3281 Print a summary identifying the repository state at REV using one or
3281 Print a summary identifying the repository state at REV using one or
3282 two parent hash identifiers, followed by a "+" if the working
3282 two parent hash identifiers, followed by a "+" if the working
3283 directory has uncommitted changes, the branch name (if not default),
3283 directory has uncommitted changes, the branch name (if not default),
3284 a list of tags, and a list of bookmarks.
3284 a list of tags, and a list of bookmarks.
3285
3285
3286 When REV is not given, print a summary of the current state of the
3286 When REV is not given, print a summary of the current state of the
3287 repository.
3287 repository.
3288
3288
3289 Specifying a path to a repository root or Mercurial bundle will
3289 Specifying a path to a repository root or Mercurial bundle will
3290 cause lookup to operate on that repository/bundle.
3290 cause lookup to operate on that repository/bundle.
3291
3291
3292 .. container:: verbose
3292 .. container:: verbose
3293
3293
3294 Examples:
3294 Examples:
3295
3295
3296 - generate a build identifier for the working directory::
3296 - generate a build identifier for the working directory::
3297
3297
3298 hg id --id > build-id.dat
3298 hg id --id > build-id.dat
3299
3299
3300 - find the revision corresponding to a tag::
3300 - find the revision corresponding to a tag::
3301
3301
3302 hg id -n -r 1.3
3302 hg id -n -r 1.3
3303
3303
3304 - check the most recent revision of a remote repository::
3304 - check the most recent revision of a remote repository::
3305
3305
3306 hg id -r tip http://selenic.com/hg/
3306 hg id -r tip http://selenic.com/hg/
3307
3307
3308 Returns 0 if successful.
3308 Returns 0 if successful.
3309 """
3309 """
3310
3310
3311 if not repo and not source:
3311 if not repo and not source:
3312 raise util.Abort(_("there is no Mercurial repository here "
3312 raise util.Abort(_("there is no Mercurial repository here "
3313 "(.hg not found)"))
3313 "(.hg not found)"))
3314
3314
3315 hexfunc = ui.debugflag and hex or short
3315 hexfunc = ui.debugflag and hex or short
3316 default = not (num or id or branch or tags or bookmarks)
3316 default = not (num or id or branch or tags or bookmarks)
3317 output = []
3317 output = []
3318 revs = []
3318 revs = []
3319
3319
3320 if source:
3320 if source:
3321 source, branches = hg.parseurl(ui.expandpath(source))
3321 source, branches = hg.parseurl(ui.expandpath(source))
3322 repo = hg.peer(ui, opts, source)
3322 repo = hg.peer(ui, opts, source)
3323 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3323 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3324
3324
3325 if not repo.local():
3325 if not repo.local():
3326 if num or branch or tags:
3326 if num or branch or tags:
3327 raise util.Abort(
3327 raise util.Abort(
3328 _("can't query remote revision number, branch, or tags"))
3328 _("can't query remote revision number, branch, or tags"))
3329 if not rev and revs:
3329 if not rev and revs:
3330 rev = revs[0]
3330 rev = revs[0]
3331 if not rev:
3331 if not rev:
3332 rev = "tip"
3332 rev = "tip"
3333
3333
3334 remoterev = repo.lookup(rev)
3334 remoterev = repo.lookup(rev)
3335 if default or id:
3335 if default or id:
3336 output = [hexfunc(remoterev)]
3336 output = [hexfunc(remoterev)]
3337
3337
3338 def getbms():
3338 def getbms():
3339 bms = []
3339 bms = []
3340
3340
3341 if 'bookmarks' in repo.listkeys('namespaces'):
3341 if 'bookmarks' in repo.listkeys('namespaces'):
3342 hexremoterev = hex(remoterev)
3342 hexremoterev = hex(remoterev)
3343 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3343 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3344 if bmr == hexremoterev]
3344 if bmr == hexremoterev]
3345
3345
3346 return bms
3346 return bms
3347
3347
3348 if bookmarks:
3348 if bookmarks:
3349 output.extend(getbms())
3349 output.extend(getbms())
3350 elif default and not ui.quiet:
3350 elif default and not ui.quiet:
3351 # multiple bookmarks for a single parent separated by '/'
3351 # multiple bookmarks for a single parent separated by '/'
3352 bm = '/'.join(getbms())
3352 bm = '/'.join(getbms())
3353 if bm:
3353 if bm:
3354 output.append(bm)
3354 output.append(bm)
3355 else:
3355 else:
3356 if not rev:
3356 if not rev:
3357 ctx = repo[None]
3357 ctx = repo[None]
3358 parents = ctx.parents()
3358 parents = ctx.parents()
3359 changed = ""
3359 changed = ""
3360 if default or id or num:
3360 if default or id or num:
3361 changed = util.any(repo.status()) and "+" or ""
3361 changed = util.any(repo.status()) and "+" or ""
3362 if default or id:
3362 if default or id:
3363 output = ["%s%s" %
3363 output = ["%s%s" %
3364 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3364 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3365 if num:
3365 if num:
3366 output.append("%s%s" %
3366 output.append("%s%s" %
3367 ('+'.join([str(p.rev()) for p in parents]), changed))
3367 ('+'.join([str(p.rev()) for p in parents]), changed))
3368 else:
3368 else:
3369 ctx = scmutil.revsingle(repo, rev)
3369 ctx = scmutil.revsingle(repo, rev)
3370 if default or id:
3370 if default or id:
3371 output = [hexfunc(ctx.node())]
3371 output = [hexfunc(ctx.node())]
3372 if num:
3372 if num:
3373 output.append(str(ctx.rev()))
3373 output.append(str(ctx.rev()))
3374
3374
3375 if default and not ui.quiet:
3375 if default and not ui.quiet:
3376 b = ctx.branch()
3376 b = ctx.branch()
3377 if b != 'default':
3377 if b != 'default':
3378 output.append("(%s)" % b)
3378 output.append("(%s)" % b)
3379
3379
3380 # multiple tags for a single parent separated by '/'
3380 # multiple tags for a single parent separated by '/'
3381 t = '/'.join(ctx.tags())
3381 t = '/'.join(ctx.tags())
3382 if t:
3382 if t:
3383 output.append(t)
3383 output.append(t)
3384
3384
3385 # multiple bookmarks for a single parent separated by '/'
3385 # multiple bookmarks for a single parent separated by '/'
3386 bm = '/'.join(ctx.bookmarks())
3386 bm = '/'.join(ctx.bookmarks())
3387 if bm:
3387 if bm:
3388 output.append(bm)
3388 output.append(bm)
3389 else:
3389 else:
3390 if branch:
3390 if branch:
3391 output.append(ctx.branch())
3391 output.append(ctx.branch())
3392
3392
3393 if tags:
3393 if tags:
3394 output.extend(ctx.tags())
3394 output.extend(ctx.tags())
3395
3395
3396 if bookmarks:
3396 if bookmarks:
3397 output.extend(ctx.bookmarks())
3397 output.extend(ctx.bookmarks())
3398
3398
3399 ui.write("%s\n" % ' '.join(output))
3399 ui.write("%s\n" % ' '.join(output))
3400
3400
3401 @command('import|patch',
3401 @command('import|patch',
3402 [('p', 'strip', 1,
3402 [('p', 'strip', 1,
3403 _('directory strip option for patch. This has the same '
3403 _('directory strip option for patch. This has the same '
3404 'meaning as the corresponding patch option'), _('NUM')),
3404 'meaning as the corresponding patch option'), _('NUM')),
3405 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3405 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3406 ('e', 'edit', False, _('invoke editor on commit messages')),
3406 ('e', 'edit', False, _('invoke editor on commit messages')),
3407 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3407 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3408 ('', 'no-commit', None,
3408 ('', 'no-commit', None,
3409 _("don't commit, just update the working directory")),
3409 _("don't commit, just update the working directory")),
3410 ('', 'bypass', None,
3410 ('', 'bypass', None,
3411 _("apply patch without touching the working directory")),
3411 _("apply patch without touching the working directory")),
3412 ('', 'exact', None,
3412 ('', 'exact', None,
3413 _('apply patch to the nodes from which it was generated')),
3413 _('apply patch to the nodes from which it was generated')),
3414 ('', 'import-branch', None,
3414 ('', 'import-branch', None,
3415 _('use any branch information in patch (implied by --exact)'))] +
3415 _('use any branch information in patch (implied by --exact)'))] +
3416 commitopts + commitopts2 + similarityopts,
3416 commitopts + commitopts2 + similarityopts,
3417 _('[OPTION]... PATCH...'))
3417 _('[OPTION]... PATCH...'))
3418 def import_(ui, repo, patch1=None, *patches, **opts):
3418 def import_(ui, repo, patch1=None, *patches, **opts):
3419 """import an ordered set of patches
3419 """import an ordered set of patches
3420
3420
3421 Import a list of patches and commit them individually (unless
3421 Import a list of patches and commit them individually (unless
3422 --no-commit is specified).
3422 --no-commit is specified).
3423
3423
3424 If there are outstanding changes in the working directory, import
3424 If there are outstanding changes in the working directory, import
3425 will abort unless given the -f/--force flag.
3425 will abort unless given the -f/--force flag.
3426
3426
3427 You can import a patch straight from a mail message. Even patches
3427 You can import a patch straight from a mail message. Even patches
3428 as attachments work (to use the body part, it must have type
3428 as attachments work (to use the body part, it must have type
3429 text/plain or text/x-patch). From and Subject headers of email
3429 text/plain or text/x-patch). From and Subject headers of email
3430 message are used as default committer and commit message. All
3430 message are used as default committer and commit message. All
3431 text/plain body parts before first diff are added to commit
3431 text/plain body parts before first diff are added to commit
3432 message.
3432 message.
3433
3433
3434 If the imported patch was generated by :hg:`export`, user and
3434 If the imported patch was generated by :hg:`export`, user and
3435 description from patch override values from message headers and
3435 description from patch override values from message headers and
3436 body. Values given on command line with -m/--message and -u/--user
3436 body. Values given on command line with -m/--message and -u/--user
3437 override these.
3437 override these.
3438
3438
3439 If --exact is specified, import will set the working directory to
3439 If --exact is specified, import will set the working directory to
3440 the parent of each patch before applying it, and will abort if the
3440 the parent of each patch before applying it, and will abort if the
3441 resulting changeset has a different ID than the one recorded in
3441 resulting changeset has a different ID than the one recorded in
3442 the patch. This may happen due to character set problems or other
3442 the patch. This may happen due to character set problems or other
3443 deficiencies in the text patch format.
3443 deficiencies in the text patch format.
3444
3444
3445 Use --bypass to apply and commit patches directly to the
3445 Use --bypass to apply and commit patches directly to the
3446 repository, not touching the working directory. Without --exact,
3446 repository, not touching the working directory. Without --exact,
3447 patches will be applied on top of the working directory parent
3447 patches will be applied on top of the working directory parent
3448 revision.
3448 revision.
3449
3449
3450 With -s/--similarity, hg will attempt to discover renames and
3450 With -s/--similarity, hg will attempt to discover renames and
3451 copies in the patch in the same way as :hg:`addremove`.
3451 copies in the patch in the same way as :hg:`addremove`.
3452
3452
3453 To read a patch from standard input, use "-" as the patch name. If
3453 To read a patch from standard input, use "-" as the patch name. If
3454 a URL is specified, the patch will be downloaded from it.
3454 a URL is specified, the patch will be downloaded from it.
3455 See :hg:`help dates` for a list of formats valid for -d/--date.
3455 See :hg:`help dates` for a list of formats valid for -d/--date.
3456
3456
3457 .. container:: verbose
3457 .. container:: verbose
3458
3458
3459 Examples:
3459 Examples:
3460
3460
3461 - import a traditional patch from a website and detect renames::
3461 - import a traditional patch from a website and detect renames::
3462
3462
3463 hg import -s 80 http://example.com/bugfix.patch
3463 hg import -s 80 http://example.com/bugfix.patch
3464
3464
3465 - import a changeset from an hgweb server::
3465 - import a changeset from an hgweb server::
3466
3466
3467 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3467 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3468
3468
3469 - import all the patches in an Unix-style mbox::
3469 - import all the patches in an Unix-style mbox::
3470
3470
3471 hg import incoming-patches.mbox
3471 hg import incoming-patches.mbox
3472
3472
3473 - attempt to exactly restore an exported changeset (not always
3473 - attempt to exactly restore an exported changeset (not always
3474 possible)::
3474 possible)::
3475
3475
3476 hg import --exact proposed-fix.patch
3476 hg import --exact proposed-fix.patch
3477
3477
3478 Returns 0 on success.
3478 Returns 0 on success.
3479 """
3479 """
3480
3480
3481 if not patch1:
3481 if not patch1:
3482 raise util.Abort(_('need at least one patch to import'))
3482 raise util.Abort(_('need at least one patch to import'))
3483
3483
3484 patches = (patch1,) + patches
3484 patches = (patch1,) + patches
3485
3485
3486 date = opts.get('date')
3486 date = opts.get('date')
3487 if date:
3487 if date:
3488 opts['date'] = util.parsedate(date)
3488 opts['date'] = util.parsedate(date)
3489
3489
3490 editor = cmdutil.commiteditor
3490 editor = cmdutil.commiteditor
3491 if opts.get('edit'):
3491 if opts.get('edit'):
3492 editor = cmdutil.commitforceeditor
3492 editor = cmdutil.commitforceeditor
3493
3493
3494 update = not opts.get('bypass')
3494 update = not opts.get('bypass')
3495 if not update and opts.get('no_commit'):
3495 if not update and opts.get('no_commit'):
3496 raise util.Abort(_('cannot use --no-commit with --bypass'))
3496 raise util.Abort(_('cannot use --no-commit with --bypass'))
3497 try:
3497 try:
3498 sim = float(opts.get('similarity') or 0)
3498 sim = float(opts.get('similarity') or 0)
3499 except ValueError:
3499 except ValueError:
3500 raise util.Abort(_('similarity must be a number'))
3500 raise util.Abort(_('similarity must be a number'))
3501 if sim < 0 or sim > 100:
3501 if sim < 0 or sim > 100:
3502 raise util.Abort(_('similarity must be between 0 and 100'))
3502 raise util.Abort(_('similarity must be between 0 and 100'))
3503 if sim and not update:
3503 if sim and not update:
3504 raise util.Abort(_('cannot use --similarity with --bypass'))
3504 raise util.Abort(_('cannot use --similarity with --bypass'))
3505
3505
3506 if (opts.get('exact') or not opts.get('force')) and update:
3506 if (opts.get('exact') or not opts.get('force')) and update:
3507 cmdutil.bailifchanged(repo)
3507 cmdutil.bailifchanged(repo)
3508
3508
3509 base = opts["base"]
3509 base = opts["base"]
3510 strip = opts["strip"]
3510 strip = opts["strip"]
3511 wlock = lock = tr = None
3511 wlock = lock = tr = None
3512 msgs = []
3512 msgs = []
3513
3513
3514 def checkexact(repo, n, nodeid):
3514 def checkexact(repo, n, nodeid):
3515 if opts.get('exact') and hex(n) != nodeid:
3515 if opts.get('exact') and hex(n) != nodeid:
3516 repo.rollback()
3516 repo.rollback()
3517 raise util.Abort(_('patch is damaged or loses information'))
3517 raise util.Abort(_('patch is damaged or loses information'))
3518
3518
3519 def tryone(ui, hunk, parents):
3519 def tryone(ui, hunk, parents):
3520 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3520 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3521 patch.extract(ui, hunk)
3521 patch.extract(ui, hunk)
3522
3522
3523 if not tmpname:
3523 if not tmpname:
3524 return (None, None)
3524 return (None, None)
3525 msg = _('applied to working directory')
3525 msg = _('applied to working directory')
3526
3526
3527 try:
3527 try:
3528 cmdline_message = cmdutil.logmessage(ui, opts)
3528 cmdline_message = cmdutil.logmessage(ui, opts)
3529 if cmdline_message:
3529 if cmdline_message:
3530 # pickup the cmdline msg
3530 # pickup the cmdline msg
3531 message = cmdline_message
3531 message = cmdline_message
3532 elif message:
3532 elif message:
3533 # pickup the patch msg
3533 # pickup the patch msg
3534 message = message.strip()
3534 message = message.strip()
3535 else:
3535 else:
3536 # launch the editor
3536 # launch the editor
3537 message = None
3537 message = None
3538 ui.debug('message:\n%s\n' % message)
3538 ui.debug('message:\n%s\n' % message)
3539
3539
3540 if len(parents) == 1:
3540 if len(parents) == 1:
3541 parents.append(repo[nullid])
3541 parents.append(repo[nullid])
3542 if opts.get('exact'):
3542 if opts.get('exact'):
3543 if not nodeid or not p1:
3543 if not nodeid or not p1:
3544 raise util.Abort(_('not a Mercurial patch'))
3544 raise util.Abort(_('not a Mercurial patch'))
3545 p1 = repo[p1]
3545 p1 = repo[p1]
3546 p2 = repo[p2 or nullid]
3546 p2 = repo[p2 or nullid]
3547 elif p2:
3547 elif p2:
3548 try:
3548 try:
3549 p1 = repo[p1]
3549 p1 = repo[p1]
3550 p2 = repo[p2]
3550 p2 = repo[p2]
3551 # Without any options, consider p2 only if the
3551 # Without any options, consider p2 only if the
3552 # patch is being applied on top of the recorded
3552 # patch is being applied on top of the recorded
3553 # first parent.
3553 # first parent.
3554 if p1 != parents[0]:
3554 if p1 != parents[0]:
3555 p1 = parents[0]
3555 p1 = parents[0]
3556 p2 = repo[nullid]
3556 p2 = repo[nullid]
3557 except error.RepoError:
3557 except error.RepoError:
3558 p1, p2 = parents
3558 p1, p2 = parents
3559 else:
3559 else:
3560 p1, p2 = parents
3560 p1, p2 = parents
3561
3561
3562 n = None
3562 n = None
3563 if update:
3563 if update:
3564 if p1 != parents[0]:
3564 if p1 != parents[0]:
3565 hg.clean(repo, p1.node())
3565 hg.clean(repo, p1.node())
3566 if p2 != parents[1]:
3566 if p2 != parents[1]:
3567 repo.dirstate.setparents(p1.node(), p2.node())
3567 repo.dirstate.setparents(p1.node(), p2.node())
3568
3568
3569 if opts.get('exact') or opts.get('import_branch'):
3569 if opts.get('exact') or opts.get('import_branch'):
3570 repo.dirstate.setbranch(branch or 'default')
3570 repo.dirstate.setbranch(branch or 'default')
3571
3571
3572 files = set()
3572 files = set()
3573 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3573 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3574 eolmode=None, similarity=sim / 100.0)
3574 eolmode=None, similarity=sim / 100.0)
3575 files = list(files)
3575 files = list(files)
3576 if opts.get('no_commit'):
3576 if opts.get('no_commit'):
3577 if message:
3577 if message:
3578 msgs.append(message)
3578 msgs.append(message)
3579 else:
3579 else:
3580 if opts.get('exact') or p2:
3580 if opts.get('exact') or p2:
3581 # If you got here, you either use --force and know what
3581 # If you got here, you either use --force and know what
3582 # you are doing or used --exact or a merge patch while
3582 # you are doing or used --exact or a merge patch while
3583 # being updated to its first parent.
3583 # being updated to its first parent.
3584 m = None
3584 m = None
3585 else:
3585 else:
3586 m = scmutil.matchfiles(repo, files or [])
3586 m = scmutil.matchfiles(repo, files or [])
3587 n = repo.commit(message, opts.get('user') or user,
3587 n = repo.commit(message, opts.get('user') or user,
3588 opts.get('date') or date, match=m,
3588 opts.get('date') or date, match=m,
3589 editor=editor)
3589 editor=editor)
3590 checkexact(repo, n, nodeid)
3590 checkexact(repo, n, nodeid)
3591 else:
3591 else:
3592 if opts.get('exact') or opts.get('import_branch'):
3592 if opts.get('exact') or opts.get('import_branch'):
3593 branch = branch or 'default'
3593 branch = branch or 'default'
3594 else:
3594 else:
3595 branch = p1.branch()
3595 branch = p1.branch()
3596 store = patch.filestore()
3596 store = patch.filestore()
3597 try:
3597 try:
3598 files = set()
3598 files = set()
3599 try:
3599 try:
3600 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3600 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3601 files, eolmode=None)
3601 files, eolmode=None)
3602 except patch.PatchError, e:
3602 except patch.PatchError, e:
3603 raise util.Abort(str(e))
3603 raise util.Abort(str(e))
3604 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3604 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3605 message,
3605 message,
3606 opts.get('user') or user,
3606 opts.get('user') or user,
3607 opts.get('date') or date,
3607 opts.get('date') or date,
3608 branch, files, store,
3608 branch, files, store,
3609 editor=cmdutil.commiteditor)
3609 editor=cmdutil.commiteditor)
3610 repo.savecommitmessage(memctx.description())
3610 repo.savecommitmessage(memctx.description())
3611 n = memctx.commit()
3611 n = memctx.commit()
3612 checkexact(repo, n, nodeid)
3612 checkexact(repo, n, nodeid)
3613 finally:
3613 finally:
3614 store.close()
3614 store.close()
3615 if n:
3615 if n:
3616 # i18n: refers to a short changeset id
3616 # i18n: refers to a short changeset id
3617 msg = _('created %s') % short(n)
3617 msg = _('created %s') % short(n)
3618 return (msg, n)
3618 return (msg, n)
3619 finally:
3619 finally:
3620 os.unlink(tmpname)
3620 os.unlink(tmpname)
3621
3621
3622 try:
3622 try:
3623 try:
3623 try:
3624 wlock = repo.wlock()
3624 wlock = repo.wlock()
3625 lock = repo.lock()
3625 lock = repo.lock()
3626 tr = repo.transaction('import')
3626 tr = repo.transaction('import')
3627 parents = repo.parents()
3627 parents = repo.parents()
3628 for patchurl in patches:
3628 for patchurl in patches:
3629 if patchurl == '-':
3629 if patchurl == '-':
3630 ui.status(_('applying patch from stdin\n'))
3630 ui.status(_('applying patch from stdin\n'))
3631 patchfile = ui.fin
3631 patchfile = ui.fin
3632 patchurl = 'stdin' # for error message
3632 patchurl = 'stdin' # for error message
3633 else:
3633 else:
3634 patchurl = os.path.join(base, patchurl)
3634 patchurl = os.path.join(base, patchurl)
3635 ui.status(_('applying %s\n') % patchurl)
3635 ui.status(_('applying %s\n') % patchurl)
3636 patchfile = url.open(ui, patchurl)
3636 patchfile = url.open(ui, patchurl)
3637
3637
3638 haspatch = False
3638 haspatch = False
3639 for hunk in patch.split(patchfile):
3639 for hunk in patch.split(patchfile):
3640 (msg, node) = tryone(ui, hunk, parents)
3640 (msg, node) = tryone(ui, hunk, parents)
3641 if msg:
3641 if msg:
3642 haspatch = True
3642 haspatch = True
3643 ui.note(msg + '\n')
3643 ui.note(msg + '\n')
3644 if update or opts.get('exact'):
3644 if update or opts.get('exact'):
3645 parents = repo.parents()
3645 parents = repo.parents()
3646 else:
3646 else:
3647 parents = [repo[node]]
3647 parents = [repo[node]]
3648
3648
3649 if not haspatch:
3649 if not haspatch:
3650 raise util.Abort(_('%s: no diffs found') % patchurl)
3650 raise util.Abort(_('%s: no diffs found') % patchurl)
3651
3651
3652 tr.close()
3652 tr.close()
3653 if msgs:
3653 if msgs:
3654 repo.savecommitmessage('\n* * *\n'.join(msgs))
3654 repo.savecommitmessage('\n* * *\n'.join(msgs))
3655 except:
3655 except:
3656 # wlock.release() indirectly calls dirstate.write(): since
3656 # wlock.release() indirectly calls dirstate.write(): since
3657 # we're crashing, we do not want to change the working dir
3657 # we're crashing, we do not want to change the working dir
3658 # parent after all, so make sure it writes nothing
3658 # parent after all, so make sure it writes nothing
3659 repo.dirstate.invalidate()
3659 repo.dirstate.invalidate()
3660 raise
3660 raise
3661 finally:
3661 finally:
3662 if tr:
3662 if tr:
3663 tr.release()
3663 tr.release()
3664 release(lock, wlock)
3664 release(lock, wlock)
3665
3665
3666 @command('incoming|in',
3666 @command('incoming|in',
3667 [('f', 'force', None,
3667 [('f', 'force', None,
3668 _('run even if remote repository is unrelated')),
3668 _('run even if remote repository is unrelated')),
3669 ('n', 'newest-first', None, _('show newest record first')),
3669 ('n', 'newest-first', None, _('show newest record first')),
3670 ('', 'bundle', '',
3670 ('', 'bundle', '',
3671 _('file to store the bundles into'), _('FILE')),
3671 _('file to store the bundles into'), _('FILE')),
3672 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3672 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3673 ('B', 'bookmarks', False, _("compare bookmarks")),
3673 ('B', 'bookmarks', False, _("compare bookmarks")),
3674 ('b', 'branch', [],
3674 ('b', 'branch', [],
3675 _('a specific branch you would like to pull'), _('BRANCH')),
3675 _('a specific branch you would like to pull'), _('BRANCH')),
3676 ] + logopts + remoteopts + subrepoopts,
3676 ] + logopts + remoteopts + subrepoopts,
3677 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3677 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3678 def incoming(ui, repo, source="default", **opts):
3678 def incoming(ui, repo, source="default", **opts):
3679 """show new changesets found in source
3679 """show new changesets found in source
3680
3680
3681 Show new changesets found in the specified path/URL or the default
3681 Show new changesets found in the specified path/URL or the default
3682 pull location. These are the changesets that would have been pulled
3682 pull location. These are the changesets that would have been pulled
3683 if a pull at the time you issued this command.
3683 if a pull at the time you issued this command.
3684
3684
3685 For remote repository, using --bundle avoids downloading the
3685 For remote repository, using --bundle avoids downloading the
3686 changesets twice if the incoming is followed by a pull.
3686 changesets twice if the incoming is followed by a pull.
3687
3687
3688 See pull for valid source format details.
3688 See pull for valid source format details.
3689
3689
3690 Returns 0 if there are incoming changes, 1 otherwise.
3690 Returns 0 if there are incoming changes, 1 otherwise.
3691 """
3691 """
3692 if opts.get('bundle') and opts.get('subrepos'):
3692 if opts.get('bundle') and opts.get('subrepos'):
3693 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3693 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3694
3694
3695 if opts.get('bookmarks'):
3695 if opts.get('bookmarks'):
3696 source, branches = hg.parseurl(ui.expandpath(source),
3696 source, branches = hg.parseurl(ui.expandpath(source),
3697 opts.get('branch'))
3697 opts.get('branch'))
3698 other = hg.peer(repo, opts, source)
3698 other = hg.peer(repo, opts, source)
3699 if 'bookmarks' not in other.listkeys('namespaces'):
3699 if 'bookmarks' not in other.listkeys('namespaces'):
3700 ui.warn(_("remote doesn't support bookmarks\n"))
3700 ui.warn(_("remote doesn't support bookmarks\n"))
3701 return 0
3701 return 0
3702 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3702 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3703 return bookmarks.diff(ui, repo, other)
3703 return bookmarks.diff(ui, repo, other)
3704
3704
3705 repo._subtoppath = ui.expandpath(source)
3705 repo._subtoppath = ui.expandpath(source)
3706 try:
3706 try:
3707 return hg.incoming(ui, repo, source, opts)
3707 return hg.incoming(ui, repo, source, opts)
3708 finally:
3708 finally:
3709 del repo._subtoppath
3709 del repo._subtoppath
3710
3710
3711
3711
3712 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3712 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3713 def init(ui, dest=".", **opts):
3713 def init(ui, dest=".", **opts):
3714 """create a new repository in the given directory
3714 """create a new repository in the given directory
3715
3715
3716 Initialize a new repository in the given directory. If the given
3716 Initialize a new repository in the given directory. If the given
3717 directory does not exist, it will be created.
3717 directory does not exist, it will be created.
3718
3718
3719 If no directory is given, the current directory is used.
3719 If no directory is given, the current directory is used.
3720
3720
3721 It is possible to specify an ``ssh://`` URL as the destination.
3721 It is possible to specify an ``ssh://`` URL as the destination.
3722 See :hg:`help urls` for more information.
3722 See :hg:`help urls` for more information.
3723
3723
3724 Returns 0 on success.
3724 Returns 0 on success.
3725 """
3725 """
3726 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3726 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3727
3727
3728 @command('locate',
3728 @command('locate',
3729 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3729 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3730 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3730 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3731 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3731 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3732 ] + walkopts,
3732 ] + walkopts,
3733 _('[OPTION]... [PATTERN]...'))
3733 _('[OPTION]... [PATTERN]...'))
3734 def locate(ui, repo, *pats, **opts):
3734 def locate(ui, repo, *pats, **opts):
3735 """locate files matching specific patterns
3735 """locate files matching specific patterns
3736
3736
3737 Print files under Mercurial control in the working directory whose
3737 Print files under Mercurial control in the working directory whose
3738 names match the given patterns.
3738 names match the given patterns.
3739
3739
3740 By default, this command searches all directories in the working
3740 By default, this command searches all directories in the working
3741 directory. To search just the current directory and its
3741 directory. To search just the current directory and its
3742 subdirectories, use "--include .".
3742 subdirectories, use "--include .".
3743
3743
3744 If no patterns are given to match, this command prints the names
3744 If no patterns are given to match, this command prints the names
3745 of all files under Mercurial control in the working directory.
3745 of all files under Mercurial control in the working directory.
3746
3746
3747 If you want to feed the output of this command into the "xargs"
3747 If you want to feed the output of this command into the "xargs"
3748 command, use the -0 option to both this command and "xargs". This
3748 command, use the -0 option to both this command and "xargs". This
3749 will avoid the problem of "xargs" treating single filenames that
3749 will avoid the problem of "xargs" treating single filenames that
3750 contain whitespace as multiple filenames.
3750 contain whitespace as multiple filenames.
3751
3751
3752 Returns 0 if a match is found, 1 otherwise.
3752 Returns 0 if a match is found, 1 otherwise.
3753 """
3753 """
3754 end = opts.get('print0') and '\0' or '\n'
3754 end = opts.get('print0') and '\0' or '\n'
3755 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3755 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3756
3756
3757 ret = 1
3757 ret = 1
3758 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3758 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3759 m.bad = lambda x, y: False
3759 m.bad = lambda x, y: False
3760 for abs in repo[rev].walk(m):
3760 for abs in repo[rev].walk(m):
3761 if not rev and abs not in repo.dirstate:
3761 if not rev and abs not in repo.dirstate:
3762 continue
3762 continue
3763 if opts.get('fullpath'):
3763 if opts.get('fullpath'):
3764 ui.write(repo.wjoin(abs), end)
3764 ui.write(repo.wjoin(abs), end)
3765 else:
3765 else:
3766 ui.write(((pats and m.rel(abs)) or abs), end)
3766 ui.write(((pats and m.rel(abs)) or abs), end)
3767 ret = 0
3767 ret = 0
3768
3768
3769 return ret
3769 return ret
3770
3770
3771 @command('^log|history',
3771 @command('^log|history',
3772 [('f', 'follow', None,
3772 [('f', 'follow', None,
3773 _('follow changeset history, or file history across copies and renames')),
3773 _('follow changeset history, or file history across copies and renames')),
3774 ('', 'follow-first', None,
3774 ('', 'follow-first', None,
3775 _('only follow the first parent of merge changesets (DEPRECATED)')),
3775 _('only follow the first parent of merge changesets (DEPRECATED)')),
3776 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3776 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3777 ('C', 'copies', None, _('show copied files')),
3777 ('C', 'copies', None, _('show copied files')),
3778 ('k', 'keyword', [],
3778 ('k', 'keyword', [],
3779 _('do case-insensitive search for a given text'), _('TEXT')),
3779 _('do case-insensitive search for a given text'), _('TEXT')),
3780 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3780 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3781 ('', 'removed', None, _('include revisions where files were removed')),
3781 ('', 'removed', None, _('include revisions where files were removed')),
3782 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3782 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3783 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3783 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3784 ('', 'only-branch', [],
3784 ('', 'only-branch', [],
3785 _('show only changesets within the given named branch (DEPRECATED)'),
3785 _('show only changesets within the given named branch (DEPRECATED)'),
3786 _('BRANCH')),
3786 _('BRANCH')),
3787 ('b', 'branch', [],
3787 ('b', 'branch', [],
3788 _('show changesets within the given named branch'), _('BRANCH')),
3788 _('show changesets within the given named branch'), _('BRANCH')),
3789 ('P', 'prune', [],
3789 ('P', 'prune', [],
3790 _('do not display revision or any of its ancestors'), _('REV')),
3790 _('do not display revision or any of its ancestors'), _('REV')),
3791 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3791 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3792 ] + logopts + walkopts,
3792 ] + logopts + walkopts,
3793 _('[OPTION]... [FILE]'))
3793 _('[OPTION]... [FILE]'))
3794 def log(ui, repo, *pats, **opts):
3794 def log(ui, repo, *pats, **opts):
3795 """show revision history of entire repository or files
3795 """show revision history of entire repository or files
3796
3796
3797 Print the revision history of the specified files or the entire
3797 Print the revision history of the specified files or the entire
3798 project.
3798 project.
3799
3799
3800 If no revision range is specified, the default is ``tip:0`` unless
3800 If no revision range is specified, the default is ``tip:0`` unless
3801 --follow is set, in which case the working directory parent is
3801 --follow is set, in which case the working directory parent is
3802 used as the starting revision.
3802 used as the starting revision.
3803
3803
3804 File history is shown without following rename or copy history of
3804 File history is shown without following rename or copy history of
3805 files. Use -f/--follow with a filename to follow history across
3805 files. Use -f/--follow with a filename to follow history across
3806 renames and copies. --follow without a filename will only show
3806 renames and copies. --follow without a filename will only show
3807 ancestors or descendants of the starting revision.
3807 ancestors or descendants of the starting revision.
3808
3808
3809 By default this command prints revision number and changeset id,
3809 By default this command prints revision number and changeset id,
3810 tags, non-trivial parents, user, date and time, and a summary for
3810 tags, non-trivial parents, user, date and time, and a summary for
3811 each commit. When the -v/--verbose switch is used, the list of
3811 each commit. When the -v/--verbose switch is used, the list of
3812 changed files and full commit message are shown.
3812 changed files and full commit message are shown.
3813
3813
3814 .. note::
3814 .. note::
3815 log -p/--patch may generate unexpected diff output for merge
3815 log -p/--patch may generate unexpected diff output for merge
3816 changesets, as it will only compare the merge changeset against
3816 changesets, as it will only compare the merge changeset against
3817 its first parent. Also, only files different from BOTH parents
3817 its first parent. Also, only files different from BOTH parents
3818 will appear in files:.
3818 will appear in files:.
3819
3819
3820 .. note::
3820 .. note::
3821 for performance reasons, log FILE may omit duplicate changes
3821 for performance reasons, log FILE may omit duplicate changes
3822 made on branches and will not show deletions. To see all
3822 made on branches and will not show deletions. To see all
3823 changes including duplicates and deletions, use the --removed
3823 changes including duplicates and deletions, use the --removed
3824 switch.
3824 switch.
3825
3825
3826 .. container:: verbose
3826 .. container:: verbose
3827
3827
3828 Some examples:
3828 Some examples:
3829
3829
3830 - changesets with full descriptions and file lists::
3830 - changesets with full descriptions and file lists::
3831
3831
3832 hg log -v
3832 hg log -v
3833
3833
3834 - changesets ancestral to the working directory::
3834 - changesets ancestral to the working directory::
3835
3835
3836 hg log -f
3836 hg log -f
3837
3837
3838 - last 10 commits on the current branch::
3838 - last 10 commits on the current branch::
3839
3839
3840 hg log -l 10 -b .
3840 hg log -l 10 -b .
3841
3841
3842 - changesets showing all modifications of a file, including removals::
3842 - changesets showing all modifications of a file, including removals::
3843
3843
3844 hg log --removed file.c
3844 hg log --removed file.c
3845
3845
3846 - all changesets that touch a directory, with diffs, excluding merges::
3846 - all changesets that touch a directory, with diffs, excluding merges::
3847
3847
3848 hg log -Mp lib/
3848 hg log -Mp lib/
3849
3849
3850 - all revision numbers that match a keyword::
3850 - all revision numbers that match a keyword::
3851
3851
3852 hg log -k bug --template "{rev}\\n"
3852 hg log -k bug --template "{rev}\\n"
3853
3853
3854 - check if a given changeset is included is a tagged release::
3854 - check if a given changeset is included is a tagged release::
3855
3855
3856 hg log -r "a21ccf and ancestor(1.9)"
3856 hg log -r "a21ccf and ancestor(1.9)"
3857
3857
3858 - find all changesets by some user in a date range::
3858 - find all changesets by some user in a date range::
3859
3859
3860 hg log -k alice -d "may 2008 to jul 2008"
3860 hg log -k alice -d "may 2008 to jul 2008"
3861
3861
3862 - summary of all changesets after the last tag::
3862 - summary of all changesets after the last tag::
3863
3863
3864 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3864 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3865
3865
3866 See :hg:`help dates` for a list of formats valid for -d/--date.
3866 See :hg:`help dates` for a list of formats valid for -d/--date.
3867
3867
3868 See :hg:`help revisions` and :hg:`help revsets` for more about
3868 See :hg:`help revisions` and :hg:`help revsets` for more about
3869 specifying revisions.
3869 specifying revisions.
3870
3870
3871 Returns 0 on success.
3871 Returns 0 on success.
3872 """
3872 """
3873
3873
3874 matchfn = scmutil.match(repo[None], pats, opts)
3874 matchfn = scmutil.match(repo[None], pats, opts)
3875 limit = cmdutil.loglimit(opts)
3875 limit = cmdutil.loglimit(opts)
3876 count = 0
3876 count = 0
3877
3877
3878 endrev = None
3878 endrev = None
3879 if opts.get('copies') and opts.get('rev'):
3879 if opts.get('copies') and opts.get('rev'):
3880 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3880 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3881
3881
3882 df = False
3882 df = False
3883 if opts["date"]:
3883 if opts["date"]:
3884 df = util.matchdate(opts["date"])
3884 df = util.matchdate(opts["date"])
3885
3885
3886 branches = opts.get('branch', []) + opts.get('only_branch', [])
3886 branches = opts.get('branch', []) + opts.get('only_branch', [])
3887 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3887 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3888
3888
3889 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3889 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3890 def prep(ctx, fns):
3890 def prep(ctx, fns):
3891 rev = ctx.rev()
3891 rev = ctx.rev()
3892 parents = [p for p in repo.changelog.parentrevs(rev)
3892 parents = [p for p in repo.changelog.parentrevs(rev)
3893 if p != nullrev]
3893 if p != nullrev]
3894 if opts.get('no_merges') and len(parents) == 2:
3894 if opts.get('no_merges') and len(parents) == 2:
3895 return
3895 return
3896 if opts.get('only_merges') and len(parents) != 2:
3896 if opts.get('only_merges') and len(parents) != 2:
3897 return
3897 return
3898 if opts.get('branch') and ctx.branch() not in opts['branch']:
3898 if opts.get('branch') and ctx.branch() not in opts['branch']:
3899 return
3899 return
3900 if not opts.get('hidden') and ctx.hidden():
3900 if not opts.get('hidden') and ctx.hidden():
3901 return
3901 return
3902 if df and not df(ctx.date()[0]):
3902 if df and not df(ctx.date()[0]):
3903 return
3903 return
3904
3904
3905 lower = encoding.lower
3905 lower = encoding.lower
3906 if opts.get('user'):
3906 if opts.get('user'):
3907 luser = lower(ctx.user())
3907 luser = lower(ctx.user())
3908 for k in [lower(x) for x in opts['user']]:
3908 for k in [lower(x) for x in opts['user']]:
3909 if (k in luser):
3909 if (k in luser):
3910 break
3910 break
3911 else:
3911 else:
3912 return
3912 return
3913 if opts.get('keyword'):
3913 if opts.get('keyword'):
3914 luser = lower(ctx.user())
3914 luser = lower(ctx.user())
3915 ldesc = lower(ctx.description())
3915 ldesc = lower(ctx.description())
3916 lfiles = lower(" ".join(ctx.files()))
3916 lfiles = lower(" ".join(ctx.files()))
3917 for k in [lower(x) for x in opts['keyword']]:
3917 for k in [lower(x) for x in opts['keyword']]:
3918 if (k in luser or k in ldesc or k in lfiles):
3918 if (k in luser or k in ldesc or k in lfiles):
3919 break
3919 break
3920 else:
3920 else:
3921 return
3921 return
3922
3922
3923 copies = None
3923 copies = None
3924 if opts.get('copies') and rev:
3924 if opts.get('copies') and rev:
3925 copies = []
3925 copies = []
3926 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3926 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3927 for fn in ctx.files():
3927 for fn in ctx.files():
3928 rename = getrenamed(fn, rev)
3928 rename = getrenamed(fn, rev)
3929 if rename:
3929 if rename:
3930 copies.append((fn, rename[0]))
3930 copies.append((fn, rename[0]))
3931
3931
3932 revmatchfn = None
3932 revmatchfn = None
3933 if opts.get('patch') or opts.get('stat'):
3933 if opts.get('patch') or opts.get('stat'):
3934 if opts.get('follow') or opts.get('follow_first'):
3934 if opts.get('follow') or opts.get('follow_first'):
3935 # note: this might be wrong when following through merges
3935 # note: this might be wrong when following through merges
3936 revmatchfn = scmutil.match(repo[None], fns, default='path')
3936 revmatchfn = scmutil.match(repo[None], fns, default='path')
3937 else:
3937 else:
3938 revmatchfn = matchfn
3938 revmatchfn = matchfn
3939
3939
3940 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3940 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3941
3941
3942 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3942 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3943 if count == limit:
3943 if count == limit:
3944 break
3944 break
3945 if displayer.flush(ctx.rev()):
3945 if displayer.flush(ctx.rev()):
3946 count += 1
3946 count += 1
3947 displayer.close()
3947 displayer.close()
3948
3948
3949 @command('manifest',
3949 @command('manifest',
3950 [('r', 'rev', '', _('revision to display'), _('REV')),
3950 [('r', 'rev', '', _('revision to display'), _('REV')),
3951 ('', 'all', False, _("list files from all revisions"))],
3951 ('', 'all', False, _("list files from all revisions"))],
3952 _('[-r REV]'))
3952 _('[-r REV]'))
3953 def manifest(ui, repo, node=None, rev=None, **opts):
3953 def manifest(ui, repo, node=None, rev=None, **opts):
3954 """output the current or given revision of the project manifest
3954 """output the current or given revision of the project manifest
3955
3955
3956 Print a list of version controlled files for the given revision.
3956 Print a list of version controlled files for the given revision.
3957 If no revision is given, the first parent of the working directory
3957 If no revision is given, the first parent of the working directory
3958 is used, or the null revision if no revision is checked out.
3958 is used, or the null revision if no revision is checked out.
3959
3959
3960 With -v, print file permissions, symlink and executable bits.
3960 With -v, print file permissions, symlink and executable bits.
3961 With --debug, print file revision hashes.
3961 With --debug, print file revision hashes.
3962
3962
3963 If option --all is specified, the list of all files from all revisions
3963 If option --all is specified, the list of all files from all revisions
3964 is printed. This includes deleted and renamed files.
3964 is printed. This includes deleted and renamed files.
3965
3965
3966 Returns 0 on success.
3966 Returns 0 on success.
3967 """
3967 """
3968 if opts.get('all'):
3968 if opts.get('all'):
3969 if rev or node:
3969 if rev or node:
3970 raise util.Abort(_("can't specify a revision with --all"))
3970 raise util.Abort(_("can't specify a revision with --all"))
3971
3971
3972 res = []
3972 res = []
3973 prefix = "data/"
3973 prefix = "data/"
3974 suffix = ".i"
3974 suffix = ".i"
3975 plen = len(prefix)
3975 plen = len(prefix)
3976 slen = len(suffix)
3976 slen = len(suffix)
3977 lock = repo.lock()
3977 lock = repo.lock()
3978 try:
3978 try:
3979 for fn, b, size in repo.store.datafiles():
3979 for fn, b, size in repo.store.datafiles():
3980 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3980 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3981 res.append(fn[plen:-slen])
3981 res.append(fn[plen:-slen])
3982 finally:
3982 finally:
3983 lock.release()
3983 lock.release()
3984 for f in sorted(res):
3984 for f in sorted(res):
3985 ui.write("%s\n" % f)
3985 ui.write("%s\n" % f)
3986 return
3986 return
3987
3987
3988 if rev and node:
3988 if rev and node:
3989 raise util.Abort(_("please specify just one revision"))
3989 raise util.Abort(_("please specify just one revision"))
3990
3990
3991 if not node:
3991 if not node:
3992 node = rev
3992 node = rev
3993
3993
3994 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3994 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3995 ctx = scmutil.revsingle(repo, node)
3995 ctx = scmutil.revsingle(repo, node)
3996 for f in ctx:
3996 for f in ctx:
3997 if ui.debugflag:
3997 if ui.debugflag:
3998 ui.write("%40s " % hex(ctx.manifest()[f]))
3998 ui.write("%40s " % hex(ctx.manifest()[f]))
3999 if ui.verbose:
3999 if ui.verbose:
4000 ui.write(decor[ctx.flags(f)])
4000 ui.write(decor[ctx.flags(f)])
4001 ui.write("%s\n" % f)
4001 ui.write("%s\n" % f)
4002
4002
4003 @command('^merge',
4003 @command('^merge',
4004 [('f', 'force', None, _('force a merge with outstanding changes')),
4004 [('f', 'force', None, _('force a merge with outstanding changes')),
4005 ('r', 'rev', '', _('revision to merge'), _('REV')),
4005 ('r', 'rev', '', _('revision to merge'), _('REV')),
4006 ('P', 'preview', None,
4006 ('P', 'preview', None,
4007 _('review revisions to merge (no merge is performed)'))
4007 _('review revisions to merge (no merge is performed)'))
4008 ] + mergetoolopts,
4008 ] + mergetoolopts,
4009 _('[-P] [-f] [[-r] REV]'))
4009 _('[-P] [-f] [[-r] REV]'))
4010 def merge(ui, repo, node=None, **opts):
4010 def merge(ui, repo, node=None, **opts):
4011 """merge working directory with another revision
4011 """merge working directory with another revision
4012
4012
4013 The current working directory is updated with all changes made in
4013 The current working directory is updated with all changes made in
4014 the requested revision since the last common predecessor revision.
4014 the requested revision since the last common predecessor revision.
4015
4015
4016 Files that changed between either parent are marked as changed for
4016 Files that changed between either parent are marked as changed for
4017 the next commit and a commit must be performed before any further
4017 the next commit and a commit must be performed before any further
4018 updates to the repository are allowed. The next commit will have
4018 updates to the repository are allowed. The next commit will have
4019 two parents.
4019 two parents.
4020
4020
4021 ``--tool`` can be used to specify the merge tool used for file
4021 ``--tool`` can be used to specify the merge tool used for file
4022 merges. It overrides the HGMERGE environment variable and your
4022 merges. It overrides the HGMERGE environment variable and your
4023 configuration files. See :hg:`help merge-tools` for options.
4023 configuration files. See :hg:`help merge-tools` for options.
4024
4024
4025 If no revision is specified, the working directory's parent is a
4025 If no revision is specified, the working directory's parent is a
4026 head revision, and the current branch contains exactly one other
4026 head revision, and the current branch contains exactly one other
4027 head, the other head is merged with by default. Otherwise, an
4027 head, the other head is merged with by default. Otherwise, an
4028 explicit revision with which to merge with must be provided.
4028 explicit revision with which to merge with must be provided.
4029
4029
4030 :hg:`resolve` must be used to resolve unresolved files.
4030 :hg:`resolve` must be used to resolve unresolved files.
4031
4031
4032 To undo an uncommitted merge, use :hg:`update --clean .` which
4032 To undo an uncommitted merge, use :hg:`update --clean .` which
4033 will check out a clean copy of the original merge parent, losing
4033 will check out a clean copy of the original merge parent, losing
4034 all changes.
4034 all changes.
4035
4035
4036 Returns 0 on success, 1 if there are unresolved files.
4036 Returns 0 on success, 1 if there are unresolved files.
4037 """
4037 """
4038
4038
4039 if opts.get('rev') and node:
4039 if opts.get('rev') and node:
4040 raise util.Abort(_("please specify just one revision"))
4040 raise util.Abort(_("please specify just one revision"))
4041 if not node:
4041 if not node:
4042 node = opts.get('rev')
4042 node = opts.get('rev')
4043
4043
4044 if not node:
4044 if not node:
4045 branch = repo[None].branch()
4045 branch = repo[None].branch()
4046 bheads = repo.branchheads(branch)
4046 bheads = repo.branchheads(branch)
4047 if len(bheads) > 2:
4047 if len(bheads) > 2:
4048 raise util.Abort(_("branch '%s' has %d heads - "
4048 raise util.Abort(_("branch '%s' has %d heads - "
4049 "please merge with an explicit rev")
4049 "please merge with an explicit rev")
4050 % (branch, len(bheads)),
4050 % (branch, len(bheads)),
4051 hint=_("run 'hg heads .' to see heads"))
4051 hint=_("run 'hg heads .' to see heads"))
4052
4052
4053 parent = repo.dirstate.p1()
4053 parent = repo.dirstate.p1()
4054 if len(bheads) == 1:
4054 if len(bheads) == 1:
4055 if len(repo.heads()) > 1:
4055 if len(repo.heads()) > 1:
4056 raise util.Abort(_("branch '%s' has one head - "
4056 raise util.Abort(_("branch '%s' has one head - "
4057 "please merge with an explicit rev")
4057 "please merge with an explicit rev")
4058 % branch,
4058 % branch,
4059 hint=_("run 'hg heads' to see all heads"))
4059 hint=_("run 'hg heads' to see all heads"))
4060 msg, hint = _('nothing to merge'), None
4060 msg, hint = _('nothing to merge'), None
4061 if parent != repo.lookup(branch):
4061 if parent != repo.lookup(branch):
4062 hint = _("use 'hg update' instead")
4062 hint = _("use 'hg update' instead")
4063 raise util.Abort(msg, hint=hint)
4063 raise util.Abort(msg, hint=hint)
4064
4064
4065 if parent not in bheads:
4065 if parent not in bheads:
4066 raise util.Abort(_('working directory not at a head revision'),
4066 raise util.Abort(_('working directory not at a head revision'),
4067 hint=_("use 'hg update' or merge with an "
4067 hint=_("use 'hg update' or merge with an "
4068 "explicit revision"))
4068 "explicit revision"))
4069 node = parent == bheads[0] and bheads[-1] or bheads[0]
4069 node = parent == bheads[0] and bheads[-1] or bheads[0]
4070 else:
4070 else:
4071 node = scmutil.revsingle(repo, node).node()
4071 node = scmutil.revsingle(repo, node).node()
4072
4072
4073 if opts.get('preview'):
4073 if opts.get('preview'):
4074 # find nodes that are ancestors of p2 but not of p1
4074 # find nodes that are ancestors of p2 but not of p1
4075 p1 = repo.lookup('.')
4075 p1 = repo.lookup('.')
4076 p2 = repo.lookup(node)
4076 p2 = repo.lookup(node)
4077 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4077 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4078
4078
4079 displayer = cmdutil.show_changeset(ui, repo, opts)
4079 displayer = cmdutil.show_changeset(ui, repo, opts)
4080 for node in nodes:
4080 for node in nodes:
4081 displayer.show(repo[node])
4081 displayer.show(repo[node])
4082 displayer.close()
4082 displayer.close()
4083 return 0
4083 return 0
4084
4084
4085 try:
4085 try:
4086 # ui.forcemerge is an internal variable, do not document
4086 # ui.forcemerge is an internal variable, do not document
4087 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4087 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4088 return hg.merge(repo, node, force=opts.get('force'))
4088 return hg.merge(repo, node, force=opts.get('force'))
4089 finally:
4089 finally:
4090 ui.setconfig('ui', 'forcemerge', '')
4090 ui.setconfig('ui', 'forcemerge', '')
4091
4091
4092 @command('outgoing|out',
4092 @command('outgoing|out',
4093 [('f', 'force', None, _('run even when the destination is unrelated')),
4093 [('f', 'force', None, _('run even when the destination is unrelated')),
4094 ('r', 'rev', [],
4094 ('r', 'rev', [],
4095 _('a changeset intended to be included in the destination'), _('REV')),
4095 _('a changeset intended to be included in the destination'), _('REV')),
4096 ('n', 'newest-first', None, _('show newest record first')),
4096 ('n', 'newest-first', None, _('show newest record first')),
4097 ('B', 'bookmarks', False, _('compare bookmarks')),
4097 ('B', 'bookmarks', False, _('compare bookmarks')),
4098 ('b', 'branch', [], _('a specific branch you would like to push'),
4098 ('b', 'branch', [], _('a specific branch you would like to push'),
4099 _('BRANCH')),
4099 _('BRANCH')),
4100 ] + logopts + remoteopts + subrepoopts,
4100 ] + logopts + remoteopts + subrepoopts,
4101 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4101 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4102 def outgoing(ui, repo, dest=None, **opts):
4102 def outgoing(ui, repo, dest=None, **opts):
4103 """show changesets not found in the destination
4103 """show changesets not found in the destination
4104
4104
4105 Show changesets not found in the specified destination repository
4105 Show changesets not found in the specified destination repository
4106 or the default push location. These are the changesets that would
4106 or the default push location. These are the changesets that would
4107 be pushed if a push was requested.
4107 be pushed if a push was requested.
4108
4108
4109 See pull for details of valid destination formats.
4109 See pull for details of valid destination formats.
4110
4110
4111 Returns 0 if there are outgoing changes, 1 otherwise.
4111 Returns 0 if there are outgoing changes, 1 otherwise.
4112 """
4112 """
4113
4113
4114 if opts.get('bookmarks'):
4114 if opts.get('bookmarks'):
4115 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4115 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4116 dest, branches = hg.parseurl(dest, opts.get('branch'))
4116 dest, branches = hg.parseurl(dest, opts.get('branch'))
4117 other = hg.peer(repo, opts, dest)
4117 other = hg.peer(repo, opts, dest)
4118 if 'bookmarks' not in other.listkeys('namespaces'):
4118 if 'bookmarks' not in other.listkeys('namespaces'):
4119 ui.warn(_("remote doesn't support bookmarks\n"))
4119 ui.warn(_("remote doesn't support bookmarks\n"))
4120 return 0
4120 return 0
4121 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4121 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4122 return bookmarks.diff(ui, other, repo)
4122 return bookmarks.diff(ui, other, repo)
4123
4123
4124 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4124 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4125 try:
4125 try:
4126 return hg.outgoing(ui, repo, dest, opts)
4126 return hg.outgoing(ui, repo, dest, opts)
4127 finally:
4127 finally:
4128 del repo._subtoppath
4128 del repo._subtoppath
4129
4129
4130 @command('parents',
4130 @command('parents',
4131 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4131 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4132 ] + templateopts,
4132 ] + templateopts,
4133 _('[-r REV] [FILE]'))
4133 _('[-r REV] [FILE]'))
4134 def parents(ui, repo, file_=None, **opts):
4134 def parents(ui, repo, file_=None, **opts):
4135 """show the parents of the working directory or revision
4135 """show the parents of the working directory or revision
4136
4136
4137 Print the working directory's parent revisions. If a revision is
4137 Print the working directory's parent revisions. If a revision is
4138 given via -r/--rev, the parent of that revision will be printed.
4138 given via -r/--rev, the parent of that revision will be printed.
4139 If a file argument is given, the revision in which the file was
4139 If a file argument is given, the revision in which the file was
4140 last changed (before the working directory revision or the
4140 last changed (before the working directory revision or the
4141 argument to --rev if given) is printed.
4141 argument to --rev if given) is printed.
4142
4142
4143 Returns 0 on success.
4143 Returns 0 on success.
4144 """
4144 """
4145
4145
4146 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4146 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4147
4147
4148 if file_:
4148 if file_:
4149 m = scmutil.match(ctx, (file_,), opts)
4149 m = scmutil.match(ctx, (file_,), opts)
4150 if m.anypats() or len(m.files()) != 1:
4150 if m.anypats() or len(m.files()) != 1:
4151 raise util.Abort(_('can only specify an explicit filename'))
4151 raise util.Abort(_('can only specify an explicit filename'))
4152 file_ = m.files()[0]
4152 file_ = m.files()[0]
4153 filenodes = []
4153 filenodes = []
4154 for cp in ctx.parents():
4154 for cp in ctx.parents():
4155 if not cp:
4155 if not cp:
4156 continue
4156 continue
4157 try:
4157 try:
4158 filenodes.append(cp.filenode(file_))
4158 filenodes.append(cp.filenode(file_))
4159 except error.LookupError:
4159 except error.LookupError:
4160 pass
4160 pass
4161 if not filenodes:
4161 if not filenodes:
4162 raise util.Abort(_("'%s' not found in manifest!") % file_)
4162 raise util.Abort(_("'%s' not found in manifest!") % file_)
4163 fl = repo.file(file_)
4163 fl = repo.file(file_)
4164 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4164 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4165 else:
4165 else:
4166 p = [cp.node() for cp in ctx.parents()]
4166 p = [cp.node() for cp in ctx.parents()]
4167
4167
4168 displayer = cmdutil.show_changeset(ui, repo, opts)
4168 displayer = cmdutil.show_changeset(ui, repo, opts)
4169 for n in p:
4169 for n in p:
4170 if n != nullid:
4170 if n != nullid:
4171 displayer.show(repo[n])
4171 displayer.show(repo[n])
4172 displayer.close()
4172 displayer.close()
4173
4173
4174 @command('paths', [], _('[NAME]'))
4174 @command('paths', [], _('[NAME]'))
4175 def paths(ui, repo, search=None):
4175 def paths(ui, repo, search=None):
4176 """show aliases for remote repositories
4176 """show aliases for remote repositories
4177
4177
4178 Show definition of symbolic path name NAME. If no name is given,
4178 Show definition of symbolic path name NAME. If no name is given,
4179 show definition of all available names.
4179 show definition of all available names.
4180
4180
4181 Option -q/--quiet suppresses all output when searching for NAME
4181 Option -q/--quiet suppresses all output when searching for NAME
4182 and shows only the path names when listing all definitions.
4182 and shows only the path names when listing all definitions.
4183
4183
4184 Path names are defined in the [paths] section of your
4184 Path names are defined in the [paths] section of your
4185 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4185 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4186 repository, ``.hg/hgrc`` is used, too.
4186 repository, ``.hg/hgrc`` is used, too.
4187
4187
4188 The path names ``default`` and ``default-push`` have a special
4188 The path names ``default`` and ``default-push`` have a special
4189 meaning. When performing a push or pull operation, they are used
4189 meaning. When performing a push or pull operation, they are used
4190 as fallbacks if no location is specified on the command-line.
4190 as fallbacks if no location is specified on the command-line.
4191 When ``default-push`` is set, it will be used for push and
4191 When ``default-push`` is set, it will be used for push and
4192 ``default`` will be used for pull; otherwise ``default`` is used
4192 ``default`` will be used for pull; otherwise ``default`` is used
4193 as the fallback for both. When cloning a repository, the clone
4193 as the fallback for both. When cloning a repository, the clone
4194 source is written as ``default`` in ``.hg/hgrc``. Note that
4194 source is written as ``default`` in ``.hg/hgrc``. Note that
4195 ``default`` and ``default-push`` apply to all inbound (e.g.
4195 ``default`` and ``default-push`` apply to all inbound (e.g.
4196 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4196 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4197 :hg:`bundle`) operations.
4197 :hg:`bundle`) operations.
4198
4198
4199 See :hg:`help urls` for more information.
4199 See :hg:`help urls` for more information.
4200
4200
4201 Returns 0 on success.
4201 Returns 0 on success.
4202 """
4202 """
4203 if search:
4203 if search:
4204 for name, path in ui.configitems("paths"):
4204 for name, path in ui.configitems("paths"):
4205 if name == search:
4205 if name == search:
4206 ui.status("%s\n" % util.hidepassword(path))
4206 ui.status("%s\n" % util.hidepassword(path))
4207 return
4207 return
4208 if not ui.quiet:
4208 if not ui.quiet:
4209 ui.warn(_("not found!\n"))
4209 ui.warn(_("not found!\n"))
4210 return 1
4210 return 1
4211 else:
4211 else:
4212 for name, path in ui.configitems("paths"):
4212 for name, path in ui.configitems("paths"):
4213 if ui.quiet:
4213 if ui.quiet:
4214 ui.write("%s\n" % name)
4214 ui.write("%s\n" % name)
4215 else:
4215 else:
4216 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4216 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4217
4217
4218 @command('^phase',
4218 @command('^phase',
4219 [('p', 'public', False, _('Set changeset to public')),
4219 [('p', 'public', False, _('Set changeset to public')),
4220 ('d', 'draft', False, _('Set changeset to draft')),
4220 ('d', 'draft', False, _('Set changeset to draft')),
4221 ('s', 'secret', False, _('Set changeset to secret')),
4221 ('s', 'secret', False, _('Set changeset to secret')),
4222 ('f', 'force', False, _('allow to move boundary backward')),
4222 ('f', 'force', False, _('allow to move boundary backward')),
4223 ('r', 'rev', [], _('target revision')),
4223 ('r', 'rev', [], _('target revision')),
4224 ],
4224 ],
4225 _('[-p|-d|-s] [-f] [-C] [-r] REV'))
4225 _('[-p|-d|-s] [-f] [-C] [-r] REV'))
4226 def phase(ui, repo, *revs, **opts):
4226 def phase(ui, repo, *revs, **opts):
4227 """set or show the current phase name
4227 """set or show the current phase name
4228
4228
4229 With no argument, show the phase name of specified revisions.
4229 With no argument, show the phase name of specified revisions.
4230
4230
4231 With one of `--public`, `--draft` or `--secret`, change the phase
4231 With one of `--public`, `--draft` or `--secret`, change the phase
4232 value of the specified revisions.
4232 value of the specified revisions.
4233
4233
4234 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4234 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4235 lower phase to an higher phase. Phases are ordered as follows:
4235 lower phase to an higher phase. Phases are ordered as follows:
4236
4236
4237 public < draft < secret
4237 public < draft < secret
4238 """
4238 """
4239 # search for a unique phase argument
4239 # search for a unique phase argument
4240 targetphase = None
4240 targetphase = None
4241 for idx, name in enumerate(phasesmod.phasenames):
4241 for idx, name in enumerate(phasesmod.phasenames):
4242 if opts[name]:
4242 if opts[name]:
4243 if targetphase is not None:
4243 if targetphase is not None:
4244 raise util.Abort(_('only one phase can be specified'))
4244 raise util.Abort(_('only one phase can be specified'))
4245 targetphase = idx
4245 targetphase = idx
4246
4246
4247 # look for specified revision
4247 # look for specified revision
4248 revs = list(revs)
4248 revs = list(revs)
4249 revs.extend(opts['rev'])
4249 revs.extend(opts['rev'])
4250 if not revs:
4250 if not revs:
4251 raise util.Abort(_('no revisions specified!'))
4251 raise util.Abort(_('no revisions specified!'))
4252
4252
4253 lock = None
4253 lock = None
4254 if targetphase is None:
4254 if targetphase is None:
4255 # display
4255 # display
4256 for ctx in repo.set('%lr', revs):
4256 for ctx in repo.set('%lr', revs):
4257 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4257 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4258 else:
4258 else:
4259 lock = repo.lock()
4259 lock = repo.lock()
4260 try:
4260 try:
4261 # set phase
4261 # set phase
4262 nodes = [ctx.node() for ctx in repo.set('%lr', revs)]
4262 nodes = [ctx.node() for ctx in repo.set('%lr', revs)]
4263 if not nodes:
4263 if not nodes:
4264 raise util.Abort(_('empty revision set'))
4264 raise util.Abort(_('empty revision set'))
4265 phasesmod.advanceboundary(repo, targetphase, nodes)
4265 phasesmod.advanceboundary(repo, targetphase, nodes)
4266 if opts['force']:
4266 if opts['force']:
4267 phasesmod.retractboundary(repo, targetphase, nodes)
4267 phasesmod.retractboundary(repo, targetphase, nodes)
4268 finally:
4268 finally:
4269 lock.release()
4269 lock.release()
4270
4270
4271 def postincoming(ui, repo, modheads, optupdate, checkout):
4271 def postincoming(ui, repo, modheads, optupdate, checkout):
4272 if modheads == 0:
4272 if modheads == 0:
4273 return
4273 return
4274 if optupdate:
4274 if optupdate:
4275 try:
4275 try:
4276 return hg.update(repo, checkout)
4276 return hg.update(repo, checkout)
4277 except util.Abort, inst:
4277 except util.Abort, inst:
4278 ui.warn(_("not updating: %s\n" % str(inst)))
4278 ui.warn(_("not updating: %s\n" % str(inst)))
4279 return 0
4279 return 0
4280 if modheads > 1:
4280 if modheads > 1:
4281 currentbranchheads = len(repo.branchheads())
4281 currentbranchheads = len(repo.branchheads())
4282 if currentbranchheads == modheads:
4282 if currentbranchheads == modheads:
4283 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4283 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4284 elif currentbranchheads > 1:
4284 elif currentbranchheads > 1:
4285 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4285 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4286 else:
4286 else:
4287 ui.status(_("(run 'hg heads' to see heads)\n"))
4287 ui.status(_("(run 'hg heads' to see heads)\n"))
4288 else:
4288 else:
4289 ui.status(_("(run 'hg update' to get a working copy)\n"))
4289 ui.status(_("(run 'hg update' to get a working copy)\n"))
4290
4290
4291 @command('^pull',
4291 @command('^pull',
4292 [('u', 'update', None,
4292 [('u', 'update', None,
4293 _('update to new branch head if changesets were pulled')),
4293 _('update to new branch head if changesets were pulled')),
4294 ('f', 'force', None, _('run even when remote repository is unrelated')),
4294 ('f', 'force', None, _('run even when remote repository is unrelated')),
4295 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4295 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4296 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4296 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4297 ('b', 'branch', [], _('a specific branch you would like to pull'),
4297 ('b', 'branch', [], _('a specific branch you would like to pull'),
4298 _('BRANCH')),
4298 _('BRANCH')),
4299 ] + remoteopts,
4299 ] + remoteopts,
4300 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4300 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4301 def pull(ui, repo, source="default", **opts):
4301 def pull(ui, repo, source="default", **opts):
4302 """pull changes from the specified source
4302 """pull changes from the specified source
4303
4303
4304 Pull changes from a remote repository to a local one.
4304 Pull changes from a remote repository to a local one.
4305
4305
4306 This finds all changes from the repository at the specified path
4306 This finds all changes from the repository at the specified path
4307 or URL and adds them to a local repository (the current one unless
4307 or URL and adds them to a local repository (the current one unless
4308 -R is specified). By default, this does not update the copy of the
4308 -R is specified). By default, this does not update the copy of the
4309 project in the working directory.
4309 project in the working directory.
4310
4310
4311 Use :hg:`incoming` if you want to see what would have been added
4311 Use :hg:`incoming` if you want to see what would have been added
4312 by a pull at the time you issued this command. If you then decide
4312 by a pull at the time you issued this command. If you then decide
4313 to add those changes to the repository, you should use :hg:`pull
4313 to add those changes to the repository, you should use :hg:`pull
4314 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4314 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4315
4315
4316 If SOURCE is omitted, the 'default' path will be used.
4316 If SOURCE is omitted, the 'default' path will be used.
4317 See :hg:`help urls` for more information.
4317 See :hg:`help urls` for more information.
4318
4318
4319 Returns 0 on success, 1 if an update had unresolved files.
4319 Returns 0 on success, 1 if an update had unresolved files.
4320 """
4320 """
4321 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4321 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4322 other = hg.peer(repo, opts, source)
4322 other = hg.peer(repo, opts, source)
4323 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4323 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4324 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4324 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4325
4325
4326 if opts.get('bookmark'):
4326 if opts.get('bookmark'):
4327 if not revs:
4327 if not revs:
4328 revs = []
4328 revs = []
4329 rb = other.listkeys('bookmarks')
4329 rb = other.listkeys('bookmarks')
4330 for b in opts['bookmark']:
4330 for b in opts['bookmark']:
4331 if b not in rb:
4331 if b not in rb:
4332 raise util.Abort(_('remote bookmark %s not found!') % b)
4332 raise util.Abort(_('remote bookmark %s not found!') % b)
4333 revs.append(rb[b])
4333 revs.append(rb[b])
4334
4334
4335 if revs:
4335 if revs:
4336 try:
4336 try:
4337 revs = [other.lookup(rev) for rev in revs]
4337 revs = [other.lookup(rev) for rev in revs]
4338 except error.CapabilityError:
4338 except error.CapabilityError:
4339 err = _("other repository doesn't support revision lookup, "
4339 err = _("other repository doesn't support revision lookup, "
4340 "so a rev cannot be specified.")
4340 "so a rev cannot be specified.")
4341 raise util.Abort(err)
4341 raise util.Abort(err)
4342
4342
4343 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4343 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4344 bookmarks.updatefromremote(ui, repo, other, source)
4344 bookmarks.updatefromremote(ui, repo, other, source)
4345 if checkout:
4345 if checkout:
4346 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4346 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4347 repo._subtoppath = source
4347 repo._subtoppath = source
4348 try:
4348 try:
4349 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4349 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4350
4350
4351 finally:
4351 finally:
4352 del repo._subtoppath
4352 del repo._subtoppath
4353
4353
4354 # update specified bookmarks
4354 # update specified bookmarks
4355 if opts.get('bookmark'):
4355 if opts.get('bookmark'):
4356 for b in opts['bookmark']:
4356 for b in opts['bookmark']:
4357 # explicit pull overrides local bookmark if any
4357 # explicit pull overrides local bookmark if any
4358 ui.status(_("importing bookmark %s\n") % b)
4358 ui.status(_("importing bookmark %s\n") % b)
4359 repo._bookmarks[b] = repo[rb[b]].node()
4359 repo._bookmarks[b] = repo[rb[b]].node()
4360 bookmarks.write(repo)
4360 bookmarks.write(repo)
4361
4361
4362 return ret
4362 return ret
4363
4363
4364 @command('^push',
4364 @command('^push',
4365 [('f', 'force', None, _('force push')),
4365 [('f', 'force', None, _('force push')),
4366 ('r', 'rev', [],
4366 ('r', 'rev', [],
4367 _('a changeset intended to be included in the destination'),
4367 _('a changeset intended to be included in the destination'),
4368 _('REV')),
4368 _('REV')),
4369 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4369 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4370 ('b', 'branch', [],
4370 ('b', 'branch', [],
4371 _('a specific branch you would like to push'), _('BRANCH')),
4371 _('a specific branch you would like to push'), _('BRANCH')),
4372 ('', 'new-branch', False, _('allow pushing a new branch')),
4372 ('', 'new-branch', False, _('allow pushing a new branch')),
4373 ] + remoteopts,
4373 ] + remoteopts,
4374 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4374 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4375 def push(ui, repo, dest=None, **opts):
4375 def push(ui, repo, dest=None, **opts):
4376 """push changes to the specified destination
4376 """push changes to the specified destination
4377
4377
4378 Push changesets from the local repository to the specified
4378 Push changesets from the local repository to the specified
4379 destination.
4379 destination.
4380
4380
4381 This operation is symmetrical to pull: it is identical to a pull
4381 This operation is symmetrical to pull: it is identical to a pull
4382 in the destination repository from the current one.
4382 in the destination repository from the current one.
4383
4383
4384 By default, push will not allow creation of new heads at the
4384 By default, push will not allow creation of new heads at the
4385 destination, since multiple heads would make it unclear which head
4385 destination, since multiple heads would make it unclear which head
4386 to use. In this situation, it is recommended to pull and merge
4386 to use. In this situation, it is recommended to pull and merge
4387 before pushing.
4387 before pushing.
4388
4388
4389 Use --new-branch if you want to allow push to create a new named
4389 Use --new-branch if you want to allow push to create a new named
4390 branch that is not present at the destination. This allows you to
4390 branch that is not present at the destination. This allows you to
4391 only create a new branch without forcing other changes.
4391 only create a new branch without forcing other changes.
4392
4392
4393 Use -f/--force to override the default behavior and push all
4393 Use -f/--force to override the default behavior and push all
4394 changesets on all branches.
4394 changesets on all branches.
4395
4395
4396 If -r/--rev is used, the specified revision and all its ancestors
4396 If -r/--rev is used, the specified revision and all its ancestors
4397 will be pushed to the remote repository.
4397 will be pushed to the remote repository.
4398
4398
4399 Please see :hg:`help urls` for important details about ``ssh://``
4399 Please see :hg:`help urls` for important details about ``ssh://``
4400 URLs. If DESTINATION is omitted, a default path will be used.
4400 URLs. If DESTINATION is omitted, a default path will be used.
4401
4401
4402 Returns 0 if push was successful, 1 if nothing to push.
4402 Returns 0 if push was successful, 1 if nothing to push.
4403 """
4403 """
4404
4404
4405 if opts.get('bookmark'):
4405 if opts.get('bookmark'):
4406 for b in opts['bookmark']:
4406 for b in opts['bookmark']:
4407 # translate -B options to -r so changesets get pushed
4407 # translate -B options to -r so changesets get pushed
4408 if b in repo._bookmarks:
4408 if b in repo._bookmarks:
4409 opts.setdefault('rev', []).append(b)
4409 opts.setdefault('rev', []).append(b)
4410 else:
4410 else:
4411 # if we try to push a deleted bookmark, translate it to null
4411 # if we try to push a deleted bookmark, translate it to null
4412 # this lets simultaneous -r, -b options continue working
4412 # this lets simultaneous -r, -b options continue working
4413 opts.setdefault('rev', []).append("null")
4413 opts.setdefault('rev', []).append("null")
4414
4414
4415 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4415 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4416 dest, branches = hg.parseurl(dest, opts.get('branch'))
4416 dest, branches = hg.parseurl(dest, opts.get('branch'))
4417 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4417 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4418 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4418 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4419 other = hg.peer(repo, opts, dest)
4419 other = hg.peer(repo, opts, dest)
4420 if revs:
4420 if revs:
4421 revs = [repo.lookup(rev) for rev in revs]
4421 revs = [repo.lookup(rev) for rev in revs]
4422
4422
4423 repo._subtoppath = dest
4423 repo._subtoppath = dest
4424 try:
4424 try:
4425 # push subrepos depth-first for coherent ordering
4425 # push subrepos depth-first for coherent ordering
4426 c = repo['']
4426 c = repo['']
4427 subs = c.substate # only repos that are committed
4427 subs = c.substate # only repos that are committed
4428 for s in sorted(subs):
4428 for s in sorted(subs):
4429 if not c.sub(s).push(opts):
4429 if not c.sub(s).push(opts):
4430 return False
4430 return False
4431 finally:
4431 finally:
4432 del repo._subtoppath
4432 del repo._subtoppath
4433 result = repo.push(other, opts.get('force'), revs=revs,
4433 result = repo.push(other, opts.get('force'), revs=revs,
4434 newbranch=opts.get('new_branch'))
4434 newbranch=opts.get('new_branch'))
4435
4435
4436 result = (result == 0)
4436 result = (result == 0)
4437
4437
4438 if opts.get('bookmark'):
4438 if opts.get('bookmark'):
4439 rb = other.listkeys('bookmarks')
4439 rb = other.listkeys('bookmarks')
4440 for b in opts['bookmark']:
4440 for b in opts['bookmark']:
4441 # explicit push overrides remote bookmark if any
4441 # explicit push overrides remote bookmark if any
4442 if b in repo._bookmarks:
4442 if b in repo._bookmarks:
4443 ui.status(_("exporting bookmark %s\n") % b)
4443 ui.status(_("exporting bookmark %s\n") % b)
4444 new = repo[b].hex()
4444 new = repo[b].hex()
4445 elif b in rb:
4445 elif b in rb:
4446 ui.status(_("deleting remote bookmark %s\n") % b)
4446 ui.status(_("deleting remote bookmark %s\n") % b)
4447 new = '' # delete
4447 new = '' # delete
4448 else:
4448 else:
4449 ui.warn(_('bookmark %s does not exist on the local '
4449 ui.warn(_('bookmark %s does not exist on the local '
4450 'or remote repository!\n') % b)
4450 'or remote repository!\n') % b)
4451 return 2
4451 return 2
4452 old = rb.get(b, '')
4452 old = rb.get(b, '')
4453 r = other.pushkey('bookmarks', b, old, new)
4453 r = other.pushkey('bookmarks', b, old, new)
4454 if not r:
4454 if not r:
4455 ui.warn(_('updating bookmark %s failed!\n') % b)
4455 ui.warn(_('updating bookmark %s failed!\n') % b)
4456 if not result:
4456 if not result:
4457 result = 2
4457 result = 2
4458
4458
4459 return result
4459 return result
4460
4460
4461 @command('recover', [])
4461 @command('recover', [])
4462 def recover(ui, repo):
4462 def recover(ui, repo):
4463 """roll back an interrupted transaction
4463 """roll back an interrupted transaction
4464
4464
4465 Recover from an interrupted commit or pull.
4465 Recover from an interrupted commit or pull.
4466
4466
4467 This command tries to fix the repository status after an
4467 This command tries to fix the repository status after an
4468 interrupted operation. It should only be necessary when Mercurial
4468 interrupted operation. It should only be necessary when Mercurial
4469 suggests it.
4469 suggests it.
4470
4470
4471 Returns 0 if successful, 1 if nothing to recover or verify fails.
4471 Returns 0 if successful, 1 if nothing to recover or verify fails.
4472 """
4472 """
4473 if repo.recover():
4473 if repo.recover():
4474 return hg.verify(repo)
4474 return hg.verify(repo)
4475 return 1
4475 return 1
4476
4476
4477 @command('^remove|rm',
4477 @command('^remove|rm',
4478 [('A', 'after', None, _('record delete for missing files')),
4478 [('A', 'after', None, _('record delete for missing files')),
4479 ('f', 'force', None,
4479 ('f', 'force', None,
4480 _('remove (and delete) file even if added or modified')),
4480 _('remove (and delete) file even if added or modified')),
4481 ] + walkopts,
4481 ] + walkopts,
4482 _('[OPTION]... FILE...'))
4482 _('[OPTION]... FILE...'))
4483 def remove(ui, repo, *pats, **opts):
4483 def remove(ui, repo, *pats, **opts):
4484 """remove the specified files on the next commit
4484 """remove the specified files on the next commit
4485
4485
4486 Schedule the indicated files for removal from the current branch.
4486 Schedule the indicated files for removal from the current branch.
4487
4487
4488 This command schedules the files to be removed at the next commit.
4488 This command schedules the files to be removed at the next commit.
4489 To undo a remove before that, see :hg:`revert`. To undo added
4489 To undo a remove before that, see :hg:`revert`. To undo added
4490 files, see :hg:`forget`.
4490 files, see :hg:`forget`.
4491
4491
4492 .. container:: verbose
4492 .. container:: verbose
4493
4493
4494 -A/--after can be used to remove only files that have already
4494 -A/--after can be used to remove only files that have already
4495 been deleted, -f/--force can be used to force deletion, and -Af
4495 been deleted, -f/--force can be used to force deletion, and -Af
4496 can be used to remove files from the next revision without
4496 can be used to remove files from the next revision without
4497 deleting them from the working directory.
4497 deleting them from the working directory.
4498
4498
4499 The following table details the behavior of remove for different
4499 The following table details the behavior of remove for different
4500 file states (columns) and option combinations (rows). The file
4500 file states (columns) and option combinations (rows). The file
4501 states are Added [A], Clean [C], Modified [M] and Missing [!]
4501 states are Added [A], Clean [C], Modified [M] and Missing [!]
4502 (as reported by :hg:`status`). The actions are Warn, Remove
4502 (as reported by :hg:`status`). The actions are Warn, Remove
4503 (from branch) and Delete (from disk):
4503 (from branch) and Delete (from disk):
4504
4504
4505 ======= == == == ==
4505 ======= == == == ==
4506 A C M !
4506 A C M !
4507 ======= == == == ==
4507 ======= == == == ==
4508 none W RD W R
4508 none W RD W R
4509 -f R RD RD R
4509 -f R RD RD R
4510 -A W W W R
4510 -A W W W R
4511 -Af R R R R
4511 -Af R R R R
4512 ======= == == == ==
4512 ======= == == == ==
4513
4513
4514 Note that remove never deletes files in Added [A] state from the
4514 Note that remove never deletes files in Added [A] state from the
4515 working directory, not even if option --force is specified.
4515 working directory, not even if option --force is specified.
4516
4516
4517 Returns 0 on success, 1 if any warnings encountered.
4517 Returns 0 on success, 1 if any warnings encountered.
4518 """
4518 """
4519
4519
4520 ret = 0
4520 ret = 0
4521 after, force = opts.get('after'), opts.get('force')
4521 after, force = opts.get('after'), opts.get('force')
4522 if not pats and not after:
4522 if not pats and not after:
4523 raise util.Abort(_('no files specified'))
4523 raise util.Abort(_('no files specified'))
4524
4524
4525 m = scmutil.match(repo[None], pats, opts)
4525 m = scmutil.match(repo[None], pats, opts)
4526 s = repo.status(match=m, clean=True)
4526 s = repo.status(match=m, clean=True)
4527 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4527 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4528
4528
4529 for f in m.files():
4529 for f in m.files():
4530 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4530 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4531 if os.path.exists(m.rel(f)):
4531 if os.path.exists(m.rel(f)):
4532 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4532 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4533 ret = 1
4533 ret = 1
4534
4534
4535 if force:
4535 if force:
4536 list = modified + deleted + clean + added
4536 list = modified + deleted + clean + added
4537 elif after:
4537 elif after:
4538 list = deleted
4538 list = deleted
4539 for f in modified + added + clean:
4539 for f in modified + added + clean:
4540 ui.warn(_('not removing %s: file still exists (use -f'
4540 ui.warn(_('not removing %s: file still exists (use -f'
4541 ' to force removal)\n') % m.rel(f))
4541 ' to force removal)\n') % m.rel(f))
4542 ret = 1
4542 ret = 1
4543 else:
4543 else:
4544 list = deleted + clean
4544 list = deleted + clean
4545 for f in modified:
4545 for f in modified:
4546 ui.warn(_('not removing %s: file is modified (use -f'
4546 ui.warn(_('not removing %s: file is modified (use -f'
4547 ' to force removal)\n') % m.rel(f))
4547 ' to force removal)\n') % m.rel(f))
4548 ret = 1
4548 ret = 1
4549 for f in added:
4549 for f in added:
4550 ui.warn(_('not removing %s: file has been marked for add'
4550 ui.warn(_('not removing %s: file has been marked for add'
4551 ' (use forget to undo)\n') % m.rel(f))
4551 ' (use forget to undo)\n') % m.rel(f))
4552 ret = 1
4552 ret = 1
4553
4553
4554 for f in sorted(list):
4554 for f in sorted(list):
4555 if ui.verbose or not m.exact(f):
4555 if ui.verbose or not m.exact(f):
4556 ui.status(_('removing %s\n') % m.rel(f))
4556 ui.status(_('removing %s\n') % m.rel(f))
4557
4557
4558 wlock = repo.wlock()
4558 wlock = repo.wlock()
4559 try:
4559 try:
4560 if not after:
4560 if not after:
4561 for f in list:
4561 for f in list:
4562 if f in added:
4562 if f in added:
4563 continue # we never unlink added files on remove
4563 continue # we never unlink added files on remove
4564 try:
4564 try:
4565 util.unlinkpath(repo.wjoin(f))
4565 util.unlinkpath(repo.wjoin(f))
4566 except OSError, inst:
4566 except OSError, inst:
4567 if inst.errno != errno.ENOENT:
4567 if inst.errno != errno.ENOENT:
4568 raise
4568 raise
4569 repo[None].forget(list)
4569 repo[None].forget(list)
4570 finally:
4570 finally:
4571 wlock.release()
4571 wlock.release()
4572
4572
4573 return ret
4573 return ret
4574
4574
4575 @command('rename|move|mv',
4575 @command('rename|move|mv',
4576 [('A', 'after', None, _('record a rename that has already occurred')),
4576 [('A', 'after', None, _('record a rename that has already occurred')),
4577 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4577 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4578 ] + walkopts + dryrunopts,
4578 ] + walkopts + dryrunopts,
4579 _('[OPTION]... SOURCE... DEST'))
4579 _('[OPTION]... SOURCE... DEST'))
4580 def rename(ui, repo, *pats, **opts):
4580 def rename(ui, repo, *pats, **opts):
4581 """rename files; equivalent of copy + remove
4581 """rename files; equivalent of copy + remove
4582
4582
4583 Mark dest as copies of sources; mark sources for deletion. If dest
4583 Mark dest as copies of sources; mark sources for deletion. If dest
4584 is a directory, copies are put in that directory. If dest is a
4584 is a directory, copies are put in that directory. If dest is a
4585 file, there can only be one source.
4585 file, there can only be one source.
4586
4586
4587 By default, this command copies the contents of files as they
4587 By default, this command copies the contents of files as they
4588 exist in the working directory. If invoked with -A/--after, the
4588 exist in the working directory. If invoked with -A/--after, the
4589 operation is recorded, but no copying is performed.
4589 operation is recorded, but no copying is performed.
4590
4590
4591 This command takes effect at the next commit. To undo a rename
4591 This command takes effect at the next commit. To undo a rename
4592 before that, see :hg:`revert`.
4592 before that, see :hg:`revert`.
4593
4593
4594 Returns 0 on success, 1 if errors are encountered.
4594 Returns 0 on success, 1 if errors are encountered.
4595 """
4595 """
4596 wlock = repo.wlock(False)
4596 wlock = repo.wlock(False)
4597 try:
4597 try:
4598 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4598 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4599 finally:
4599 finally:
4600 wlock.release()
4600 wlock.release()
4601
4601
4602 @command('resolve',
4602 @command('resolve',
4603 [('a', 'all', None, _('select all unresolved files')),
4603 [('a', 'all', None, _('select all unresolved files')),
4604 ('l', 'list', None, _('list state of files needing merge')),
4604 ('l', 'list', None, _('list state of files needing merge')),
4605 ('m', 'mark', None, _('mark files as resolved')),
4605 ('m', 'mark', None, _('mark files as resolved')),
4606 ('u', 'unmark', None, _('mark files as unresolved')),
4606 ('u', 'unmark', None, _('mark files as unresolved')),
4607 ('n', 'no-status', None, _('hide status prefix'))]
4607 ('n', 'no-status', None, _('hide status prefix'))]
4608 + mergetoolopts + walkopts,
4608 + mergetoolopts + walkopts,
4609 _('[OPTION]... [FILE]...'))
4609 _('[OPTION]... [FILE]...'))
4610 def resolve(ui, repo, *pats, **opts):
4610 def resolve(ui, repo, *pats, **opts):
4611 """redo merges or set/view the merge status of files
4611 """redo merges or set/view the merge status of files
4612
4612
4613 Merges with unresolved conflicts are often the result of
4613 Merges with unresolved conflicts are often the result of
4614 non-interactive merging using the ``internal:merge`` configuration
4614 non-interactive merging using the ``internal:merge`` configuration
4615 setting, or a command-line merge tool like ``diff3``. The resolve
4615 setting, or a command-line merge tool like ``diff3``. The resolve
4616 command is used to manage the files involved in a merge, after
4616 command is used to manage the files involved in a merge, after
4617 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4617 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4618 working directory must have two parents).
4618 working directory must have two parents).
4619
4619
4620 The resolve command can be used in the following ways:
4620 The resolve command can be used in the following ways:
4621
4621
4622 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4622 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4623 files, discarding any previous merge attempts. Re-merging is not
4623 files, discarding any previous merge attempts. Re-merging is not
4624 performed for files already marked as resolved. Use ``--all/-a``
4624 performed for files already marked as resolved. Use ``--all/-a``
4625 to select all unresolved files. ``--tool`` can be used to specify
4625 to select all unresolved files. ``--tool`` can be used to specify
4626 the merge tool used for the given files. It overrides the HGMERGE
4626 the merge tool used for the given files. It overrides the HGMERGE
4627 environment variable and your configuration files. Previous file
4627 environment variable and your configuration files. Previous file
4628 contents are saved with a ``.orig`` suffix.
4628 contents are saved with a ``.orig`` suffix.
4629
4629
4630 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4630 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4631 (e.g. after having manually fixed-up the files). The default is
4631 (e.g. after having manually fixed-up the files). The default is
4632 to mark all unresolved files.
4632 to mark all unresolved files.
4633
4633
4634 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4634 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4635 default is to mark all resolved files.
4635 default is to mark all resolved files.
4636
4636
4637 - :hg:`resolve -l`: list files which had or still have conflicts.
4637 - :hg:`resolve -l`: list files which had or still have conflicts.
4638 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4638 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4639
4639
4640 Note that Mercurial will not let you commit files with unresolved
4640 Note that Mercurial will not let you commit files with unresolved
4641 merge conflicts. You must use :hg:`resolve -m ...` before you can
4641 merge conflicts. You must use :hg:`resolve -m ...` before you can
4642 commit after a conflicting merge.
4642 commit after a conflicting merge.
4643
4643
4644 Returns 0 on success, 1 if any files fail a resolve attempt.
4644 Returns 0 on success, 1 if any files fail a resolve attempt.
4645 """
4645 """
4646
4646
4647 all, mark, unmark, show, nostatus = \
4647 all, mark, unmark, show, nostatus = \
4648 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4648 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4649
4649
4650 if (show and (mark or unmark)) or (mark and unmark):
4650 if (show and (mark or unmark)) or (mark and unmark):
4651 raise util.Abort(_("too many options specified"))
4651 raise util.Abort(_("too many options specified"))
4652 if pats and all:
4652 if pats and all:
4653 raise util.Abort(_("can't specify --all and patterns"))
4653 raise util.Abort(_("can't specify --all and patterns"))
4654 if not (all or pats or show or mark or unmark):
4654 if not (all or pats or show or mark or unmark):
4655 raise util.Abort(_('no files or directories specified; '
4655 raise util.Abort(_('no files or directories specified; '
4656 'use --all to remerge all files'))
4656 'use --all to remerge all files'))
4657
4657
4658 ms = mergemod.mergestate(repo)
4658 ms = mergemod.mergestate(repo)
4659 m = scmutil.match(repo[None], pats, opts)
4659 m = scmutil.match(repo[None], pats, opts)
4660 ret = 0
4660 ret = 0
4661
4661
4662 for f in ms:
4662 for f in ms:
4663 if m(f):
4663 if m(f):
4664 if show:
4664 if show:
4665 if nostatus:
4665 if nostatus:
4666 ui.write("%s\n" % f)
4666 ui.write("%s\n" % f)
4667 else:
4667 else:
4668 ui.write("%s %s\n" % (ms[f].upper(), f),
4668 ui.write("%s %s\n" % (ms[f].upper(), f),
4669 label='resolve.' +
4669 label='resolve.' +
4670 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4670 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4671 elif mark:
4671 elif mark:
4672 ms.mark(f, "r")
4672 ms.mark(f, "r")
4673 elif unmark:
4673 elif unmark:
4674 ms.mark(f, "u")
4674 ms.mark(f, "u")
4675 else:
4675 else:
4676 wctx = repo[None]
4676 wctx = repo[None]
4677 mctx = wctx.parents()[-1]
4677 mctx = wctx.parents()[-1]
4678
4678
4679 # backup pre-resolve (merge uses .orig for its own purposes)
4679 # backup pre-resolve (merge uses .orig for its own purposes)
4680 a = repo.wjoin(f)
4680 a = repo.wjoin(f)
4681 util.copyfile(a, a + ".resolve")
4681 util.copyfile(a, a + ".resolve")
4682
4682
4683 try:
4683 try:
4684 # resolve file
4684 # resolve file
4685 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4685 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4686 if ms.resolve(f, wctx, mctx):
4686 if ms.resolve(f, wctx, mctx):
4687 ret = 1
4687 ret = 1
4688 finally:
4688 finally:
4689 ui.setconfig('ui', 'forcemerge', '')
4689 ui.setconfig('ui', 'forcemerge', '')
4690
4690
4691 # replace filemerge's .orig file with our resolve file
4691 # replace filemerge's .orig file with our resolve file
4692 util.rename(a + ".resolve", a + ".orig")
4692 util.rename(a + ".resolve", a + ".orig")
4693
4693
4694 ms.commit()
4694 ms.commit()
4695 return ret
4695 return ret
4696
4696
4697 @command('revert',
4697 @command('revert',
4698 [('a', 'all', None, _('revert all changes when no arguments given')),
4698 [('a', 'all', None, _('revert all changes when no arguments given')),
4699 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4699 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4700 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4700 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4701 ('C', 'no-backup', None, _('do not save backup copies of files')),
4701 ('C', 'no-backup', None, _('do not save backup copies of files')),
4702 ] + walkopts + dryrunopts,
4702 ] + walkopts + dryrunopts,
4703 _('[OPTION]... [-r REV] [NAME]...'))
4703 _('[OPTION]... [-r REV] [NAME]...'))
4704 def revert(ui, repo, *pats, **opts):
4704 def revert(ui, repo, *pats, **opts):
4705 """restore files to their checkout state
4705 """restore files to their checkout state
4706
4706
4707 .. note::
4707 .. note::
4708 To check out earlier revisions, you should use :hg:`update REV`.
4708 To check out earlier revisions, you should use :hg:`update REV`.
4709 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4709 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4710
4710
4711 With no revision specified, revert the specified files or directories
4711 With no revision specified, revert the specified files or directories
4712 to the contents they had in the parent of the working directory.
4712 to the contents they had in the parent of the working directory.
4713 This restores the contents of files to an unmodified
4713 This restores the contents of files to an unmodified
4714 state and unschedules adds, removes, copies, and renames. If the
4714 state and unschedules adds, removes, copies, and renames. If the
4715 working directory has two parents, you must explicitly specify a
4715 working directory has two parents, you must explicitly specify a
4716 revision.
4716 revision.
4717
4717
4718 Using the -r/--rev or -d/--date options, revert the given files or
4718 Using the -r/--rev or -d/--date options, revert the given files or
4719 directories to their states as of a specific revision. Because
4719 directories to their states as of a specific revision. Because
4720 revert does not change the working directory parents, this will
4720 revert does not change the working directory parents, this will
4721 cause these files to appear modified. This can be helpful to "back
4721 cause these files to appear modified. This can be helpful to "back
4722 out" some or all of an earlier change. See :hg:`backout` for a
4722 out" some or all of an earlier change. See :hg:`backout` for a
4723 related method.
4723 related method.
4724
4724
4725 Modified files are saved with a .orig suffix before reverting.
4725 Modified files are saved with a .orig suffix before reverting.
4726 To disable these backups, use --no-backup.
4726 To disable these backups, use --no-backup.
4727
4727
4728 See :hg:`help dates` for a list of formats valid for -d/--date.
4728 See :hg:`help dates` for a list of formats valid for -d/--date.
4729
4729
4730 Returns 0 on success.
4730 Returns 0 on success.
4731 """
4731 """
4732
4732
4733 if opts.get("date"):
4733 if opts.get("date"):
4734 if opts.get("rev"):
4734 if opts.get("rev"):
4735 raise util.Abort(_("you can't specify a revision and a date"))
4735 raise util.Abort(_("you can't specify a revision and a date"))
4736 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4736 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4737
4737
4738 parent, p2 = repo.dirstate.parents()
4738 parent, p2 = repo.dirstate.parents()
4739 if not opts.get('rev') and p2 != nullid:
4739 if not opts.get('rev') and p2 != nullid:
4740 # revert after merge is a trap for new users (issue2915)
4740 # revert after merge is a trap for new users (issue2915)
4741 raise util.Abort(_('uncommitted merge with no revision specified'),
4741 raise util.Abort(_('uncommitted merge with no revision specified'),
4742 hint=_('use "hg update" or see "hg help revert"'))
4742 hint=_('use "hg update" or see "hg help revert"'))
4743
4743
4744 ctx = scmutil.revsingle(repo, opts.get('rev'))
4744 ctx = scmutil.revsingle(repo, opts.get('rev'))
4745 node = ctx.node()
4745 node = ctx.node()
4746
4746
4747 if not pats and not opts.get('all'):
4747 if not pats and not opts.get('all'):
4748 msg = _("no files or directories specified")
4748 msg = _("no files or directories specified")
4749 if p2 != nullid:
4749 if p2 != nullid:
4750 hint = _("uncommitted merge, use --all to discard all changes,"
4750 hint = _("uncommitted merge, use --all to discard all changes,"
4751 " or 'hg update -C .' to abort the merge")
4751 " or 'hg update -C .' to abort the merge")
4752 raise util.Abort(msg, hint=hint)
4752 raise util.Abort(msg, hint=hint)
4753 dirty = util.any(repo.status())
4753 dirty = util.any(repo.status())
4754 if node != parent:
4754 if node != parent:
4755 if dirty:
4755 if dirty:
4756 hint = _("uncommitted changes, use --all to discard all"
4756 hint = _("uncommitted changes, use --all to discard all"
4757 " changes, or 'hg update %s' to update") % ctx.rev()
4757 " changes, or 'hg update %s' to update") % ctx.rev()
4758 else:
4758 else:
4759 hint = _("use --all to revert all files,"
4759 hint = _("use --all to revert all files,"
4760 " or 'hg update %s' to update") % ctx.rev()
4760 " or 'hg update %s' to update") % ctx.rev()
4761 elif dirty:
4761 elif dirty:
4762 hint = _("uncommitted changes, use --all to discard all changes")
4762 hint = _("uncommitted changes, use --all to discard all changes")
4763 else:
4763 else:
4764 hint = _("use --all to revert all files")
4764 hint = _("use --all to revert all files")
4765 raise util.Abort(msg, hint=hint)
4765 raise util.Abort(msg, hint=hint)
4766
4766
4767 mf = ctx.manifest()
4767 mf = ctx.manifest()
4768 if node == parent:
4768 if node == parent:
4769 pmf = mf
4769 pmf = mf
4770 else:
4770 else:
4771 pmf = None
4771 pmf = None
4772
4772
4773 # need all matching names in dirstate and manifest of target rev,
4773 # need all matching names in dirstate and manifest of target rev,
4774 # so have to walk both. do not print errors if files exist in one
4774 # so have to walk both. do not print errors if files exist in one
4775 # but not other.
4775 # but not other.
4776
4776
4777 names = {}
4777 names = {}
4778
4778
4779 wlock = repo.wlock()
4779 wlock = repo.wlock()
4780 try:
4780 try:
4781 # walk dirstate.
4781 # walk dirstate.
4782
4782
4783 m = scmutil.match(repo[None], pats, opts)
4783 m = scmutil.match(repo[None], pats, opts)
4784 m.bad = lambda x, y: False
4784 m.bad = lambda x, y: False
4785 for abs in repo.walk(m):
4785 for abs in repo.walk(m):
4786 names[abs] = m.rel(abs), m.exact(abs)
4786 names[abs] = m.rel(abs), m.exact(abs)
4787
4787
4788 # walk target manifest.
4788 # walk target manifest.
4789
4789
4790 def badfn(path, msg):
4790 def badfn(path, msg):
4791 if path in names:
4791 if path in names:
4792 return
4792 return
4793 if path in repo[node].substate:
4793 if path in repo[node].substate:
4794 ui.warn("%s: %s\n" % (m.rel(path),
4794 ui.warn("%s: %s\n" % (m.rel(path),
4795 'reverting subrepos is unsupported'))
4795 'reverting subrepos is unsupported'))
4796 return
4796 return
4797 path_ = path + '/'
4797 path_ = path + '/'
4798 for f in names:
4798 for f in names:
4799 if f.startswith(path_):
4799 if f.startswith(path_):
4800 return
4800 return
4801 ui.warn("%s: %s\n" % (m.rel(path), msg))
4801 ui.warn("%s: %s\n" % (m.rel(path), msg))
4802
4802
4803 m = scmutil.match(repo[node], pats, opts)
4803 m = scmutil.match(repo[node], pats, opts)
4804 m.bad = badfn
4804 m.bad = badfn
4805 for abs in repo[node].walk(m):
4805 for abs in repo[node].walk(m):
4806 if abs not in names:
4806 if abs not in names:
4807 names[abs] = m.rel(abs), m.exact(abs)
4807 names[abs] = m.rel(abs), m.exact(abs)
4808
4808
4809 m = scmutil.matchfiles(repo, names)
4809 m = scmutil.matchfiles(repo, names)
4810 changes = repo.status(match=m)[:4]
4810 changes = repo.status(match=m)[:4]
4811 modified, added, removed, deleted = map(set, changes)
4811 modified, added, removed, deleted = map(set, changes)
4812
4812
4813 # if f is a rename, also revert the source
4813 # if f is a rename, also revert the source
4814 cwd = repo.getcwd()
4814 cwd = repo.getcwd()
4815 for f in added:
4815 for f in added:
4816 src = repo.dirstate.copied(f)
4816 src = repo.dirstate.copied(f)
4817 if src and src not in names and repo.dirstate[src] == 'r':
4817 if src and src not in names and repo.dirstate[src] == 'r':
4818 removed.add(src)
4818 removed.add(src)
4819 names[src] = (repo.pathto(src, cwd), True)
4819 names[src] = (repo.pathto(src, cwd), True)
4820
4820
4821 def removeforget(abs):
4821 def removeforget(abs):
4822 if repo.dirstate[abs] == 'a':
4822 if repo.dirstate[abs] == 'a':
4823 return _('forgetting %s\n')
4823 return _('forgetting %s\n')
4824 return _('removing %s\n')
4824 return _('removing %s\n')
4825
4825
4826 revert = ([], _('reverting %s\n'))
4826 revert = ([], _('reverting %s\n'))
4827 add = ([], _('adding %s\n'))
4827 add = ([], _('adding %s\n'))
4828 remove = ([], removeforget)
4828 remove = ([], removeforget)
4829 undelete = ([], _('undeleting %s\n'))
4829 undelete = ([], _('undeleting %s\n'))
4830
4830
4831 disptable = (
4831 disptable = (
4832 # dispatch table:
4832 # dispatch table:
4833 # file state
4833 # file state
4834 # action if in target manifest
4834 # action if in target manifest
4835 # action if not in target manifest
4835 # action if not in target manifest
4836 # make backup if in target manifest
4836 # make backup if in target manifest
4837 # make backup if not in target manifest
4837 # make backup if not in target manifest
4838 (modified, revert, remove, True, True),
4838 (modified, revert, remove, True, True),
4839 (added, revert, remove, True, False),
4839 (added, revert, remove, True, False),
4840 (removed, undelete, None, False, False),
4840 (removed, undelete, None, False, False),
4841 (deleted, revert, remove, False, False),
4841 (deleted, revert, remove, False, False),
4842 )
4842 )
4843
4843
4844 for abs, (rel, exact) in sorted(names.items()):
4844 for abs, (rel, exact) in sorted(names.items()):
4845 mfentry = mf.get(abs)
4845 mfentry = mf.get(abs)
4846 target = repo.wjoin(abs)
4846 target = repo.wjoin(abs)
4847 def handle(xlist, dobackup):
4847 def handle(xlist, dobackup):
4848 xlist[0].append(abs)
4848 xlist[0].append(abs)
4849 if (dobackup and not opts.get('no_backup') and
4849 if (dobackup and not opts.get('no_backup') and
4850 os.path.lexists(target)):
4850 os.path.lexists(target)):
4851 bakname = "%s.orig" % rel
4851 bakname = "%s.orig" % rel
4852 ui.note(_('saving current version of %s as %s\n') %
4852 ui.note(_('saving current version of %s as %s\n') %
4853 (rel, bakname))
4853 (rel, bakname))
4854 if not opts.get('dry_run'):
4854 if not opts.get('dry_run'):
4855 util.rename(target, bakname)
4855 util.rename(target, bakname)
4856 if ui.verbose or not exact:
4856 if ui.verbose or not exact:
4857 msg = xlist[1]
4857 msg = xlist[1]
4858 if not isinstance(msg, basestring):
4858 if not isinstance(msg, basestring):
4859 msg = msg(abs)
4859 msg = msg(abs)
4860 ui.status(msg % rel)
4860 ui.status(msg % rel)
4861 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4861 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4862 if abs not in table:
4862 if abs not in table:
4863 continue
4863 continue
4864 # file has changed in dirstate
4864 # file has changed in dirstate
4865 if mfentry:
4865 if mfentry:
4866 handle(hitlist, backuphit)
4866 handle(hitlist, backuphit)
4867 elif misslist is not None:
4867 elif misslist is not None:
4868 handle(misslist, backupmiss)
4868 handle(misslist, backupmiss)
4869 break
4869 break
4870 else:
4870 else:
4871 if abs not in repo.dirstate:
4871 if abs not in repo.dirstate:
4872 if mfentry:
4872 if mfentry:
4873 handle(add, True)
4873 handle(add, True)
4874 elif exact:
4874 elif exact:
4875 ui.warn(_('file not managed: %s\n') % rel)
4875 ui.warn(_('file not managed: %s\n') % rel)
4876 continue
4876 continue
4877 # file has not changed in dirstate
4877 # file has not changed in dirstate
4878 if node == parent:
4878 if node == parent:
4879 if exact:
4879 if exact:
4880 ui.warn(_('no changes needed to %s\n') % rel)
4880 ui.warn(_('no changes needed to %s\n') % rel)
4881 continue
4881 continue
4882 if pmf is None:
4882 if pmf is None:
4883 # only need parent manifest in this unlikely case,
4883 # only need parent manifest in this unlikely case,
4884 # so do not read by default
4884 # so do not read by default
4885 pmf = repo[parent].manifest()
4885 pmf = repo[parent].manifest()
4886 if abs in pmf and mfentry:
4886 if abs in pmf and mfentry:
4887 # if version of file is same in parent and target
4887 # if version of file is same in parent and target
4888 # manifests, do nothing
4888 # manifests, do nothing
4889 if (pmf[abs] != mfentry or
4889 if (pmf[abs] != mfentry or
4890 pmf.flags(abs) != mf.flags(abs)):
4890 pmf.flags(abs) != mf.flags(abs)):
4891 handle(revert, False)
4891 handle(revert, False)
4892 else:
4892 else:
4893 handle(remove, False)
4893 handle(remove, False)
4894
4894
4895 if not opts.get('dry_run'):
4895 if not opts.get('dry_run'):
4896 def checkout(f):
4896 def checkout(f):
4897 fc = ctx[f]
4897 fc = ctx[f]
4898 repo.wwrite(f, fc.data(), fc.flags())
4898 repo.wwrite(f, fc.data(), fc.flags())
4899
4899
4900 audit_path = scmutil.pathauditor(repo.root)
4900 audit_path = scmutil.pathauditor(repo.root)
4901 for f in remove[0]:
4901 for f in remove[0]:
4902 if repo.dirstate[f] == 'a':
4902 if repo.dirstate[f] == 'a':
4903 repo.dirstate.drop(f)
4903 repo.dirstate.drop(f)
4904 continue
4904 continue
4905 audit_path(f)
4905 audit_path(f)
4906 try:
4906 try:
4907 util.unlinkpath(repo.wjoin(f))
4907 util.unlinkpath(repo.wjoin(f))
4908 except OSError:
4908 except OSError:
4909 pass
4909 pass
4910 repo.dirstate.remove(f)
4910 repo.dirstate.remove(f)
4911
4911
4912 normal = None
4912 normal = None
4913 if node == parent:
4913 if node == parent:
4914 # We're reverting to our parent. If possible, we'd like status
4914 # We're reverting to our parent. If possible, we'd like status
4915 # to report the file as clean. We have to use normallookup for
4915 # to report the file as clean. We have to use normallookup for
4916 # merges to avoid losing information about merged/dirty files.
4916 # merges to avoid losing information about merged/dirty files.
4917 if p2 != nullid:
4917 if p2 != nullid:
4918 normal = repo.dirstate.normallookup
4918 normal = repo.dirstate.normallookup
4919 else:
4919 else:
4920 normal = repo.dirstate.normal
4920 normal = repo.dirstate.normal
4921 for f in revert[0]:
4921 for f in revert[0]:
4922 checkout(f)
4922 checkout(f)
4923 if normal:
4923 if normal:
4924 normal(f)
4924 normal(f)
4925
4925
4926 for f in add[0]:
4926 for f in add[0]:
4927 checkout(f)
4927 checkout(f)
4928 repo.dirstate.add(f)
4928 repo.dirstate.add(f)
4929
4929
4930 normal = repo.dirstate.normallookup
4930 normal = repo.dirstate.normallookup
4931 if node == parent and p2 == nullid:
4931 if node == parent and p2 == nullid:
4932 normal = repo.dirstate.normal
4932 normal = repo.dirstate.normal
4933 for f in undelete[0]:
4933 for f in undelete[0]:
4934 checkout(f)
4934 checkout(f)
4935 normal(f)
4935 normal(f)
4936
4936
4937 finally:
4937 finally:
4938 wlock.release()
4938 wlock.release()
4939
4939
4940 @command('rollback', dryrunopts +
4940 @command('rollback', dryrunopts +
4941 [('f', 'force', False, _('ignore safety measures'))])
4941 [('f', 'force', False, _('ignore safety measures'))])
4942 def rollback(ui, repo, **opts):
4942 def rollback(ui, repo, **opts):
4943 """roll back the last transaction (dangerous)
4943 """roll back the last transaction (dangerous)
4944
4944
4945 This command should be used with care. There is only one level of
4945 This command should be used with care. There is only one level of
4946 rollback, and there is no way to undo a rollback. It will also
4946 rollback, and there is no way to undo a rollback. It will also
4947 restore the dirstate at the time of the last transaction, losing
4947 restore the dirstate at the time of the last transaction, losing
4948 any dirstate changes since that time. This command does not alter
4948 any dirstate changes since that time. This command does not alter
4949 the working directory.
4949 the working directory.
4950
4950
4951 Transactions are used to encapsulate the effects of all commands
4951 Transactions are used to encapsulate the effects of all commands
4952 that create new changesets or propagate existing changesets into a
4952 that create new changesets or propagate existing changesets into a
4953 repository. For example, the following commands are transactional,
4953 repository. For example, the following commands are transactional,
4954 and their effects can be rolled back:
4954 and their effects can be rolled back:
4955
4955
4956 - commit
4956 - commit
4957 - import
4957 - import
4958 - pull
4958 - pull
4959 - push (with this repository as the destination)
4959 - push (with this repository as the destination)
4960 - unbundle
4960 - unbundle
4961
4961
4962 To avoid permanent data loss, rollback will refuse to rollback a
4962 To avoid permanent data loss, rollback will refuse to rollback a
4963 commit transaction if it isn't checked out. Use --force to
4963 commit transaction if it isn't checked out. Use --force to
4964 override this protection.
4964 override this protection.
4965
4965
4966 This command is not intended for use on public repositories. Once
4966 This command is not intended for use on public repositories. Once
4967 changes are visible for pull by other users, rolling a transaction
4967 changes are visible for pull by other users, rolling a transaction
4968 back locally is ineffective (someone else may already have pulled
4968 back locally is ineffective (someone else may already have pulled
4969 the changes). Furthermore, a race is possible with readers of the
4969 the changes). Furthermore, a race is possible with readers of the
4970 repository; for example an in-progress pull from the repository
4970 repository; for example an in-progress pull from the repository
4971 may fail if a rollback is performed.
4971 may fail if a rollback is performed.
4972
4972
4973 Returns 0 on success, 1 if no rollback data is available.
4973 Returns 0 on success, 1 if no rollback data is available.
4974 """
4974 """
4975 return repo.rollback(dryrun=opts.get('dry_run'),
4975 return repo.rollback(dryrun=opts.get('dry_run'),
4976 force=opts.get('force'))
4976 force=opts.get('force'))
4977
4977
4978 @command('root', [])
4978 @command('root', [])
4979 def root(ui, repo):
4979 def root(ui, repo):
4980 """print the root (top) of the current working directory
4980 """print the root (top) of the current working directory
4981
4981
4982 Print the root directory of the current repository.
4982 Print the root directory of the current repository.
4983
4983
4984 Returns 0 on success.
4984 Returns 0 on success.
4985 """
4985 """
4986 ui.write(repo.root + "\n")
4986 ui.write(repo.root + "\n")
4987
4987
4988 @command('^serve',
4988 @command('^serve',
4989 [('A', 'accesslog', '', _('name of access log file to write to'),
4989 [('A', 'accesslog', '', _('name of access log file to write to'),
4990 _('FILE')),
4990 _('FILE')),
4991 ('d', 'daemon', None, _('run server in background')),
4991 ('d', 'daemon', None, _('run server in background')),
4992 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4992 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4993 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4993 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4994 # use string type, then we can check if something was passed
4994 # use string type, then we can check if something was passed
4995 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4995 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4996 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4996 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4997 _('ADDR')),
4997 _('ADDR')),
4998 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4998 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4999 _('PREFIX')),
4999 _('PREFIX')),
5000 ('n', 'name', '',
5000 ('n', 'name', '',
5001 _('name to show in web pages (default: working directory)'), _('NAME')),
5001 _('name to show in web pages (default: working directory)'), _('NAME')),
5002 ('', 'web-conf', '',
5002 ('', 'web-conf', '',
5003 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5003 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5004 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5004 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5005 _('FILE')),
5005 _('FILE')),
5006 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5006 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5007 ('', 'stdio', None, _('for remote clients')),
5007 ('', 'stdio', None, _('for remote clients')),
5008 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5008 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5009 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5009 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5010 ('', 'style', '', _('template style to use'), _('STYLE')),
5010 ('', 'style', '', _('template style to use'), _('STYLE')),
5011 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5011 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5012 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5012 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5013 _('[OPTION]...'))
5013 _('[OPTION]...'))
5014 def serve(ui, repo, **opts):
5014 def serve(ui, repo, **opts):
5015 """start stand-alone webserver
5015 """start stand-alone webserver
5016
5016
5017 Start a local HTTP repository browser and pull server. You can use
5017 Start a local HTTP repository browser and pull server. You can use
5018 this for ad-hoc sharing and browsing of repositories. It is
5018 this for ad-hoc sharing and browsing of repositories. It is
5019 recommended to use a real web server to serve a repository for
5019 recommended to use a real web server to serve a repository for
5020 longer periods of time.
5020 longer periods of time.
5021
5021
5022 Please note that the server does not implement access control.
5022 Please note that the server does not implement access control.
5023 This means that, by default, anybody can read from the server and
5023 This means that, by default, anybody can read from the server and
5024 nobody can write to it by default. Set the ``web.allow_push``
5024 nobody can write to it by default. Set the ``web.allow_push``
5025 option to ``*`` to allow everybody to push to the server. You
5025 option to ``*`` to allow everybody to push to the server. You
5026 should use a real web server if you need to authenticate users.
5026 should use a real web server if you need to authenticate users.
5027
5027
5028 By default, the server logs accesses to stdout and errors to
5028 By default, the server logs accesses to stdout and errors to
5029 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5029 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5030 files.
5030 files.
5031
5031
5032 To have the server choose a free port number to listen on, specify
5032 To have the server choose a free port number to listen on, specify
5033 a port number of 0; in this case, the server will print the port
5033 a port number of 0; in this case, the server will print the port
5034 number it uses.
5034 number it uses.
5035
5035
5036 Returns 0 on success.
5036 Returns 0 on success.
5037 """
5037 """
5038
5038
5039 if opts["stdio"] and opts["cmdserver"]:
5039 if opts["stdio"] and opts["cmdserver"]:
5040 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5040 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5041
5041
5042 def checkrepo():
5042 def checkrepo():
5043 if repo is None:
5043 if repo is None:
5044 raise error.RepoError(_("There is no Mercurial repository here"
5044 raise error.RepoError(_("There is no Mercurial repository here"
5045 " (.hg not found)"))
5045 " (.hg not found)"))
5046
5046
5047 if opts["stdio"]:
5047 if opts["stdio"]:
5048 checkrepo()
5048 checkrepo()
5049 s = sshserver.sshserver(ui, repo)
5049 s = sshserver.sshserver(ui, repo)
5050 s.serve_forever()
5050 s.serve_forever()
5051
5051
5052 if opts["cmdserver"]:
5052 if opts["cmdserver"]:
5053 checkrepo()
5053 checkrepo()
5054 s = commandserver.server(ui, repo, opts["cmdserver"])
5054 s = commandserver.server(ui, repo, opts["cmdserver"])
5055 return s.serve()
5055 return s.serve()
5056
5056
5057 # this way we can check if something was given in the command-line
5057 # this way we can check if something was given in the command-line
5058 if opts.get('port'):
5058 if opts.get('port'):
5059 opts['port'] = util.getport(opts.get('port'))
5059 opts['port'] = util.getport(opts.get('port'))
5060
5060
5061 baseui = repo and repo.baseui or ui
5061 baseui = repo and repo.baseui or ui
5062 optlist = ("name templates style address port prefix ipv6"
5062 optlist = ("name templates style address port prefix ipv6"
5063 " accesslog errorlog certificate encoding")
5063 " accesslog errorlog certificate encoding")
5064 for o in optlist.split():
5064 for o in optlist.split():
5065 val = opts.get(o, '')
5065 val = opts.get(o, '')
5066 if val in (None, ''): # should check against default options instead
5066 if val in (None, ''): # should check against default options instead
5067 continue
5067 continue
5068 baseui.setconfig("web", o, val)
5068 baseui.setconfig("web", o, val)
5069 if repo and repo.ui != baseui:
5069 if repo and repo.ui != baseui:
5070 repo.ui.setconfig("web", o, val)
5070 repo.ui.setconfig("web", o, val)
5071
5071
5072 o = opts.get('web_conf') or opts.get('webdir_conf')
5072 o = opts.get('web_conf') or opts.get('webdir_conf')
5073 if not o:
5073 if not o:
5074 if not repo:
5074 if not repo:
5075 raise error.RepoError(_("There is no Mercurial repository"
5075 raise error.RepoError(_("There is no Mercurial repository"
5076 " here (.hg not found)"))
5076 " here (.hg not found)"))
5077 o = repo.root
5077 o = repo.root
5078
5078
5079 app = hgweb.hgweb(o, baseui=ui)
5079 app = hgweb.hgweb(o, baseui=ui)
5080
5080
5081 class service(object):
5081 class service(object):
5082 def init(self):
5082 def init(self):
5083 util.setsignalhandler()
5083 util.setsignalhandler()
5084 self.httpd = hgweb.server.create_server(ui, app)
5084 self.httpd = hgweb.server.create_server(ui, app)
5085
5085
5086 if opts['port'] and not ui.verbose:
5086 if opts['port'] and not ui.verbose:
5087 return
5087 return
5088
5088
5089 if self.httpd.prefix:
5089 if self.httpd.prefix:
5090 prefix = self.httpd.prefix.strip('/') + '/'
5090 prefix = self.httpd.prefix.strip('/') + '/'
5091 else:
5091 else:
5092 prefix = ''
5092 prefix = ''
5093
5093
5094 port = ':%d' % self.httpd.port
5094 port = ':%d' % self.httpd.port
5095 if port == ':80':
5095 if port == ':80':
5096 port = ''
5096 port = ''
5097
5097
5098 bindaddr = self.httpd.addr
5098 bindaddr = self.httpd.addr
5099 if bindaddr == '0.0.0.0':
5099 if bindaddr == '0.0.0.0':
5100 bindaddr = '*'
5100 bindaddr = '*'
5101 elif ':' in bindaddr: # IPv6
5101 elif ':' in bindaddr: # IPv6
5102 bindaddr = '[%s]' % bindaddr
5102 bindaddr = '[%s]' % bindaddr
5103
5103
5104 fqaddr = self.httpd.fqaddr
5104 fqaddr = self.httpd.fqaddr
5105 if ':' in fqaddr:
5105 if ':' in fqaddr:
5106 fqaddr = '[%s]' % fqaddr
5106 fqaddr = '[%s]' % fqaddr
5107 if opts['port']:
5107 if opts['port']:
5108 write = ui.status
5108 write = ui.status
5109 else:
5109 else:
5110 write = ui.write
5110 write = ui.write
5111 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5111 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5112 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5112 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5113
5113
5114 def run(self):
5114 def run(self):
5115 self.httpd.serve_forever()
5115 self.httpd.serve_forever()
5116
5116
5117 service = service()
5117 service = service()
5118
5118
5119 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5119 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5120
5120
5121 @command('showconfig|debugconfig',
5121 @command('showconfig|debugconfig',
5122 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5122 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5123 _('[-u] [NAME]...'))
5123 _('[-u] [NAME]...'))
5124 def showconfig(ui, repo, *values, **opts):
5124 def showconfig(ui, repo, *values, **opts):
5125 """show combined config settings from all hgrc files
5125 """show combined config settings from all hgrc files
5126
5126
5127 With no arguments, print names and values of all config items.
5127 With no arguments, print names and values of all config items.
5128
5128
5129 With one argument of the form section.name, print just the value
5129 With one argument of the form section.name, print just the value
5130 of that config item.
5130 of that config item.
5131
5131
5132 With multiple arguments, print names and values of all config
5132 With multiple arguments, print names and values of all config
5133 items with matching section names.
5133 items with matching section names.
5134
5134
5135 With --debug, the source (filename and line number) is printed
5135 With --debug, the source (filename and line number) is printed
5136 for each config item.
5136 for each config item.
5137
5137
5138 Returns 0 on success.
5138 Returns 0 on success.
5139 """
5139 """
5140
5140
5141 for f in scmutil.rcpath():
5141 for f in scmutil.rcpath():
5142 ui.debug('read config from: %s\n' % f)
5142 ui.debug('read config from: %s\n' % f)
5143 untrusted = bool(opts.get('untrusted'))
5143 untrusted = bool(opts.get('untrusted'))
5144 if values:
5144 if values:
5145 sections = [v for v in values if '.' not in v]
5145 sections = [v for v in values if '.' not in v]
5146 items = [v for v in values if '.' in v]
5146 items = [v for v in values if '.' in v]
5147 if len(items) > 1 or items and sections:
5147 if len(items) > 1 or items and sections:
5148 raise util.Abort(_('only one config item permitted'))
5148 raise util.Abort(_('only one config item permitted'))
5149 for section, name, value in ui.walkconfig(untrusted=untrusted):
5149 for section, name, value in ui.walkconfig(untrusted=untrusted):
5150 value = str(value).replace('\n', '\\n')
5150 value = str(value).replace('\n', '\\n')
5151 sectname = section + '.' + name
5151 sectname = section + '.' + name
5152 if values:
5152 if values:
5153 for v in values:
5153 for v in values:
5154 if v == section:
5154 if v == section:
5155 ui.debug('%s: ' %
5155 ui.debug('%s: ' %
5156 ui.configsource(section, name, untrusted))
5156 ui.configsource(section, name, untrusted))
5157 ui.write('%s=%s\n' % (sectname, value))
5157 ui.write('%s=%s\n' % (sectname, value))
5158 elif v == sectname:
5158 elif v == sectname:
5159 ui.debug('%s: ' %
5159 ui.debug('%s: ' %
5160 ui.configsource(section, name, untrusted))
5160 ui.configsource(section, name, untrusted))
5161 ui.write(value, '\n')
5161 ui.write(value, '\n')
5162 else:
5162 else:
5163 ui.debug('%s: ' %
5163 ui.debug('%s: ' %
5164 ui.configsource(section, name, untrusted))
5164 ui.configsource(section, name, untrusted))
5165 ui.write('%s=%s\n' % (sectname, value))
5165 ui.write('%s=%s\n' % (sectname, value))
5166
5166
5167 @command('^status|st',
5167 @command('^status|st',
5168 [('A', 'all', None, _('show status of all files')),
5168 [('A', 'all', None, _('show status of all files')),
5169 ('m', 'modified', None, _('show only modified files')),
5169 ('m', 'modified', None, _('show only modified files')),
5170 ('a', 'added', None, _('show only added files')),
5170 ('a', 'added', None, _('show only added files')),
5171 ('r', 'removed', None, _('show only removed files')),
5171 ('r', 'removed', None, _('show only removed files')),
5172 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5172 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5173 ('c', 'clean', None, _('show only files without changes')),
5173 ('c', 'clean', None, _('show only files without changes')),
5174 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5174 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5175 ('i', 'ignored', None, _('show only ignored files')),
5175 ('i', 'ignored', None, _('show only ignored files')),
5176 ('n', 'no-status', None, _('hide status prefix')),
5176 ('n', 'no-status', None, _('hide status prefix')),
5177 ('C', 'copies', None, _('show source of copied files')),
5177 ('C', 'copies', None, _('show source of copied files')),
5178 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5178 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5179 ('', 'rev', [], _('show difference from revision'), _('REV')),
5179 ('', 'rev', [], _('show difference from revision'), _('REV')),
5180 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5180 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5181 ] + walkopts + subrepoopts,
5181 ] + walkopts + subrepoopts,
5182 _('[OPTION]... [FILE]...'))
5182 _('[OPTION]... [FILE]...'))
5183 def status(ui, repo, *pats, **opts):
5183 def status(ui, repo, *pats, **opts):
5184 """show changed files in the working directory
5184 """show changed files in the working directory
5185
5185
5186 Show status of files in the repository. If names are given, only
5186 Show status of files in the repository. If names are given, only
5187 files that match are shown. Files that are clean or ignored or
5187 files that match are shown. Files that are clean or ignored or
5188 the source of a copy/move operation, are not listed unless
5188 the source of a copy/move operation, are not listed unless
5189 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5189 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5190 Unless options described with "show only ..." are given, the
5190 Unless options described with "show only ..." are given, the
5191 options -mardu are used.
5191 options -mardu are used.
5192
5192
5193 Option -q/--quiet hides untracked (unknown and ignored) files
5193 Option -q/--quiet hides untracked (unknown and ignored) files
5194 unless explicitly requested with -u/--unknown or -i/--ignored.
5194 unless explicitly requested with -u/--unknown or -i/--ignored.
5195
5195
5196 .. note::
5196 .. note::
5197 status may appear to disagree with diff if permissions have
5197 status may appear to disagree with diff if permissions have
5198 changed or a merge has occurred. The standard diff format does
5198 changed or a merge has occurred. The standard diff format does
5199 not report permission changes and diff only reports changes
5199 not report permission changes and diff only reports changes
5200 relative to one merge parent.
5200 relative to one merge parent.
5201
5201
5202 If one revision is given, it is used as the base revision.
5202 If one revision is given, it is used as the base revision.
5203 If two revisions are given, the differences between them are
5203 If two revisions are given, the differences between them are
5204 shown. The --change option can also be used as a shortcut to list
5204 shown. The --change option can also be used as a shortcut to list
5205 the changed files of a revision from its first parent.
5205 the changed files of a revision from its first parent.
5206
5206
5207 The codes used to show the status of files are::
5207 The codes used to show the status of files are::
5208
5208
5209 M = modified
5209 M = modified
5210 A = added
5210 A = added
5211 R = removed
5211 R = removed
5212 C = clean
5212 C = clean
5213 ! = missing (deleted by non-hg command, but still tracked)
5213 ! = missing (deleted by non-hg command, but still tracked)
5214 ? = not tracked
5214 ? = not tracked
5215 I = ignored
5215 I = ignored
5216 = origin of the previous file listed as A (added)
5216 = origin of the previous file listed as A (added)
5217
5217
5218 .. container:: verbose
5218 .. container:: verbose
5219
5219
5220 Examples:
5220 Examples:
5221
5221
5222 - show changes in the working directory relative to a
5222 - show changes in the working directory relative to a
5223 changeset::
5223 changeset::
5224
5224
5225 hg status --rev 9353
5225 hg status --rev 9353
5226
5226
5227 - show all changes including copies in an existing changeset::
5227 - show all changes including copies in an existing changeset::
5228
5228
5229 hg status --copies --change 9353
5229 hg status --copies --change 9353
5230
5230
5231 - get a NUL separated list of added files, suitable for xargs::
5231 - get a NUL separated list of added files, suitable for xargs::
5232
5232
5233 hg status -an0
5233 hg status -an0
5234
5234
5235 Returns 0 on success.
5235 Returns 0 on success.
5236 """
5236 """
5237
5237
5238 revs = opts.get('rev')
5238 revs = opts.get('rev')
5239 change = opts.get('change')
5239 change = opts.get('change')
5240
5240
5241 if revs and change:
5241 if revs and change:
5242 msg = _('cannot specify --rev and --change at the same time')
5242 msg = _('cannot specify --rev and --change at the same time')
5243 raise util.Abort(msg)
5243 raise util.Abort(msg)
5244 elif change:
5244 elif change:
5245 node2 = scmutil.revsingle(repo, change, None).node()
5245 node2 = scmutil.revsingle(repo, change, None).node()
5246 node1 = repo[node2].p1().node()
5246 node1 = repo[node2].p1().node()
5247 else:
5247 else:
5248 node1, node2 = scmutil.revpair(repo, revs)
5248 node1, node2 = scmutil.revpair(repo, revs)
5249
5249
5250 cwd = (pats and repo.getcwd()) or ''
5250 cwd = (pats and repo.getcwd()) or ''
5251 end = opts.get('print0') and '\0' or '\n'
5251 end = opts.get('print0') and '\0' or '\n'
5252 copy = {}
5252 copy = {}
5253 states = 'modified added removed deleted unknown ignored clean'.split()
5253 states = 'modified added removed deleted unknown ignored clean'.split()
5254 show = [k for k in states if opts.get(k)]
5254 show = [k for k in states if opts.get(k)]
5255 if opts.get('all'):
5255 if opts.get('all'):
5256 show += ui.quiet and (states[:4] + ['clean']) or states
5256 show += ui.quiet and (states[:4] + ['clean']) or states
5257 if not show:
5257 if not show:
5258 show = ui.quiet and states[:4] or states[:5]
5258 show = ui.quiet and states[:4] or states[:5]
5259
5259
5260 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5260 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5261 'ignored' in show, 'clean' in show, 'unknown' in show,
5261 'ignored' in show, 'clean' in show, 'unknown' in show,
5262 opts.get('subrepos'))
5262 opts.get('subrepos'))
5263 changestates = zip(states, 'MAR!?IC', stat)
5263 changestates = zip(states, 'MAR!?IC', stat)
5264
5264
5265 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5265 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5266 copy = copies.pathcopies(repo[node1], repo[node2])
5266 copy = copies.pathcopies(repo[node1], repo[node2])
5267
5267
5268 for state, char, files in changestates:
5268 for state, char, files in changestates:
5269 if state in show:
5269 if state in show:
5270 format = "%s %%s%s" % (char, end)
5270 format = "%s %%s%s" % (char, end)
5271 if opts.get('no_status'):
5271 if opts.get('no_status'):
5272 format = "%%s%s" % end
5272 format = "%%s%s" % end
5273
5273
5274 for f in files:
5274 for f in files:
5275 ui.write(format % repo.pathto(f, cwd),
5275 ui.write(format % repo.pathto(f, cwd),
5276 label='status.' + state)
5276 label='status.' + state)
5277 if f in copy:
5277 if f in copy:
5278 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5278 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
5279 label='status.copied')
5279 label='status.copied')
5280
5280
5281 @command('^summary|sum',
5281 @command('^summary|sum',
5282 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5282 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5283 def summary(ui, repo, **opts):
5283 def summary(ui, repo, **opts):
5284 """summarize working directory state
5284 """summarize working directory state
5285
5285
5286 This generates a brief summary of the working directory state,
5286 This generates a brief summary of the working directory state,
5287 including parents, branch, commit status, and available updates.
5287 including parents, branch, commit status, and available updates.
5288
5288
5289 With the --remote option, this will check the default paths for
5289 With the --remote option, this will check the default paths for
5290 incoming and outgoing changes. This can be time-consuming.
5290 incoming and outgoing changes. This can be time-consuming.
5291
5291
5292 Returns 0 on success.
5292 Returns 0 on success.
5293 """
5293 """
5294
5294
5295 ctx = repo[None]
5295 ctx = repo[None]
5296 parents = ctx.parents()
5296 parents = ctx.parents()
5297 pnode = parents[0].node()
5297 pnode = parents[0].node()
5298 marks = []
5298 marks = []
5299
5299
5300 for p in parents:
5300 for p in parents:
5301 # label with log.changeset (instead of log.parent) since this
5301 # label with log.changeset (instead of log.parent) since this
5302 # shows a working directory parent *changeset*:
5302 # shows a working directory parent *changeset*:
5303 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5303 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5304 label='log.changeset')
5304 label='log.changeset')
5305 ui.write(' '.join(p.tags()), label='log.tag')
5305 ui.write(' '.join(p.tags()), label='log.tag')
5306 if p.bookmarks():
5306 if p.bookmarks():
5307 marks.extend(p.bookmarks())
5307 marks.extend(p.bookmarks())
5308 if p.rev() == -1:
5308 if p.rev() == -1:
5309 if not len(repo):
5309 if not len(repo):
5310 ui.write(_(' (empty repository)'))
5310 ui.write(_(' (empty repository)'))
5311 else:
5311 else:
5312 ui.write(_(' (no revision checked out)'))
5312 ui.write(_(' (no revision checked out)'))
5313 ui.write('\n')
5313 ui.write('\n')
5314 if p.description():
5314 if p.description():
5315 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5315 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5316 label='log.summary')
5316 label='log.summary')
5317
5317
5318 branch = ctx.branch()
5318 branch = ctx.branch()
5319 bheads = repo.branchheads(branch)
5319 bheads = repo.branchheads(branch)
5320 m = _('branch: %s\n') % branch
5320 m = _('branch: %s\n') % branch
5321 if branch != 'default':
5321 if branch != 'default':
5322 ui.write(m, label='log.branch')
5322 ui.write(m, label='log.branch')
5323 else:
5323 else:
5324 ui.status(m, label='log.branch')
5324 ui.status(m, label='log.branch')
5325
5325
5326 if marks:
5326 if marks:
5327 current = repo._bookmarkcurrent
5327 current = repo._bookmarkcurrent
5328 ui.write(_('bookmarks:'), label='log.bookmark')
5328 ui.write(_('bookmarks:'), label='log.bookmark')
5329 if current is not None:
5329 if current is not None:
5330 try:
5330 try:
5331 marks.remove(current)
5331 marks.remove(current)
5332 ui.write(' *' + current, label='bookmarks.current')
5332 ui.write(' *' + current, label='bookmarks.current')
5333 except ValueError:
5333 except ValueError:
5334 # current bookmark not in parent ctx marks
5334 # current bookmark not in parent ctx marks
5335 pass
5335 pass
5336 for m in marks:
5336 for m in marks:
5337 ui.write(' ' + m, label='log.bookmark')
5337 ui.write(' ' + m, label='log.bookmark')
5338 ui.write('\n', label='log.bookmark')
5338 ui.write('\n', label='log.bookmark')
5339
5339
5340 st = list(repo.status(unknown=True))[:6]
5340 st = list(repo.status(unknown=True))[:6]
5341
5341
5342 c = repo.dirstate.copies()
5342 c = repo.dirstate.copies()
5343 copied, renamed = [], []
5343 copied, renamed = [], []
5344 for d, s in c.iteritems():
5344 for d, s in c.iteritems():
5345 if s in st[2]:
5345 if s in st[2]:
5346 st[2].remove(s)
5346 st[2].remove(s)
5347 renamed.append(d)
5347 renamed.append(d)
5348 else:
5348 else:
5349 copied.append(d)
5349 copied.append(d)
5350 if d in st[1]:
5350 if d in st[1]:
5351 st[1].remove(d)
5351 st[1].remove(d)
5352 st.insert(3, renamed)
5352 st.insert(3, renamed)
5353 st.insert(4, copied)
5353 st.insert(4, copied)
5354
5354
5355 ms = mergemod.mergestate(repo)
5355 ms = mergemod.mergestate(repo)
5356 st.append([f for f in ms if ms[f] == 'u'])
5356 st.append([f for f in ms if ms[f] == 'u'])
5357
5357
5358 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5358 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5359 st.append(subs)
5359 st.append(subs)
5360
5360
5361 labels = [ui.label(_('%d modified'), 'status.modified'),
5361 labels = [ui.label(_('%d modified'), 'status.modified'),
5362 ui.label(_('%d added'), 'status.added'),
5362 ui.label(_('%d added'), 'status.added'),
5363 ui.label(_('%d removed'), 'status.removed'),
5363 ui.label(_('%d removed'), 'status.removed'),
5364 ui.label(_('%d renamed'), 'status.copied'),
5364 ui.label(_('%d renamed'), 'status.copied'),
5365 ui.label(_('%d copied'), 'status.copied'),
5365 ui.label(_('%d copied'), 'status.copied'),
5366 ui.label(_('%d deleted'), 'status.deleted'),
5366 ui.label(_('%d deleted'), 'status.deleted'),
5367 ui.label(_('%d unknown'), 'status.unknown'),
5367 ui.label(_('%d unknown'), 'status.unknown'),
5368 ui.label(_('%d ignored'), 'status.ignored'),
5368 ui.label(_('%d ignored'), 'status.ignored'),
5369 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5369 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5370 ui.label(_('%d subrepos'), 'status.modified')]
5370 ui.label(_('%d subrepos'), 'status.modified')]
5371 t = []
5371 t = []
5372 for s, l in zip(st, labels):
5372 for s, l in zip(st, labels):
5373 if s:
5373 if s:
5374 t.append(l % len(s))
5374 t.append(l % len(s))
5375
5375
5376 t = ', '.join(t)
5376 t = ', '.join(t)
5377 cleanworkdir = False
5377 cleanworkdir = False
5378
5378
5379 if len(parents) > 1:
5379 if len(parents) > 1:
5380 t += _(' (merge)')
5380 t += _(' (merge)')
5381 elif branch != parents[0].branch():
5381 elif branch != parents[0].branch():
5382 t += _(' (new branch)')
5382 t += _(' (new branch)')
5383 elif (parents[0].extra().get('close') and
5383 elif (parents[0].extra().get('close') and
5384 pnode in repo.branchheads(branch, closed=True)):
5384 pnode in repo.branchheads(branch, closed=True)):
5385 t += _(' (head closed)')
5385 t += _(' (head closed)')
5386 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5386 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5387 t += _(' (clean)')
5387 t += _(' (clean)')
5388 cleanworkdir = True
5388 cleanworkdir = True
5389 elif pnode not in bheads:
5389 elif pnode not in bheads:
5390 t += _(' (new branch head)')
5390 t += _(' (new branch head)')
5391
5391
5392 if cleanworkdir:
5392 if cleanworkdir:
5393 ui.status(_('commit: %s\n') % t.strip())
5393 ui.status(_('commit: %s\n') % t.strip())
5394 else:
5394 else:
5395 ui.write(_('commit: %s\n') % t.strip())
5395 ui.write(_('commit: %s\n') % t.strip())
5396
5396
5397 # all ancestors of branch heads - all ancestors of parent = new csets
5397 # all ancestors of branch heads - all ancestors of parent = new csets
5398 new = [0] * len(repo)
5398 new = [0] * len(repo)
5399 cl = repo.changelog
5399 cl = repo.changelog
5400 for a in [cl.rev(n) for n in bheads]:
5400 for a in [cl.rev(n) for n in bheads]:
5401 new[a] = 1
5401 new[a] = 1
5402 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5402 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5403 new[a] = 1
5403 new[a] = 1
5404 for a in [p.rev() for p in parents]:
5404 for a in [p.rev() for p in parents]:
5405 if a >= 0:
5405 if a >= 0:
5406 new[a] = 0
5406 new[a] = 0
5407 for a in cl.ancestors(*[p.rev() for p in parents]):
5407 for a in cl.ancestors(*[p.rev() for p in parents]):
5408 new[a] = 0
5408 new[a] = 0
5409 new = sum(new)
5409 new = sum(new)
5410
5410
5411 if new == 0:
5411 if new == 0:
5412 ui.status(_('update: (current)\n'))
5412 ui.status(_('update: (current)\n'))
5413 elif pnode not in bheads:
5413 elif pnode not in bheads:
5414 ui.write(_('update: %d new changesets (update)\n') % new)
5414 ui.write(_('update: %d new changesets (update)\n') % new)
5415 else:
5415 else:
5416 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5416 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5417 (new, len(bheads)))
5417 (new, len(bheads)))
5418
5418
5419 if opts.get('remote'):
5419 if opts.get('remote'):
5420 t = []
5420 t = []
5421 source, branches = hg.parseurl(ui.expandpath('default'))
5421 source, branches = hg.parseurl(ui.expandpath('default'))
5422 other = hg.peer(repo, {}, source)
5422 other = hg.peer(repo, {}, source)
5423 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5423 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5424 ui.debug('comparing with %s\n' % util.hidepassword(source))
5424 ui.debug('comparing with %s\n' % util.hidepassword(source))
5425 repo.ui.pushbuffer()
5425 repo.ui.pushbuffer()
5426 commoninc = discovery.findcommonincoming(repo, other)
5426 commoninc = discovery.findcommonincoming(repo, other)
5427 _common, incoming, _rheads = commoninc
5427 _common, incoming, _rheads = commoninc
5428 repo.ui.popbuffer()
5428 repo.ui.popbuffer()
5429 if incoming:
5429 if incoming:
5430 t.append(_('1 or more incoming'))
5430 t.append(_('1 or more incoming'))
5431
5431
5432 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5432 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5433 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5433 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5434 if source != dest:
5434 if source != dest:
5435 other = hg.peer(repo, {}, dest)
5435 other = hg.peer(repo, {}, dest)
5436 commoninc = None
5436 commoninc = None
5437 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5437 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5438 repo.ui.pushbuffer()
5438 repo.ui.pushbuffer()
5439 common, outheads = discovery.findcommonoutgoing(repo, other,
5439 outgoing = discovery.findcommonoutgoing(repo, other,
5440 commoninc=commoninc)
5440 commoninc=commoninc)
5441 repo.ui.popbuffer()
5441 repo.ui.popbuffer()
5442 o = repo.changelog.findmissing(common=common, heads=outheads)
5442 o = outgoing.missing
5443 if o:
5443 if o:
5444 t.append(_('%d outgoing') % len(o))
5444 t.append(_('%d outgoing') % len(o))
5445 if 'bookmarks' in other.listkeys('namespaces'):
5445 if 'bookmarks' in other.listkeys('namespaces'):
5446 lmarks = repo.listkeys('bookmarks')
5446 lmarks = repo.listkeys('bookmarks')
5447 rmarks = other.listkeys('bookmarks')
5447 rmarks = other.listkeys('bookmarks')
5448 diff = set(rmarks) - set(lmarks)
5448 diff = set(rmarks) - set(lmarks)
5449 if len(diff) > 0:
5449 if len(diff) > 0:
5450 t.append(_('%d incoming bookmarks') % len(diff))
5450 t.append(_('%d incoming bookmarks') % len(diff))
5451 diff = set(lmarks) - set(rmarks)
5451 diff = set(lmarks) - set(rmarks)
5452 if len(diff) > 0:
5452 if len(diff) > 0:
5453 t.append(_('%d outgoing bookmarks') % len(diff))
5453 t.append(_('%d outgoing bookmarks') % len(diff))
5454
5454
5455 if t:
5455 if t:
5456 ui.write(_('remote: %s\n') % (', '.join(t)))
5456 ui.write(_('remote: %s\n') % (', '.join(t)))
5457 else:
5457 else:
5458 ui.status(_('remote: (synced)\n'))
5458 ui.status(_('remote: (synced)\n'))
5459
5459
5460 @command('tag',
5460 @command('tag',
5461 [('f', 'force', None, _('force tag')),
5461 [('f', 'force', None, _('force tag')),
5462 ('l', 'local', None, _('make the tag local')),
5462 ('l', 'local', None, _('make the tag local')),
5463 ('r', 'rev', '', _('revision to tag'), _('REV')),
5463 ('r', 'rev', '', _('revision to tag'), _('REV')),
5464 ('', 'remove', None, _('remove a tag')),
5464 ('', 'remove', None, _('remove a tag')),
5465 # -l/--local is already there, commitopts cannot be used
5465 # -l/--local is already there, commitopts cannot be used
5466 ('e', 'edit', None, _('edit commit message')),
5466 ('e', 'edit', None, _('edit commit message')),
5467 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5467 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5468 ] + commitopts2,
5468 ] + commitopts2,
5469 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5469 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5470 def tag(ui, repo, name1, *names, **opts):
5470 def tag(ui, repo, name1, *names, **opts):
5471 """add one or more tags for the current or given revision
5471 """add one or more tags for the current or given revision
5472
5472
5473 Name a particular revision using <name>.
5473 Name a particular revision using <name>.
5474
5474
5475 Tags are used to name particular revisions of the repository and are
5475 Tags are used to name particular revisions of the repository and are
5476 very useful to compare different revisions, to go back to significant
5476 very useful to compare different revisions, to go back to significant
5477 earlier versions or to mark branch points as releases, etc. Changing
5477 earlier versions or to mark branch points as releases, etc. Changing
5478 an existing tag is normally disallowed; use -f/--force to override.
5478 an existing tag is normally disallowed; use -f/--force to override.
5479
5479
5480 If no revision is given, the parent of the working directory is
5480 If no revision is given, the parent of the working directory is
5481 used, or tip if no revision is checked out.
5481 used, or tip if no revision is checked out.
5482
5482
5483 To facilitate version control, distribution, and merging of tags,
5483 To facilitate version control, distribution, and merging of tags,
5484 they are stored as a file named ".hgtags" which is managed similarly
5484 they are stored as a file named ".hgtags" which is managed similarly
5485 to other project files and can be hand-edited if necessary. This
5485 to other project files and can be hand-edited if necessary. This
5486 also means that tagging creates a new commit. The file
5486 also means that tagging creates a new commit. The file
5487 ".hg/localtags" is used for local tags (not shared among
5487 ".hg/localtags" is used for local tags (not shared among
5488 repositories).
5488 repositories).
5489
5489
5490 Tag commits are usually made at the head of a branch. If the parent
5490 Tag commits are usually made at the head of a branch. If the parent
5491 of the working directory is not a branch head, :hg:`tag` aborts; use
5491 of the working directory is not a branch head, :hg:`tag` aborts; use
5492 -f/--force to force the tag commit to be based on a non-head
5492 -f/--force to force the tag commit to be based on a non-head
5493 changeset.
5493 changeset.
5494
5494
5495 See :hg:`help dates` for a list of formats valid for -d/--date.
5495 See :hg:`help dates` for a list of formats valid for -d/--date.
5496
5496
5497 Since tag names have priority over branch names during revision
5497 Since tag names have priority over branch names during revision
5498 lookup, using an existing branch name as a tag name is discouraged.
5498 lookup, using an existing branch name as a tag name is discouraged.
5499
5499
5500 Returns 0 on success.
5500 Returns 0 on success.
5501 """
5501 """
5502
5502
5503 rev_ = "."
5503 rev_ = "."
5504 names = [t.strip() for t in (name1,) + names]
5504 names = [t.strip() for t in (name1,) + names]
5505 if len(names) != len(set(names)):
5505 if len(names) != len(set(names)):
5506 raise util.Abort(_('tag names must be unique'))
5506 raise util.Abort(_('tag names must be unique'))
5507 for n in names:
5507 for n in names:
5508 if n in ['tip', '.', 'null']:
5508 if n in ['tip', '.', 'null']:
5509 raise util.Abort(_("the name '%s' is reserved") % n)
5509 raise util.Abort(_("the name '%s' is reserved") % n)
5510 if not n:
5510 if not n:
5511 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5511 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
5512 if opts.get('rev') and opts.get('remove'):
5512 if opts.get('rev') and opts.get('remove'):
5513 raise util.Abort(_("--rev and --remove are incompatible"))
5513 raise util.Abort(_("--rev and --remove are incompatible"))
5514 if opts.get('rev'):
5514 if opts.get('rev'):
5515 rev_ = opts['rev']
5515 rev_ = opts['rev']
5516 message = opts.get('message')
5516 message = opts.get('message')
5517 if opts.get('remove'):
5517 if opts.get('remove'):
5518 expectedtype = opts.get('local') and 'local' or 'global'
5518 expectedtype = opts.get('local') and 'local' or 'global'
5519 for n in names:
5519 for n in names:
5520 if not repo.tagtype(n):
5520 if not repo.tagtype(n):
5521 raise util.Abort(_("tag '%s' does not exist") % n)
5521 raise util.Abort(_("tag '%s' does not exist") % n)
5522 if repo.tagtype(n) != expectedtype:
5522 if repo.tagtype(n) != expectedtype:
5523 if expectedtype == 'global':
5523 if expectedtype == 'global':
5524 raise util.Abort(_("tag '%s' is not a global tag") % n)
5524 raise util.Abort(_("tag '%s' is not a global tag") % n)
5525 else:
5525 else:
5526 raise util.Abort(_("tag '%s' is not a local tag") % n)
5526 raise util.Abort(_("tag '%s' is not a local tag") % n)
5527 rev_ = nullid
5527 rev_ = nullid
5528 if not message:
5528 if not message:
5529 # we don't translate commit messages
5529 # we don't translate commit messages
5530 message = 'Removed tag %s' % ', '.join(names)
5530 message = 'Removed tag %s' % ', '.join(names)
5531 elif not opts.get('force'):
5531 elif not opts.get('force'):
5532 for n in names:
5532 for n in names:
5533 if n in repo.tags():
5533 if n in repo.tags():
5534 raise util.Abort(_("tag '%s' already exists "
5534 raise util.Abort(_("tag '%s' already exists "
5535 "(use -f to force)") % n)
5535 "(use -f to force)") % n)
5536 if not opts.get('local'):
5536 if not opts.get('local'):
5537 p1, p2 = repo.dirstate.parents()
5537 p1, p2 = repo.dirstate.parents()
5538 if p2 != nullid:
5538 if p2 != nullid:
5539 raise util.Abort(_('uncommitted merge'))
5539 raise util.Abort(_('uncommitted merge'))
5540 bheads = repo.branchheads()
5540 bheads = repo.branchheads()
5541 if not opts.get('force') and bheads and p1 not in bheads:
5541 if not opts.get('force') and bheads and p1 not in bheads:
5542 raise util.Abort(_('not at a branch head (use -f to force)'))
5542 raise util.Abort(_('not at a branch head (use -f to force)'))
5543 r = scmutil.revsingle(repo, rev_).node()
5543 r = scmutil.revsingle(repo, rev_).node()
5544
5544
5545 if not message:
5545 if not message:
5546 # we don't translate commit messages
5546 # we don't translate commit messages
5547 message = ('Added tag %s for changeset %s' %
5547 message = ('Added tag %s for changeset %s' %
5548 (', '.join(names), short(r)))
5548 (', '.join(names), short(r)))
5549
5549
5550 date = opts.get('date')
5550 date = opts.get('date')
5551 if date:
5551 if date:
5552 date = util.parsedate(date)
5552 date = util.parsedate(date)
5553
5553
5554 if opts.get('edit'):
5554 if opts.get('edit'):
5555 message = ui.edit(message, ui.username())
5555 message = ui.edit(message, ui.username())
5556
5556
5557 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5557 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5558
5558
5559 @command('tags', [], '')
5559 @command('tags', [], '')
5560 def tags(ui, repo):
5560 def tags(ui, repo):
5561 """list repository tags
5561 """list repository tags
5562
5562
5563 This lists both regular and local tags. When the -v/--verbose
5563 This lists both regular and local tags. When the -v/--verbose
5564 switch is used, a third column "local" is printed for local tags.
5564 switch is used, a third column "local" is printed for local tags.
5565
5565
5566 Returns 0 on success.
5566 Returns 0 on success.
5567 """
5567 """
5568
5568
5569 hexfunc = ui.debugflag and hex or short
5569 hexfunc = ui.debugflag and hex or short
5570 tagtype = ""
5570 tagtype = ""
5571
5571
5572 for t, n in reversed(repo.tagslist()):
5572 for t, n in reversed(repo.tagslist()):
5573 if ui.quiet:
5573 if ui.quiet:
5574 ui.write("%s\n" % t, label='tags.normal')
5574 ui.write("%s\n" % t, label='tags.normal')
5575 continue
5575 continue
5576
5576
5577 hn = hexfunc(n)
5577 hn = hexfunc(n)
5578 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5578 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5579 rev = ui.label(r, 'log.changeset')
5579 rev = ui.label(r, 'log.changeset')
5580 spaces = " " * (30 - encoding.colwidth(t))
5580 spaces = " " * (30 - encoding.colwidth(t))
5581
5581
5582 tag = ui.label(t, 'tags.normal')
5582 tag = ui.label(t, 'tags.normal')
5583 if ui.verbose:
5583 if ui.verbose:
5584 if repo.tagtype(t) == 'local':
5584 if repo.tagtype(t) == 'local':
5585 tagtype = " local"
5585 tagtype = " local"
5586 tag = ui.label(t, 'tags.local')
5586 tag = ui.label(t, 'tags.local')
5587 else:
5587 else:
5588 tagtype = ""
5588 tagtype = ""
5589 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5589 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5590
5590
5591 @command('tip',
5591 @command('tip',
5592 [('p', 'patch', None, _('show patch')),
5592 [('p', 'patch', None, _('show patch')),
5593 ('g', 'git', None, _('use git extended diff format')),
5593 ('g', 'git', None, _('use git extended diff format')),
5594 ] + templateopts,
5594 ] + templateopts,
5595 _('[-p] [-g]'))
5595 _('[-p] [-g]'))
5596 def tip(ui, repo, **opts):
5596 def tip(ui, repo, **opts):
5597 """show the tip revision
5597 """show the tip revision
5598
5598
5599 The tip revision (usually just called the tip) is the changeset
5599 The tip revision (usually just called the tip) is the changeset
5600 most recently added to the repository (and therefore the most
5600 most recently added to the repository (and therefore the most
5601 recently changed head).
5601 recently changed head).
5602
5602
5603 If you have just made a commit, that commit will be the tip. If
5603 If you have just made a commit, that commit will be the tip. If
5604 you have just pulled changes from another repository, the tip of
5604 you have just pulled changes from another repository, the tip of
5605 that repository becomes the current tip. The "tip" tag is special
5605 that repository becomes the current tip. The "tip" tag is special
5606 and cannot be renamed or assigned to a different changeset.
5606 and cannot be renamed or assigned to a different changeset.
5607
5607
5608 Returns 0 on success.
5608 Returns 0 on success.
5609 """
5609 """
5610 displayer = cmdutil.show_changeset(ui, repo, opts)
5610 displayer = cmdutil.show_changeset(ui, repo, opts)
5611 displayer.show(repo[len(repo) - 1])
5611 displayer.show(repo[len(repo) - 1])
5612 displayer.close()
5612 displayer.close()
5613
5613
5614 @command('unbundle',
5614 @command('unbundle',
5615 [('u', 'update', None,
5615 [('u', 'update', None,
5616 _('update to new branch head if changesets were unbundled'))],
5616 _('update to new branch head if changesets were unbundled'))],
5617 _('[-u] FILE...'))
5617 _('[-u] FILE...'))
5618 def unbundle(ui, repo, fname1, *fnames, **opts):
5618 def unbundle(ui, repo, fname1, *fnames, **opts):
5619 """apply one or more changegroup files
5619 """apply one or more changegroup files
5620
5620
5621 Apply one or more compressed changegroup files generated by the
5621 Apply one or more compressed changegroup files generated by the
5622 bundle command.
5622 bundle command.
5623
5623
5624 Returns 0 on success, 1 if an update has unresolved files.
5624 Returns 0 on success, 1 if an update has unresolved files.
5625 """
5625 """
5626 fnames = (fname1,) + fnames
5626 fnames = (fname1,) + fnames
5627
5627
5628 lock = repo.lock()
5628 lock = repo.lock()
5629 wc = repo['.']
5629 wc = repo['.']
5630 try:
5630 try:
5631 for fname in fnames:
5631 for fname in fnames:
5632 f = url.open(ui, fname)
5632 f = url.open(ui, fname)
5633 gen = changegroup.readbundle(f, fname)
5633 gen = changegroup.readbundle(f, fname)
5634 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5634 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5635 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5635 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5636 finally:
5636 finally:
5637 lock.release()
5637 lock.release()
5638 return postincoming(ui, repo, modheads, opts.get('update'), None)
5638 return postincoming(ui, repo, modheads, opts.get('update'), None)
5639
5639
5640 @command('^update|up|checkout|co',
5640 @command('^update|up|checkout|co',
5641 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5641 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5642 ('c', 'check', None,
5642 ('c', 'check', None,
5643 _('update across branches if no uncommitted changes')),
5643 _('update across branches if no uncommitted changes')),
5644 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5644 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5645 ('r', 'rev', '', _('revision'), _('REV'))],
5645 ('r', 'rev', '', _('revision'), _('REV'))],
5646 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5646 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5647 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5647 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5648 """update working directory (or switch revisions)
5648 """update working directory (or switch revisions)
5649
5649
5650 Update the repository's working directory to the specified
5650 Update the repository's working directory to the specified
5651 changeset. If no changeset is specified, update to the tip of the
5651 changeset. If no changeset is specified, update to the tip of the
5652 current named branch.
5652 current named branch.
5653
5653
5654 If the changeset is not a descendant of the working directory's
5654 If the changeset is not a descendant of the working directory's
5655 parent, the update is aborted. With the -c/--check option, the
5655 parent, the update is aborted. With the -c/--check option, the
5656 working directory is checked for uncommitted changes; if none are
5656 working directory is checked for uncommitted changes; if none are
5657 found, the working directory is updated to the specified
5657 found, the working directory is updated to the specified
5658 changeset.
5658 changeset.
5659
5659
5660 Update sets the working directory's parent revison to the specified
5660 Update sets the working directory's parent revison to the specified
5661 changeset (see :hg:`help parents`).
5661 changeset (see :hg:`help parents`).
5662
5662
5663 The following rules apply when the working directory contains
5663 The following rules apply when the working directory contains
5664 uncommitted changes:
5664 uncommitted changes:
5665
5665
5666 1. If neither -c/--check nor -C/--clean is specified, and if
5666 1. If neither -c/--check nor -C/--clean is specified, and if
5667 the requested changeset is an ancestor or descendant of
5667 the requested changeset is an ancestor or descendant of
5668 the working directory's parent, the uncommitted changes
5668 the working directory's parent, the uncommitted changes
5669 are merged into the requested changeset and the merged
5669 are merged into the requested changeset and the merged
5670 result is left uncommitted. If the requested changeset is
5670 result is left uncommitted. If the requested changeset is
5671 not an ancestor or descendant (that is, it is on another
5671 not an ancestor or descendant (that is, it is on another
5672 branch), the update is aborted and the uncommitted changes
5672 branch), the update is aborted and the uncommitted changes
5673 are preserved.
5673 are preserved.
5674
5674
5675 2. With the -c/--check option, the update is aborted and the
5675 2. With the -c/--check option, the update is aborted and the
5676 uncommitted changes are preserved.
5676 uncommitted changes are preserved.
5677
5677
5678 3. With the -C/--clean option, uncommitted changes are discarded and
5678 3. With the -C/--clean option, uncommitted changes are discarded and
5679 the working directory is updated to the requested changeset.
5679 the working directory is updated to the requested changeset.
5680
5680
5681 Use null as the changeset to remove the working directory (like
5681 Use null as the changeset to remove the working directory (like
5682 :hg:`clone -U`).
5682 :hg:`clone -U`).
5683
5683
5684 If you want to revert just one file to an older revision, use
5684 If you want to revert just one file to an older revision, use
5685 :hg:`revert [-r REV] NAME`.
5685 :hg:`revert [-r REV] NAME`.
5686
5686
5687 See :hg:`help dates` for a list of formats valid for -d/--date.
5687 See :hg:`help dates` for a list of formats valid for -d/--date.
5688
5688
5689 Returns 0 on success, 1 if there are unresolved files.
5689 Returns 0 on success, 1 if there are unresolved files.
5690 """
5690 """
5691 if rev and node:
5691 if rev and node:
5692 raise util.Abort(_("please specify just one revision"))
5692 raise util.Abort(_("please specify just one revision"))
5693
5693
5694 if rev is None or rev == '':
5694 if rev is None or rev == '':
5695 rev = node
5695 rev = node
5696
5696
5697 # if we defined a bookmark, we have to remember the original bookmark name
5697 # if we defined a bookmark, we have to remember the original bookmark name
5698 brev = rev
5698 brev = rev
5699 rev = scmutil.revsingle(repo, rev, rev).rev()
5699 rev = scmutil.revsingle(repo, rev, rev).rev()
5700
5700
5701 if check and clean:
5701 if check and clean:
5702 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5702 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5703
5703
5704 if check:
5704 if check:
5705 # we could use dirty() but we can ignore merge and branch trivia
5705 # we could use dirty() but we can ignore merge and branch trivia
5706 c = repo[None]
5706 c = repo[None]
5707 if c.modified() or c.added() or c.removed():
5707 if c.modified() or c.added() or c.removed():
5708 raise util.Abort(_("uncommitted local changes"))
5708 raise util.Abort(_("uncommitted local changes"))
5709
5709
5710 if date:
5710 if date:
5711 if rev is not None:
5711 if rev is not None:
5712 raise util.Abort(_("you can't specify a revision and a date"))
5712 raise util.Abort(_("you can't specify a revision and a date"))
5713 rev = cmdutil.finddate(ui, repo, date)
5713 rev = cmdutil.finddate(ui, repo, date)
5714
5714
5715 if clean or check:
5715 if clean or check:
5716 ret = hg.clean(repo, rev)
5716 ret = hg.clean(repo, rev)
5717 else:
5717 else:
5718 ret = hg.update(repo, rev)
5718 ret = hg.update(repo, rev)
5719
5719
5720 if brev in repo._bookmarks:
5720 if brev in repo._bookmarks:
5721 bookmarks.setcurrent(repo, brev)
5721 bookmarks.setcurrent(repo, brev)
5722
5722
5723 return ret
5723 return ret
5724
5724
5725 @command('verify', [])
5725 @command('verify', [])
5726 def verify(ui, repo):
5726 def verify(ui, repo):
5727 """verify the integrity of the repository
5727 """verify the integrity of the repository
5728
5728
5729 Verify the integrity of the current repository.
5729 Verify the integrity of the current repository.
5730
5730
5731 This will perform an extensive check of the repository's
5731 This will perform an extensive check of the repository's
5732 integrity, validating the hashes and checksums of each entry in
5732 integrity, validating the hashes and checksums of each entry in
5733 the changelog, manifest, and tracked files, as well as the
5733 the changelog, manifest, and tracked files, as well as the
5734 integrity of their crosslinks and indices.
5734 integrity of their crosslinks and indices.
5735
5735
5736 Returns 0 on success, 1 if errors are encountered.
5736 Returns 0 on success, 1 if errors are encountered.
5737 """
5737 """
5738 return hg.verify(repo)
5738 return hg.verify(repo)
5739
5739
5740 @command('version', [])
5740 @command('version', [])
5741 def version_(ui):
5741 def version_(ui):
5742 """output version and copyright information"""
5742 """output version and copyright information"""
5743 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5743 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5744 % util.version())
5744 % util.version())
5745 ui.status(_(
5745 ui.status(_(
5746 "(see http://mercurial.selenic.com for more information)\n"
5746 "(see http://mercurial.selenic.com for more information)\n"
5747 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5747 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5748 "This is free software; see the source for copying conditions. "
5748 "This is free software; see the source for copying conditions. "
5749 "There is NO\nwarranty; "
5749 "There is NO\nwarranty; "
5750 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5750 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5751 ))
5751 ))
5752
5752
5753 norepo = ("clone init version help debugcommands debugcomplete"
5753 norepo = ("clone init version help debugcommands debugcomplete"
5754 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5754 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5755 " debugknown debuggetbundle debugbundle")
5755 " debugknown debuggetbundle debugbundle")
5756 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5756 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5757 " debugdata debugindex debugindexdot debugrevlog")
5757 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,218 +1,255 b''
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 nullid, short
8 from node import nullid, short
9 from i18n import _
9 from i18n import _
10 import util, setdiscovery, treediscovery
10 import util, setdiscovery, treediscovery
11
11
12 def findcommonincoming(repo, remote, heads=None, force=False):
12 def findcommonincoming(repo, remote, heads=None, force=False):
13 """Return a tuple (common, anyincoming, heads) used to identify the common
13 """Return a tuple (common, anyincoming, heads) used to identify the common
14 subset of nodes between repo and remote.
14 subset of nodes between repo and remote.
15
15
16 "common" is a list of (at least) the heads of the common subset.
16 "common" is a list of (at least) the heads of the common subset.
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 locally. If remote does not support getbundle, this actually is a list of
18 locally. If remote does not support getbundle, this actually is a list of
19 roots of the nodes that would be incoming, to be supplied to
19 roots of the nodes that would be incoming, to be supplied to
20 changegroupsubset. No code except for pull should be relying on this fact
20 changegroupsubset. No code except for pull should be relying on this fact
21 any longer.
21 any longer.
22 "heads" is either the supplied heads, or else the remote's heads.
22 "heads" is either the supplied heads, or else the remote's heads.
23
23
24 If you pass heads and they are all known locally, the reponse lists justs
24 If you pass heads and they are all known locally, the reponse lists justs
25 these heads in "common" and in "heads".
25 these heads in "common" and in "heads".
26
26
27 Please use findcommonoutgoing to compute the set of outgoing nodes to give
27 Please use findcommonoutgoing to compute the set of outgoing nodes to give
28 extensions a good hook into outgoing.
28 extensions a good hook into outgoing.
29 """
29 """
30
30
31 if not remote.capable('getbundle'):
31 if not remote.capable('getbundle'):
32 return treediscovery.findcommonincoming(repo, remote, heads, force)
32 return treediscovery.findcommonincoming(repo, remote, heads, force)
33
33
34 if heads:
34 if heads:
35 allknown = True
35 allknown = True
36 nm = repo.changelog.nodemap
36 nm = repo.changelog.nodemap
37 for h in heads:
37 for h in heads:
38 if nm.get(h) is None:
38 if nm.get(h) is None:
39 allknown = False
39 allknown = False
40 break
40 break
41 if allknown:
41 if allknown:
42 return (heads, False, heads)
42 return (heads, False, heads)
43
43
44 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
44 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
45 abortwhenunrelated=not force)
45 abortwhenunrelated=not force)
46 common, anyinc, srvheads = res
46 common, anyinc, srvheads = res
47 return (list(common), anyinc, heads or list(srvheads))
47 return (list(common), anyinc, heads or list(srvheads))
48
48
49 class outgoing(object):
50 '''Represents the set of nodes present in a local repo but not in a
51 (possibly) remote one.
52
53 Members:
54
55 missing is a list of all nodes present in local but not in remote.
56 common is a list of all nodes shared between the two repos.
57 missingheads is the list of heads of missing.
58 commonheads is the list of heads of common.
59
60 The sets are computed on demand from the heads, unless provided upfront
61 by discovery.'''
62
63 def __init__(self, revlog, commonheads, missingheads):
64 self.commonheads = commonheads
65 self.missingheads = missingheads
66 self._revlog = revlog
67 self._common = None
68 self._missing = None
69
70 def _computecommonmissing(self):
71 sets = self._revlog.findcommonmissing(self.commonheads,
72 self.missingheads)
73 self._common, self._missing = sets
74
75 @util.propertycache
76 def common(self):
77 if self._common is None:
78 self._computecommonmissing()
79 return self._common
80
81 @util.propertycache
82 def missing(self):
83 if self._missing is None:
84 self._computecommonmissing()
85 return self._missing
86
49 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
87 def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
50 '''Return a tuple (common, anyoutgoing, heads) used to identify the set
88 '''Return an outgoing instance to identify the nodes present in repo but
51 of nodes present in repo but not in other.
89 not in other.
52
90
53 If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
91 If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
54 are included. If you already know the local repo's heads, passing them in
92 are included. If you already know the local repo's heads, passing them in
55 onlyheads is faster than letting them be recomputed here.
93 onlyheads is faster than letting them be recomputed here.
56
94
57 If commoninc is given, it must the the result of a prior call to
95 If commoninc is given, it must the the result of a prior call to
58 findcommonincoming(repo, other, force) to avoid recomputing it here.
96 findcommonincoming(repo, other, force) to avoid recomputing it here.'''
59
60 The returned tuple is meant to be passed to changelog.findmissing.'''
61 common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
97 common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
62 return (common, onlyheads or repo.heads())
98 return outgoing(repo.changelog, common, onlyheads or repo.heads())
63
99
64 def prepush(repo, remote, force, revs, newbranch):
100 def prepush(repo, remote, force, revs, newbranch):
65 '''Analyze the local and remote repositories and determine which
101 '''Analyze the local and remote repositories and determine which
66 changesets need to be pushed to the remote. Return value depends
102 changesets need to be pushed to the remote. Return value depends
67 on circumstances:
103 on circumstances:
68
104
69 If we are not going to push anything, return a tuple (None,
105 If we are not going to push anything, return a tuple (None,
70 outgoing, common) where outgoing is 0 if there are no outgoing
106 outgoing, common) where outgoing is 0 if there are no outgoing
71 changesets and 1 if there are, but we refuse to push them
107 changesets and 1 if there are, but we refuse to push them
72 (e.g. would create new remote heads). The third element "common"
108 (e.g. would create new remote heads). The third element "common"
73 is the list of heads of the common set between local and remote.
109 is the list of heads of the common set between local and remote.
74
110
75 Otherwise, return a tuple (changegroup, remoteheads, futureheads),
111 Otherwise, return a tuple (changegroup, remoteheads, futureheads),
76 where changegroup is a readable file-like object whose read()
112 where changegroup is a readable file-like object whose read()
77 returns successive changegroup chunks ready to be sent over the
113 returns successive changegroup chunks ready to be sent over the
78 wire, remoteheads is the list of remote heads and futureheads is
114 wire, remoteheads is the list of remote heads and futureheads is
79 the list of heads of the common set between local and remote to
115 the list of heads of the common set between local and remote to
80 be after push completion.
116 be after push completion.
81 '''
117 '''
82 commoninc = findcommonincoming(repo, remote, force=force)
118 commoninc = findcommonincoming(repo, remote, force=force)
83 common, revs = findcommonoutgoing(repo, remote, onlyheads=revs,
119 outgoing = findcommonoutgoing(repo, remote, onlyheads=revs,
84 commoninc=commoninc, force=force)
120 commoninc=commoninc, force=force)
85 _common, inc, remoteheads = commoninc
121 _common, inc, remoteheads = commoninc
86
122
87 cl = repo.changelog
123 cl = repo.changelog
88 alloutg = cl.findmissing(common, revs)
124 alloutg = outgoing.missing
125 common = outgoing.commonheads
89 outg = []
126 outg = []
90 secret = []
127 secret = []
91 for o in alloutg:
128 for o in alloutg:
92 if repo[o].phase() >= 2:
129 if repo[o].phase() >= 2:
93 secret.append(o)
130 secret.append(o)
94 else:
131 else:
95 outg.append(o)
132 outg.append(o)
96
133
97 if not outg:
134 if not outg:
98 if secret:
135 if secret:
99 repo.ui.status(_("no changes to push but %i secret changesets\n")
136 repo.ui.status(_("no changes to push but %i secret changesets\n")
100 % len(secret))
137 % len(secret))
101 else:
138 else:
102 repo.ui.status(_("no changes found\n"))
139 repo.ui.status(_("no changes found\n"))
103 return None, 1, common
140 return None, 1, common
104
141
105 if secret:
142 if secret:
106 # recompute target revs
143 # recompute target revs
107 revs = [ctx.node() for ctx in repo.set('heads(::(%ld))',
144 revs = [ctx.node() for ctx in repo.set('heads(::(%ld))',
108 map(repo.changelog.rev, outg))]
145 map(repo.changelog.rev, outg))]
109
146
110 if not force and remoteheads != [nullid]:
147 if not force and remoteheads != [nullid]:
111 if remote.capable('branchmap'):
148 if remote.capable('branchmap'):
112 # Check for each named branch if we're creating new remote heads.
149 # Check for each named branch if we're creating new remote heads.
113 # To be a remote head after push, node must be either:
150 # To be a remote head after push, node must be either:
114 # - unknown locally
151 # - unknown locally
115 # - a local outgoing head descended from update
152 # - a local outgoing head descended from update
116 # - a remote head that's known locally and not
153 # - a remote head that's known locally and not
117 # ancestral to an outgoing head
154 # ancestral to an outgoing head
118
155
119 # 1. Create set of branches involved in the push.
156 # 1. Create set of branches involved in the push.
120 branches = set(repo[n].branch() for n in outg)
157 branches = set(repo[n].branch() for n in outg)
121
158
122 # 2. Check for new branches on the remote.
159 # 2. Check for new branches on the remote.
123 remotemap = remote.branchmap()
160 remotemap = remote.branchmap()
124 newbranches = branches - set(remotemap)
161 newbranches = branches - set(remotemap)
125 if newbranches and not newbranch: # new branch requires --new-branch
162 if newbranches and not newbranch: # new branch requires --new-branch
126 branchnames = ', '.join(sorted(newbranches))
163 branchnames = ', '.join(sorted(newbranches))
127 raise util.Abort(_("push creates new remote branches: %s!")
164 raise util.Abort(_("push creates new remote branches: %s!")
128 % branchnames,
165 % branchnames,
129 hint=_("use 'hg push --new-branch' to create"
166 hint=_("use 'hg push --new-branch' to create"
130 " new remote branches"))
167 " new remote branches"))
131 branches.difference_update(newbranches)
168 branches.difference_update(newbranches)
132
169
133 # 3. Construct the initial oldmap and newmap dicts.
170 # 3. Construct the initial oldmap and newmap dicts.
134 # They contain information about the remote heads before and
171 # They contain information about the remote heads before and
135 # after the push, respectively.
172 # after the push, respectively.
136 # Heads not found locally are not included in either dict,
173 # Heads not found locally are not included in either dict,
137 # since they won't be affected by the push.
174 # since they won't be affected by the push.
138 # unsynced contains all branches with incoming changesets.
175 # unsynced contains all branches with incoming changesets.
139 oldmap = {}
176 oldmap = {}
140 newmap = {}
177 newmap = {}
141 unsynced = set()
178 unsynced = set()
142 for branch in branches:
179 for branch in branches:
143 remotebrheads = remotemap[branch]
180 remotebrheads = remotemap[branch]
144 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
181 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
145 oldmap[branch] = prunedbrheads
182 oldmap[branch] = prunedbrheads
146 newmap[branch] = list(prunedbrheads)
183 newmap[branch] = list(prunedbrheads)
147 if len(remotebrheads) > len(prunedbrheads):
184 if len(remotebrheads) > len(prunedbrheads):
148 unsynced.add(branch)
185 unsynced.add(branch)
149
186
150 # 4. Update newmap with outgoing changes.
187 # 4. Update newmap with outgoing changes.
151 # This will possibly add new heads and remove existing ones.
188 # This will possibly add new heads and remove existing ones.
152 ctxgen = (repo[n] for n in outg)
189 ctxgen = (repo[n] for n in outg)
153 repo._updatebranchcache(newmap, ctxgen)
190 repo._updatebranchcache(newmap, ctxgen)
154
191
155 else:
192 else:
156 # 1-4b. old servers: Check for new topological heads.
193 # 1-4b. old servers: Check for new topological heads.
157 # Construct {old,new}map with branch = None (topological branch).
194 # Construct {old,new}map with branch = None (topological branch).
158 # (code based on _updatebranchcache)
195 # (code based on _updatebranchcache)
159 oldheads = set(h for h in remoteheads if h in cl.nodemap)
196 oldheads = set(h for h in remoteheads if h in cl.nodemap)
160 newheads = oldheads.union(outg)
197 newheads = oldheads.union(outg)
161 if len(newheads) > 1:
198 if len(newheads) > 1:
162 for latest in reversed(outg):
199 for latest in reversed(outg):
163 if latest not in newheads:
200 if latest not in newheads:
164 continue
201 continue
165 minhrev = min(cl.rev(h) for h in newheads)
202 minhrev = min(cl.rev(h) for h in newheads)
166 reachable = cl.reachable(latest, cl.node(minhrev))
203 reachable = cl.reachable(latest, cl.node(minhrev))
167 reachable.remove(latest)
204 reachable.remove(latest)
168 newheads.difference_update(reachable)
205 newheads.difference_update(reachable)
169 branches = set([None])
206 branches = set([None])
170 newmap = {None: newheads}
207 newmap = {None: newheads}
171 oldmap = {None: oldheads}
208 oldmap = {None: oldheads}
172 unsynced = inc and branches or set()
209 unsynced = inc and branches or set()
173
210
174 # 5. Check for new heads.
211 # 5. Check for new heads.
175 # If there are more heads after the push than before, a suitable
212 # If there are more heads after the push than before, a suitable
176 # error message, depending on unsynced status, is displayed.
213 # error message, depending on unsynced status, is displayed.
177 error = None
214 error = None
178 for branch in branches:
215 for branch in branches:
179 newhs = set(newmap[branch])
216 newhs = set(newmap[branch])
180 oldhs = set(oldmap[branch])
217 oldhs = set(oldmap[branch])
181 if len(newhs) > len(oldhs):
218 if len(newhs) > len(oldhs):
182 dhs = list(newhs - oldhs)
219 dhs = list(newhs - oldhs)
183 if error is None:
220 if error is None:
184 if branch not in ('default', None):
221 if branch not in ('default', None):
185 error = _("push creates new remote head %s "
222 error = _("push creates new remote head %s "
186 "on branch '%s'!") % (short(dhs[0]), branch)
223 "on branch '%s'!") % (short(dhs[0]), branch)
187 else:
224 else:
188 error = _("push creates new remote head %s!"
225 error = _("push creates new remote head %s!"
189 ) % short(dhs[0])
226 ) % short(dhs[0])
190 if branch in unsynced:
227 if branch in unsynced:
191 hint = _("you should pull and merge or "
228 hint = _("you should pull and merge or "
192 "use push -f to force")
229 "use push -f to force")
193 else:
230 else:
194 hint = _("did you forget to merge? "
231 hint = _("did you forget to merge? "
195 "use push -f to force")
232 "use push -f to force")
196 if branch is not None:
233 if branch is not None:
197 repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
234 repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
198 for h in dhs:
235 for h in dhs:
199 repo.ui.note(_("new remote head %s\n") % short(h))
236 repo.ui.note(_("new remote head %s\n") % short(h))
200 if error:
237 if error:
201 raise util.Abort(error, hint=hint)
238 raise util.Abort(error, hint=hint)
202
239
203 # 6. Check for unsynced changes on involved branches.
240 # 6. Check for unsynced changes on involved branches.
204 if unsynced:
241 if unsynced:
205 repo.ui.warn(_("note: unsynced remote changes!\n"))
242 repo.ui.warn(_("note: unsynced remote changes!\n"))
206
243
207 if revs is None:
244 if revs is None:
208 # use the fast path, no race possible on push
245 # use the fast path, no race possible on push
209 cg = repo._changegroup(outg, 'push')
246 cg = repo._changegroup(outg, 'push')
210 else:
247 else:
211 cg = repo.getbundle('push', heads=revs, common=common)
248 cg = repo.getlocalbundle('push', outgoing)
212 # no need to compute outg ancestor. All node in outg have either:
249 # no need to compute outg ancestor. All node in outg have either:
213 # - parents in outg
250 # - parents in outg
214 # - parents in common
251 # - parents in common
215 # - nullid parent
252 # - nullid parent
216 rset = repo.set('heads(%ln + %ln)', common, outg)
253 rset = repo.set('heads(%ln + %ln)', common, outg)
217 futureheads = [ctx.node() for ctx in rset]
254 futureheads = [ctx.node() for ctx in rset]
218 return cg, remoteheads, futureheads
255 return cg, remoteheads, futureheads
@@ -1,584 +1,584 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 import lock, util, extensions, error, node
13 import lock, util, extensions, error, node
14 import cmdutil, discovery
14 import cmdutil, discovery
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(util.urllocalpath(path))
20 path = util.expandpath(util.urllocalpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, repo, branches, revs):
23 def addbranchrevs(lrepo, repo, branches, revs):
24 hashbranch, branches = branches
24 hashbranch, branches = branches
25 if not hashbranch and not branches:
25 if not hashbranch and not branches:
26 return revs or None, revs and revs[0] or None
26 return revs or None, revs and revs[0] or None
27 revs = revs and list(revs) or []
27 revs = revs and list(revs) or []
28 if not repo.capable('branchmap'):
28 if not repo.capable('branchmap'):
29 if branches:
29 if branches:
30 raise util.Abort(_("remote branch lookup not supported"))
30 raise util.Abort(_("remote branch lookup not supported"))
31 revs.append(hashbranch)
31 revs.append(hashbranch)
32 return revs, revs[0]
32 return revs, revs[0]
33 branchmap = repo.branchmap()
33 branchmap = repo.branchmap()
34
34
35 def primary(branch):
35 def primary(branch):
36 if branch == '.':
36 if branch == '.':
37 if not lrepo or not lrepo.local():
37 if not lrepo or not lrepo.local():
38 raise util.Abort(_("dirstate branch not accessible"))
38 raise util.Abort(_("dirstate branch not accessible"))
39 branch = lrepo.dirstate.branch()
39 branch = lrepo.dirstate.branch()
40 if branch in branchmap:
40 if branch in branchmap:
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 return True
42 return True
43 else:
43 else:
44 return False
44 return False
45
45
46 for branch in branches:
46 for branch in branches:
47 if not primary(branch):
47 if not primary(branch):
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 if hashbranch:
49 if hashbranch:
50 if not primary(hashbranch):
50 if not primary(hashbranch):
51 revs.append(hashbranch)
51 revs.append(hashbranch)
52 return revs, revs[0]
52 return revs, revs[0]
53
53
54 def parseurl(path, branches=None):
54 def parseurl(path, branches=None):
55 '''parse url#branch, returning (url, (branch, branches))'''
55 '''parse url#branch, returning (url, (branch, branches))'''
56
56
57 u = util.url(path)
57 u = util.url(path)
58 branch = None
58 branch = None
59 if u.fragment:
59 if u.fragment:
60 branch = u.fragment
60 branch = u.fragment
61 u.fragment = None
61 u.fragment = None
62 return str(u), (branch, branches or [])
62 return str(u), (branch, branches or [])
63
63
64 schemes = {
64 schemes = {
65 'bundle': bundlerepo,
65 'bundle': bundlerepo,
66 'file': _local,
66 'file': _local,
67 'http': httprepo,
67 'http': httprepo,
68 'https': httprepo,
68 'https': httprepo,
69 'ssh': sshrepo,
69 'ssh': sshrepo,
70 'static-http': statichttprepo,
70 'static-http': statichttprepo,
71 }
71 }
72
72
73 def _peerlookup(path):
73 def _peerlookup(path):
74 u = util.url(path)
74 u = util.url(path)
75 scheme = u.scheme or 'file'
75 scheme = u.scheme or 'file'
76 thing = schemes.get(scheme) or schemes['file']
76 thing = schemes.get(scheme) or schemes['file']
77 try:
77 try:
78 return thing(path)
78 return thing(path)
79 except TypeError:
79 except TypeError:
80 return thing
80 return thing
81
81
82 def islocal(repo):
82 def islocal(repo):
83 '''return true if repo or path is local'''
83 '''return true if repo or path is local'''
84 if isinstance(repo, str):
84 if isinstance(repo, str):
85 try:
85 try:
86 return _peerlookup(repo).islocal(repo)
86 return _peerlookup(repo).islocal(repo)
87 except AttributeError:
87 except AttributeError:
88 return False
88 return False
89 return repo.local()
89 return repo.local()
90
90
91 def repository(ui, path='', create=False):
91 def repository(ui, path='', create=False):
92 """return a repository object for the specified path"""
92 """return a repository object for the specified path"""
93 repo = _peerlookup(path).instance(ui, path, create)
93 repo = _peerlookup(path).instance(ui, path, create)
94 ui = getattr(repo, "ui", ui)
94 ui = getattr(repo, "ui", ui)
95 for name, module in extensions.extensions():
95 for name, module in extensions.extensions():
96 hook = getattr(module, 'reposetup', None)
96 hook = getattr(module, 'reposetup', None)
97 if hook:
97 if hook:
98 hook(ui, repo)
98 hook(ui, repo)
99 return repo
99 return repo
100
100
101 def peer(uiorrepo, opts, path, create=False):
101 def peer(uiorrepo, opts, path, create=False):
102 '''return a repository peer for the specified path'''
102 '''return a repository peer for the specified path'''
103 rui = remoteui(uiorrepo, opts)
103 rui = remoteui(uiorrepo, opts)
104 return repository(rui, path, create)
104 return repository(rui, path, create)
105
105
106 def defaultdest(source):
106 def defaultdest(source):
107 '''return default destination of clone if none is given'''
107 '''return default destination of clone if none is given'''
108 return os.path.basename(os.path.normpath(source))
108 return os.path.basename(os.path.normpath(source))
109
109
110 def share(ui, source, dest=None, update=True):
110 def share(ui, source, dest=None, update=True):
111 '''create a shared repository'''
111 '''create a shared repository'''
112
112
113 if not islocal(source):
113 if not islocal(source):
114 raise util.Abort(_('can only share local repositories'))
114 raise util.Abort(_('can only share local repositories'))
115
115
116 if not dest:
116 if not dest:
117 dest = defaultdest(source)
117 dest = defaultdest(source)
118 else:
118 else:
119 dest = ui.expandpath(dest)
119 dest = ui.expandpath(dest)
120
120
121 if isinstance(source, str):
121 if isinstance(source, str):
122 origsource = ui.expandpath(source)
122 origsource = ui.expandpath(source)
123 source, branches = parseurl(origsource)
123 source, branches = parseurl(origsource)
124 srcrepo = repository(ui, source)
124 srcrepo = repository(ui, source)
125 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
125 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
126 else:
126 else:
127 srcrepo = source
127 srcrepo = source
128 origsource = source = srcrepo.url()
128 origsource = source = srcrepo.url()
129 checkout = None
129 checkout = None
130
130
131 sharedpath = srcrepo.sharedpath # if our source is already sharing
131 sharedpath = srcrepo.sharedpath # if our source is already sharing
132
132
133 root = os.path.realpath(dest)
133 root = os.path.realpath(dest)
134 roothg = os.path.join(root, '.hg')
134 roothg = os.path.join(root, '.hg')
135
135
136 if os.path.exists(roothg):
136 if os.path.exists(roothg):
137 raise util.Abort(_('destination already exists'))
137 raise util.Abort(_('destination already exists'))
138
138
139 if not os.path.isdir(root):
139 if not os.path.isdir(root):
140 os.mkdir(root)
140 os.mkdir(root)
141 util.makedir(roothg, notindexed=True)
141 util.makedir(roothg, notindexed=True)
142
142
143 requirements = ''
143 requirements = ''
144 try:
144 try:
145 requirements = srcrepo.opener.read('requires')
145 requirements = srcrepo.opener.read('requires')
146 except IOError, inst:
146 except IOError, inst:
147 if inst.errno != errno.ENOENT:
147 if inst.errno != errno.ENOENT:
148 raise
148 raise
149
149
150 requirements += 'shared\n'
150 requirements += 'shared\n'
151 util.writefile(os.path.join(roothg, 'requires'), requirements)
151 util.writefile(os.path.join(roothg, 'requires'), requirements)
152 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
152 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
153
153
154 r = repository(ui, root)
154 r = repository(ui, root)
155
155
156 default = srcrepo.ui.config('paths', 'default')
156 default = srcrepo.ui.config('paths', 'default')
157 if default:
157 if default:
158 fp = r.opener("hgrc", "w", text=True)
158 fp = r.opener("hgrc", "w", text=True)
159 fp.write("[paths]\n")
159 fp.write("[paths]\n")
160 fp.write("default = %s\n" % default)
160 fp.write("default = %s\n" % default)
161 fp.close()
161 fp.close()
162
162
163 if update:
163 if update:
164 r.ui.status(_("updating working directory\n"))
164 r.ui.status(_("updating working directory\n"))
165 if update is not True:
165 if update is not True:
166 checkout = update
166 checkout = update
167 for test in (checkout, 'default', 'tip'):
167 for test in (checkout, 'default', 'tip'):
168 if test is None:
168 if test is None:
169 continue
169 continue
170 try:
170 try:
171 uprev = r.lookup(test)
171 uprev = r.lookup(test)
172 break
172 break
173 except error.RepoLookupError:
173 except error.RepoLookupError:
174 continue
174 continue
175 _update(r, uprev)
175 _update(r, uprev)
176
176
177 def copystore(ui, srcrepo, destpath):
177 def copystore(ui, srcrepo, destpath):
178 '''copy files from store of srcrepo in destpath
178 '''copy files from store of srcrepo in destpath
179
179
180 returns destlock
180 returns destlock
181 '''
181 '''
182 destlock = None
182 destlock = None
183 try:
183 try:
184 hardlink = None
184 hardlink = None
185 num = 0
185 num = 0
186 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
186 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
187 for f in srcrepo.store.copylist():
187 for f in srcrepo.store.copylist():
188 if srcpublishing and f.endswith('phaseroots'):
188 if srcpublishing and f.endswith('phaseroots'):
189 continue
189 continue
190 src = os.path.join(srcrepo.sharedpath, f)
190 src = os.path.join(srcrepo.sharedpath, f)
191 dst = os.path.join(destpath, f)
191 dst = os.path.join(destpath, f)
192 dstbase = os.path.dirname(dst)
192 dstbase = os.path.dirname(dst)
193 if dstbase and not os.path.exists(dstbase):
193 if dstbase and not os.path.exists(dstbase):
194 os.mkdir(dstbase)
194 os.mkdir(dstbase)
195 if os.path.exists(src):
195 if os.path.exists(src):
196 if dst.endswith('data'):
196 if dst.endswith('data'):
197 # lock to avoid premature writing to the target
197 # lock to avoid premature writing to the target
198 destlock = lock.lock(os.path.join(dstbase, "lock"))
198 destlock = lock.lock(os.path.join(dstbase, "lock"))
199 hardlink, n = util.copyfiles(src, dst, hardlink)
199 hardlink, n = util.copyfiles(src, dst, hardlink)
200 num += n
200 num += n
201 if hardlink:
201 if hardlink:
202 ui.debug("linked %d files\n" % num)
202 ui.debug("linked %d files\n" % num)
203 else:
203 else:
204 ui.debug("copied %d files\n" % num)
204 ui.debug("copied %d files\n" % num)
205 return destlock
205 return destlock
206 except:
206 except:
207 release(destlock)
207 release(destlock)
208 raise
208 raise
209
209
210 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
210 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
211 update=True, stream=False, branch=None):
211 update=True, stream=False, branch=None):
212 """Make a copy of an existing repository.
212 """Make a copy of an existing repository.
213
213
214 Create a copy of an existing repository in a new directory. The
214 Create a copy of an existing repository in a new directory. The
215 source and destination are URLs, as passed to the repository
215 source and destination are URLs, as passed to the repository
216 function. Returns a pair of repository objects, the source and
216 function. Returns a pair of repository objects, the source and
217 newly created destination.
217 newly created destination.
218
218
219 The location of the source is added to the new repository's
219 The location of the source is added to the new repository's
220 .hg/hgrc file, as the default to be used for future pulls and
220 .hg/hgrc file, as the default to be used for future pulls and
221 pushes.
221 pushes.
222
222
223 If an exception is raised, the partly cloned/updated destination
223 If an exception is raised, the partly cloned/updated destination
224 repository will be deleted.
224 repository will be deleted.
225
225
226 Arguments:
226 Arguments:
227
227
228 source: repository object or URL
228 source: repository object or URL
229
229
230 dest: URL of destination repository to create (defaults to base
230 dest: URL of destination repository to create (defaults to base
231 name of source repository)
231 name of source repository)
232
232
233 pull: always pull from source repository, even in local case
233 pull: always pull from source repository, even in local case
234
234
235 stream: stream raw data uncompressed from repository (fast over
235 stream: stream raw data uncompressed from repository (fast over
236 LAN, slow over WAN)
236 LAN, slow over WAN)
237
237
238 rev: revision to clone up to (implies pull=True)
238 rev: revision to clone up to (implies pull=True)
239
239
240 update: update working directory after clone completes, if
240 update: update working directory after clone completes, if
241 destination is local repository (True means update to default rev,
241 destination is local repository (True means update to default rev,
242 anything else is treated as a revision)
242 anything else is treated as a revision)
243
243
244 branch: branches to clone
244 branch: branches to clone
245 """
245 """
246
246
247 if isinstance(source, str):
247 if isinstance(source, str):
248 origsource = ui.expandpath(source)
248 origsource = ui.expandpath(source)
249 source, branch = parseurl(origsource, branch)
249 source, branch = parseurl(origsource, branch)
250 srcrepo = repository(remoteui(ui, peeropts), source)
250 srcrepo = repository(remoteui(ui, peeropts), source)
251 else:
251 else:
252 srcrepo = source
252 srcrepo = source
253 branch = (None, branch or [])
253 branch = (None, branch or [])
254 origsource = source = srcrepo.url()
254 origsource = source = srcrepo.url()
255 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
255 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
256
256
257 if dest is None:
257 if dest is None:
258 dest = defaultdest(source)
258 dest = defaultdest(source)
259 ui.status(_("destination directory: %s\n") % dest)
259 ui.status(_("destination directory: %s\n") % dest)
260 else:
260 else:
261 dest = ui.expandpath(dest)
261 dest = ui.expandpath(dest)
262
262
263 dest = util.urllocalpath(dest)
263 dest = util.urllocalpath(dest)
264 source = util.urllocalpath(source)
264 source = util.urllocalpath(source)
265
265
266 if os.path.exists(dest):
266 if os.path.exists(dest):
267 if not os.path.isdir(dest):
267 if not os.path.isdir(dest):
268 raise util.Abort(_("destination '%s' already exists") % dest)
268 raise util.Abort(_("destination '%s' already exists") % dest)
269 elif os.listdir(dest):
269 elif os.listdir(dest):
270 raise util.Abort(_("destination '%s' is not empty") % dest)
270 raise util.Abort(_("destination '%s' is not empty") % dest)
271
271
272 class DirCleanup(object):
272 class DirCleanup(object):
273 def __init__(self, dir_):
273 def __init__(self, dir_):
274 self.rmtree = shutil.rmtree
274 self.rmtree = shutil.rmtree
275 self.dir_ = dir_
275 self.dir_ = dir_
276 def close(self):
276 def close(self):
277 self.dir_ = None
277 self.dir_ = None
278 def cleanup(self):
278 def cleanup(self):
279 if self.dir_:
279 if self.dir_:
280 self.rmtree(self.dir_, True)
280 self.rmtree(self.dir_, True)
281
281
282 srclock = destlock = dircleanup = None
282 srclock = destlock = dircleanup = None
283 try:
283 try:
284 abspath = origsource
284 abspath = origsource
285 if islocal(origsource):
285 if islocal(origsource):
286 abspath = os.path.abspath(util.urllocalpath(origsource))
286 abspath = os.path.abspath(util.urllocalpath(origsource))
287
287
288 if islocal(dest):
288 if islocal(dest):
289 dircleanup = DirCleanup(dest)
289 dircleanup = DirCleanup(dest)
290
290
291 copy = False
291 copy = False
292 if srcrepo.cancopy() and islocal(dest):
292 if srcrepo.cancopy() and islocal(dest):
293 copy = not pull and not rev
293 copy = not pull and not rev
294
294
295 if copy:
295 if copy:
296 try:
296 try:
297 # we use a lock here because if we race with commit, we
297 # we use a lock here because if we race with commit, we
298 # can end up with extra data in the cloned revlogs that's
298 # can end up with extra data in the cloned revlogs that's
299 # not pointed to by changesets, thus causing verify to
299 # not pointed to by changesets, thus causing verify to
300 # fail
300 # fail
301 srclock = srcrepo.lock(wait=False)
301 srclock = srcrepo.lock(wait=False)
302 except error.LockError:
302 except error.LockError:
303 copy = False
303 copy = False
304
304
305 if copy:
305 if copy:
306 srcrepo.hook('preoutgoing', throw=True, source='clone')
306 srcrepo.hook('preoutgoing', throw=True, source='clone')
307 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
307 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
308 if not os.path.exists(dest):
308 if not os.path.exists(dest):
309 os.mkdir(dest)
309 os.mkdir(dest)
310 else:
310 else:
311 # only clean up directories we create ourselves
311 # only clean up directories we create ourselves
312 dircleanup.dir_ = hgdir
312 dircleanup.dir_ = hgdir
313 try:
313 try:
314 destpath = hgdir
314 destpath = hgdir
315 util.makedir(destpath, notindexed=True)
315 util.makedir(destpath, notindexed=True)
316 except OSError, inst:
316 except OSError, inst:
317 if inst.errno == errno.EEXIST:
317 if inst.errno == errno.EEXIST:
318 dircleanup.close()
318 dircleanup.close()
319 raise util.Abort(_("destination '%s' already exists")
319 raise util.Abort(_("destination '%s' already exists")
320 % dest)
320 % dest)
321 raise
321 raise
322
322
323 destlock = copystore(ui, srcrepo, destpath)
323 destlock = copystore(ui, srcrepo, destpath)
324
324
325 # we need to re-init the repo after manually copying the data
325 # we need to re-init the repo after manually copying the data
326 # into it
326 # into it
327 destrepo = repository(remoteui(ui, peeropts), dest)
327 destrepo = repository(remoteui(ui, peeropts), dest)
328 srcrepo.hook('outgoing', source='clone',
328 srcrepo.hook('outgoing', source='clone',
329 node=node.hex(node.nullid))
329 node=node.hex(node.nullid))
330 else:
330 else:
331 try:
331 try:
332 destrepo = repository(remoteui(ui, peeropts), dest,
332 destrepo = repository(remoteui(ui, peeropts), dest,
333 create=True)
333 create=True)
334 except OSError, inst:
334 except OSError, inst:
335 if inst.errno == errno.EEXIST:
335 if inst.errno == errno.EEXIST:
336 dircleanup.close()
336 dircleanup.close()
337 raise util.Abort(_("destination '%s' already exists")
337 raise util.Abort(_("destination '%s' already exists")
338 % dest)
338 % dest)
339 raise
339 raise
340
340
341 revs = None
341 revs = None
342 if rev:
342 if rev:
343 if not srcrepo.capable('lookup'):
343 if not srcrepo.capable('lookup'):
344 raise util.Abort(_("src repository does not support "
344 raise util.Abort(_("src repository does not support "
345 "revision lookup and so doesn't "
345 "revision lookup and so doesn't "
346 "support clone by revision"))
346 "support clone by revision"))
347 revs = [srcrepo.lookup(r) for r in rev]
347 revs = [srcrepo.lookup(r) for r in rev]
348 checkout = revs[0]
348 checkout = revs[0]
349 if destrepo.local():
349 if destrepo.local():
350 destrepo.clone(srcrepo, heads=revs, stream=stream)
350 destrepo.clone(srcrepo, heads=revs, stream=stream)
351 elif srcrepo.local():
351 elif srcrepo.local():
352 srcrepo.push(destrepo, revs=revs)
352 srcrepo.push(destrepo, revs=revs)
353 else:
353 else:
354 raise util.Abort(_("clone from remote to remote not supported"))
354 raise util.Abort(_("clone from remote to remote not supported"))
355
355
356 if dircleanup:
356 if dircleanup:
357 dircleanup.close()
357 dircleanup.close()
358
358
359 # clone all bookmarks
359 # clone all bookmarks
360 if destrepo.local() and srcrepo.capable("pushkey"):
360 if destrepo.local() and srcrepo.capable("pushkey"):
361 rb = srcrepo.listkeys('bookmarks')
361 rb = srcrepo.listkeys('bookmarks')
362 for k, n in rb.iteritems():
362 for k, n in rb.iteritems():
363 try:
363 try:
364 m = destrepo.lookup(n)
364 m = destrepo.lookup(n)
365 destrepo._bookmarks[k] = m
365 destrepo._bookmarks[k] = m
366 except error.RepoLookupError:
366 except error.RepoLookupError:
367 pass
367 pass
368 if rb:
368 if rb:
369 bookmarks.write(destrepo)
369 bookmarks.write(destrepo)
370 elif srcrepo.local() and destrepo.capable("pushkey"):
370 elif srcrepo.local() and destrepo.capable("pushkey"):
371 for k, n in srcrepo._bookmarks.iteritems():
371 for k, n in srcrepo._bookmarks.iteritems():
372 destrepo.pushkey('bookmarks', k, '', hex(n))
372 destrepo.pushkey('bookmarks', k, '', hex(n))
373
373
374 if destrepo.local():
374 if destrepo.local():
375 fp = destrepo.opener("hgrc", "w", text=True)
375 fp = destrepo.opener("hgrc", "w", text=True)
376 fp.write("[paths]\n")
376 fp.write("[paths]\n")
377 u = util.url(abspath)
377 u = util.url(abspath)
378 u.passwd = None
378 u.passwd = None
379 defaulturl = str(u)
379 defaulturl = str(u)
380 fp.write("default = %s\n" % defaulturl)
380 fp.write("default = %s\n" % defaulturl)
381 fp.close()
381 fp.close()
382
382
383 destrepo.ui.setconfig('paths', 'default', defaulturl)
383 destrepo.ui.setconfig('paths', 'default', defaulturl)
384
384
385 if update:
385 if update:
386 if update is not True:
386 if update is not True:
387 checkout = update
387 checkout = update
388 if srcrepo.local():
388 if srcrepo.local():
389 checkout = srcrepo.lookup(update)
389 checkout = srcrepo.lookup(update)
390 for test in (checkout, 'default', 'tip'):
390 for test in (checkout, 'default', 'tip'):
391 if test is None:
391 if test is None:
392 continue
392 continue
393 try:
393 try:
394 uprev = destrepo.lookup(test)
394 uprev = destrepo.lookup(test)
395 break
395 break
396 except error.RepoLookupError:
396 except error.RepoLookupError:
397 continue
397 continue
398 bn = destrepo[uprev].branch()
398 bn = destrepo[uprev].branch()
399 destrepo.ui.status(_("updating to branch %s\n") % bn)
399 destrepo.ui.status(_("updating to branch %s\n") % bn)
400 _update(destrepo, uprev)
400 _update(destrepo, uprev)
401
401
402 return srcrepo, destrepo
402 return srcrepo, destrepo
403 finally:
403 finally:
404 release(srclock, destlock)
404 release(srclock, destlock)
405 if dircleanup is not None:
405 if dircleanup is not None:
406 dircleanup.cleanup()
406 dircleanup.cleanup()
407
407
408 def _showstats(repo, stats):
408 def _showstats(repo, stats):
409 repo.ui.status(_("%d files updated, %d files merged, "
409 repo.ui.status(_("%d files updated, %d files merged, "
410 "%d files removed, %d files unresolved\n") % stats)
410 "%d files removed, %d files unresolved\n") % stats)
411
411
412 def update(repo, node):
412 def update(repo, node):
413 """update the working directory to node, merging linear changes"""
413 """update the working directory to node, merging linear changes"""
414 stats = mergemod.update(repo, node, False, False, None)
414 stats = mergemod.update(repo, node, False, False, None)
415 _showstats(repo, stats)
415 _showstats(repo, stats)
416 if stats[3]:
416 if stats[3]:
417 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
417 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
418 return stats[3] > 0
418 return stats[3] > 0
419
419
420 # naming conflict in clone()
420 # naming conflict in clone()
421 _update = update
421 _update = update
422
422
423 def clean(repo, node, show_stats=True):
423 def clean(repo, node, show_stats=True):
424 """forcibly switch the working directory to node, clobbering changes"""
424 """forcibly switch the working directory to node, clobbering changes"""
425 stats = mergemod.update(repo, node, False, True, None)
425 stats = mergemod.update(repo, node, False, True, None)
426 if show_stats:
426 if show_stats:
427 _showstats(repo, stats)
427 _showstats(repo, stats)
428 return stats[3] > 0
428 return stats[3] > 0
429
429
430 def merge(repo, node, force=None, remind=True):
430 def merge(repo, node, force=None, remind=True):
431 """Branch merge with node, resolving changes. Return true if any
431 """Branch merge with node, resolving changes. Return true if any
432 unresolved conflicts."""
432 unresolved conflicts."""
433 stats = mergemod.update(repo, node, True, force, False)
433 stats = mergemod.update(repo, node, True, force, False)
434 _showstats(repo, stats)
434 _showstats(repo, stats)
435 if stats[3]:
435 if stats[3]:
436 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
436 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
437 "or 'hg update -C .' to abandon\n"))
437 "or 'hg update -C .' to abandon\n"))
438 elif remind:
438 elif remind:
439 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
439 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
440 return stats[3] > 0
440 return stats[3] > 0
441
441
442 def _incoming(displaychlist, subreporecurse, ui, repo, source,
442 def _incoming(displaychlist, subreporecurse, ui, repo, source,
443 opts, buffered=False):
443 opts, buffered=False):
444 """
444 """
445 Helper for incoming / gincoming.
445 Helper for incoming / gincoming.
446 displaychlist gets called with
446 displaychlist gets called with
447 (remoterepo, incomingchangesetlist, displayer) parameters,
447 (remoterepo, incomingchangesetlist, displayer) parameters,
448 and is supposed to contain only code that can't be unified.
448 and is supposed to contain only code that can't be unified.
449 """
449 """
450 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
450 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
451 other = peer(repo, opts, source)
451 other = peer(repo, opts, source)
452 ui.status(_('comparing with %s\n') % util.hidepassword(source))
452 ui.status(_('comparing with %s\n') % util.hidepassword(source))
453 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
453 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
454
454
455 if revs:
455 if revs:
456 revs = [other.lookup(rev) for rev in revs]
456 revs = [other.lookup(rev) for rev in revs]
457 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
457 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
458 revs, opts["bundle"], opts["force"])
458 revs, opts["bundle"], opts["force"])
459 try:
459 try:
460 if not chlist:
460 if not chlist:
461 ui.status(_("no changes found\n"))
461 ui.status(_("no changes found\n"))
462 return subreporecurse()
462 return subreporecurse()
463
463
464 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
464 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
465
465
466 # XXX once graphlog extension makes it into core,
466 # XXX once graphlog extension makes it into core,
467 # should be replaced by a if graph/else
467 # should be replaced by a if graph/else
468 displaychlist(other, chlist, displayer)
468 displaychlist(other, chlist, displayer)
469
469
470 displayer.close()
470 displayer.close()
471 finally:
471 finally:
472 cleanupfn()
472 cleanupfn()
473 subreporecurse()
473 subreporecurse()
474 return 0 # exit code is zero since we found incoming changes
474 return 0 # exit code is zero since we found incoming changes
475
475
476 def incoming(ui, repo, source, opts):
476 def incoming(ui, repo, source, opts):
477 def subreporecurse():
477 def subreporecurse():
478 ret = 1
478 ret = 1
479 if opts.get('subrepos'):
479 if opts.get('subrepos'):
480 ctx = repo[None]
480 ctx = repo[None]
481 for subpath in sorted(ctx.substate):
481 for subpath in sorted(ctx.substate):
482 sub = ctx.sub(subpath)
482 sub = ctx.sub(subpath)
483 ret = min(ret, sub.incoming(ui, source, opts))
483 ret = min(ret, sub.incoming(ui, source, opts))
484 return ret
484 return ret
485
485
486 def display(other, chlist, displayer):
486 def display(other, chlist, displayer):
487 limit = cmdutil.loglimit(opts)
487 limit = cmdutil.loglimit(opts)
488 if opts.get('newest_first'):
488 if opts.get('newest_first'):
489 chlist.reverse()
489 chlist.reverse()
490 count = 0
490 count = 0
491 for n in chlist:
491 for n in chlist:
492 if limit is not None and count >= limit:
492 if limit is not None and count >= limit:
493 break
493 break
494 parents = [p for p in other.changelog.parents(n) if p != nullid]
494 parents = [p for p in other.changelog.parents(n) if p != nullid]
495 if opts.get('no_merges') and len(parents) == 2:
495 if opts.get('no_merges') and len(parents) == 2:
496 continue
496 continue
497 count += 1
497 count += 1
498 displayer.show(other[n])
498 displayer.show(other[n])
499 return _incoming(display, subreporecurse, ui, repo, source, opts)
499 return _incoming(display, subreporecurse, ui, repo, source, opts)
500
500
501 def _outgoing(ui, repo, dest, opts):
501 def _outgoing(ui, repo, dest, opts):
502 dest = ui.expandpath(dest or 'default-push', dest or 'default')
502 dest = ui.expandpath(dest or 'default-push', dest or 'default')
503 dest, branches = parseurl(dest, opts.get('branch'))
503 dest, branches = parseurl(dest, opts.get('branch'))
504 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
504 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
505 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
505 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
506 if revs:
506 if revs:
507 revs = [repo.lookup(rev) for rev in revs]
507 revs = [repo.lookup(rev) for rev in revs]
508
508
509 other = peer(repo, opts, dest)
509 other = peer(repo, opts, dest)
510 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
510 outgoing = discovery.findcommonoutgoing(repo, other, revs,
511 force=opts.get('force'))
511 force=opts.get('force'))
512 o = repo.changelog.findmissing(common, outheads)
512 o = outgoing.missing
513 if not o:
513 if not o:
514 ui.status(_("no changes found\n"))
514 ui.status(_("no changes found\n"))
515 return None
515 return None
516 return o
516 return o
517
517
518 def outgoing(ui, repo, dest, opts):
518 def outgoing(ui, repo, dest, opts):
519 def recurse():
519 def recurse():
520 ret = 1
520 ret = 1
521 if opts.get('subrepos'):
521 if opts.get('subrepos'):
522 ctx = repo[None]
522 ctx = repo[None]
523 for subpath in sorted(ctx.substate):
523 for subpath in sorted(ctx.substate):
524 sub = ctx.sub(subpath)
524 sub = ctx.sub(subpath)
525 ret = min(ret, sub.outgoing(ui, dest, opts))
525 ret = min(ret, sub.outgoing(ui, dest, opts))
526 return ret
526 return ret
527
527
528 limit = cmdutil.loglimit(opts)
528 limit = cmdutil.loglimit(opts)
529 o = _outgoing(ui, repo, dest, opts)
529 o = _outgoing(ui, repo, dest, opts)
530 if o is None:
530 if o is None:
531 return recurse()
531 return recurse()
532
532
533 if opts.get('newest_first'):
533 if opts.get('newest_first'):
534 o.reverse()
534 o.reverse()
535 displayer = cmdutil.show_changeset(ui, repo, opts)
535 displayer = cmdutil.show_changeset(ui, repo, opts)
536 count = 0
536 count = 0
537 for n in o:
537 for n in o:
538 if limit is not None and count >= limit:
538 if limit is not None and count >= limit:
539 break
539 break
540 parents = [p for p in repo.changelog.parents(n) if p != nullid]
540 parents = [p for p in repo.changelog.parents(n) if p != nullid]
541 if opts.get('no_merges') and len(parents) == 2:
541 if opts.get('no_merges') and len(parents) == 2:
542 continue
542 continue
543 count += 1
543 count += 1
544 displayer.show(repo[n])
544 displayer.show(repo[n])
545 displayer.close()
545 displayer.close()
546 recurse()
546 recurse()
547 return 0 # exit code is zero since we found outgoing changes
547 return 0 # exit code is zero since we found outgoing changes
548
548
549 def revert(repo, node, choose):
549 def revert(repo, node, choose):
550 """revert changes to revision in node without updating dirstate"""
550 """revert changes to revision in node without updating dirstate"""
551 return mergemod.update(repo, node, False, True, choose)[3] > 0
551 return mergemod.update(repo, node, False, True, choose)[3] > 0
552
552
553 def verify(repo):
553 def verify(repo):
554 """verify the consistency of a repository"""
554 """verify the consistency of a repository"""
555 return verifymod.verify(repo)
555 return verifymod.verify(repo)
556
556
557 def remoteui(src, opts):
557 def remoteui(src, opts):
558 'build a remote ui from ui or repo and opts'
558 'build a remote ui from ui or repo and opts'
559 if util.safehasattr(src, 'baseui'): # looks like a repository
559 if util.safehasattr(src, 'baseui'): # looks like a repository
560 dst = src.baseui.copy() # drop repo-specific config
560 dst = src.baseui.copy() # drop repo-specific config
561 src = src.ui # copy target options from repo
561 src = src.ui # copy target options from repo
562 else: # assume it's a global ui object
562 else: # assume it's a global ui object
563 dst = src.copy() # keep all global options
563 dst = src.copy() # keep all global options
564
564
565 # copy ssh-specific options
565 # copy ssh-specific options
566 for o in 'ssh', 'remotecmd':
566 for o in 'ssh', 'remotecmd':
567 v = opts.get(o) or src.config('ui', o)
567 v = opts.get(o) or src.config('ui', o)
568 if v:
568 if v:
569 dst.setconfig("ui", o, v)
569 dst.setconfig("ui", o, v)
570
570
571 # copy bundle-specific options
571 # copy bundle-specific options
572 r = src.config('bundle', 'mainreporoot')
572 r = src.config('bundle', 'mainreporoot')
573 if r:
573 if r:
574 dst.setconfig('bundle', 'mainreporoot', r)
574 dst.setconfig('bundle', 'mainreporoot', r)
575
575
576 # copy selected local settings to the remote ui
576 # copy selected local settings to the remote ui
577 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
577 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
578 for key, val in src.configitems(sect):
578 for key, val in src.configitems(sect):
579 dst.setconfig(sect, key, val)
579 dst.setconfig(sect, key, val)
580 v = src.config('web', 'cacerts')
580 v = src.config('web', 'cacerts')
581 if v:
581 if v:
582 dst.setconfig('web', 'cacerts', util.expandpath(v))
582 dst.setconfig('web', 'cacerts', util.expandpath(v))
583
583
584 return dst
584 return dst
@@ -1,2260 +1,2270 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo, discovery, pushkey
10 import repo, changegroup, subrepo, discovery, pushkey
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import scmutil, util, extensions, hook, error, revset
13 import scmutil, util, extensions, hook, error, revset
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 from lock import release
17 from lock import release
18 import weakref, errno, os, time, inspect
18 import weakref, errno, os, time, inspect
19 propertycache = util.propertycache
19 propertycache = util.propertycache
20 filecache = scmutil.filecache
20 filecache = scmutil.filecache
21
21
22 class localrepository(repo.repository):
22 class localrepository(repo.repository):
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
24 'known', 'getbundle'))
24 'known', 'getbundle'))
25 supportedformats = set(('revlogv1', 'generaldelta'))
25 supportedformats = set(('revlogv1', 'generaldelta'))
26 supported = supportedformats | set(('store', 'fncache', 'shared',
26 supported = supportedformats | set(('store', 'fncache', 'shared',
27 'dotencode'))
27 'dotencode'))
28
28
29 def __init__(self, baseui, path=None, create=False):
29 def __init__(self, baseui, path=None, create=False):
30 repo.repository.__init__(self)
30 repo.repository.__init__(self)
31 self.root = os.path.realpath(util.expandpath(path))
31 self.root = os.path.realpath(util.expandpath(path))
32 self.path = os.path.join(self.root, ".hg")
32 self.path = os.path.join(self.root, ".hg")
33 self.origroot = path
33 self.origroot = path
34 self.auditor = scmutil.pathauditor(self.root, self._checknested)
34 self.auditor = scmutil.pathauditor(self.root, self._checknested)
35 self.opener = scmutil.opener(self.path)
35 self.opener = scmutil.opener(self.path)
36 self.wopener = scmutil.opener(self.root)
36 self.wopener = scmutil.opener(self.root)
37 self.baseui = baseui
37 self.baseui = baseui
38 self.ui = baseui.copy()
38 self.ui = baseui.copy()
39 self._dirtyphases = False
39 self._dirtyphases = False
40
40
41 try:
41 try:
42 self.ui.readconfig(self.join("hgrc"), self.root)
42 self.ui.readconfig(self.join("hgrc"), self.root)
43 extensions.loadall(self.ui)
43 extensions.loadall(self.ui)
44 except IOError:
44 except IOError:
45 pass
45 pass
46
46
47 if not os.path.isdir(self.path):
47 if not os.path.isdir(self.path):
48 if create:
48 if create:
49 if not os.path.exists(path):
49 if not os.path.exists(path):
50 util.makedirs(path)
50 util.makedirs(path)
51 util.makedir(self.path, notindexed=True)
51 util.makedir(self.path, notindexed=True)
52 requirements = ["revlogv1"]
52 requirements = ["revlogv1"]
53 if self.ui.configbool('format', 'usestore', True):
53 if self.ui.configbool('format', 'usestore', True):
54 os.mkdir(os.path.join(self.path, "store"))
54 os.mkdir(os.path.join(self.path, "store"))
55 requirements.append("store")
55 requirements.append("store")
56 if self.ui.configbool('format', 'usefncache', True):
56 if self.ui.configbool('format', 'usefncache', True):
57 requirements.append("fncache")
57 requirements.append("fncache")
58 if self.ui.configbool('format', 'dotencode', True):
58 if self.ui.configbool('format', 'dotencode', True):
59 requirements.append('dotencode')
59 requirements.append('dotencode')
60 # create an invalid changelog
60 # create an invalid changelog
61 self.opener.append(
61 self.opener.append(
62 "00changelog.i",
62 "00changelog.i",
63 '\0\0\0\2' # represents revlogv2
63 '\0\0\0\2' # represents revlogv2
64 ' dummy changelog to prevent using the old repo layout'
64 ' dummy changelog to prevent using the old repo layout'
65 )
65 )
66 if self.ui.configbool('format', 'generaldelta', False):
66 if self.ui.configbool('format', 'generaldelta', False):
67 requirements.append("generaldelta")
67 requirements.append("generaldelta")
68 requirements = set(requirements)
68 requirements = set(requirements)
69 else:
69 else:
70 raise error.RepoError(_("repository %s not found") % path)
70 raise error.RepoError(_("repository %s not found") % path)
71 elif create:
71 elif create:
72 raise error.RepoError(_("repository %s already exists") % path)
72 raise error.RepoError(_("repository %s already exists") % path)
73 else:
73 else:
74 try:
74 try:
75 requirements = scmutil.readrequires(self.opener, self.supported)
75 requirements = scmutil.readrequires(self.opener, self.supported)
76 except IOError, inst:
76 except IOError, inst:
77 if inst.errno != errno.ENOENT:
77 if inst.errno != errno.ENOENT:
78 raise
78 raise
79 requirements = set()
79 requirements = set()
80
80
81 self.sharedpath = self.path
81 self.sharedpath = self.path
82 try:
82 try:
83 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
83 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
84 if not os.path.exists(s):
84 if not os.path.exists(s):
85 raise error.RepoError(
85 raise error.RepoError(
86 _('.hg/sharedpath points to nonexistent directory %s') % s)
86 _('.hg/sharedpath points to nonexistent directory %s') % s)
87 self.sharedpath = s
87 self.sharedpath = s
88 except IOError, inst:
88 except IOError, inst:
89 if inst.errno != errno.ENOENT:
89 if inst.errno != errno.ENOENT:
90 raise
90 raise
91
91
92 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
92 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
93 self.spath = self.store.path
93 self.spath = self.store.path
94 self.sopener = self.store.opener
94 self.sopener = self.store.opener
95 self.sjoin = self.store.join
95 self.sjoin = self.store.join
96 self.opener.createmode = self.store.createmode
96 self.opener.createmode = self.store.createmode
97 self._applyrequirements(requirements)
97 self._applyrequirements(requirements)
98 if create:
98 if create:
99 self._writerequirements()
99 self._writerequirements()
100
100
101
101
102 self._branchcache = None
102 self._branchcache = None
103 self._branchcachetip = None
103 self._branchcachetip = None
104 self.filterpats = {}
104 self.filterpats = {}
105 self._datafilters = {}
105 self._datafilters = {}
106 self._transref = self._lockref = self._wlockref = None
106 self._transref = self._lockref = self._wlockref = None
107
107
108 # A cache for various files under .hg/ that tracks file changes,
108 # A cache for various files under .hg/ that tracks file changes,
109 # (used by the filecache decorator)
109 # (used by the filecache decorator)
110 #
110 #
111 # Maps a property name to its util.filecacheentry
111 # Maps a property name to its util.filecacheentry
112 self._filecache = {}
112 self._filecache = {}
113
113
114 def _applyrequirements(self, requirements):
114 def _applyrequirements(self, requirements):
115 self.requirements = requirements
115 self.requirements = requirements
116 openerreqs = set(('revlogv1', 'generaldelta'))
116 openerreqs = set(('revlogv1', 'generaldelta'))
117 self.sopener.options = dict((r, 1) for r in requirements
117 self.sopener.options = dict((r, 1) for r in requirements
118 if r in openerreqs)
118 if r in openerreqs)
119
119
120 def _writerequirements(self):
120 def _writerequirements(self):
121 reqfile = self.opener("requires", "w")
121 reqfile = self.opener("requires", "w")
122 for r in self.requirements:
122 for r in self.requirements:
123 reqfile.write("%s\n" % r)
123 reqfile.write("%s\n" % r)
124 reqfile.close()
124 reqfile.close()
125
125
126 def _checknested(self, path):
126 def _checknested(self, path):
127 """Determine if path is a legal nested repository."""
127 """Determine if path is a legal nested repository."""
128 if not path.startswith(self.root):
128 if not path.startswith(self.root):
129 return False
129 return False
130 subpath = path[len(self.root) + 1:]
130 subpath = path[len(self.root) + 1:]
131 normsubpath = util.pconvert(subpath)
131 normsubpath = util.pconvert(subpath)
132
132
133 # XXX: Checking against the current working copy is wrong in
133 # XXX: Checking against the current working copy is wrong in
134 # the sense that it can reject things like
134 # the sense that it can reject things like
135 #
135 #
136 # $ hg cat -r 10 sub/x.txt
136 # $ hg cat -r 10 sub/x.txt
137 #
137 #
138 # if sub/ is no longer a subrepository in the working copy
138 # if sub/ is no longer a subrepository in the working copy
139 # parent revision.
139 # parent revision.
140 #
140 #
141 # However, it can of course also allow things that would have
141 # However, it can of course also allow things that would have
142 # been rejected before, such as the above cat command if sub/
142 # been rejected before, such as the above cat command if sub/
143 # is a subrepository now, but was a normal directory before.
143 # is a subrepository now, but was a normal directory before.
144 # The old path auditor would have rejected by mistake since it
144 # The old path auditor would have rejected by mistake since it
145 # panics when it sees sub/.hg/.
145 # panics when it sees sub/.hg/.
146 #
146 #
147 # All in all, checking against the working copy seems sensible
147 # All in all, checking against the working copy seems sensible
148 # since we want to prevent access to nested repositories on
148 # since we want to prevent access to nested repositories on
149 # the filesystem *now*.
149 # the filesystem *now*.
150 ctx = self[None]
150 ctx = self[None]
151 parts = util.splitpath(subpath)
151 parts = util.splitpath(subpath)
152 while parts:
152 while parts:
153 prefix = '/'.join(parts)
153 prefix = '/'.join(parts)
154 if prefix in ctx.substate:
154 if prefix in ctx.substate:
155 if prefix == normsubpath:
155 if prefix == normsubpath:
156 return True
156 return True
157 else:
157 else:
158 sub = ctx.sub(prefix)
158 sub = ctx.sub(prefix)
159 return sub.checknested(subpath[len(prefix) + 1:])
159 return sub.checknested(subpath[len(prefix) + 1:])
160 else:
160 else:
161 parts.pop()
161 parts.pop()
162 return False
162 return False
163
163
164 @filecache('bookmarks')
164 @filecache('bookmarks')
165 def _bookmarks(self):
165 def _bookmarks(self):
166 return bookmarks.read(self)
166 return bookmarks.read(self)
167
167
168 @filecache('bookmarks.current')
168 @filecache('bookmarks.current')
169 def _bookmarkcurrent(self):
169 def _bookmarkcurrent(self):
170 return bookmarks.readcurrent(self)
170 return bookmarks.readcurrent(self)
171
171
172 def _writebookmarks(self, marks):
172 def _writebookmarks(self, marks):
173 bookmarks.write(self)
173 bookmarks.write(self)
174
174
175 @filecache('phaseroots')
175 @filecache('phaseroots')
176 def _phaseroots(self):
176 def _phaseroots(self):
177 self._dirtyphases = False
177 self._dirtyphases = False
178 phaseroots = phases.readroots(self)
178 phaseroots = phases.readroots(self)
179 phases.filterunknown(self, phaseroots)
179 phases.filterunknown(self, phaseroots)
180 return phaseroots
180 return phaseroots
181
181
182 @propertycache
182 @propertycache
183 def _phaserev(self):
183 def _phaserev(self):
184 cache = [phases.public] * len(self)
184 cache = [phases.public] * len(self)
185 for phase in phases.trackedphases:
185 for phase in phases.trackedphases:
186 roots = map(self.changelog.rev, self._phaseroots[phase])
186 roots = map(self.changelog.rev, self._phaseroots[phase])
187 if roots:
187 if roots:
188 for rev in roots:
188 for rev in roots:
189 cache[rev] = phase
189 cache[rev] = phase
190 for rev in self.changelog.descendants(*roots):
190 for rev in self.changelog.descendants(*roots):
191 cache[rev] = phase
191 cache[rev] = phase
192 return cache
192 return cache
193
193
194 @filecache('00changelog.i', True)
194 @filecache('00changelog.i', True)
195 def changelog(self):
195 def changelog(self):
196 c = changelog.changelog(self.sopener)
196 c = changelog.changelog(self.sopener)
197 if 'HG_PENDING' in os.environ:
197 if 'HG_PENDING' in os.environ:
198 p = os.environ['HG_PENDING']
198 p = os.environ['HG_PENDING']
199 if p.startswith(self.root):
199 if p.startswith(self.root):
200 c.readpending('00changelog.i.a')
200 c.readpending('00changelog.i.a')
201 return c
201 return c
202
202
203 @filecache('00manifest.i', True)
203 @filecache('00manifest.i', True)
204 def manifest(self):
204 def manifest(self):
205 return manifest.manifest(self.sopener)
205 return manifest.manifest(self.sopener)
206
206
207 @filecache('dirstate')
207 @filecache('dirstate')
208 def dirstate(self):
208 def dirstate(self):
209 warned = [0]
209 warned = [0]
210 def validate(node):
210 def validate(node):
211 try:
211 try:
212 self.changelog.rev(node)
212 self.changelog.rev(node)
213 return node
213 return node
214 except error.LookupError:
214 except error.LookupError:
215 if not warned[0]:
215 if not warned[0]:
216 warned[0] = True
216 warned[0] = True
217 self.ui.warn(_("warning: ignoring unknown"
217 self.ui.warn(_("warning: ignoring unknown"
218 " working parent %s!\n") % short(node))
218 " working parent %s!\n") % short(node))
219 return nullid
219 return nullid
220
220
221 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
221 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
222
222
223 def __getitem__(self, changeid):
223 def __getitem__(self, changeid):
224 if changeid is None:
224 if changeid is None:
225 return context.workingctx(self)
225 return context.workingctx(self)
226 return context.changectx(self, changeid)
226 return context.changectx(self, changeid)
227
227
228 def __contains__(self, changeid):
228 def __contains__(self, changeid):
229 try:
229 try:
230 return bool(self.lookup(changeid))
230 return bool(self.lookup(changeid))
231 except error.RepoLookupError:
231 except error.RepoLookupError:
232 return False
232 return False
233
233
234 def __nonzero__(self):
234 def __nonzero__(self):
235 return True
235 return True
236
236
237 def __len__(self):
237 def __len__(self):
238 return len(self.changelog)
238 return len(self.changelog)
239
239
240 def __iter__(self):
240 def __iter__(self):
241 for i in xrange(len(self)):
241 for i in xrange(len(self)):
242 yield i
242 yield i
243
243
244 def revs(self, expr, *args):
244 def revs(self, expr, *args):
245 '''Return a list of revisions matching the given revset'''
245 '''Return a list of revisions matching the given revset'''
246 expr = revset.formatspec(expr, *args)
246 expr = revset.formatspec(expr, *args)
247 m = revset.match(None, expr)
247 m = revset.match(None, expr)
248 return [r for r in m(self, range(len(self)))]
248 return [r for r in m(self, range(len(self)))]
249
249
250 def set(self, expr, *args):
250 def set(self, expr, *args):
251 '''
251 '''
252 Yield a context for each matching revision, after doing arg
252 Yield a context for each matching revision, after doing arg
253 replacement via revset.formatspec
253 replacement via revset.formatspec
254 '''
254 '''
255 for r in self.revs(expr, *args):
255 for r in self.revs(expr, *args):
256 yield self[r]
256 yield self[r]
257
257
258 def url(self):
258 def url(self):
259 return 'file:' + self.root
259 return 'file:' + self.root
260
260
261 def hook(self, name, throw=False, **args):
261 def hook(self, name, throw=False, **args):
262 return hook.hook(self.ui, self, name, throw, **args)
262 return hook.hook(self.ui, self, name, throw, **args)
263
263
264 tag_disallowed = ':\r\n'
264 tag_disallowed = ':\r\n'
265
265
266 def _tag(self, names, node, message, local, user, date, extra={}):
266 def _tag(self, names, node, message, local, user, date, extra={}):
267 if isinstance(names, str):
267 if isinstance(names, str):
268 allchars = names
268 allchars = names
269 names = (names,)
269 names = (names,)
270 else:
270 else:
271 allchars = ''.join(names)
271 allchars = ''.join(names)
272 for c in self.tag_disallowed:
272 for c in self.tag_disallowed:
273 if c in allchars:
273 if c in allchars:
274 raise util.Abort(_('%r cannot be used in a tag name') % c)
274 raise util.Abort(_('%r cannot be used in a tag name') % c)
275
275
276 branches = self.branchmap()
276 branches = self.branchmap()
277 for name in names:
277 for name in names:
278 self.hook('pretag', throw=True, node=hex(node), tag=name,
278 self.hook('pretag', throw=True, node=hex(node), tag=name,
279 local=local)
279 local=local)
280 if name in branches:
280 if name in branches:
281 self.ui.warn(_("warning: tag %s conflicts with existing"
281 self.ui.warn(_("warning: tag %s conflicts with existing"
282 " branch name\n") % name)
282 " branch name\n") % name)
283
283
284 def writetags(fp, names, munge, prevtags):
284 def writetags(fp, names, munge, prevtags):
285 fp.seek(0, 2)
285 fp.seek(0, 2)
286 if prevtags and prevtags[-1] != '\n':
286 if prevtags and prevtags[-1] != '\n':
287 fp.write('\n')
287 fp.write('\n')
288 for name in names:
288 for name in names:
289 m = munge and munge(name) or name
289 m = munge and munge(name) or name
290 if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
290 if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
291 old = self.tags().get(name, nullid)
291 old = self.tags().get(name, nullid)
292 fp.write('%s %s\n' % (hex(old), m))
292 fp.write('%s %s\n' % (hex(old), m))
293 fp.write('%s %s\n' % (hex(node), m))
293 fp.write('%s %s\n' % (hex(node), m))
294 fp.close()
294 fp.close()
295
295
296 prevtags = ''
296 prevtags = ''
297 if local:
297 if local:
298 try:
298 try:
299 fp = self.opener('localtags', 'r+')
299 fp = self.opener('localtags', 'r+')
300 except IOError:
300 except IOError:
301 fp = self.opener('localtags', 'a')
301 fp = self.opener('localtags', 'a')
302 else:
302 else:
303 prevtags = fp.read()
303 prevtags = fp.read()
304
304
305 # local tags are stored in the current charset
305 # local tags are stored in the current charset
306 writetags(fp, names, None, prevtags)
306 writetags(fp, names, None, prevtags)
307 for name in names:
307 for name in names:
308 self.hook('tag', node=hex(node), tag=name, local=local)
308 self.hook('tag', node=hex(node), tag=name, local=local)
309 return
309 return
310
310
311 try:
311 try:
312 fp = self.wfile('.hgtags', 'rb+')
312 fp = self.wfile('.hgtags', 'rb+')
313 except IOError, e:
313 except IOError, e:
314 if e.errno != errno.ENOENT:
314 if e.errno != errno.ENOENT:
315 raise
315 raise
316 fp = self.wfile('.hgtags', 'ab')
316 fp = self.wfile('.hgtags', 'ab')
317 else:
317 else:
318 prevtags = fp.read()
318 prevtags = fp.read()
319
319
320 # committed tags are stored in UTF-8
320 # committed tags are stored in UTF-8
321 writetags(fp, names, encoding.fromlocal, prevtags)
321 writetags(fp, names, encoding.fromlocal, prevtags)
322
322
323 fp.close()
323 fp.close()
324
324
325 if '.hgtags' not in self.dirstate:
325 if '.hgtags' not in self.dirstate:
326 self[None].add(['.hgtags'])
326 self[None].add(['.hgtags'])
327
327
328 m = matchmod.exact(self.root, '', ['.hgtags'])
328 m = matchmod.exact(self.root, '', ['.hgtags'])
329 tagnode = self.commit(message, user, date, extra=extra, match=m)
329 tagnode = self.commit(message, user, date, extra=extra, match=m)
330
330
331 for name in names:
331 for name in names:
332 self.hook('tag', node=hex(node), tag=name, local=local)
332 self.hook('tag', node=hex(node), tag=name, local=local)
333
333
334 return tagnode
334 return tagnode
335
335
336 def tag(self, names, node, message, local, user, date):
336 def tag(self, names, node, message, local, user, date):
337 '''tag a revision with one or more symbolic names.
337 '''tag a revision with one or more symbolic names.
338
338
339 names is a list of strings or, when adding a single tag, names may be a
339 names is a list of strings or, when adding a single tag, names may be a
340 string.
340 string.
341
341
342 if local is True, the tags are stored in a per-repository file.
342 if local is True, the tags are stored in a per-repository file.
343 otherwise, they are stored in the .hgtags file, and a new
343 otherwise, they are stored in the .hgtags file, and a new
344 changeset is committed with the change.
344 changeset is committed with the change.
345
345
346 keyword arguments:
346 keyword arguments:
347
347
348 local: whether to store tags in non-version-controlled file
348 local: whether to store tags in non-version-controlled file
349 (default False)
349 (default False)
350
350
351 message: commit message to use if committing
351 message: commit message to use if committing
352
352
353 user: name of user to use if committing
353 user: name of user to use if committing
354
354
355 date: date tuple to use if committing'''
355 date: date tuple to use if committing'''
356
356
357 if not local:
357 if not local:
358 for x in self.status()[:5]:
358 for x in self.status()[:5]:
359 if '.hgtags' in x:
359 if '.hgtags' in x:
360 raise util.Abort(_('working copy of .hgtags is changed '
360 raise util.Abort(_('working copy of .hgtags is changed '
361 '(please commit .hgtags manually)'))
361 '(please commit .hgtags manually)'))
362
362
363 self.tags() # instantiate the cache
363 self.tags() # instantiate the cache
364 self._tag(names, node, message, local, user, date)
364 self._tag(names, node, message, local, user, date)
365
365
366 @propertycache
366 @propertycache
367 def _tagscache(self):
367 def _tagscache(self):
368 '''Returns a tagscache object that contains various tags related caches.'''
368 '''Returns a tagscache object that contains various tags related caches.'''
369
369
370 # This simplifies its cache management by having one decorated
370 # This simplifies its cache management by having one decorated
371 # function (this one) and the rest simply fetch things from it.
371 # function (this one) and the rest simply fetch things from it.
372 class tagscache(object):
372 class tagscache(object):
373 def __init__(self):
373 def __init__(self):
374 # These two define the set of tags for this repository. tags
374 # These two define the set of tags for this repository. tags
375 # maps tag name to node; tagtypes maps tag name to 'global' or
375 # maps tag name to node; tagtypes maps tag name to 'global' or
376 # 'local'. (Global tags are defined by .hgtags across all
376 # 'local'. (Global tags are defined by .hgtags across all
377 # heads, and local tags are defined in .hg/localtags.)
377 # heads, and local tags are defined in .hg/localtags.)
378 # They constitute the in-memory cache of tags.
378 # They constitute the in-memory cache of tags.
379 self.tags = self.tagtypes = None
379 self.tags = self.tagtypes = None
380
380
381 self.nodetagscache = self.tagslist = None
381 self.nodetagscache = self.tagslist = None
382
382
383 cache = tagscache()
383 cache = tagscache()
384 cache.tags, cache.tagtypes = self._findtags()
384 cache.tags, cache.tagtypes = self._findtags()
385
385
386 return cache
386 return cache
387
387
388 def tags(self):
388 def tags(self):
389 '''return a mapping of tag to node'''
389 '''return a mapping of tag to node'''
390 return self._tagscache.tags
390 return self._tagscache.tags
391
391
392 def _findtags(self):
392 def _findtags(self):
393 '''Do the hard work of finding tags. Return a pair of dicts
393 '''Do the hard work of finding tags. Return a pair of dicts
394 (tags, tagtypes) where tags maps tag name to node, and tagtypes
394 (tags, tagtypes) where tags maps tag name to node, and tagtypes
395 maps tag name to a string like \'global\' or \'local\'.
395 maps tag name to a string like \'global\' or \'local\'.
396 Subclasses or extensions are free to add their own tags, but
396 Subclasses or extensions are free to add their own tags, but
397 should be aware that the returned dicts will be retained for the
397 should be aware that the returned dicts will be retained for the
398 duration of the localrepo object.'''
398 duration of the localrepo object.'''
399
399
400 # XXX what tagtype should subclasses/extensions use? Currently
400 # XXX what tagtype should subclasses/extensions use? Currently
401 # mq and bookmarks add tags, but do not set the tagtype at all.
401 # mq and bookmarks add tags, but do not set the tagtype at all.
402 # Should each extension invent its own tag type? Should there
402 # Should each extension invent its own tag type? Should there
403 # be one tagtype for all such "virtual" tags? Or is the status
403 # be one tagtype for all such "virtual" tags? Or is the status
404 # quo fine?
404 # quo fine?
405
405
406 alltags = {} # map tag name to (node, hist)
406 alltags = {} # map tag name to (node, hist)
407 tagtypes = {}
407 tagtypes = {}
408
408
409 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
409 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
410 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
410 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
411
411
412 # Build the return dicts. Have to re-encode tag names because
412 # Build the return dicts. Have to re-encode tag names because
413 # the tags module always uses UTF-8 (in order not to lose info
413 # the tags module always uses UTF-8 (in order not to lose info
414 # writing to the cache), but the rest of Mercurial wants them in
414 # writing to the cache), but the rest of Mercurial wants them in
415 # local encoding.
415 # local encoding.
416 tags = {}
416 tags = {}
417 for (name, (node, hist)) in alltags.iteritems():
417 for (name, (node, hist)) in alltags.iteritems():
418 if node != nullid:
418 if node != nullid:
419 try:
419 try:
420 # ignore tags to unknown nodes
420 # ignore tags to unknown nodes
421 self.changelog.lookup(node)
421 self.changelog.lookup(node)
422 tags[encoding.tolocal(name)] = node
422 tags[encoding.tolocal(name)] = node
423 except error.LookupError:
423 except error.LookupError:
424 pass
424 pass
425 tags['tip'] = self.changelog.tip()
425 tags['tip'] = self.changelog.tip()
426 tagtypes = dict([(encoding.tolocal(name), value)
426 tagtypes = dict([(encoding.tolocal(name), value)
427 for (name, value) in tagtypes.iteritems()])
427 for (name, value) in tagtypes.iteritems()])
428 return (tags, tagtypes)
428 return (tags, tagtypes)
429
429
430 def tagtype(self, tagname):
430 def tagtype(self, tagname):
431 '''
431 '''
432 return the type of the given tag. result can be:
432 return the type of the given tag. result can be:
433
433
434 'local' : a local tag
434 'local' : a local tag
435 'global' : a global tag
435 'global' : a global tag
436 None : tag does not exist
436 None : tag does not exist
437 '''
437 '''
438
438
439 return self._tagscache.tagtypes.get(tagname)
439 return self._tagscache.tagtypes.get(tagname)
440
440
441 def tagslist(self):
441 def tagslist(self):
442 '''return a list of tags ordered by revision'''
442 '''return a list of tags ordered by revision'''
443 if not self._tagscache.tagslist:
443 if not self._tagscache.tagslist:
444 l = []
444 l = []
445 for t, n in self.tags().iteritems():
445 for t, n in self.tags().iteritems():
446 r = self.changelog.rev(n)
446 r = self.changelog.rev(n)
447 l.append((r, t, n))
447 l.append((r, t, n))
448 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
448 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
449
449
450 return self._tagscache.tagslist
450 return self._tagscache.tagslist
451
451
452 def nodetags(self, node):
452 def nodetags(self, node):
453 '''return the tags associated with a node'''
453 '''return the tags associated with a node'''
454 if not self._tagscache.nodetagscache:
454 if not self._tagscache.nodetagscache:
455 nodetagscache = {}
455 nodetagscache = {}
456 for t, n in self.tags().iteritems():
456 for t, n in self.tags().iteritems():
457 nodetagscache.setdefault(n, []).append(t)
457 nodetagscache.setdefault(n, []).append(t)
458 for tags in nodetagscache.itervalues():
458 for tags in nodetagscache.itervalues():
459 tags.sort()
459 tags.sort()
460 self._tagscache.nodetagscache = nodetagscache
460 self._tagscache.nodetagscache = nodetagscache
461 return self._tagscache.nodetagscache.get(node, [])
461 return self._tagscache.nodetagscache.get(node, [])
462
462
463 def nodebookmarks(self, node):
463 def nodebookmarks(self, node):
464 marks = []
464 marks = []
465 for bookmark, n in self._bookmarks.iteritems():
465 for bookmark, n in self._bookmarks.iteritems():
466 if n == node:
466 if n == node:
467 marks.append(bookmark)
467 marks.append(bookmark)
468 return sorted(marks)
468 return sorted(marks)
469
469
470 def _branchtags(self, partial, lrev):
470 def _branchtags(self, partial, lrev):
471 # TODO: rename this function?
471 # TODO: rename this function?
472 tiprev = len(self) - 1
472 tiprev = len(self) - 1
473 if lrev != tiprev:
473 if lrev != tiprev:
474 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
474 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
475 self._updatebranchcache(partial, ctxgen)
475 self._updatebranchcache(partial, ctxgen)
476 self._writebranchcache(partial, self.changelog.tip(), tiprev)
476 self._writebranchcache(partial, self.changelog.tip(), tiprev)
477
477
478 return partial
478 return partial
479
479
480 def updatebranchcache(self):
480 def updatebranchcache(self):
481 tip = self.changelog.tip()
481 tip = self.changelog.tip()
482 if self._branchcache is not None and self._branchcachetip == tip:
482 if self._branchcache is not None and self._branchcachetip == tip:
483 return self._branchcache
483 return self._branchcache
484
484
485 oldtip = self._branchcachetip
485 oldtip = self._branchcachetip
486 self._branchcachetip = tip
486 self._branchcachetip = tip
487 if oldtip is None or oldtip not in self.changelog.nodemap:
487 if oldtip is None or oldtip not in self.changelog.nodemap:
488 partial, last, lrev = self._readbranchcache()
488 partial, last, lrev = self._readbranchcache()
489 else:
489 else:
490 lrev = self.changelog.rev(oldtip)
490 lrev = self.changelog.rev(oldtip)
491 partial = self._branchcache
491 partial = self._branchcache
492
492
493 self._branchtags(partial, lrev)
493 self._branchtags(partial, lrev)
494 # this private cache holds all heads (not just tips)
494 # this private cache holds all heads (not just tips)
495 self._branchcache = partial
495 self._branchcache = partial
496
496
497 def branchmap(self):
497 def branchmap(self):
498 '''returns a dictionary {branch: [branchheads]}'''
498 '''returns a dictionary {branch: [branchheads]}'''
499 self.updatebranchcache()
499 self.updatebranchcache()
500 return self._branchcache
500 return self._branchcache
501
501
502 def branchtags(self):
502 def branchtags(self):
503 '''return a dict where branch names map to the tipmost head of
503 '''return a dict where branch names map to the tipmost head of
504 the branch, open heads come before closed'''
504 the branch, open heads come before closed'''
505 bt = {}
505 bt = {}
506 for bn, heads in self.branchmap().iteritems():
506 for bn, heads in self.branchmap().iteritems():
507 tip = heads[-1]
507 tip = heads[-1]
508 for h in reversed(heads):
508 for h in reversed(heads):
509 if 'close' not in self.changelog.read(h)[5]:
509 if 'close' not in self.changelog.read(h)[5]:
510 tip = h
510 tip = h
511 break
511 break
512 bt[bn] = tip
512 bt[bn] = tip
513 return bt
513 return bt
514
514
515 def _readbranchcache(self):
515 def _readbranchcache(self):
516 partial = {}
516 partial = {}
517 try:
517 try:
518 f = self.opener("cache/branchheads")
518 f = self.opener("cache/branchheads")
519 lines = f.read().split('\n')
519 lines = f.read().split('\n')
520 f.close()
520 f.close()
521 except (IOError, OSError):
521 except (IOError, OSError):
522 return {}, nullid, nullrev
522 return {}, nullid, nullrev
523
523
524 try:
524 try:
525 last, lrev = lines.pop(0).split(" ", 1)
525 last, lrev = lines.pop(0).split(" ", 1)
526 last, lrev = bin(last), int(lrev)
526 last, lrev = bin(last), int(lrev)
527 if lrev >= len(self) or self[lrev].node() != last:
527 if lrev >= len(self) or self[lrev].node() != last:
528 # invalidate the cache
528 # invalidate the cache
529 raise ValueError('invalidating branch cache (tip differs)')
529 raise ValueError('invalidating branch cache (tip differs)')
530 for l in lines:
530 for l in lines:
531 if not l:
531 if not l:
532 continue
532 continue
533 node, label = l.split(" ", 1)
533 node, label = l.split(" ", 1)
534 label = encoding.tolocal(label.strip())
534 label = encoding.tolocal(label.strip())
535 partial.setdefault(label, []).append(bin(node))
535 partial.setdefault(label, []).append(bin(node))
536 except KeyboardInterrupt:
536 except KeyboardInterrupt:
537 raise
537 raise
538 except Exception, inst:
538 except Exception, inst:
539 if self.ui.debugflag:
539 if self.ui.debugflag:
540 self.ui.warn(str(inst), '\n')
540 self.ui.warn(str(inst), '\n')
541 partial, last, lrev = {}, nullid, nullrev
541 partial, last, lrev = {}, nullid, nullrev
542 return partial, last, lrev
542 return partial, last, lrev
543
543
544 def _writebranchcache(self, branches, tip, tiprev):
544 def _writebranchcache(self, branches, tip, tiprev):
545 try:
545 try:
546 f = self.opener("cache/branchheads", "w", atomictemp=True)
546 f = self.opener("cache/branchheads", "w", atomictemp=True)
547 f.write("%s %s\n" % (hex(tip), tiprev))
547 f.write("%s %s\n" % (hex(tip), tiprev))
548 for label, nodes in branches.iteritems():
548 for label, nodes in branches.iteritems():
549 for node in nodes:
549 for node in nodes:
550 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
550 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
551 f.close()
551 f.close()
552 except (IOError, OSError):
552 except (IOError, OSError):
553 pass
553 pass
554
554
555 def _updatebranchcache(self, partial, ctxgen):
555 def _updatebranchcache(self, partial, ctxgen):
556 # collect new branch entries
556 # collect new branch entries
557 newbranches = {}
557 newbranches = {}
558 for c in ctxgen:
558 for c in ctxgen:
559 newbranches.setdefault(c.branch(), []).append(c.node())
559 newbranches.setdefault(c.branch(), []).append(c.node())
560 # if older branchheads are reachable from new ones, they aren't
560 # if older branchheads are reachable from new ones, they aren't
561 # really branchheads. Note checking parents is insufficient:
561 # really branchheads. Note checking parents is insufficient:
562 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
562 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
563 for branch, newnodes in newbranches.iteritems():
563 for branch, newnodes in newbranches.iteritems():
564 bheads = partial.setdefault(branch, [])
564 bheads = partial.setdefault(branch, [])
565 bheads.extend(newnodes)
565 bheads.extend(newnodes)
566 if len(bheads) <= 1:
566 if len(bheads) <= 1:
567 continue
567 continue
568 bheads = sorted(bheads, key=lambda x: self[x].rev())
568 bheads = sorted(bheads, key=lambda x: self[x].rev())
569 # starting from tip means fewer passes over reachable
569 # starting from tip means fewer passes over reachable
570 while newnodes:
570 while newnodes:
571 latest = newnodes.pop()
571 latest = newnodes.pop()
572 if latest not in bheads:
572 if latest not in bheads:
573 continue
573 continue
574 minbhrev = self[bheads[0]].node()
574 minbhrev = self[bheads[0]].node()
575 reachable = self.changelog.reachable(latest, minbhrev)
575 reachable = self.changelog.reachable(latest, minbhrev)
576 reachable.remove(latest)
576 reachable.remove(latest)
577 if reachable:
577 if reachable:
578 bheads = [b for b in bheads if b not in reachable]
578 bheads = [b for b in bheads if b not in reachable]
579 partial[branch] = bheads
579 partial[branch] = bheads
580
580
581 def lookup(self, key):
581 def lookup(self, key):
582 if isinstance(key, int):
582 if isinstance(key, int):
583 return self.changelog.node(key)
583 return self.changelog.node(key)
584 elif key == '.':
584 elif key == '.':
585 return self.dirstate.p1()
585 return self.dirstate.p1()
586 elif key == 'null':
586 elif key == 'null':
587 return nullid
587 return nullid
588 elif key == 'tip':
588 elif key == 'tip':
589 return self.changelog.tip()
589 return self.changelog.tip()
590 n = self.changelog._match(key)
590 n = self.changelog._match(key)
591 if n:
591 if n:
592 return n
592 return n
593 if key in self._bookmarks:
593 if key in self._bookmarks:
594 return self._bookmarks[key]
594 return self._bookmarks[key]
595 if key in self.tags():
595 if key in self.tags():
596 return self.tags()[key]
596 return self.tags()[key]
597 if key in self.branchtags():
597 if key in self.branchtags():
598 return self.branchtags()[key]
598 return self.branchtags()[key]
599 n = self.changelog._partialmatch(key)
599 n = self.changelog._partialmatch(key)
600 if n:
600 if n:
601 return n
601 return n
602
602
603 # can't find key, check if it might have come from damaged dirstate
603 # can't find key, check if it might have come from damaged dirstate
604 if key in self.dirstate.parents():
604 if key in self.dirstate.parents():
605 raise error.Abort(_("working directory has unknown parent '%s'!")
605 raise error.Abort(_("working directory has unknown parent '%s'!")
606 % short(key))
606 % short(key))
607 try:
607 try:
608 if len(key) == 20:
608 if len(key) == 20:
609 key = hex(key)
609 key = hex(key)
610 except TypeError:
610 except TypeError:
611 pass
611 pass
612 raise error.RepoLookupError(_("unknown revision '%s'") % key)
612 raise error.RepoLookupError(_("unknown revision '%s'") % key)
613
613
614 def lookupbranch(self, key, remote=None):
614 def lookupbranch(self, key, remote=None):
615 repo = remote or self
615 repo = remote or self
616 if key in repo.branchmap():
616 if key in repo.branchmap():
617 return key
617 return key
618
618
619 repo = (remote and remote.local()) and remote or self
619 repo = (remote and remote.local()) and remote or self
620 return repo[key].branch()
620 return repo[key].branch()
621
621
622 def known(self, nodes):
622 def known(self, nodes):
623 nm = self.changelog.nodemap
623 nm = self.changelog.nodemap
624 return [(n in nm) for n in nodes]
624 return [(n in nm) for n in nodes]
625
625
626 def local(self):
626 def local(self):
627 return self
627 return self
628
628
629 def join(self, f):
629 def join(self, f):
630 return os.path.join(self.path, f)
630 return os.path.join(self.path, f)
631
631
632 def wjoin(self, f):
632 def wjoin(self, f):
633 return os.path.join(self.root, f)
633 return os.path.join(self.root, f)
634
634
635 def file(self, f):
635 def file(self, f):
636 if f[0] == '/':
636 if f[0] == '/':
637 f = f[1:]
637 f = f[1:]
638 return filelog.filelog(self.sopener, f)
638 return filelog.filelog(self.sopener, f)
639
639
640 def changectx(self, changeid):
640 def changectx(self, changeid):
641 return self[changeid]
641 return self[changeid]
642
642
643 def parents(self, changeid=None):
643 def parents(self, changeid=None):
644 '''get list of changectxs for parents of changeid'''
644 '''get list of changectxs for parents of changeid'''
645 return self[changeid].parents()
645 return self[changeid].parents()
646
646
647 def filectx(self, path, changeid=None, fileid=None):
647 def filectx(self, path, changeid=None, fileid=None):
648 """changeid can be a changeset revision, node, or tag.
648 """changeid can be a changeset revision, node, or tag.
649 fileid can be a file revision or node."""
649 fileid can be a file revision or node."""
650 return context.filectx(self, path, changeid, fileid)
650 return context.filectx(self, path, changeid, fileid)
651
651
652 def getcwd(self):
652 def getcwd(self):
653 return self.dirstate.getcwd()
653 return self.dirstate.getcwd()
654
654
655 def pathto(self, f, cwd=None):
655 def pathto(self, f, cwd=None):
656 return self.dirstate.pathto(f, cwd)
656 return self.dirstate.pathto(f, cwd)
657
657
658 def wfile(self, f, mode='r'):
658 def wfile(self, f, mode='r'):
659 return self.wopener(f, mode)
659 return self.wopener(f, mode)
660
660
661 def _link(self, f):
661 def _link(self, f):
662 return os.path.islink(self.wjoin(f))
662 return os.path.islink(self.wjoin(f))
663
663
664 def _loadfilter(self, filter):
664 def _loadfilter(self, filter):
665 if filter not in self.filterpats:
665 if filter not in self.filterpats:
666 l = []
666 l = []
667 for pat, cmd in self.ui.configitems(filter):
667 for pat, cmd in self.ui.configitems(filter):
668 if cmd == '!':
668 if cmd == '!':
669 continue
669 continue
670 mf = matchmod.match(self.root, '', [pat])
670 mf = matchmod.match(self.root, '', [pat])
671 fn = None
671 fn = None
672 params = cmd
672 params = cmd
673 for name, filterfn in self._datafilters.iteritems():
673 for name, filterfn in self._datafilters.iteritems():
674 if cmd.startswith(name):
674 if cmd.startswith(name):
675 fn = filterfn
675 fn = filterfn
676 params = cmd[len(name):].lstrip()
676 params = cmd[len(name):].lstrip()
677 break
677 break
678 if not fn:
678 if not fn:
679 fn = lambda s, c, **kwargs: util.filter(s, c)
679 fn = lambda s, c, **kwargs: util.filter(s, c)
680 # Wrap old filters not supporting keyword arguments
680 # Wrap old filters not supporting keyword arguments
681 if not inspect.getargspec(fn)[2]:
681 if not inspect.getargspec(fn)[2]:
682 oldfn = fn
682 oldfn = fn
683 fn = lambda s, c, **kwargs: oldfn(s, c)
683 fn = lambda s, c, **kwargs: oldfn(s, c)
684 l.append((mf, fn, params))
684 l.append((mf, fn, params))
685 self.filterpats[filter] = l
685 self.filterpats[filter] = l
686 return self.filterpats[filter]
686 return self.filterpats[filter]
687
687
688 def _filter(self, filterpats, filename, data):
688 def _filter(self, filterpats, filename, data):
689 for mf, fn, cmd in filterpats:
689 for mf, fn, cmd in filterpats:
690 if mf(filename):
690 if mf(filename):
691 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
691 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
692 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
692 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
693 break
693 break
694
694
695 return data
695 return data
696
696
697 @propertycache
697 @propertycache
698 def _encodefilterpats(self):
698 def _encodefilterpats(self):
699 return self._loadfilter('encode')
699 return self._loadfilter('encode')
700
700
701 @propertycache
701 @propertycache
702 def _decodefilterpats(self):
702 def _decodefilterpats(self):
703 return self._loadfilter('decode')
703 return self._loadfilter('decode')
704
704
705 def adddatafilter(self, name, filter):
705 def adddatafilter(self, name, filter):
706 self._datafilters[name] = filter
706 self._datafilters[name] = filter
707
707
708 def wread(self, filename):
708 def wread(self, filename):
709 if self._link(filename):
709 if self._link(filename):
710 data = os.readlink(self.wjoin(filename))
710 data = os.readlink(self.wjoin(filename))
711 else:
711 else:
712 data = self.wopener.read(filename)
712 data = self.wopener.read(filename)
713 return self._filter(self._encodefilterpats, filename, data)
713 return self._filter(self._encodefilterpats, filename, data)
714
714
715 def wwrite(self, filename, data, flags):
715 def wwrite(self, filename, data, flags):
716 data = self._filter(self._decodefilterpats, filename, data)
716 data = self._filter(self._decodefilterpats, filename, data)
717 if 'l' in flags:
717 if 'l' in flags:
718 self.wopener.symlink(data, filename)
718 self.wopener.symlink(data, filename)
719 else:
719 else:
720 self.wopener.write(filename, data)
720 self.wopener.write(filename, data)
721 if 'x' in flags:
721 if 'x' in flags:
722 util.setflags(self.wjoin(filename), False, True)
722 util.setflags(self.wjoin(filename), False, True)
723
723
724 def wwritedata(self, filename, data):
724 def wwritedata(self, filename, data):
725 return self._filter(self._decodefilterpats, filename, data)
725 return self._filter(self._decodefilterpats, filename, data)
726
726
727 def transaction(self, desc):
727 def transaction(self, desc):
728 tr = self._transref and self._transref() or None
728 tr = self._transref and self._transref() or None
729 if tr and tr.running():
729 if tr and tr.running():
730 return tr.nest()
730 return tr.nest()
731
731
732 # abort here if the journal already exists
732 # abort here if the journal already exists
733 if os.path.exists(self.sjoin("journal")):
733 if os.path.exists(self.sjoin("journal")):
734 raise error.RepoError(
734 raise error.RepoError(
735 _("abandoned transaction found - run hg recover"))
735 _("abandoned transaction found - run hg recover"))
736
736
737 journalfiles = self._writejournal(desc)
737 journalfiles = self._writejournal(desc)
738 renames = [(x, undoname(x)) for x in journalfiles]
738 renames = [(x, undoname(x)) for x in journalfiles]
739
739
740 tr = transaction.transaction(self.ui.warn, self.sopener,
740 tr = transaction.transaction(self.ui.warn, self.sopener,
741 self.sjoin("journal"),
741 self.sjoin("journal"),
742 aftertrans(renames),
742 aftertrans(renames),
743 self.store.createmode)
743 self.store.createmode)
744 self._transref = weakref.ref(tr)
744 self._transref = weakref.ref(tr)
745 return tr
745 return tr
746
746
747 def _writejournal(self, desc):
747 def _writejournal(self, desc):
748 # save dirstate for rollback
748 # save dirstate for rollback
749 try:
749 try:
750 ds = self.opener.read("dirstate")
750 ds = self.opener.read("dirstate")
751 except IOError:
751 except IOError:
752 ds = ""
752 ds = ""
753 self.opener.write("journal.dirstate", ds)
753 self.opener.write("journal.dirstate", ds)
754 self.opener.write("journal.branch",
754 self.opener.write("journal.branch",
755 encoding.fromlocal(self.dirstate.branch()))
755 encoding.fromlocal(self.dirstate.branch()))
756 self.opener.write("journal.desc",
756 self.opener.write("journal.desc",
757 "%d\n%s\n" % (len(self), desc))
757 "%d\n%s\n" % (len(self), desc))
758
758
759 bkname = self.join('bookmarks')
759 bkname = self.join('bookmarks')
760 if os.path.exists(bkname):
760 if os.path.exists(bkname):
761 util.copyfile(bkname, self.join('journal.bookmarks'))
761 util.copyfile(bkname, self.join('journal.bookmarks'))
762 else:
762 else:
763 self.opener.write('journal.bookmarks', '')
763 self.opener.write('journal.bookmarks', '')
764 phasesname = self.sjoin('phaseroots')
764 phasesname = self.sjoin('phaseroots')
765 if os.path.exists(phasesname):
765 if os.path.exists(phasesname):
766 util.copyfile(phasesname, self.sjoin('journal.phaseroots'))
766 util.copyfile(phasesname, self.sjoin('journal.phaseroots'))
767 else:
767 else:
768 self.sopener.write('journal.phaseroots', '')
768 self.sopener.write('journal.phaseroots', '')
769
769
770 return (self.sjoin('journal'), self.join('journal.dirstate'),
770 return (self.sjoin('journal'), self.join('journal.dirstate'),
771 self.join('journal.branch'), self.join('journal.desc'),
771 self.join('journal.branch'), self.join('journal.desc'),
772 self.join('journal.bookmarks'),
772 self.join('journal.bookmarks'),
773 self.sjoin('journal.phaseroots'))
773 self.sjoin('journal.phaseroots'))
774
774
775 def recover(self):
775 def recover(self):
776 lock = self.lock()
776 lock = self.lock()
777 try:
777 try:
778 if os.path.exists(self.sjoin("journal")):
778 if os.path.exists(self.sjoin("journal")):
779 self.ui.status(_("rolling back interrupted transaction\n"))
779 self.ui.status(_("rolling back interrupted transaction\n"))
780 transaction.rollback(self.sopener, self.sjoin("journal"),
780 transaction.rollback(self.sopener, self.sjoin("journal"),
781 self.ui.warn)
781 self.ui.warn)
782 self.invalidate()
782 self.invalidate()
783 return True
783 return True
784 else:
784 else:
785 self.ui.warn(_("no interrupted transaction available\n"))
785 self.ui.warn(_("no interrupted transaction available\n"))
786 return False
786 return False
787 finally:
787 finally:
788 lock.release()
788 lock.release()
789
789
790 def rollback(self, dryrun=False, force=False):
790 def rollback(self, dryrun=False, force=False):
791 wlock = lock = None
791 wlock = lock = None
792 try:
792 try:
793 wlock = self.wlock()
793 wlock = self.wlock()
794 lock = self.lock()
794 lock = self.lock()
795 if os.path.exists(self.sjoin("undo")):
795 if os.path.exists(self.sjoin("undo")):
796 return self._rollback(dryrun, force)
796 return self._rollback(dryrun, force)
797 else:
797 else:
798 self.ui.warn(_("no rollback information available\n"))
798 self.ui.warn(_("no rollback information available\n"))
799 return 1
799 return 1
800 finally:
800 finally:
801 release(lock, wlock)
801 release(lock, wlock)
802
802
803 def _rollback(self, dryrun, force):
803 def _rollback(self, dryrun, force):
804 ui = self.ui
804 ui = self.ui
805 try:
805 try:
806 args = self.opener.read('undo.desc').splitlines()
806 args = self.opener.read('undo.desc').splitlines()
807 (oldlen, desc, detail) = (int(args[0]), args[1], None)
807 (oldlen, desc, detail) = (int(args[0]), args[1], None)
808 if len(args) >= 3:
808 if len(args) >= 3:
809 detail = args[2]
809 detail = args[2]
810 oldtip = oldlen - 1
810 oldtip = oldlen - 1
811
811
812 if detail and ui.verbose:
812 if detail and ui.verbose:
813 msg = (_('repository tip rolled back to revision %s'
813 msg = (_('repository tip rolled back to revision %s'
814 ' (undo %s: %s)\n')
814 ' (undo %s: %s)\n')
815 % (oldtip, desc, detail))
815 % (oldtip, desc, detail))
816 else:
816 else:
817 msg = (_('repository tip rolled back to revision %s'
817 msg = (_('repository tip rolled back to revision %s'
818 ' (undo %s)\n')
818 ' (undo %s)\n')
819 % (oldtip, desc))
819 % (oldtip, desc))
820 except IOError:
820 except IOError:
821 msg = _('rolling back unknown transaction\n')
821 msg = _('rolling back unknown transaction\n')
822 desc = None
822 desc = None
823
823
824 if not force and self['.'] != self['tip'] and desc == 'commit':
824 if not force and self['.'] != self['tip'] and desc == 'commit':
825 raise util.Abort(
825 raise util.Abort(
826 _('rollback of last commit while not checked out '
826 _('rollback of last commit while not checked out '
827 'may lose data'), hint=_('use -f to force'))
827 'may lose data'), hint=_('use -f to force'))
828
828
829 ui.status(msg)
829 ui.status(msg)
830 if dryrun:
830 if dryrun:
831 return 0
831 return 0
832
832
833 parents = self.dirstate.parents()
833 parents = self.dirstate.parents()
834 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
834 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
835 if os.path.exists(self.join('undo.bookmarks')):
835 if os.path.exists(self.join('undo.bookmarks')):
836 util.rename(self.join('undo.bookmarks'),
836 util.rename(self.join('undo.bookmarks'),
837 self.join('bookmarks'))
837 self.join('bookmarks'))
838 if os.path.exists(self.sjoin('undo.phaseroots')):
838 if os.path.exists(self.sjoin('undo.phaseroots')):
839 util.rename(self.sjoin('undo.phaseroots'),
839 util.rename(self.sjoin('undo.phaseroots'),
840 self.sjoin('phaseroots'))
840 self.sjoin('phaseroots'))
841 self.invalidate()
841 self.invalidate()
842
842
843 parentgone = (parents[0] not in self.changelog.nodemap or
843 parentgone = (parents[0] not in self.changelog.nodemap or
844 parents[1] not in self.changelog.nodemap)
844 parents[1] not in self.changelog.nodemap)
845 if parentgone:
845 if parentgone:
846 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
846 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
847 try:
847 try:
848 branch = self.opener.read('undo.branch')
848 branch = self.opener.read('undo.branch')
849 self.dirstate.setbranch(branch)
849 self.dirstate.setbranch(branch)
850 except IOError:
850 except IOError:
851 ui.warn(_('named branch could not be reset: '
851 ui.warn(_('named branch could not be reset: '
852 'current branch is still \'%s\'\n')
852 'current branch is still \'%s\'\n')
853 % self.dirstate.branch())
853 % self.dirstate.branch())
854
854
855 self.dirstate.invalidate()
855 self.dirstate.invalidate()
856 parents = tuple([p.rev() for p in self.parents()])
856 parents = tuple([p.rev() for p in self.parents()])
857 if len(parents) > 1:
857 if len(parents) > 1:
858 ui.status(_('working directory now based on '
858 ui.status(_('working directory now based on '
859 'revisions %d and %d\n') % parents)
859 'revisions %d and %d\n') % parents)
860 else:
860 else:
861 ui.status(_('working directory now based on '
861 ui.status(_('working directory now based on '
862 'revision %d\n') % parents)
862 'revision %d\n') % parents)
863 self.destroyed()
863 self.destroyed()
864 return 0
864 return 0
865
865
866 def invalidatecaches(self):
866 def invalidatecaches(self):
867 try:
867 try:
868 delattr(self, '_tagscache')
868 delattr(self, '_tagscache')
869 except AttributeError:
869 except AttributeError:
870 pass
870 pass
871
871
872 self._branchcache = None # in UTF-8
872 self._branchcache = None # in UTF-8
873 self._branchcachetip = None
873 self._branchcachetip = None
874
874
875 def invalidatedirstate(self):
875 def invalidatedirstate(self):
876 '''Invalidates the dirstate, causing the next call to dirstate
876 '''Invalidates the dirstate, causing the next call to dirstate
877 to check if it was modified since the last time it was read,
877 to check if it was modified since the last time it was read,
878 rereading it if it has.
878 rereading it if it has.
879
879
880 This is different to dirstate.invalidate() that it doesn't always
880 This is different to dirstate.invalidate() that it doesn't always
881 rereads the dirstate. Use dirstate.invalidate() if you want to
881 rereads the dirstate. Use dirstate.invalidate() if you want to
882 explicitly read the dirstate again (i.e. restoring it to a previous
882 explicitly read the dirstate again (i.e. restoring it to a previous
883 known good state).'''
883 known good state).'''
884 try:
884 try:
885 delattr(self, 'dirstate')
885 delattr(self, 'dirstate')
886 except AttributeError:
886 except AttributeError:
887 pass
887 pass
888
888
889 def invalidate(self):
889 def invalidate(self):
890 for k in self._filecache:
890 for k in self._filecache:
891 # dirstate is invalidated separately in invalidatedirstate()
891 # dirstate is invalidated separately in invalidatedirstate()
892 if k == 'dirstate':
892 if k == 'dirstate':
893 continue
893 continue
894
894
895 try:
895 try:
896 delattr(self, k)
896 delattr(self, k)
897 except AttributeError:
897 except AttributeError:
898 pass
898 pass
899 self.invalidatecaches()
899 self.invalidatecaches()
900
900
901 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
901 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
902 try:
902 try:
903 l = lock.lock(lockname, 0, releasefn, desc=desc)
903 l = lock.lock(lockname, 0, releasefn, desc=desc)
904 except error.LockHeld, inst:
904 except error.LockHeld, inst:
905 if not wait:
905 if not wait:
906 raise
906 raise
907 self.ui.warn(_("waiting for lock on %s held by %r\n") %
907 self.ui.warn(_("waiting for lock on %s held by %r\n") %
908 (desc, inst.locker))
908 (desc, inst.locker))
909 # default to 600 seconds timeout
909 # default to 600 seconds timeout
910 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
910 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
911 releasefn, desc=desc)
911 releasefn, desc=desc)
912 if acquirefn:
912 if acquirefn:
913 acquirefn()
913 acquirefn()
914 return l
914 return l
915
915
916 def _afterlock(self, callback):
916 def _afterlock(self, callback):
917 """add a callback to the current repository lock.
917 """add a callback to the current repository lock.
918
918
919 The callback will be executed on lock release."""
919 The callback will be executed on lock release."""
920 l = self._lockref and self._lockref()
920 l = self._lockref and self._lockref()
921 if l:
921 if l:
922 l.postrelease.append(callback)
922 l.postrelease.append(callback)
923
923
924 def lock(self, wait=True):
924 def lock(self, wait=True):
925 '''Lock the repository store (.hg/store) and return a weak reference
925 '''Lock the repository store (.hg/store) and return a weak reference
926 to the lock. Use this before modifying the store (e.g. committing or
926 to the lock. Use this before modifying the store (e.g. committing or
927 stripping). If you are opening a transaction, get a lock as well.)'''
927 stripping). If you are opening a transaction, get a lock as well.)'''
928 l = self._lockref and self._lockref()
928 l = self._lockref and self._lockref()
929 if l is not None and l.held:
929 if l is not None and l.held:
930 l.lock()
930 l.lock()
931 return l
931 return l
932
932
933 def unlock():
933 def unlock():
934 self.store.write()
934 self.store.write()
935 if self._dirtyphases:
935 if self._dirtyphases:
936 phases.writeroots(self)
936 phases.writeroots(self)
937 for k, ce in self._filecache.items():
937 for k, ce in self._filecache.items():
938 if k == 'dirstate':
938 if k == 'dirstate':
939 continue
939 continue
940 ce.refresh()
940 ce.refresh()
941
941
942 l = self._lock(self.sjoin("lock"), wait, unlock,
942 l = self._lock(self.sjoin("lock"), wait, unlock,
943 self.invalidate, _('repository %s') % self.origroot)
943 self.invalidate, _('repository %s') % self.origroot)
944 self._lockref = weakref.ref(l)
944 self._lockref = weakref.ref(l)
945 return l
945 return l
946
946
947 def wlock(self, wait=True):
947 def wlock(self, wait=True):
948 '''Lock the non-store parts of the repository (everything under
948 '''Lock the non-store parts of the repository (everything under
949 .hg except .hg/store) and return a weak reference to the lock.
949 .hg except .hg/store) and return a weak reference to the lock.
950 Use this before modifying files in .hg.'''
950 Use this before modifying files in .hg.'''
951 l = self._wlockref and self._wlockref()
951 l = self._wlockref and self._wlockref()
952 if l is not None and l.held:
952 if l is not None and l.held:
953 l.lock()
953 l.lock()
954 return l
954 return l
955
955
956 def unlock():
956 def unlock():
957 self.dirstate.write()
957 self.dirstate.write()
958 ce = self._filecache.get('dirstate')
958 ce = self._filecache.get('dirstate')
959 if ce:
959 if ce:
960 ce.refresh()
960 ce.refresh()
961
961
962 l = self._lock(self.join("wlock"), wait, unlock,
962 l = self._lock(self.join("wlock"), wait, unlock,
963 self.invalidatedirstate, _('working directory of %s') %
963 self.invalidatedirstate, _('working directory of %s') %
964 self.origroot)
964 self.origroot)
965 self._wlockref = weakref.ref(l)
965 self._wlockref = weakref.ref(l)
966 return l
966 return l
967
967
968 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
968 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
969 """
969 """
970 commit an individual file as part of a larger transaction
970 commit an individual file as part of a larger transaction
971 """
971 """
972
972
973 fname = fctx.path()
973 fname = fctx.path()
974 text = fctx.data()
974 text = fctx.data()
975 flog = self.file(fname)
975 flog = self.file(fname)
976 fparent1 = manifest1.get(fname, nullid)
976 fparent1 = manifest1.get(fname, nullid)
977 fparent2 = fparent2o = manifest2.get(fname, nullid)
977 fparent2 = fparent2o = manifest2.get(fname, nullid)
978
978
979 meta = {}
979 meta = {}
980 copy = fctx.renamed()
980 copy = fctx.renamed()
981 if copy and copy[0] != fname:
981 if copy and copy[0] != fname:
982 # Mark the new revision of this file as a copy of another
982 # Mark the new revision of this file as a copy of another
983 # file. This copy data will effectively act as a parent
983 # file. This copy data will effectively act as a parent
984 # of this new revision. If this is a merge, the first
984 # of this new revision. If this is a merge, the first
985 # parent will be the nullid (meaning "look up the copy data")
985 # parent will be the nullid (meaning "look up the copy data")
986 # and the second one will be the other parent. For example:
986 # and the second one will be the other parent. For example:
987 #
987 #
988 # 0 --- 1 --- 3 rev1 changes file foo
988 # 0 --- 1 --- 3 rev1 changes file foo
989 # \ / rev2 renames foo to bar and changes it
989 # \ / rev2 renames foo to bar and changes it
990 # \- 2 -/ rev3 should have bar with all changes and
990 # \- 2 -/ rev3 should have bar with all changes and
991 # should record that bar descends from
991 # should record that bar descends from
992 # bar in rev2 and foo in rev1
992 # bar in rev2 and foo in rev1
993 #
993 #
994 # this allows this merge to succeed:
994 # this allows this merge to succeed:
995 #
995 #
996 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
996 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
997 # \ / merging rev3 and rev4 should use bar@rev2
997 # \ / merging rev3 and rev4 should use bar@rev2
998 # \- 2 --- 4 as the merge base
998 # \- 2 --- 4 as the merge base
999 #
999 #
1000
1000
1001 cfname = copy[0]
1001 cfname = copy[0]
1002 crev = manifest1.get(cfname)
1002 crev = manifest1.get(cfname)
1003 newfparent = fparent2
1003 newfparent = fparent2
1004
1004
1005 if manifest2: # branch merge
1005 if manifest2: # branch merge
1006 if fparent2 == nullid or crev is None: # copied on remote side
1006 if fparent2 == nullid or crev is None: # copied on remote side
1007 if cfname in manifest2:
1007 if cfname in manifest2:
1008 crev = manifest2[cfname]
1008 crev = manifest2[cfname]
1009 newfparent = fparent1
1009 newfparent = fparent1
1010
1010
1011 # find source in nearest ancestor if we've lost track
1011 # find source in nearest ancestor if we've lost track
1012 if not crev:
1012 if not crev:
1013 self.ui.debug(" %s: searching for copy revision for %s\n" %
1013 self.ui.debug(" %s: searching for copy revision for %s\n" %
1014 (fname, cfname))
1014 (fname, cfname))
1015 for ancestor in self[None].ancestors():
1015 for ancestor in self[None].ancestors():
1016 if cfname in ancestor:
1016 if cfname in ancestor:
1017 crev = ancestor[cfname].filenode()
1017 crev = ancestor[cfname].filenode()
1018 break
1018 break
1019
1019
1020 if crev:
1020 if crev:
1021 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1021 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1022 meta["copy"] = cfname
1022 meta["copy"] = cfname
1023 meta["copyrev"] = hex(crev)
1023 meta["copyrev"] = hex(crev)
1024 fparent1, fparent2 = nullid, newfparent
1024 fparent1, fparent2 = nullid, newfparent
1025 else:
1025 else:
1026 self.ui.warn(_("warning: can't find ancestor for '%s' "
1026 self.ui.warn(_("warning: can't find ancestor for '%s' "
1027 "copied from '%s'!\n") % (fname, cfname))
1027 "copied from '%s'!\n") % (fname, cfname))
1028
1028
1029 elif fparent2 != nullid:
1029 elif fparent2 != nullid:
1030 # is one parent an ancestor of the other?
1030 # is one parent an ancestor of the other?
1031 fparentancestor = flog.ancestor(fparent1, fparent2)
1031 fparentancestor = flog.ancestor(fparent1, fparent2)
1032 if fparentancestor == fparent1:
1032 if fparentancestor == fparent1:
1033 fparent1, fparent2 = fparent2, nullid
1033 fparent1, fparent2 = fparent2, nullid
1034 elif fparentancestor == fparent2:
1034 elif fparentancestor == fparent2:
1035 fparent2 = nullid
1035 fparent2 = nullid
1036
1036
1037 # is the file changed?
1037 # is the file changed?
1038 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1038 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1039 changelist.append(fname)
1039 changelist.append(fname)
1040 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1040 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1041
1041
1042 # are just the flags changed during merge?
1042 # are just the flags changed during merge?
1043 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1043 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1044 changelist.append(fname)
1044 changelist.append(fname)
1045
1045
1046 return fparent1
1046 return fparent1
1047
1047
1048 def commit(self, text="", user=None, date=None, match=None, force=False,
1048 def commit(self, text="", user=None, date=None, match=None, force=False,
1049 editor=False, extra={}):
1049 editor=False, extra={}):
1050 """Add a new revision to current repository.
1050 """Add a new revision to current repository.
1051
1051
1052 Revision information is gathered from the working directory,
1052 Revision information is gathered from the working directory,
1053 match can be used to filter the committed files. If editor is
1053 match can be used to filter the committed files. If editor is
1054 supplied, it is called to get a commit message.
1054 supplied, it is called to get a commit message.
1055 """
1055 """
1056
1056
1057 def fail(f, msg):
1057 def fail(f, msg):
1058 raise util.Abort('%s: %s' % (f, msg))
1058 raise util.Abort('%s: %s' % (f, msg))
1059
1059
1060 if not match:
1060 if not match:
1061 match = matchmod.always(self.root, '')
1061 match = matchmod.always(self.root, '')
1062
1062
1063 if not force:
1063 if not force:
1064 vdirs = []
1064 vdirs = []
1065 match.dir = vdirs.append
1065 match.dir = vdirs.append
1066 match.bad = fail
1066 match.bad = fail
1067
1067
1068 wlock = self.wlock()
1068 wlock = self.wlock()
1069 try:
1069 try:
1070 wctx = self[None]
1070 wctx = self[None]
1071 merge = len(wctx.parents()) > 1
1071 merge = len(wctx.parents()) > 1
1072
1072
1073 if (not force and merge and match and
1073 if (not force and merge and match and
1074 (match.files() or match.anypats())):
1074 (match.files() or match.anypats())):
1075 raise util.Abort(_('cannot partially commit a merge '
1075 raise util.Abort(_('cannot partially commit a merge '
1076 '(do not specify files or patterns)'))
1076 '(do not specify files or patterns)'))
1077
1077
1078 changes = self.status(match=match, clean=force)
1078 changes = self.status(match=match, clean=force)
1079 if force:
1079 if force:
1080 changes[0].extend(changes[6]) # mq may commit unchanged files
1080 changes[0].extend(changes[6]) # mq may commit unchanged files
1081
1081
1082 # check subrepos
1082 # check subrepos
1083 subs = []
1083 subs = []
1084 removedsubs = set()
1084 removedsubs = set()
1085 if '.hgsub' in wctx:
1085 if '.hgsub' in wctx:
1086 # only manage subrepos and .hgsubstate if .hgsub is present
1086 # only manage subrepos and .hgsubstate if .hgsub is present
1087 for p in wctx.parents():
1087 for p in wctx.parents():
1088 removedsubs.update(s for s in p.substate if match(s))
1088 removedsubs.update(s for s in p.substate if match(s))
1089 for s in wctx.substate:
1089 for s in wctx.substate:
1090 removedsubs.discard(s)
1090 removedsubs.discard(s)
1091 if match(s) and wctx.sub(s).dirty():
1091 if match(s) and wctx.sub(s).dirty():
1092 subs.append(s)
1092 subs.append(s)
1093 if (subs or removedsubs):
1093 if (subs or removedsubs):
1094 if (not match('.hgsub') and
1094 if (not match('.hgsub') and
1095 '.hgsub' in (wctx.modified() + wctx.added())):
1095 '.hgsub' in (wctx.modified() + wctx.added())):
1096 raise util.Abort(
1096 raise util.Abort(
1097 _("can't commit subrepos without .hgsub"))
1097 _("can't commit subrepos without .hgsub"))
1098 if '.hgsubstate' not in changes[0]:
1098 if '.hgsubstate' not in changes[0]:
1099 changes[0].insert(0, '.hgsubstate')
1099 changes[0].insert(0, '.hgsubstate')
1100 if '.hgsubstate' in changes[2]:
1100 if '.hgsubstate' in changes[2]:
1101 changes[2].remove('.hgsubstate')
1101 changes[2].remove('.hgsubstate')
1102 elif '.hgsub' in changes[2]:
1102 elif '.hgsub' in changes[2]:
1103 # clean up .hgsubstate when .hgsub is removed
1103 # clean up .hgsubstate when .hgsub is removed
1104 if ('.hgsubstate' in wctx and
1104 if ('.hgsubstate' in wctx and
1105 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1105 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1106 changes[2].insert(0, '.hgsubstate')
1106 changes[2].insert(0, '.hgsubstate')
1107
1107
1108 if subs and not self.ui.configbool('ui', 'commitsubrepos', False):
1108 if subs and not self.ui.configbool('ui', 'commitsubrepos', False):
1109 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
1109 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
1110 if changedsubs:
1110 if changedsubs:
1111 raise util.Abort(_("uncommitted changes in subrepo %s")
1111 raise util.Abort(_("uncommitted changes in subrepo %s")
1112 % changedsubs[0],
1112 % changedsubs[0],
1113 hint=_("use --subrepos for recursive commit"))
1113 hint=_("use --subrepos for recursive commit"))
1114
1114
1115 # make sure all explicit patterns are matched
1115 # make sure all explicit patterns are matched
1116 if not force and match.files():
1116 if not force and match.files():
1117 matched = set(changes[0] + changes[1] + changes[2])
1117 matched = set(changes[0] + changes[1] + changes[2])
1118
1118
1119 for f in match.files():
1119 for f in match.files():
1120 if f == '.' or f in matched or f in wctx.substate:
1120 if f == '.' or f in matched or f in wctx.substate:
1121 continue
1121 continue
1122 if f in changes[3]: # missing
1122 if f in changes[3]: # missing
1123 fail(f, _('file not found!'))
1123 fail(f, _('file not found!'))
1124 if f in vdirs: # visited directory
1124 if f in vdirs: # visited directory
1125 d = f + '/'
1125 d = f + '/'
1126 for mf in matched:
1126 for mf in matched:
1127 if mf.startswith(d):
1127 if mf.startswith(d):
1128 break
1128 break
1129 else:
1129 else:
1130 fail(f, _("no match under directory!"))
1130 fail(f, _("no match under directory!"))
1131 elif f not in self.dirstate:
1131 elif f not in self.dirstate:
1132 fail(f, _("file not tracked!"))
1132 fail(f, _("file not tracked!"))
1133
1133
1134 if (not force and not extra.get("close") and not merge
1134 if (not force and not extra.get("close") and not merge
1135 and not (changes[0] or changes[1] or changes[2])
1135 and not (changes[0] or changes[1] or changes[2])
1136 and wctx.branch() == wctx.p1().branch()):
1136 and wctx.branch() == wctx.p1().branch()):
1137 return None
1137 return None
1138
1138
1139 ms = mergemod.mergestate(self)
1139 ms = mergemod.mergestate(self)
1140 for f in changes[0]:
1140 for f in changes[0]:
1141 if f in ms and ms[f] == 'u':
1141 if f in ms and ms[f] == 'u':
1142 raise util.Abort(_("unresolved merge conflicts "
1142 raise util.Abort(_("unresolved merge conflicts "
1143 "(see hg help resolve)"))
1143 "(see hg help resolve)"))
1144
1144
1145 cctx = context.workingctx(self, text, user, date, extra, changes)
1145 cctx = context.workingctx(self, text, user, date, extra, changes)
1146 if editor:
1146 if editor:
1147 cctx._text = editor(self, cctx, subs)
1147 cctx._text = editor(self, cctx, subs)
1148 edited = (text != cctx._text)
1148 edited = (text != cctx._text)
1149
1149
1150 # commit subs
1150 # commit subs
1151 if subs or removedsubs:
1151 if subs or removedsubs:
1152 state = wctx.substate.copy()
1152 state = wctx.substate.copy()
1153 for s in sorted(subs):
1153 for s in sorted(subs):
1154 sub = wctx.sub(s)
1154 sub = wctx.sub(s)
1155 self.ui.status(_('committing subrepository %s\n') %
1155 self.ui.status(_('committing subrepository %s\n') %
1156 subrepo.subrelpath(sub))
1156 subrepo.subrelpath(sub))
1157 sr = sub.commit(cctx._text, user, date)
1157 sr = sub.commit(cctx._text, user, date)
1158 state[s] = (state[s][0], sr)
1158 state[s] = (state[s][0], sr)
1159 subrepo.writestate(self, state)
1159 subrepo.writestate(self, state)
1160
1160
1161 # Save commit message in case this transaction gets rolled back
1161 # Save commit message in case this transaction gets rolled back
1162 # (e.g. by a pretxncommit hook). Leave the content alone on
1162 # (e.g. by a pretxncommit hook). Leave the content alone on
1163 # the assumption that the user will use the same editor again.
1163 # the assumption that the user will use the same editor again.
1164 msgfn = self.savecommitmessage(cctx._text)
1164 msgfn = self.savecommitmessage(cctx._text)
1165
1165
1166 p1, p2 = self.dirstate.parents()
1166 p1, p2 = self.dirstate.parents()
1167 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1167 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1168 try:
1168 try:
1169 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1169 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1170 ret = self.commitctx(cctx, True)
1170 ret = self.commitctx(cctx, True)
1171 except:
1171 except:
1172 if edited:
1172 if edited:
1173 self.ui.write(
1173 self.ui.write(
1174 _('note: commit message saved in %s\n') % msgfn)
1174 _('note: commit message saved in %s\n') % msgfn)
1175 raise
1175 raise
1176
1176
1177 # update bookmarks, dirstate and mergestate
1177 # update bookmarks, dirstate and mergestate
1178 bookmarks.update(self, p1, ret)
1178 bookmarks.update(self, p1, ret)
1179 for f in changes[0] + changes[1]:
1179 for f in changes[0] + changes[1]:
1180 self.dirstate.normal(f)
1180 self.dirstate.normal(f)
1181 for f in changes[2]:
1181 for f in changes[2]:
1182 self.dirstate.drop(f)
1182 self.dirstate.drop(f)
1183 self.dirstate.setparents(ret)
1183 self.dirstate.setparents(ret)
1184 ms.reset()
1184 ms.reset()
1185 finally:
1185 finally:
1186 wlock.release()
1186 wlock.release()
1187
1187
1188 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1188 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1189 return ret
1189 return ret
1190
1190
1191 def commitctx(self, ctx, error=False):
1191 def commitctx(self, ctx, error=False):
1192 """Add a new revision to current repository.
1192 """Add a new revision to current repository.
1193 Revision information is passed via the context argument.
1193 Revision information is passed via the context argument.
1194 """
1194 """
1195
1195
1196 tr = lock = None
1196 tr = lock = None
1197 removed = list(ctx.removed())
1197 removed = list(ctx.removed())
1198 p1, p2 = ctx.p1(), ctx.p2()
1198 p1, p2 = ctx.p1(), ctx.p2()
1199 user = ctx.user()
1199 user = ctx.user()
1200
1200
1201 lock = self.lock()
1201 lock = self.lock()
1202 try:
1202 try:
1203 tr = self.transaction("commit")
1203 tr = self.transaction("commit")
1204 trp = weakref.proxy(tr)
1204 trp = weakref.proxy(tr)
1205
1205
1206 if ctx.files():
1206 if ctx.files():
1207 m1 = p1.manifest().copy()
1207 m1 = p1.manifest().copy()
1208 m2 = p2.manifest()
1208 m2 = p2.manifest()
1209
1209
1210 # check in files
1210 # check in files
1211 new = {}
1211 new = {}
1212 changed = []
1212 changed = []
1213 linkrev = len(self)
1213 linkrev = len(self)
1214 for f in sorted(ctx.modified() + ctx.added()):
1214 for f in sorted(ctx.modified() + ctx.added()):
1215 self.ui.note(f + "\n")
1215 self.ui.note(f + "\n")
1216 try:
1216 try:
1217 fctx = ctx[f]
1217 fctx = ctx[f]
1218 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1218 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1219 changed)
1219 changed)
1220 m1.set(f, fctx.flags())
1220 m1.set(f, fctx.flags())
1221 except OSError, inst:
1221 except OSError, inst:
1222 self.ui.warn(_("trouble committing %s!\n") % f)
1222 self.ui.warn(_("trouble committing %s!\n") % f)
1223 raise
1223 raise
1224 except IOError, inst:
1224 except IOError, inst:
1225 errcode = getattr(inst, 'errno', errno.ENOENT)
1225 errcode = getattr(inst, 'errno', errno.ENOENT)
1226 if error or errcode and errcode != errno.ENOENT:
1226 if error or errcode and errcode != errno.ENOENT:
1227 self.ui.warn(_("trouble committing %s!\n") % f)
1227 self.ui.warn(_("trouble committing %s!\n") % f)
1228 raise
1228 raise
1229 else:
1229 else:
1230 removed.append(f)
1230 removed.append(f)
1231
1231
1232 # update manifest
1232 # update manifest
1233 m1.update(new)
1233 m1.update(new)
1234 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1234 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1235 drop = [f for f in removed if f in m1]
1235 drop = [f for f in removed if f in m1]
1236 for f in drop:
1236 for f in drop:
1237 del m1[f]
1237 del m1[f]
1238 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1238 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1239 p2.manifestnode(), (new, drop))
1239 p2.manifestnode(), (new, drop))
1240 files = changed + removed
1240 files = changed + removed
1241 else:
1241 else:
1242 mn = p1.manifestnode()
1242 mn = p1.manifestnode()
1243 files = []
1243 files = []
1244
1244
1245 # update changelog
1245 # update changelog
1246 self.changelog.delayupdate()
1246 self.changelog.delayupdate()
1247 n = self.changelog.add(mn, files, ctx.description(),
1247 n = self.changelog.add(mn, files, ctx.description(),
1248 trp, p1.node(), p2.node(),
1248 trp, p1.node(), p2.node(),
1249 user, ctx.date(), ctx.extra().copy())
1249 user, ctx.date(), ctx.extra().copy())
1250 p = lambda: self.changelog.writepending() and self.root or ""
1250 p = lambda: self.changelog.writepending() and self.root or ""
1251 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1251 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1252 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1252 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1253 parent2=xp2, pending=p)
1253 parent2=xp2, pending=p)
1254 self.changelog.finalize(trp)
1254 self.changelog.finalize(trp)
1255 # set the new commit is proper phase
1255 # set the new commit is proper phase
1256 targetphase = self.ui.configint('phases', 'new-commit',
1256 targetphase = self.ui.configint('phases', 'new-commit',
1257 phases.draft)
1257 phases.draft)
1258 if targetphase:
1258 if targetphase:
1259 # retract boundary do not alter parent changeset.
1259 # retract boundary do not alter parent changeset.
1260 # if a parent have higher the resulting phase will
1260 # if a parent have higher the resulting phase will
1261 # be compliant anyway
1261 # be compliant anyway
1262 #
1262 #
1263 # if minimal phase was 0 we don't need to retract anything
1263 # if minimal phase was 0 we don't need to retract anything
1264 phases.retractboundary(self, targetphase, [n])
1264 phases.retractboundary(self, targetphase, [n])
1265 tr.close()
1265 tr.close()
1266
1266
1267 if self._branchcache:
1267 if self._branchcache:
1268 self.updatebranchcache()
1268 self.updatebranchcache()
1269 return n
1269 return n
1270 finally:
1270 finally:
1271 if tr:
1271 if tr:
1272 tr.release()
1272 tr.release()
1273 lock.release()
1273 lock.release()
1274
1274
1275 def destroyed(self):
1275 def destroyed(self):
1276 '''Inform the repository that nodes have been destroyed.
1276 '''Inform the repository that nodes have been destroyed.
1277 Intended for use by strip and rollback, so there's a common
1277 Intended for use by strip and rollback, so there's a common
1278 place for anything that has to be done after destroying history.'''
1278 place for anything that has to be done after destroying history.'''
1279 # XXX it might be nice if we could take the list of destroyed
1279 # XXX it might be nice if we could take the list of destroyed
1280 # nodes, but I don't see an easy way for rollback() to do that
1280 # nodes, but I don't see an easy way for rollback() to do that
1281
1281
1282 # Ensure the persistent tag cache is updated. Doing it now
1282 # Ensure the persistent tag cache is updated. Doing it now
1283 # means that the tag cache only has to worry about destroyed
1283 # means that the tag cache only has to worry about destroyed
1284 # heads immediately after a strip/rollback. That in turn
1284 # heads immediately after a strip/rollback. That in turn
1285 # guarantees that "cachetip == currenttip" (comparing both rev
1285 # guarantees that "cachetip == currenttip" (comparing both rev
1286 # and node) always means no nodes have been added or destroyed.
1286 # and node) always means no nodes have been added or destroyed.
1287
1287
1288 # XXX this is suboptimal when qrefresh'ing: we strip the current
1288 # XXX this is suboptimal when qrefresh'ing: we strip the current
1289 # head, refresh the tag cache, then immediately add a new head.
1289 # head, refresh the tag cache, then immediately add a new head.
1290 # But I think doing it this way is necessary for the "instant
1290 # But I think doing it this way is necessary for the "instant
1291 # tag cache retrieval" case to work.
1291 # tag cache retrieval" case to work.
1292 self.invalidatecaches()
1292 self.invalidatecaches()
1293
1293
1294 def walk(self, match, node=None):
1294 def walk(self, match, node=None):
1295 '''
1295 '''
1296 walk recursively through the directory tree or a given
1296 walk recursively through the directory tree or a given
1297 changeset, finding all files matched by the match
1297 changeset, finding all files matched by the match
1298 function
1298 function
1299 '''
1299 '''
1300 return self[node].walk(match)
1300 return self[node].walk(match)
1301
1301
1302 def status(self, node1='.', node2=None, match=None,
1302 def status(self, node1='.', node2=None, match=None,
1303 ignored=False, clean=False, unknown=False,
1303 ignored=False, clean=False, unknown=False,
1304 listsubrepos=False):
1304 listsubrepos=False):
1305 """return status of files between two nodes or node and working directory
1305 """return status of files between two nodes or node and working directory
1306
1306
1307 If node1 is None, use the first dirstate parent instead.
1307 If node1 is None, use the first dirstate parent instead.
1308 If node2 is None, compare node1 with working directory.
1308 If node2 is None, compare node1 with working directory.
1309 """
1309 """
1310
1310
1311 def mfmatches(ctx):
1311 def mfmatches(ctx):
1312 mf = ctx.manifest().copy()
1312 mf = ctx.manifest().copy()
1313 for fn in mf.keys():
1313 for fn in mf.keys():
1314 if not match(fn):
1314 if not match(fn):
1315 del mf[fn]
1315 del mf[fn]
1316 return mf
1316 return mf
1317
1317
1318 if isinstance(node1, context.changectx):
1318 if isinstance(node1, context.changectx):
1319 ctx1 = node1
1319 ctx1 = node1
1320 else:
1320 else:
1321 ctx1 = self[node1]
1321 ctx1 = self[node1]
1322 if isinstance(node2, context.changectx):
1322 if isinstance(node2, context.changectx):
1323 ctx2 = node2
1323 ctx2 = node2
1324 else:
1324 else:
1325 ctx2 = self[node2]
1325 ctx2 = self[node2]
1326
1326
1327 working = ctx2.rev() is None
1327 working = ctx2.rev() is None
1328 parentworking = working and ctx1 == self['.']
1328 parentworking = working and ctx1 == self['.']
1329 match = match or matchmod.always(self.root, self.getcwd())
1329 match = match or matchmod.always(self.root, self.getcwd())
1330 listignored, listclean, listunknown = ignored, clean, unknown
1330 listignored, listclean, listunknown = ignored, clean, unknown
1331
1331
1332 # load earliest manifest first for caching reasons
1332 # load earliest manifest first for caching reasons
1333 if not working and ctx2.rev() < ctx1.rev():
1333 if not working and ctx2.rev() < ctx1.rev():
1334 ctx2.manifest()
1334 ctx2.manifest()
1335
1335
1336 if not parentworking:
1336 if not parentworking:
1337 def bad(f, msg):
1337 def bad(f, msg):
1338 if f not in ctx1:
1338 if f not in ctx1:
1339 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1339 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1340 match.bad = bad
1340 match.bad = bad
1341
1341
1342 if working: # we need to scan the working dir
1342 if working: # we need to scan the working dir
1343 subrepos = []
1343 subrepos = []
1344 if '.hgsub' in self.dirstate:
1344 if '.hgsub' in self.dirstate:
1345 subrepos = ctx2.substate.keys()
1345 subrepos = ctx2.substate.keys()
1346 s = self.dirstate.status(match, subrepos, listignored,
1346 s = self.dirstate.status(match, subrepos, listignored,
1347 listclean, listunknown)
1347 listclean, listunknown)
1348 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1348 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1349
1349
1350 # check for any possibly clean files
1350 # check for any possibly clean files
1351 if parentworking and cmp:
1351 if parentworking and cmp:
1352 fixup = []
1352 fixup = []
1353 # do a full compare of any files that might have changed
1353 # do a full compare of any files that might have changed
1354 for f in sorted(cmp):
1354 for f in sorted(cmp):
1355 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1355 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1356 or ctx1[f].cmp(ctx2[f])):
1356 or ctx1[f].cmp(ctx2[f])):
1357 modified.append(f)
1357 modified.append(f)
1358 else:
1358 else:
1359 fixup.append(f)
1359 fixup.append(f)
1360
1360
1361 # update dirstate for files that are actually clean
1361 # update dirstate for files that are actually clean
1362 if fixup:
1362 if fixup:
1363 if listclean:
1363 if listclean:
1364 clean += fixup
1364 clean += fixup
1365
1365
1366 try:
1366 try:
1367 # updating the dirstate is optional
1367 # updating the dirstate is optional
1368 # so we don't wait on the lock
1368 # so we don't wait on the lock
1369 wlock = self.wlock(False)
1369 wlock = self.wlock(False)
1370 try:
1370 try:
1371 for f in fixup:
1371 for f in fixup:
1372 self.dirstate.normal(f)
1372 self.dirstate.normal(f)
1373 finally:
1373 finally:
1374 wlock.release()
1374 wlock.release()
1375 except error.LockError:
1375 except error.LockError:
1376 pass
1376 pass
1377
1377
1378 if not parentworking:
1378 if not parentworking:
1379 mf1 = mfmatches(ctx1)
1379 mf1 = mfmatches(ctx1)
1380 if working:
1380 if working:
1381 # we are comparing working dir against non-parent
1381 # we are comparing working dir against non-parent
1382 # generate a pseudo-manifest for the working dir
1382 # generate a pseudo-manifest for the working dir
1383 mf2 = mfmatches(self['.'])
1383 mf2 = mfmatches(self['.'])
1384 for f in cmp + modified + added:
1384 for f in cmp + modified + added:
1385 mf2[f] = None
1385 mf2[f] = None
1386 mf2.set(f, ctx2.flags(f))
1386 mf2.set(f, ctx2.flags(f))
1387 for f in removed:
1387 for f in removed:
1388 if f in mf2:
1388 if f in mf2:
1389 del mf2[f]
1389 del mf2[f]
1390 else:
1390 else:
1391 # we are comparing two revisions
1391 # we are comparing two revisions
1392 deleted, unknown, ignored = [], [], []
1392 deleted, unknown, ignored = [], [], []
1393 mf2 = mfmatches(ctx2)
1393 mf2 = mfmatches(ctx2)
1394
1394
1395 modified, added, clean = [], [], []
1395 modified, added, clean = [], [], []
1396 for fn in mf2:
1396 for fn in mf2:
1397 if fn in mf1:
1397 if fn in mf1:
1398 if (fn not in deleted and
1398 if (fn not in deleted and
1399 (mf1.flags(fn) != mf2.flags(fn) or
1399 (mf1.flags(fn) != mf2.flags(fn) or
1400 (mf1[fn] != mf2[fn] and
1400 (mf1[fn] != mf2[fn] and
1401 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1401 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1402 modified.append(fn)
1402 modified.append(fn)
1403 elif listclean:
1403 elif listclean:
1404 clean.append(fn)
1404 clean.append(fn)
1405 del mf1[fn]
1405 del mf1[fn]
1406 elif fn not in deleted:
1406 elif fn not in deleted:
1407 added.append(fn)
1407 added.append(fn)
1408 removed = mf1.keys()
1408 removed = mf1.keys()
1409
1409
1410 if working and modified and not self.dirstate._checklink:
1410 if working and modified and not self.dirstate._checklink:
1411 # Symlink placeholders may get non-symlink-like contents
1411 # Symlink placeholders may get non-symlink-like contents
1412 # via user error or dereferencing by NFS or Samba servers,
1412 # via user error or dereferencing by NFS or Samba servers,
1413 # so we filter out any placeholders that don't look like a
1413 # so we filter out any placeholders that don't look like a
1414 # symlink
1414 # symlink
1415 sane = []
1415 sane = []
1416 for f in modified:
1416 for f in modified:
1417 if ctx2.flags(f) == 'l':
1417 if ctx2.flags(f) == 'l':
1418 d = ctx2[f].data()
1418 d = ctx2[f].data()
1419 if len(d) >= 1024 or '\n' in d or util.binary(d):
1419 if len(d) >= 1024 or '\n' in d or util.binary(d):
1420 self.ui.debug('ignoring suspect symlink placeholder'
1420 self.ui.debug('ignoring suspect symlink placeholder'
1421 ' "%s"\n' % f)
1421 ' "%s"\n' % f)
1422 continue
1422 continue
1423 sane.append(f)
1423 sane.append(f)
1424 modified = sane
1424 modified = sane
1425
1425
1426 r = modified, added, removed, deleted, unknown, ignored, clean
1426 r = modified, added, removed, deleted, unknown, ignored, clean
1427
1427
1428 if listsubrepos:
1428 if listsubrepos:
1429 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1429 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1430 if working:
1430 if working:
1431 rev2 = None
1431 rev2 = None
1432 else:
1432 else:
1433 rev2 = ctx2.substate[subpath][1]
1433 rev2 = ctx2.substate[subpath][1]
1434 try:
1434 try:
1435 submatch = matchmod.narrowmatcher(subpath, match)
1435 submatch = matchmod.narrowmatcher(subpath, match)
1436 s = sub.status(rev2, match=submatch, ignored=listignored,
1436 s = sub.status(rev2, match=submatch, ignored=listignored,
1437 clean=listclean, unknown=listunknown,
1437 clean=listclean, unknown=listunknown,
1438 listsubrepos=True)
1438 listsubrepos=True)
1439 for rfiles, sfiles in zip(r, s):
1439 for rfiles, sfiles in zip(r, s):
1440 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1440 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1441 except error.LookupError:
1441 except error.LookupError:
1442 self.ui.status(_("skipping missing subrepository: %s\n")
1442 self.ui.status(_("skipping missing subrepository: %s\n")
1443 % subpath)
1443 % subpath)
1444
1444
1445 for l in r:
1445 for l in r:
1446 l.sort()
1446 l.sort()
1447 return r
1447 return r
1448
1448
1449 def heads(self, start=None):
1449 def heads(self, start=None):
1450 heads = self.changelog.heads(start)
1450 heads = self.changelog.heads(start)
1451 # sort the output in rev descending order
1451 # sort the output in rev descending order
1452 return sorted(heads, key=self.changelog.rev, reverse=True)
1452 return sorted(heads, key=self.changelog.rev, reverse=True)
1453
1453
1454 def branchheads(self, branch=None, start=None, closed=False):
1454 def branchheads(self, branch=None, start=None, closed=False):
1455 '''return a (possibly filtered) list of heads for the given branch
1455 '''return a (possibly filtered) list of heads for the given branch
1456
1456
1457 Heads are returned in topological order, from newest to oldest.
1457 Heads are returned in topological order, from newest to oldest.
1458 If branch is None, use the dirstate branch.
1458 If branch is None, use the dirstate branch.
1459 If start is not None, return only heads reachable from start.
1459 If start is not None, return only heads reachable from start.
1460 If closed is True, return heads that are marked as closed as well.
1460 If closed is True, return heads that are marked as closed as well.
1461 '''
1461 '''
1462 if branch is None:
1462 if branch is None:
1463 branch = self[None].branch()
1463 branch = self[None].branch()
1464 branches = self.branchmap()
1464 branches = self.branchmap()
1465 if branch not in branches:
1465 if branch not in branches:
1466 return []
1466 return []
1467 # the cache returns heads ordered lowest to highest
1467 # the cache returns heads ordered lowest to highest
1468 bheads = list(reversed(branches[branch]))
1468 bheads = list(reversed(branches[branch]))
1469 if start is not None:
1469 if start is not None:
1470 # filter out the heads that cannot be reached from startrev
1470 # filter out the heads that cannot be reached from startrev
1471 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1471 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1472 bheads = [h for h in bheads if h in fbheads]
1472 bheads = [h for h in bheads if h in fbheads]
1473 if not closed:
1473 if not closed:
1474 bheads = [h for h in bheads if
1474 bheads = [h for h in bheads if
1475 ('close' not in self.changelog.read(h)[5])]
1475 ('close' not in self.changelog.read(h)[5])]
1476 return bheads
1476 return bheads
1477
1477
1478 def branches(self, nodes):
1478 def branches(self, nodes):
1479 if not nodes:
1479 if not nodes:
1480 nodes = [self.changelog.tip()]
1480 nodes = [self.changelog.tip()]
1481 b = []
1481 b = []
1482 for n in nodes:
1482 for n in nodes:
1483 t = n
1483 t = n
1484 while True:
1484 while True:
1485 p = self.changelog.parents(n)
1485 p = self.changelog.parents(n)
1486 if p[1] != nullid or p[0] == nullid:
1486 if p[1] != nullid or p[0] == nullid:
1487 b.append((t, n, p[0], p[1]))
1487 b.append((t, n, p[0], p[1]))
1488 break
1488 break
1489 n = p[0]
1489 n = p[0]
1490 return b
1490 return b
1491
1491
1492 def between(self, pairs):
1492 def between(self, pairs):
1493 r = []
1493 r = []
1494
1494
1495 for top, bottom in pairs:
1495 for top, bottom in pairs:
1496 n, l, i = top, [], 0
1496 n, l, i = top, [], 0
1497 f = 1
1497 f = 1
1498
1498
1499 while n != bottom and n != nullid:
1499 while n != bottom and n != nullid:
1500 p = self.changelog.parents(n)[0]
1500 p = self.changelog.parents(n)[0]
1501 if i == f:
1501 if i == f:
1502 l.append(n)
1502 l.append(n)
1503 f = f * 2
1503 f = f * 2
1504 n = p
1504 n = p
1505 i += 1
1505 i += 1
1506
1506
1507 r.append(l)
1507 r.append(l)
1508
1508
1509 return r
1509 return r
1510
1510
1511 def pull(self, remote, heads=None, force=False):
1511 def pull(self, remote, heads=None, force=False):
1512 lock = self.lock()
1512 lock = self.lock()
1513 try:
1513 try:
1514 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1514 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1515 force=force)
1515 force=force)
1516 common, fetch, rheads = tmp
1516 common, fetch, rheads = tmp
1517 if not fetch:
1517 if not fetch:
1518 self.ui.status(_("no changes found\n"))
1518 self.ui.status(_("no changes found\n"))
1519 added = []
1519 added = []
1520 result = 0
1520 result = 0
1521 else:
1521 else:
1522 if heads is None and list(common) == [nullid]:
1522 if heads is None and list(common) == [nullid]:
1523 self.ui.status(_("requesting all changes\n"))
1523 self.ui.status(_("requesting all changes\n"))
1524 elif heads is None and remote.capable('changegroupsubset'):
1524 elif heads is None and remote.capable('changegroupsubset'):
1525 # issue1320, avoid a race if remote changed after discovery
1525 # issue1320, avoid a race if remote changed after discovery
1526 heads = rheads
1526 heads = rheads
1527
1527
1528 if remote.capable('getbundle'):
1528 if remote.capable('getbundle'):
1529 cg = remote.getbundle('pull', common=common,
1529 cg = remote.getbundle('pull', common=common,
1530 heads=heads or rheads)
1530 heads=heads or rheads)
1531 elif heads is None:
1531 elif heads is None:
1532 cg = remote.changegroup(fetch, 'pull')
1532 cg = remote.changegroup(fetch, 'pull')
1533 elif not remote.capable('changegroupsubset'):
1533 elif not remote.capable('changegroupsubset'):
1534 raise util.Abort(_("partial pull cannot be done because "
1534 raise util.Abort(_("partial pull cannot be done because "
1535 "other repository doesn't support "
1535 "other repository doesn't support "
1536 "changegroupsubset."))
1536 "changegroupsubset."))
1537 else:
1537 else:
1538 cg = remote.changegroupsubset(fetch, heads, 'pull')
1538 cg = remote.changegroupsubset(fetch, heads, 'pull')
1539 clstart = len(self.changelog)
1539 clstart = len(self.changelog)
1540 result = self.addchangegroup(cg, 'pull', remote.url())
1540 result = self.addchangegroup(cg, 'pull', remote.url())
1541 clend = len(self.changelog)
1541 clend = len(self.changelog)
1542 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1542 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1543
1543
1544
1544
1545 # Get remote phases data from remote
1545 # Get remote phases data from remote
1546 remotephases = remote.listkeys('phases')
1546 remotephases = remote.listkeys('phases')
1547 publishing = bool(remotephases.get('publishing', False))
1547 publishing = bool(remotephases.get('publishing', False))
1548 if remotephases and not publishing:
1548 if remotephases and not publishing:
1549 # remote is new and unpublishing
1549 # remote is new and unpublishing
1550 subset = common + added
1550 subset = common + added
1551 rheads, rroots = phases.analyzeremotephases(self, subset,
1551 rheads, rroots = phases.analyzeremotephases(self, subset,
1552 remotephases)
1552 remotephases)
1553 for phase, boundary in enumerate(rheads):
1553 for phase, boundary in enumerate(rheads):
1554 phases.advanceboundary(self, phase, boundary)
1554 phases.advanceboundary(self, phase, boundary)
1555 else:
1555 else:
1556 # Remote is old or publishing all common changesets
1556 # Remote is old or publishing all common changesets
1557 # should be seen as public
1557 # should be seen as public
1558 phases.advanceboundary(self, phases.public, common + added)
1558 phases.advanceboundary(self, phases.public, common + added)
1559 finally:
1559 finally:
1560 lock.release()
1560 lock.release()
1561
1561
1562 return result
1562 return result
1563
1563
1564 def checkpush(self, force, revs):
1564 def checkpush(self, force, revs):
1565 """Extensions can override this function if additional checks have
1565 """Extensions can override this function if additional checks have
1566 to be performed before pushing, or call it if they override push
1566 to be performed before pushing, or call it if they override push
1567 command.
1567 command.
1568 """
1568 """
1569 pass
1569 pass
1570
1570
1571 def push(self, remote, force=False, revs=None, newbranch=False):
1571 def push(self, remote, force=False, revs=None, newbranch=False):
1572 '''Push outgoing changesets (limited by revs) from the current
1572 '''Push outgoing changesets (limited by revs) from the current
1573 repository to remote. Return an integer:
1573 repository to remote. Return an integer:
1574 - 0 means HTTP error *or* nothing to push
1574 - 0 means HTTP error *or* nothing to push
1575 - 1 means we pushed and remote head count is unchanged *or*
1575 - 1 means we pushed and remote head count is unchanged *or*
1576 we have outgoing changesets but refused to push
1576 we have outgoing changesets but refused to push
1577 - other values as described by addchangegroup()
1577 - other values as described by addchangegroup()
1578 '''
1578 '''
1579 # there are two ways to push to remote repo:
1579 # there are two ways to push to remote repo:
1580 #
1580 #
1581 # addchangegroup assumes local user can lock remote
1581 # addchangegroup assumes local user can lock remote
1582 # repo (local filesystem, old ssh servers).
1582 # repo (local filesystem, old ssh servers).
1583 #
1583 #
1584 # unbundle assumes local user cannot lock remote repo (new ssh
1584 # unbundle assumes local user cannot lock remote repo (new ssh
1585 # servers, http servers).
1585 # servers, http servers).
1586
1586
1587 self.checkpush(force, revs)
1587 self.checkpush(force, revs)
1588 lock = None
1588 lock = None
1589 unbundle = remote.capable('unbundle')
1589 unbundle = remote.capable('unbundle')
1590 if not unbundle:
1590 if not unbundle:
1591 lock = remote.lock()
1591 lock = remote.lock()
1592 try:
1592 try:
1593 # get local lock as we might write phase data
1593 # get local lock as we might write phase data
1594 locallock = self.lock()
1594 locallock = self.lock()
1595 try:
1595 try:
1596 cg, remote_heads, fut = discovery.prepush(self, remote, force,
1596 cg, remote_heads, fut = discovery.prepush(self, remote, force,
1597 revs, newbranch)
1597 revs, newbranch)
1598 ret = remote_heads
1598 ret = remote_heads
1599 # create a callback for addchangegroup.
1599 # create a callback for addchangegroup.
1600 # If will be used branch of the conditionnal too.
1600 # If will be used branch of the conditionnal too.
1601 if cg is not None:
1601 if cg is not None:
1602 if unbundle:
1602 if unbundle:
1603 # local repo finds heads on server, finds out what
1603 # local repo finds heads on server, finds out what
1604 # revs it must push. once revs transferred, if server
1604 # revs it must push. once revs transferred, if server
1605 # finds it has different heads (someone else won
1605 # finds it has different heads (someone else won
1606 # commit/push race), server aborts.
1606 # commit/push race), server aborts.
1607 if force:
1607 if force:
1608 remote_heads = ['force']
1608 remote_heads = ['force']
1609 # ssh: return remote's addchangegroup()
1609 # ssh: return remote's addchangegroup()
1610 # http: return remote's addchangegroup() or 0 for error
1610 # http: return remote's addchangegroup() or 0 for error
1611 ret = remote.unbundle(cg, remote_heads, 'push')
1611 ret = remote.unbundle(cg, remote_heads, 'push')
1612 else:
1612 else:
1613 # we return an integer indicating remote head count change
1613 # we return an integer indicating remote head count change
1614 ret = remote.addchangegroup(cg, 'push', self.url())
1614 ret = remote.addchangegroup(cg, 'push', self.url())
1615
1615
1616 # even when we don't push, exchanging phase data is useful
1616 # even when we don't push, exchanging phase data is useful
1617 remotephases = remote.listkeys('phases')
1617 remotephases = remote.listkeys('phases')
1618 if not remotephases: # old server or public only repo
1618 if not remotephases: # old server or public only repo
1619 phases.advanceboundary(self, phases.public, fut)
1619 phases.advanceboundary(self, phases.public, fut)
1620 # don't push any phase data as there is nothing to push
1620 # don't push any phase data as there is nothing to push
1621 else:
1621 else:
1622 ana = phases.analyzeremotephases(self, fut, remotephases)
1622 ana = phases.analyzeremotephases(self, fut, remotephases)
1623 rheads, rroots = ana
1623 rheads, rroots = ana
1624 ### Apply remote phase on local
1624 ### Apply remote phase on local
1625 if remotephases.get('publishing', False):
1625 if remotephases.get('publishing', False):
1626 phases.advanceboundary(self, phases.public, fut)
1626 phases.advanceboundary(self, phases.public, fut)
1627 else: # publish = False
1627 else: # publish = False
1628 for phase, rpheads in enumerate(rheads):
1628 for phase, rpheads in enumerate(rheads):
1629 phases.advanceboundary(self, phase, rpheads)
1629 phases.advanceboundary(self, phase, rpheads)
1630 ### Apply local phase on remote
1630 ### Apply local phase on remote
1631 #
1631 #
1632 # XXX If push failed we should use strict common and not
1632 # XXX If push failed we should use strict common and not
1633 # future to avoir pushing phase data on unknown changeset.
1633 # future to avoir pushing phase data on unknown changeset.
1634 # This is to done later.
1634 # This is to done later.
1635
1635
1636 # element we want to push
1636 # element we want to push
1637 topush = []
1637 topush = []
1638
1638
1639 # store details of known remote phase of several revision
1639 # store details of known remote phase of several revision
1640 # /!\ set of index I holds rev where: I <= rev.phase()
1640 # /!\ set of index I holds rev where: I <= rev.phase()
1641 # /!\ public phase (index 0) is ignored
1641 # /!\ public phase (index 0) is ignored
1642 remdetails = [set() for i in xrange(len(phases.allphases))]
1642 remdetails = [set() for i in xrange(len(phases.allphases))]
1643 _revs = set()
1643 _revs = set()
1644 for relremphase in phases.trackedphases[::-1]:
1644 for relremphase in phases.trackedphases[::-1]:
1645 # we iterate backward because the list alway grows
1645 # we iterate backward because the list alway grows
1646 # when filled in this direction.
1646 # when filled in this direction.
1647 _revs.update(self.revs('%ln::%ln',
1647 _revs.update(self.revs('%ln::%ln',
1648 rroots[relremphase], fut))
1648 rroots[relremphase], fut))
1649 remdetails[relremphase].update(_revs)
1649 remdetails[relremphase].update(_revs)
1650
1650
1651 for phase in phases.allphases[:-1]:
1651 for phase in phases.allphases[:-1]:
1652 # We don't need the last phase as we will never want to
1652 # We don't need the last phase as we will never want to
1653 # move anything to it while moving phase backward.
1653 # move anything to it while moving phase backward.
1654
1654
1655 # Get the list of all revs on remote which are in a
1655 # Get the list of all revs on remote which are in a
1656 # phase higher than currently processed phase.
1656 # phase higher than currently processed phase.
1657 relremrev = remdetails[phase + 1]
1657 relremrev = remdetails[phase + 1]
1658
1658
1659 if not relremrev:
1659 if not relremrev:
1660 # no candidate to remote push anymore
1660 # no candidate to remote push anymore
1661 # break before any expensive revset
1661 # break before any expensive revset
1662 break
1662 break
1663
1663
1664 #dynamical inject appropriate phase symbol
1664 #dynamical inject appropriate phase symbol
1665 phasename = phases.phasenames[phase]
1665 phasename = phases.phasenames[phase]
1666 odrevset = 'heads(%%ld and %s())' % phasename
1666 odrevset = 'heads(%%ld and %s())' % phasename
1667 outdated = self.set(odrevset, relremrev)
1667 outdated = self.set(odrevset, relremrev)
1668 for od in outdated:
1668 for od in outdated:
1669 candstart = len(remdetails) - 1
1669 candstart = len(remdetails) - 1
1670 candstop = phase + 1
1670 candstop = phase + 1
1671 candidateold = xrange(candstart, candstop, -1)
1671 candidateold = xrange(candstart, candstop, -1)
1672 for oldphase in candidateold:
1672 for oldphase in candidateold:
1673 if od.rev() in remdetails[oldphase]:
1673 if od.rev() in remdetails[oldphase]:
1674 break
1674 break
1675 else: # last one: no need to search
1675 else: # last one: no need to search
1676 oldphase = phase + 1
1676 oldphase = phase + 1
1677 topush.append((oldphase, phase, od))
1677 topush.append((oldphase, phase, od))
1678
1678
1679 # push every needed data
1679 # push every needed data
1680 for oldphase, newphase, newremotehead in topush:
1680 for oldphase, newphase, newremotehead in topush:
1681 r = remote.pushkey('phases',
1681 r = remote.pushkey('phases',
1682 newremotehead.hex(),
1682 newremotehead.hex(),
1683 str(oldphase), str(newphase))
1683 str(oldphase), str(newphase))
1684 if not r:
1684 if not r:
1685 self.ui.warn(_('updating phase of %s '
1685 self.ui.warn(_('updating phase of %s '
1686 'to %s from %s failed!\n')
1686 'to %s from %s failed!\n')
1687 % (newremotehead, newphase,
1687 % (newremotehead, newphase,
1688 oldphase))
1688 oldphase))
1689 finally:
1689 finally:
1690 locallock.release()
1690 locallock.release()
1691 finally:
1691 finally:
1692 if lock is not None:
1692 if lock is not None:
1693 lock.release()
1693 lock.release()
1694
1694
1695 self.ui.debug("checking for updated bookmarks\n")
1695 self.ui.debug("checking for updated bookmarks\n")
1696 rb = remote.listkeys('bookmarks')
1696 rb = remote.listkeys('bookmarks')
1697 for k in rb.keys():
1697 for k in rb.keys():
1698 if k in self._bookmarks:
1698 if k in self._bookmarks:
1699 nr, nl = rb[k], hex(self._bookmarks[k])
1699 nr, nl = rb[k], hex(self._bookmarks[k])
1700 if nr in self:
1700 if nr in self:
1701 cr = self[nr]
1701 cr = self[nr]
1702 cl = self[nl]
1702 cl = self[nl]
1703 if cl in cr.descendants():
1703 if cl in cr.descendants():
1704 r = remote.pushkey('bookmarks', k, nr, nl)
1704 r = remote.pushkey('bookmarks', k, nr, nl)
1705 if r:
1705 if r:
1706 self.ui.status(_("updating bookmark %s\n") % k)
1706 self.ui.status(_("updating bookmark %s\n") % k)
1707 else:
1707 else:
1708 self.ui.warn(_('updating bookmark %s'
1708 self.ui.warn(_('updating bookmark %s'
1709 ' failed!\n') % k)
1709 ' failed!\n') % k)
1710
1710
1711 return ret
1711 return ret
1712
1712
1713 def changegroupinfo(self, nodes, source):
1713 def changegroupinfo(self, nodes, source):
1714 if self.ui.verbose or source == 'bundle':
1714 if self.ui.verbose or source == 'bundle':
1715 self.ui.status(_("%d changesets found\n") % len(nodes))
1715 self.ui.status(_("%d changesets found\n") % len(nodes))
1716 if self.ui.debugflag:
1716 if self.ui.debugflag:
1717 self.ui.debug("list of changesets:\n")
1717 self.ui.debug("list of changesets:\n")
1718 for node in nodes:
1718 for node in nodes:
1719 self.ui.debug("%s\n" % hex(node))
1719 self.ui.debug("%s\n" % hex(node))
1720
1720
1721 def changegroupsubset(self, bases, heads, source):
1721 def changegroupsubset(self, bases, heads, source):
1722 """Compute a changegroup consisting of all the nodes that are
1722 """Compute a changegroup consisting of all the nodes that are
1723 descendants of any of the bases and ancestors of any of the heads.
1723 descendants of any of the bases and ancestors of any of the heads.
1724 Return a chunkbuffer object whose read() method will return
1724 Return a chunkbuffer object whose read() method will return
1725 successive changegroup chunks.
1725 successive changegroup chunks.
1726
1726
1727 It is fairly complex as determining which filenodes and which
1727 It is fairly complex as determining which filenodes and which
1728 manifest nodes need to be included for the changeset to be complete
1728 manifest nodes need to be included for the changeset to be complete
1729 is non-trivial.
1729 is non-trivial.
1730
1730
1731 Another wrinkle is doing the reverse, figuring out which changeset in
1731 Another wrinkle is doing the reverse, figuring out which changeset in
1732 the changegroup a particular filenode or manifestnode belongs to.
1732 the changegroup a particular filenode or manifestnode belongs to.
1733 """
1733 """
1734 cl = self.changelog
1734 cl = self.changelog
1735 if not bases:
1735 if not bases:
1736 bases = [nullid]
1736 bases = [nullid]
1737 csets, bases, heads = cl.nodesbetween(bases, heads)
1737 csets, bases, heads = cl.nodesbetween(bases, heads)
1738 # We assume that all ancestors of bases are known
1738 # We assume that all ancestors of bases are known
1739 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1739 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1740 return self._changegroupsubset(common, csets, heads, source)
1740 return self._changegroupsubset(common, csets, heads, source)
1741
1741
1742 def getlocalbundle(self, source, outgoing):
1743 """Like getbundle, but taking a discovery.outgoing as an argument.
1744
1745 This is only implemented for local repos and reuses potentially
1746 precomputed sets in outgoing."""
1747 if not outgoing.missing:
1748 return None
1749 return self._changegroupsubset(outgoing.common,
1750 outgoing.missing,
1751 outgoing.missingheads,
1752 source)
1753
1742 def getbundle(self, source, heads=None, common=None):
1754 def getbundle(self, source, heads=None, common=None):
1743 """Like changegroupsubset, but returns the set difference between the
1755 """Like changegroupsubset, but returns the set difference between the
1744 ancestors of heads and the ancestors common.
1756 ancestors of heads and the ancestors common.
1745
1757
1746 If heads is None, use the local heads. If common is None, use [nullid].
1758 If heads is None, use the local heads. If common is None, use [nullid].
1747
1759
1748 The nodes in common might not all be known locally due to the way the
1760 The nodes in common might not all be known locally due to the way the
1749 current discovery protocol works.
1761 current discovery protocol works.
1750 """
1762 """
1751 cl = self.changelog
1763 cl = self.changelog
1752 if common:
1764 if common:
1753 nm = cl.nodemap
1765 nm = cl.nodemap
1754 common = [n for n in common if n in nm]
1766 common = [n for n in common if n in nm]
1755 else:
1767 else:
1756 common = [nullid]
1768 common = [nullid]
1757 if not heads:
1769 if not heads:
1758 heads = cl.heads()
1770 heads = cl.heads()
1759 common, missing = cl.findcommonmissing(common, heads)
1771 return self.getlocalbundle(source,
1760 if not missing:
1772 discovery.outgoing(cl, common, heads))
1761 return None
1762 return self._changegroupsubset(common, missing, heads, source)
1763
1773
1764 def _changegroupsubset(self, commonrevs, csets, heads, source):
1774 def _changegroupsubset(self, commonrevs, csets, heads, source):
1765
1775
1766 cl = self.changelog
1776 cl = self.changelog
1767 mf = self.manifest
1777 mf = self.manifest
1768 mfs = {} # needed manifests
1778 mfs = {} # needed manifests
1769 fnodes = {} # needed file nodes
1779 fnodes = {} # needed file nodes
1770 changedfiles = set()
1780 changedfiles = set()
1771 fstate = ['', {}]
1781 fstate = ['', {}]
1772 count = [0]
1782 count = [0]
1773
1783
1774 # can we go through the fast path ?
1784 # can we go through the fast path ?
1775 heads.sort()
1785 heads.sort()
1776 if heads == sorted(self.heads()):
1786 if heads == sorted(self.heads()):
1777 return self._changegroup(csets, source)
1787 return self._changegroup(csets, source)
1778
1788
1779 # slow path
1789 # slow path
1780 self.hook('preoutgoing', throw=True, source=source)
1790 self.hook('preoutgoing', throw=True, source=source)
1781 self.changegroupinfo(csets, source)
1791 self.changegroupinfo(csets, source)
1782
1792
1783 # filter any nodes that claim to be part of the known set
1793 # filter any nodes that claim to be part of the known set
1784 def prune(revlog, missing):
1794 def prune(revlog, missing):
1785 return [n for n in missing
1795 return [n for n in missing
1786 if revlog.linkrev(revlog.rev(n)) not in commonrevs]
1796 if revlog.linkrev(revlog.rev(n)) not in commonrevs]
1787
1797
1788 def lookup(revlog, x):
1798 def lookup(revlog, x):
1789 if revlog == cl:
1799 if revlog == cl:
1790 c = cl.read(x)
1800 c = cl.read(x)
1791 changedfiles.update(c[3])
1801 changedfiles.update(c[3])
1792 mfs.setdefault(c[0], x)
1802 mfs.setdefault(c[0], x)
1793 count[0] += 1
1803 count[0] += 1
1794 self.ui.progress(_('bundling'), count[0],
1804 self.ui.progress(_('bundling'), count[0],
1795 unit=_('changesets'), total=len(csets))
1805 unit=_('changesets'), total=len(csets))
1796 return x
1806 return x
1797 elif revlog == mf:
1807 elif revlog == mf:
1798 clnode = mfs[x]
1808 clnode = mfs[x]
1799 mdata = mf.readfast(x)
1809 mdata = mf.readfast(x)
1800 for f in changedfiles:
1810 for f in changedfiles:
1801 if f in mdata:
1811 if f in mdata:
1802 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1812 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1803 count[0] += 1
1813 count[0] += 1
1804 self.ui.progress(_('bundling'), count[0],
1814 self.ui.progress(_('bundling'), count[0],
1805 unit=_('manifests'), total=len(mfs))
1815 unit=_('manifests'), total=len(mfs))
1806 return mfs[x]
1816 return mfs[x]
1807 else:
1817 else:
1808 self.ui.progress(
1818 self.ui.progress(
1809 _('bundling'), count[0], item=fstate[0],
1819 _('bundling'), count[0], item=fstate[0],
1810 unit=_('files'), total=len(changedfiles))
1820 unit=_('files'), total=len(changedfiles))
1811 return fstate[1][x]
1821 return fstate[1][x]
1812
1822
1813 bundler = changegroup.bundle10(lookup)
1823 bundler = changegroup.bundle10(lookup)
1814 reorder = self.ui.config('bundle', 'reorder', 'auto')
1824 reorder = self.ui.config('bundle', 'reorder', 'auto')
1815 if reorder == 'auto':
1825 if reorder == 'auto':
1816 reorder = None
1826 reorder = None
1817 else:
1827 else:
1818 reorder = util.parsebool(reorder)
1828 reorder = util.parsebool(reorder)
1819
1829
1820 def gengroup():
1830 def gengroup():
1821 # Create a changenode group generator that will call our functions
1831 # Create a changenode group generator that will call our functions
1822 # back to lookup the owning changenode and collect information.
1832 # back to lookup the owning changenode and collect information.
1823 for chunk in cl.group(csets, bundler, reorder=reorder):
1833 for chunk in cl.group(csets, bundler, reorder=reorder):
1824 yield chunk
1834 yield chunk
1825 self.ui.progress(_('bundling'), None)
1835 self.ui.progress(_('bundling'), None)
1826
1836
1827 # Create a generator for the manifestnodes that calls our lookup
1837 # Create a generator for the manifestnodes that calls our lookup
1828 # and data collection functions back.
1838 # and data collection functions back.
1829 count[0] = 0
1839 count[0] = 0
1830 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
1840 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
1831 yield chunk
1841 yield chunk
1832 self.ui.progress(_('bundling'), None)
1842 self.ui.progress(_('bundling'), None)
1833
1843
1834 mfs.clear()
1844 mfs.clear()
1835
1845
1836 # Go through all our files in order sorted by name.
1846 # Go through all our files in order sorted by name.
1837 count[0] = 0
1847 count[0] = 0
1838 for fname in sorted(changedfiles):
1848 for fname in sorted(changedfiles):
1839 filerevlog = self.file(fname)
1849 filerevlog = self.file(fname)
1840 if not len(filerevlog):
1850 if not len(filerevlog):
1841 raise util.Abort(_("empty or missing revlog for %s") % fname)
1851 raise util.Abort(_("empty or missing revlog for %s") % fname)
1842 fstate[0] = fname
1852 fstate[0] = fname
1843 fstate[1] = fnodes.pop(fname, {})
1853 fstate[1] = fnodes.pop(fname, {})
1844
1854
1845 nodelist = prune(filerevlog, fstate[1])
1855 nodelist = prune(filerevlog, fstate[1])
1846 if nodelist:
1856 if nodelist:
1847 count[0] += 1
1857 count[0] += 1
1848 yield bundler.fileheader(fname)
1858 yield bundler.fileheader(fname)
1849 for chunk in filerevlog.group(nodelist, bundler, reorder):
1859 for chunk in filerevlog.group(nodelist, bundler, reorder):
1850 yield chunk
1860 yield chunk
1851
1861
1852 # Signal that no more groups are left.
1862 # Signal that no more groups are left.
1853 yield bundler.close()
1863 yield bundler.close()
1854 self.ui.progress(_('bundling'), None)
1864 self.ui.progress(_('bundling'), None)
1855
1865
1856 if csets:
1866 if csets:
1857 self.hook('outgoing', node=hex(csets[0]), source=source)
1867 self.hook('outgoing', node=hex(csets[0]), source=source)
1858
1868
1859 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1869 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1860
1870
1861 def changegroup(self, basenodes, source):
1871 def changegroup(self, basenodes, source):
1862 # to avoid a race we use changegroupsubset() (issue1320)
1872 # to avoid a race we use changegroupsubset() (issue1320)
1863 return self.changegroupsubset(basenodes, self.heads(), source)
1873 return self.changegroupsubset(basenodes, self.heads(), source)
1864
1874
1865 def _changegroup(self, nodes, source):
1875 def _changegroup(self, nodes, source):
1866 """Compute the changegroup of all nodes that we have that a recipient
1876 """Compute the changegroup of all nodes that we have that a recipient
1867 doesn't. Return a chunkbuffer object whose read() method will return
1877 doesn't. Return a chunkbuffer object whose read() method will return
1868 successive changegroup chunks.
1878 successive changegroup chunks.
1869
1879
1870 This is much easier than the previous function as we can assume that
1880 This is much easier than the previous function as we can assume that
1871 the recipient has any changenode we aren't sending them.
1881 the recipient has any changenode we aren't sending them.
1872
1882
1873 nodes is the set of nodes to send"""
1883 nodes is the set of nodes to send"""
1874
1884
1875 cl = self.changelog
1885 cl = self.changelog
1876 mf = self.manifest
1886 mf = self.manifest
1877 mfs = {}
1887 mfs = {}
1878 changedfiles = set()
1888 changedfiles = set()
1879 fstate = ['']
1889 fstate = ['']
1880 count = [0]
1890 count = [0]
1881
1891
1882 self.hook('preoutgoing', throw=True, source=source)
1892 self.hook('preoutgoing', throw=True, source=source)
1883 self.changegroupinfo(nodes, source)
1893 self.changegroupinfo(nodes, source)
1884
1894
1885 revset = set([cl.rev(n) for n in nodes])
1895 revset = set([cl.rev(n) for n in nodes])
1886
1896
1887 def gennodelst(log):
1897 def gennodelst(log):
1888 return [log.node(r) for r in log if log.linkrev(r) in revset]
1898 return [log.node(r) for r in log if log.linkrev(r) in revset]
1889
1899
1890 def lookup(revlog, x):
1900 def lookup(revlog, x):
1891 if revlog == cl:
1901 if revlog == cl:
1892 c = cl.read(x)
1902 c = cl.read(x)
1893 changedfiles.update(c[3])
1903 changedfiles.update(c[3])
1894 mfs.setdefault(c[0], x)
1904 mfs.setdefault(c[0], x)
1895 count[0] += 1
1905 count[0] += 1
1896 self.ui.progress(_('bundling'), count[0],
1906 self.ui.progress(_('bundling'), count[0],
1897 unit=_('changesets'), total=len(nodes))
1907 unit=_('changesets'), total=len(nodes))
1898 return x
1908 return x
1899 elif revlog == mf:
1909 elif revlog == mf:
1900 count[0] += 1
1910 count[0] += 1
1901 self.ui.progress(_('bundling'), count[0],
1911 self.ui.progress(_('bundling'), count[0],
1902 unit=_('manifests'), total=len(mfs))
1912 unit=_('manifests'), total=len(mfs))
1903 return cl.node(revlog.linkrev(revlog.rev(x)))
1913 return cl.node(revlog.linkrev(revlog.rev(x)))
1904 else:
1914 else:
1905 self.ui.progress(
1915 self.ui.progress(
1906 _('bundling'), count[0], item=fstate[0],
1916 _('bundling'), count[0], item=fstate[0],
1907 total=len(changedfiles), unit=_('files'))
1917 total=len(changedfiles), unit=_('files'))
1908 return cl.node(revlog.linkrev(revlog.rev(x)))
1918 return cl.node(revlog.linkrev(revlog.rev(x)))
1909
1919
1910 bundler = changegroup.bundle10(lookup)
1920 bundler = changegroup.bundle10(lookup)
1911 reorder = self.ui.config('bundle', 'reorder', 'auto')
1921 reorder = self.ui.config('bundle', 'reorder', 'auto')
1912 if reorder == 'auto':
1922 if reorder == 'auto':
1913 reorder = None
1923 reorder = None
1914 else:
1924 else:
1915 reorder = util.parsebool(reorder)
1925 reorder = util.parsebool(reorder)
1916
1926
1917 def gengroup():
1927 def gengroup():
1918 '''yield a sequence of changegroup chunks (strings)'''
1928 '''yield a sequence of changegroup chunks (strings)'''
1919 # construct a list of all changed files
1929 # construct a list of all changed files
1920
1930
1921 for chunk in cl.group(nodes, bundler, reorder=reorder):
1931 for chunk in cl.group(nodes, bundler, reorder=reorder):
1922 yield chunk
1932 yield chunk
1923 self.ui.progress(_('bundling'), None)
1933 self.ui.progress(_('bundling'), None)
1924
1934
1925 count[0] = 0
1935 count[0] = 0
1926 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
1936 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
1927 yield chunk
1937 yield chunk
1928 self.ui.progress(_('bundling'), None)
1938 self.ui.progress(_('bundling'), None)
1929
1939
1930 count[0] = 0
1940 count[0] = 0
1931 for fname in sorted(changedfiles):
1941 for fname in sorted(changedfiles):
1932 filerevlog = self.file(fname)
1942 filerevlog = self.file(fname)
1933 if not len(filerevlog):
1943 if not len(filerevlog):
1934 raise util.Abort(_("empty or missing revlog for %s") % fname)
1944 raise util.Abort(_("empty or missing revlog for %s") % fname)
1935 fstate[0] = fname
1945 fstate[0] = fname
1936 nodelist = gennodelst(filerevlog)
1946 nodelist = gennodelst(filerevlog)
1937 if nodelist:
1947 if nodelist:
1938 count[0] += 1
1948 count[0] += 1
1939 yield bundler.fileheader(fname)
1949 yield bundler.fileheader(fname)
1940 for chunk in filerevlog.group(nodelist, bundler, reorder):
1950 for chunk in filerevlog.group(nodelist, bundler, reorder):
1941 yield chunk
1951 yield chunk
1942 yield bundler.close()
1952 yield bundler.close()
1943 self.ui.progress(_('bundling'), None)
1953 self.ui.progress(_('bundling'), None)
1944
1954
1945 if nodes:
1955 if nodes:
1946 self.hook('outgoing', node=hex(nodes[0]), source=source)
1956 self.hook('outgoing', node=hex(nodes[0]), source=source)
1947
1957
1948 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1958 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1949
1959
1950 def addchangegroup(self, source, srctype, url, emptyok=False):
1960 def addchangegroup(self, source, srctype, url, emptyok=False):
1951 """Add the changegroup returned by source.read() to this repo.
1961 """Add the changegroup returned by source.read() to this repo.
1952 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1962 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1953 the URL of the repo where this changegroup is coming from.
1963 the URL of the repo where this changegroup is coming from.
1954
1964
1955 Return an integer summarizing the change to this repo:
1965 Return an integer summarizing the change to this repo:
1956 - nothing changed or no source: 0
1966 - nothing changed or no source: 0
1957 - more heads than before: 1+added heads (2..n)
1967 - more heads than before: 1+added heads (2..n)
1958 - fewer heads than before: -1-removed heads (-2..-n)
1968 - fewer heads than before: -1-removed heads (-2..-n)
1959 - number of heads stays the same: 1
1969 - number of heads stays the same: 1
1960 """
1970 """
1961 def csmap(x):
1971 def csmap(x):
1962 self.ui.debug("add changeset %s\n" % short(x))
1972 self.ui.debug("add changeset %s\n" % short(x))
1963 return len(cl)
1973 return len(cl)
1964
1974
1965 def revmap(x):
1975 def revmap(x):
1966 return cl.rev(x)
1976 return cl.rev(x)
1967
1977
1968 if not source:
1978 if not source:
1969 return 0
1979 return 0
1970
1980
1971 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1981 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1972
1982
1973 changesets = files = revisions = 0
1983 changesets = files = revisions = 0
1974 efiles = set()
1984 efiles = set()
1975
1985
1976 # write changelog data to temp files so concurrent readers will not see
1986 # write changelog data to temp files so concurrent readers will not see
1977 # inconsistent view
1987 # inconsistent view
1978 cl = self.changelog
1988 cl = self.changelog
1979 cl.delayupdate()
1989 cl.delayupdate()
1980 oldheads = cl.heads()
1990 oldheads = cl.heads()
1981
1991
1982 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
1992 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
1983 try:
1993 try:
1984 trp = weakref.proxy(tr)
1994 trp = weakref.proxy(tr)
1985 # pull off the changeset group
1995 # pull off the changeset group
1986 self.ui.status(_("adding changesets\n"))
1996 self.ui.status(_("adding changesets\n"))
1987 clstart = len(cl)
1997 clstart = len(cl)
1988 class prog(object):
1998 class prog(object):
1989 step = _('changesets')
1999 step = _('changesets')
1990 count = 1
2000 count = 1
1991 ui = self.ui
2001 ui = self.ui
1992 total = None
2002 total = None
1993 def __call__(self):
2003 def __call__(self):
1994 self.ui.progress(self.step, self.count, unit=_('chunks'),
2004 self.ui.progress(self.step, self.count, unit=_('chunks'),
1995 total=self.total)
2005 total=self.total)
1996 self.count += 1
2006 self.count += 1
1997 pr = prog()
2007 pr = prog()
1998 source.callback = pr
2008 source.callback = pr
1999
2009
2000 source.changelogheader()
2010 source.changelogheader()
2001 if (cl.addgroup(source, csmap, trp) is None
2011 if (cl.addgroup(source, csmap, trp) is None
2002 and not emptyok):
2012 and not emptyok):
2003 raise util.Abort(_("received changelog group is empty"))
2013 raise util.Abort(_("received changelog group is empty"))
2004 clend = len(cl)
2014 clend = len(cl)
2005 changesets = clend - clstart
2015 changesets = clend - clstart
2006 for c in xrange(clstart, clend):
2016 for c in xrange(clstart, clend):
2007 efiles.update(self[c].files())
2017 efiles.update(self[c].files())
2008 efiles = len(efiles)
2018 efiles = len(efiles)
2009 self.ui.progress(_('changesets'), None)
2019 self.ui.progress(_('changesets'), None)
2010
2020
2011 # pull off the manifest group
2021 # pull off the manifest group
2012 self.ui.status(_("adding manifests\n"))
2022 self.ui.status(_("adding manifests\n"))
2013 pr.step = _('manifests')
2023 pr.step = _('manifests')
2014 pr.count = 1
2024 pr.count = 1
2015 pr.total = changesets # manifests <= changesets
2025 pr.total = changesets # manifests <= changesets
2016 # no need to check for empty manifest group here:
2026 # no need to check for empty manifest group here:
2017 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2027 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2018 # no new manifest will be created and the manifest group will
2028 # no new manifest will be created and the manifest group will
2019 # be empty during the pull
2029 # be empty during the pull
2020 source.manifestheader()
2030 source.manifestheader()
2021 self.manifest.addgroup(source, revmap, trp)
2031 self.manifest.addgroup(source, revmap, trp)
2022 self.ui.progress(_('manifests'), None)
2032 self.ui.progress(_('manifests'), None)
2023
2033
2024 needfiles = {}
2034 needfiles = {}
2025 if self.ui.configbool('server', 'validate', default=False):
2035 if self.ui.configbool('server', 'validate', default=False):
2026 # validate incoming csets have their manifests
2036 # validate incoming csets have their manifests
2027 for cset in xrange(clstart, clend):
2037 for cset in xrange(clstart, clend):
2028 mfest = self.changelog.read(self.changelog.node(cset))[0]
2038 mfest = self.changelog.read(self.changelog.node(cset))[0]
2029 mfest = self.manifest.readdelta(mfest)
2039 mfest = self.manifest.readdelta(mfest)
2030 # store file nodes we must see
2040 # store file nodes we must see
2031 for f, n in mfest.iteritems():
2041 for f, n in mfest.iteritems():
2032 needfiles.setdefault(f, set()).add(n)
2042 needfiles.setdefault(f, set()).add(n)
2033
2043
2034 # process the files
2044 # process the files
2035 self.ui.status(_("adding file changes\n"))
2045 self.ui.status(_("adding file changes\n"))
2036 pr.step = _('files')
2046 pr.step = _('files')
2037 pr.count = 1
2047 pr.count = 1
2038 pr.total = efiles
2048 pr.total = efiles
2039 source.callback = None
2049 source.callback = None
2040
2050
2041 while True:
2051 while True:
2042 chunkdata = source.filelogheader()
2052 chunkdata = source.filelogheader()
2043 if not chunkdata:
2053 if not chunkdata:
2044 break
2054 break
2045 f = chunkdata["filename"]
2055 f = chunkdata["filename"]
2046 self.ui.debug("adding %s revisions\n" % f)
2056 self.ui.debug("adding %s revisions\n" % f)
2047 pr()
2057 pr()
2048 fl = self.file(f)
2058 fl = self.file(f)
2049 o = len(fl)
2059 o = len(fl)
2050 if fl.addgroup(source, revmap, trp) is None:
2060 if fl.addgroup(source, revmap, trp) is None:
2051 raise util.Abort(_("received file revlog group is empty"))
2061 raise util.Abort(_("received file revlog group is empty"))
2052 revisions += len(fl) - o
2062 revisions += len(fl) - o
2053 files += 1
2063 files += 1
2054 if f in needfiles:
2064 if f in needfiles:
2055 needs = needfiles[f]
2065 needs = needfiles[f]
2056 for new in xrange(o, len(fl)):
2066 for new in xrange(o, len(fl)):
2057 n = fl.node(new)
2067 n = fl.node(new)
2058 if n in needs:
2068 if n in needs:
2059 needs.remove(n)
2069 needs.remove(n)
2060 if not needs:
2070 if not needs:
2061 del needfiles[f]
2071 del needfiles[f]
2062 self.ui.progress(_('files'), None)
2072 self.ui.progress(_('files'), None)
2063
2073
2064 for f, needs in needfiles.iteritems():
2074 for f, needs in needfiles.iteritems():
2065 fl = self.file(f)
2075 fl = self.file(f)
2066 for n in needs:
2076 for n in needs:
2067 try:
2077 try:
2068 fl.rev(n)
2078 fl.rev(n)
2069 except error.LookupError:
2079 except error.LookupError:
2070 raise util.Abort(
2080 raise util.Abort(
2071 _('missing file data for %s:%s - run hg verify') %
2081 _('missing file data for %s:%s - run hg verify') %
2072 (f, hex(n)))
2082 (f, hex(n)))
2073
2083
2074 dh = 0
2084 dh = 0
2075 if oldheads:
2085 if oldheads:
2076 heads = cl.heads()
2086 heads = cl.heads()
2077 dh = len(heads) - len(oldheads)
2087 dh = len(heads) - len(oldheads)
2078 for h in heads:
2088 for h in heads:
2079 if h not in oldheads and 'close' in self[h].extra():
2089 if h not in oldheads and 'close' in self[h].extra():
2080 dh -= 1
2090 dh -= 1
2081 htext = ""
2091 htext = ""
2082 if dh:
2092 if dh:
2083 htext = _(" (%+d heads)") % dh
2093 htext = _(" (%+d heads)") % dh
2084
2094
2085 self.ui.status(_("added %d changesets"
2095 self.ui.status(_("added %d changesets"
2086 " with %d changes to %d files%s\n")
2096 " with %d changes to %d files%s\n")
2087 % (changesets, revisions, files, htext))
2097 % (changesets, revisions, files, htext))
2088
2098
2089 if changesets > 0:
2099 if changesets > 0:
2090 p = lambda: cl.writepending() and self.root or ""
2100 p = lambda: cl.writepending() and self.root or ""
2091 self.hook('pretxnchangegroup', throw=True,
2101 self.hook('pretxnchangegroup', throw=True,
2092 node=hex(cl.node(clstart)), source=srctype,
2102 node=hex(cl.node(clstart)), source=srctype,
2093 url=url, pending=p)
2103 url=url, pending=p)
2094
2104
2095 added = [cl.node(r) for r in xrange(clstart, clend)]
2105 added = [cl.node(r) for r in xrange(clstart, clend)]
2096 publishing = self.ui.configbool('phases', 'publish', True)
2106 publishing = self.ui.configbool('phases', 'publish', True)
2097 if publishing and srctype == 'push':
2107 if publishing and srctype == 'push':
2098 # Old server can not push the boundary themself.
2108 # Old server can not push the boundary themself.
2099 # This clause ensure pushed changeset are alway marked as public
2109 # This clause ensure pushed changeset are alway marked as public
2100 phases.advanceboundary(self, phases.public, added)
2110 phases.advanceboundary(self, phases.public, added)
2101 elif srctype != 'strip': # strip should not touch boundary at all
2111 elif srctype != 'strip': # strip should not touch boundary at all
2102 phases.retractboundary(self, phases.draft, added)
2112 phases.retractboundary(self, phases.draft, added)
2103
2113
2104 # make changelog see real files again
2114 # make changelog see real files again
2105 cl.finalize(trp)
2115 cl.finalize(trp)
2106
2116
2107 tr.close()
2117 tr.close()
2108
2118
2109 if changesets > 0:
2119 if changesets > 0:
2110 def runhooks():
2120 def runhooks():
2111 # forcefully update the on-disk branch cache
2121 # forcefully update the on-disk branch cache
2112 self.ui.debug("updating the branch cache\n")
2122 self.ui.debug("updating the branch cache\n")
2113 self.updatebranchcache()
2123 self.updatebranchcache()
2114 self.hook("changegroup", node=hex(cl.node(clstart)),
2124 self.hook("changegroup", node=hex(cl.node(clstart)),
2115 source=srctype, url=url)
2125 source=srctype, url=url)
2116
2126
2117 for n in added:
2127 for n in added:
2118 self.hook("incoming", node=hex(n), source=srctype,
2128 self.hook("incoming", node=hex(n), source=srctype,
2119 url=url)
2129 url=url)
2120 self._afterlock(runhooks)
2130 self._afterlock(runhooks)
2121
2131
2122 finally:
2132 finally:
2123 tr.release()
2133 tr.release()
2124 # never return 0 here:
2134 # never return 0 here:
2125 if dh < 0:
2135 if dh < 0:
2126 return dh - 1
2136 return dh - 1
2127 else:
2137 else:
2128 return dh + 1
2138 return dh + 1
2129
2139
2130 def stream_in(self, remote, requirements):
2140 def stream_in(self, remote, requirements):
2131 lock = self.lock()
2141 lock = self.lock()
2132 try:
2142 try:
2133 fp = remote.stream_out()
2143 fp = remote.stream_out()
2134 l = fp.readline()
2144 l = fp.readline()
2135 try:
2145 try:
2136 resp = int(l)
2146 resp = int(l)
2137 except ValueError:
2147 except ValueError:
2138 raise error.ResponseError(
2148 raise error.ResponseError(
2139 _('Unexpected response from remote server:'), l)
2149 _('Unexpected response from remote server:'), l)
2140 if resp == 1:
2150 if resp == 1:
2141 raise util.Abort(_('operation forbidden by server'))
2151 raise util.Abort(_('operation forbidden by server'))
2142 elif resp == 2:
2152 elif resp == 2:
2143 raise util.Abort(_('locking the remote repository failed'))
2153 raise util.Abort(_('locking the remote repository failed'))
2144 elif resp != 0:
2154 elif resp != 0:
2145 raise util.Abort(_('the server sent an unknown error code'))
2155 raise util.Abort(_('the server sent an unknown error code'))
2146 self.ui.status(_('streaming all changes\n'))
2156 self.ui.status(_('streaming all changes\n'))
2147 l = fp.readline()
2157 l = fp.readline()
2148 try:
2158 try:
2149 total_files, total_bytes = map(int, l.split(' ', 1))
2159 total_files, total_bytes = map(int, l.split(' ', 1))
2150 except (ValueError, TypeError):
2160 except (ValueError, TypeError):
2151 raise error.ResponseError(
2161 raise error.ResponseError(
2152 _('Unexpected response from remote server:'), l)
2162 _('Unexpected response from remote server:'), l)
2153 self.ui.status(_('%d files to transfer, %s of data\n') %
2163 self.ui.status(_('%d files to transfer, %s of data\n') %
2154 (total_files, util.bytecount(total_bytes)))
2164 (total_files, util.bytecount(total_bytes)))
2155 start = time.time()
2165 start = time.time()
2156 for i in xrange(total_files):
2166 for i in xrange(total_files):
2157 # XXX doesn't support '\n' or '\r' in filenames
2167 # XXX doesn't support '\n' or '\r' in filenames
2158 l = fp.readline()
2168 l = fp.readline()
2159 try:
2169 try:
2160 name, size = l.split('\0', 1)
2170 name, size = l.split('\0', 1)
2161 size = int(size)
2171 size = int(size)
2162 except (ValueError, TypeError):
2172 except (ValueError, TypeError):
2163 raise error.ResponseError(
2173 raise error.ResponseError(
2164 _('Unexpected response from remote server:'), l)
2174 _('Unexpected response from remote server:'), l)
2165 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2175 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2166 # for backwards compat, name was partially encoded
2176 # for backwards compat, name was partially encoded
2167 ofp = self.sopener(store.decodedir(name), 'w')
2177 ofp = self.sopener(store.decodedir(name), 'w')
2168 for chunk in util.filechunkiter(fp, limit=size):
2178 for chunk in util.filechunkiter(fp, limit=size):
2169 ofp.write(chunk)
2179 ofp.write(chunk)
2170 ofp.close()
2180 ofp.close()
2171 elapsed = time.time() - start
2181 elapsed = time.time() - start
2172 if elapsed <= 0:
2182 if elapsed <= 0:
2173 elapsed = 0.001
2183 elapsed = 0.001
2174 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2184 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2175 (util.bytecount(total_bytes), elapsed,
2185 (util.bytecount(total_bytes), elapsed,
2176 util.bytecount(total_bytes / elapsed)))
2186 util.bytecount(total_bytes / elapsed)))
2177
2187
2178 # new requirements = old non-format requirements + new format-related
2188 # new requirements = old non-format requirements + new format-related
2179 # requirements from the streamed-in repository
2189 # requirements from the streamed-in repository
2180 requirements.update(set(self.requirements) - self.supportedformats)
2190 requirements.update(set(self.requirements) - self.supportedformats)
2181 self._applyrequirements(requirements)
2191 self._applyrequirements(requirements)
2182 self._writerequirements()
2192 self._writerequirements()
2183
2193
2184 self.invalidate()
2194 self.invalidate()
2185 return len(self.heads()) + 1
2195 return len(self.heads()) + 1
2186 finally:
2196 finally:
2187 lock.release()
2197 lock.release()
2188
2198
2189 def clone(self, remote, heads=[], stream=False):
2199 def clone(self, remote, heads=[], stream=False):
2190 '''clone remote repository.
2200 '''clone remote repository.
2191
2201
2192 keyword arguments:
2202 keyword arguments:
2193 heads: list of revs to clone (forces use of pull)
2203 heads: list of revs to clone (forces use of pull)
2194 stream: use streaming clone if possible'''
2204 stream: use streaming clone if possible'''
2195
2205
2196 # now, all clients that can request uncompressed clones can
2206 # now, all clients that can request uncompressed clones can
2197 # read repo formats supported by all servers that can serve
2207 # read repo formats supported by all servers that can serve
2198 # them.
2208 # them.
2199
2209
2200 # if revlog format changes, client will have to check version
2210 # if revlog format changes, client will have to check version
2201 # and format flags on "stream" capability, and use
2211 # and format flags on "stream" capability, and use
2202 # uncompressed only if compatible.
2212 # uncompressed only if compatible.
2203
2213
2204 if stream and not heads:
2214 if stream and not heads:
2205 # 'stream' means remote revlog format is revlogv1 only
2215 # 'stream' means remote revlog format is revlogv1 only
2206 if remote.capable('stream'):
2216 if remote.capable('stream'):
2207 return self.stream_in(remote, set(('revlogv1',)))
2217 return self.stream_in(remote, set(('revlogv1',)))
2208 # otherwise, 'streamreqs' contains the remote revlog format
2218 # otherwise, 'streamreqs' contains the remote revlog format
2209 streamreqs = remote.capable('streamreqs')
2219 streamreqs = remote.capable('streamreqs')
2210 if streamreqs:
2220 if streamreqs:
2211 streamreqs = set(streamreqs.split(','))
2221 streamreqs = set(streamreqs.split(','))
2212 # if we support it, stream in and adjust our requirements
2222 # if we support it, stream in and adjust our requirements
2213 if not streamreqs - self.supportedformats:
2223 if not streamreqs - self.supportedformats:
2214 return self.stream_in(remote, streamreqs)
2224 return self.stream_in(remote, streamreqs)
2215 return self.pull(remote, heads)
2225 return self.pull(remote, heads)
2216
2226
2217 def pushkey(self, namespace, key, old, new):
2227 def pushkey(self, namespace, key, old, new):
2218 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2228 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2219 old=old, new=new)
2229 old=old, new=new)
2220 ret = pushkey.push(self, namespace, key, old, new)
2230 ret = pushkey.push(self, namespace, key, old, new)
2221 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2231 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2222 ret=ret)
2232 ret=ret)
2223 return ret
2233 return ret
2224
2234
2225 def listkeys(self, namespace):
2235 def listkeys(self, namespace):
2226 self.hook('prelistkeys', throw=True, namespace=namespace)
2236 self.hook('prelistkeys', throw=True, namespace=namespace)
2227 values = pushkey.list(self, namespace)
2237 values = pushkey.list(self, namespace)
2228 self.hook('listkeys', namespace=namespace, values=values)
2238 self.hook('listkeys', namespace=namespace, values=values)
2229 return values
2239 return values
2230
2240
2231 def debugwireargs(self, one, two, three=None, four=None, five=None):
2241 def debugwireargs(self, one, two, three=None, four=None, five=None):
2232 '''used to test argument passing over the wire'''
2242 '''used to test argument passing over the wire'''
2233 return "%s %s %s %s %s" % (one, two, three, four, five)
2243 return "%s %s %s %s %s" % (one, two, three, four, five)
2234
2244
2235 def savecommitmessage(self, text):
2245 def savecommitmessage(self, text):
2236 fp = self.opener('last-message.txt', 'wb')
2246 fp = self.opener('last-message.txt', 'wb')
2237 try:
2247 try:
2238 fp.write(text)
2248 fp.write(text)
2239 finally:
2249 finally:
2240 fp.close()
2250 fp.close()
2241 return self.pathto(fp.name[len(self.root)+1:])
2251 return self.pathto(fp.name[len(self.root)+1:])
2242
2252
2243 # used to avoid circular references so destructors work
2253 # used to avoid circular references so destructors work
2244 def aftertrans(files):
2254 def aftertrans(files):
2245 renamefiles = [tuple(t) for t in files]
2255 renamefiles = [tuple(t) for t in files]
2246 def a():
2256 def a():
2247 for src, dest in renamefiles:
2257 for src, dest in renamefiles:
2248 util.rename(src, dest)
2258 util.rename(src, dest)
2249 return a
2259 return a
2250
2260
2251 def undoname(fn):
2261 def undoname(fn):
2252 base, name = os.path.split(fn)
2262 base, name = os.path.split(fn)
2253 assert name.startswith('journal')
2263 assert name.startswith('journal')
2254 return os.path.join(base, name.replace('journal', 'undo', 1))
2264 return os.path.join(base, name.replace('journal', 'undo', 1))
2255
2265
2256 def instance(ui, path, create):
2266 def instance(ui, path, create):
2257 return localrepository(ui, util.urllocalpath(path), create)
2267 return localrepository(ui, util.urllocalpath(path), create)
2258
2268
2259 def islocal(path):
2269 def islocal(path):
2260 return True
2270 return True
@@ -1,1163 +1,1163 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node as nodemod
10 import node as nodemod
11 import bookmarks as bookmarksmod
11 import bookmarks as bookmarksmod
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15
15
16 elements = {
16 elements = {
17 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
17 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
18 "~": (18, None, ("ancestor", 18)),
18 "~": (18, None, ("ancestor", 18)),
19 "^": (18, None, ("parent", 18), ("parentpost", 18)),
19 "^": (18, None, ("parent", 18), ("parentpost", 18)),
20 "-": (5, ("negate", 19), ("minus", 5)),
20 "-": (5, ("negate", 19), ("minus", 5)),
21 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
21 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
22 ("dagrangepost", 17)),
22 ("dagrangepost", 17)),
23 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
23 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
24 ("dagrangepost", 17)),
24 ("dagrangepost", 17)),
25 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
25 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
26 "not": (10, ("not", 10)),
26 "not": (10, ("not", 10)),
27 "!": (10, ("not", 10)),
27 "!": (10, ("not", 10)),
28 "and": (5, None, ("and", 5)),
28 "and": (5, None, ("and", 5)),
29 "&": (5, None, ("and", 5)),
29 "&": (5, None, ("and", 5)),
30 "or": (4, None, ("or", 4)),
30 "or": (4, None, ("or", 4)),
31 "|": (4, None, ("or", 4)),
31 "|": (4, None, ("or", 4)),
32 "+": (4, None, ("or", 4)),
32 "+": (4, None, ("or", 4)),
33 ",": (2, None, ("list", 2)),
33 ",": (2, None, ("list", 2)),
34 ")": (0, None, None),
34 ")": (0, None, None),
35 "symbol": (0, ("symbol",), None),
35 "symbol": (0, ("symbol",), None),
36 "string": (0, ("string",), None),
36 "string": (0, ("string",), None),
37 "end": (0, None, None),
37 "end": (0, None, None),
38 }
38 }
39
39
40 keywords = set(['and', 'or', 'not'])
40 keywords = set(['and', 'or', 'not'])
41
41
42 def tokenize(program):
42 def tokenize(program):
43 pos, l = 0, len(program)
43 pos, l = 0, len(program)
44 while pos < l:
44 while pos < l:
45 c = program[pos]
45 c = program[pos]
46 if c.isspace(): # skip inter-token whitespace
46 if c.isspace(): # skip inter-token whitespace
47 pass
47 pass
48 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
48 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
49 yield ('::', None, pos)
49 yield ('::', None, pos)
50 pos += 1 # skip ahead
50 pos += 1 # skip ahead
51 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
51 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
52 yield ('..', None, pos)
52 yield ('..', None, pos)
53 pos += 1 # skip ahead
53 pos += 1 # skip ahead
54 elif c in "():,-|&+!~^": # handle simple operators
54 elif c in "():,-|&+!~^": # handle simple operators
55 yield (c, None, pos)
55 yield (c, None, pos)
56 elif (c in '"\'' or c == 'r' and
56 elif (c in '"\'' or c == 'r' and
57 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
57 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
58 if c == 'r':
58 if c == 'r':
59 pos += 1
59 pos += 1
60 c = program[pos]
60 c = program[pos]
61 decode = lambda x: x
61 decode = lambda x: x
62 else:
62 else:
63 decode = lambda x: x.decode('string-escape')
63 decode = lambda x: x.decode('string-escape')
64 pos += 1
64 pos += 1
65 s = pos
65 s = pos
66 while pos < l: # find closing quote
66 while pos < l: # find closing quote
67 d = program[pos]
67 d = program[pos]
68 if d == '\\': # skip over escaped characters
68 if d == '\\': # skip over escaped characters
69 pos += 2
69 pos += 2
70 continue
70 continue
71 if d == c:
71 if d == c:
72 yield ('string', decode(program[s:pos]), s)
72 yield ('string', decode(program[s:pos]), s)
73 break
73 break
74 pos += 1
74 pos += 1
75 else:
75 else:
76 raise error.ParseError(_("unterminated string"), s)
76 raise error.ParseError(_("unterminated string"), s)
77 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
77 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
78 s = pos
78 s = pos
79 pos += 1
79 pos += 1
80 while pos < l: # find end of symbol
80 while pos < l: # find end of symbol
81 d = program[pos]
81 d = program[pos]
82 if not (d.isalnum() or d in "._" or ord(d) > 127):
82 if not (d.isalnum() or d in "._" or ord(d) > 127):
83 break
83 break
84 if d == '.' and program[pos - 1] == '.': # special case for ..
84 if d == '.' and program[pos - 1] == '.': # special case for ..
85 pos -= 1
85 pos -= 1
86 break
86 break
87 pos += 1
87 pos += 1
88 sym = program[s:pos]
88 sym = program[s:pos]
89 if sym in keywords: # operator keywords
89 if sym in keywords: # operator keywords
90 yield (sym, None, s)
90 yield (sym, None, s)
91 else:
91 else:
92 yield ('symbol', sym, s)
92 yield ('symbol', sym, s)
93 pos -= 1
93 pos -= 1
94 else:
94 else:
95 raise error.ParseError(_("syntax error"), pos)
95 raise error.ParseError(_("syntax error"), pos)
96 pos += 1
96 pos += 1
97 yield ('end', None, pos)
97 yield ('end', None, pos)
98
98
99 # helpers
99 # helpers
100
100
101 def getstring(x, err):
101 def getstring(x, err):
102 if x and (x[0] == 'string' or x[0] == 'symbol'):
102 if x and (x[0] == 'string' or x[0] == 'symbol'):
103 return x[1]
103 return x[1]
104 raise error.ParseError(err)
104 raise error.ParseError(err)
105
105
106 def getlist(x):
106 def getlist(x):
107 if not x:
107 if not x:
108 return []
108 return []
109 if x[0] == 'list':
109 if x[0] == 'list':
110 return getlist(x[1]) + [x[2]]
110 return getlist(x[1]) + [x[2]]
111 return [x]
111 return [x]
112
112
113 def getargs(x, min, max, err):
113 def getargs(x, min, max, err):
114 l = getlist(x)
114 l = getlist(x)
115 if len(l) < min or len(l) > max:
115 if len(l) < min or len(l) > max:
116 raise error.ParseError(err)
116 raise error.ParseError(err)
117 return l
117 return l
118
118
119 def getset(repo, subset, x):
119 def getset(repo, subset, x):
120 if not x:
120 if not x:
121 raise error.ParseError(_("missing argument"))
121 raise error.ParseError(_("missing argument"))
122 return methods[x[0]](repo, subset, *x[1:])
122 return methods[x[0]](repo, subset, *x[1:])
123
123
124 # operator methods
124 # operator methods
125
125
126 def stringset(repo, subset, x):
126 def stringset(repo, subset, x):
127 x = repo[x].rev()
127 x = repo[x].rev()
128 if x == -1 and len(subset) == len(repo):
128 if x == -1 and len(subset) == len(repo):
129 return [-1]
129 return [-1]
130 if len(subset) == len(repo) or x in subset:
130 if len(subset) == len(repo) or x in subset:
131 return [x]
131 return [x]
132 return []
132 return []
133
133
134 def symbolset(repo, subset, x):
134 def symbolset(repo, subset, x):
135 if x in symbols:
135 if x in symbols:
136 raise error.ParseError(_("can't use %s here") % x)
136 raise error.ParseError(_("can't use %s here") % x)
137 return stringset(repo, subset, x)
137 return stringset(repo, subset, x)
138
138
139 def rangeset(repo, subset, x, y):
139 def rangeset(repo, subset, x, y):
140 m = getset(repo, subset, x)
140 m = getset(repo, subset, x)
141 if not m:
141 if not m:
142 m = getset(repo, range(len(repo)), x)
142 m = getset(repo, range(len(repo)), x)
143
143
144 n = getset(repo, subset, y)
144 n = getset(repo, subset, y)
145 if not n:
145 if not n:
146 n = getset(repo, range(len(repo)), y)
146 n = getset(repo, range(len(repo)), y)
147
147
148 if not m or not n:
148 if not m or not n:
149 return []
149 return []
150 m, n = m[0], n[-1]
150 m, n = m[0], n[-1]
151
151
152 if m < n:
152 if m < n:
153 r = range(m, n + 1)
153 r = range(m, n + 1)
154 else:
154 else:
155 r = range(m, n - 1, -1)
155 r = range(m, n - 1, -1)
156 s = set(subset)
156 s = set(subset)
157 return [x for x in r if x in s]
157 return [x for x in r if x in s]
158
158
159 def andset(repo, subset, x, y):
159 def andset(repo, subset, x, y):
160 return getset(repo, getset(repo, subset, x), y)
160 return getset(repo, getset(repo, subset, x), y)
161
161
162 def orset(repo, subset, x, y):
162 def orset(repo, subset, x, y):
163 xl = getset(repo, subset, x)
163 xl = getset(repo, subset, x)
164 s = set(xl)
164 s = set(xl)
165 yl = getset(repo, [r for r in subset if r not in s], y)
165 yl = getset(repo, [r for r in subset if r not in s], y)
166 return xl + yl
166 return xl + yl
167
167
168 def notset(repo, subset, x):
168 def notset(repo, subset, x):
169 s = set(getset(repo, subset, x))
169 s = set(getset(repo, subset, x))
170 return [r for r in subset if r not in s]
170 return [r for r in subset if r not in s]
171
171
172 def listset(repo, subset, a, b):
172 def listset(repo, subset, a, b):
173 raise error.ParseError(_("can't use a list in this context"))
173 raise error.ParseError(_("can't use a list in this context"))
174
174
175 def func(repo, subset, a, b):
175 def func(repo, subset, a, b):
176 if a[0] == 'symbol' and a[1] in symbols:
176 if a[0] == 'symbol' and a[1] in symbols:
177 return symbols[a[1]](repo, subset, b)
177 return symbols[a[1]](repo, subset, b)
178 raise error.ParseError(_("not a function: %s") % a[1])
178 raise error.ParseError(_("not a function: %s") % a[1])
179
179
180 # functions
180 # functions
181
181
182 def adds(repo, subset, x):
182 def adds(repo, subset, x):
183 """``adds(pattern)``
183 """``adds(pattern)``
184 Changesets that add a file matching pattern.
184 Changesets that add a file matching pattern.
185 """
185 """
186 # i18n: "adds" is a keyword
186 # i18n: "adds" is a keyword
187 pat = getstring(x, _("adds requires a pattern"))
187 pat = getstring(x, _("adds requires a pattern"))
188 return checkstatus(repo, subset, pat, 1)
188 return checkstatus(repo, subset, pat, 1)
189
189
190 def ancestor(repo, subset, x):
190 def ancestor(repo, subset, x):
191 """``ancestor(single, single)``
191 """``ancestor(single, single)``
192 Greatest common ancestor of the two changesets.
192 Greatest common ancestor of the two changesets.
193 """
193 """
194 # i18n: "ancestor" is a keyword
194 # i18n: "ancestor" is a keyword
195 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
195 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
196 r = range(len(repo))
196 r = range(len(repo))
197 a = getset(repo, r, l[0])
197 a = getset(repo, r, l[0])
198 b = getset(repo, r, l[1])
198 b = getset(repo, r, l[1])
199 if len(a) != 1 or len(b) != 1:
199 if len(a) != 1 or len(b) != 1:
200 # i18n: "ancestor" is a keyword
200 # i18n: "ancestor" is a keyword
201 raise error.ParseError(_("ancestor arguments must be single revisions"))
201 raise error.ParseError(_("ancestor arguments must be single revisions"))
202 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
202 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
203
203
204 return [r for r in an if r in subset]
204 return [r for r in an if r in subset]
205
205
206 def ancestors(repo, subset, x):
206 def ancestors(repo, subset, x):
207 """``ancestors(set)``
207 """``ancestors(set)``
208 Changesets that are ancestors of a changeset in set.
208 Changesets that are ancestors of a changeset in set.
209 """
209 """
210 args = getset(repo, range(len(repo)), x)
210 args = getset(repo, range(len(repo)), x)
211 if not args:
211 if not args:
212 return []
212 return []
213 s = set(repo.changelog.ancestors(*args)) | set(args)
213 s = set(repo.changelog.ancestors(*args)) | set(args)
214 return [r for r in subset if r in s]
214 return [r for r in subset if r in s]
215
215
216 def ancestorspec(repo, subset, x, n):
216 def ancestorspec(repo, subset, x, n):
217 """``set~n``
217 """``set~n``
218 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
218 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
219 """
219 """
220 try:
220 try:
221 n = int(n[1])
221 n = int(n[1])
222 except (TypeError, ValueError):
222 except (TypeError, ValueError):
223 raise error.ParseError(_("~ expects a number"))
223 raise error.ParseError(_("~ expects a number"))
224 ps = set()
224 ps = set()
225 cl = repo.changelog
225 cl = repo.changelog
226 for r in getset(repo, subset, x):
226 for r in getset(repo, subset, x):
227 for i in range(n):
227 for i in range(n):
228 r = cl.parentrevs(r)[0]
228 r = cl.parentrevs(r)[0]
229 ps.add(r)
229 ps.add(r)
230 return [r for r in subset if r in ps]
230 return [r for r in subset if r in ps]
231
231
232 def author(repo, subset, x):
232 def author(repo, subset, x):
233 """``author(string)``
233 """``author(string)``
234 Alias for ``user(string)``.
234 Alias for ``user(string)``.
235 """
235 """
236 # i18n: "author" is a keyword
236 # i18n: "author" is a keyword
237 n = encoding.lower(getstring(x, _("author requires a string")))
237 n = encoding.lower(getstring(x, _("author requires a string")))
238 return [r for r in subset if n in encoding.lower(repo[r].user())]
238 return [r for r in subset if n in encoding.lower(repo[r].user())]
239
239
240 def bisect(repo, subset, x):
240 def bisect(repo, subset, x):
241 """``bisect(string)``
241 """``bisect(string)``
242 Changesets marked in the specified bisect status:
242 Changesets marked in the specified bisect status:
243
243
244 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
244 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
245 - ``goods``, ``bads`` : csets topologicaly good/bad
245 - ``goods``, ``bads`` : csets topologicaly good/bad
246 - ``range`` : csets taking part in the bisection
246 - ``range`` : csets taking part in the bisection
247 - ``pruned`` : csets that are goods, bads or skipped
247 - ``pruned`` : csets that are goods, bads or skipped
248 - ``untested`` : csets whose fate is yet unknown
248 - ``untested`` : csets whose fate is yet unknown
249 - ``ignored`` : csets ignored due to DAG topology
249 - ``ignored`` : csets ignored due to DAG topology
250 """
250 """
251 status = getstring(x, _("bisect requires a string")).lower()
251 status = getstring(x, _("bisect requires a string")).lower()
252 return [r for r in subset if r in hbisect.get(repo, status)]
252 return [r for r in subset if r in hbisect.get(repo, status)]
253
253
254 # Backward-compatibility
254 # Backward-compatibility
255 # - no help entry so that we do not advertise it any more
255 # - no help entry so that we do not advertise it any more
256 def bisected(repo, subset, x):
256 def bisected(repo, subset, x):
257 return bisect(repo, subset, x)
257 return bisect(repo, subset, x)
258
258
259 def bookmark(repo, subset, x):
259 def bookmark(repo, subset, x):
260 """``bookmark([name])``
260 """``bookmark([name])``
261 The named bookmark or all bookmarks.
261 The named bookmark or all bookmarks.
262 """
262 """
263 # i18n: "bookmark" is a keyword
263 # i18n: "bookmark" is a keyword
264 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
264 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
265 if args:
265 if args:
266 bm = getstring(args[0],
266 bm = getstring(args[0],
267 # i18n: "bookmark" is a keyword
267 # i18n: "bookmark" is a keyword
268 _('the argument to bookmark must be a string'))
268 _('the argument to bookmark must be a string'))
269 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
269 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
270 if not bmrev:
270 if not bmrev:
271 raise util.Abort(_("bookmark '%s' does not exist") % bm)
271 raise util.Abort(_("bookmark '%s' does not exist") % bm)
272 bmrev = repo[bmrev].rev()
272 bmrev = repo[bmrev].rev()
273 return [r for r in subset if r == bmrev]
273 return [r for r in subset if r == bmrev]
274 bms = set([repo[r].rev()
274 bms = set([repo[r].rev()
275 for r in bookmarksmod.listbookmarks(repo).values()])
275 for r in bookmarksmod.listbookmarks(repo).values()])
276 return [r for r in subset if r in bms]
276 return [r for r in subset if r in bms]
277
277
278 def branch(repo, subset, x):
278 def branch(repo, subset, x):
279 """``branch(string or set)``
279 """``branch(string or set)``
280 All changesets belonging to the given branch or the branches of the given
280 All changesets belonging to the given branch or the branches of the given
281 changesets.
281 changesets.
282 """
282 """
283 try:
283 try:
284 b = getstring(x, '')
284 b = getstring(x, '')
285 if b in repo.branchmap():
285 if b in repo.branchmap():
286 return [r for r in subset if repo[r].branch() == b]
286 return [r for r in subset if repo[r].branch() == b]
287 except error.ParseError:
287 except error.ParseError:
288 # not a string, but another revspec, e.g. tip()
288 # not a string, but another revspec, e.g. tip()
289 pass
289 pass
290
290
291 s = getset(repo, range(len(repo)), x)
291 s = getset(repo, range(len(repo)), x)
292 b = set()
292 b = set()
293 for r in s:
293 for r in s:
294 b.add(repo[r].branch())
294 b.add(repo[r].branch())
295 s = set(s)
295 s = set(s)
296 return [r for r in subset if r in s or repo[r].branch() in b]
296 return [r for r in subset if r in s or repo[r].branch() in b]
297
297
298 def checkstatus(repo, subset, pat, field):
298 def checkstatus(repo, subset, pat, field):
299 m = matchmod.match(repo.root, repo.getcwd(), [pat])
299 m = matchmod.match(repo.root, repo.getcwd(), [pat])
300 s = []
300 s = []
301 fast = (m.files() == [pat])
301 fast = (m.files() == [pat])
302 for r in subset:
302 for r in subset:
303 c = repo[r]
303 c = repo[r]
304 if fast:
304 if fast:
305 if pat not in c.files():
305 if pat not in c.files():
306 continue
306 continue
307 else:
307 else:
308 for f in c.files():
308 for f in c.files():
309 if m(f):
309 if m(f):
310 break
310 break
311 else:
311 else:
312 continue
312 continue
313 files = repo.status(c.p1().node(), c.node())[field]
313 files = repo.status(c.p1().node(), c.node())[field]
314 if fast:
314 if fast:
315 if pat in files:
315 if pat in files:
316 s.append(r)
316 s.append(r)
317 else:
317 else:
318 for f in files:
318 for f in files:
319 if m(f):
319 if m(f):
320 s.append(r)
320 s.append(r)
321 break
321 break
322 return s
322 return s
323
323
324 def children(repo, subset, x):
324 def children(repo, subset, x):
325 """``children(set)``
325 """``children(set)``
326 Child changesets of changesets in set.
326 Child changesets of changesets in set.
327 """
327 """
328 cs = set()
328 cs = set()
329 cl = repo.changelog
329 cl = repo.changelog
330 s = set(getset(repo, range(len(repo)), x))
330 s = set(getset(repo, range(len(repo)), x))
331 for r in xrange(0, len(repo)):
331 for r in xrange(0, len(repo)):
332 for p in cl.parentrevs(r):
332 for p in cl.parentrevs(r):
333 if p in s:
333 if p in s:
334 cs.add(r)
334 cs.add(r)
335 return [r for r in subset if r in cs]
335 return [r for r in subset if r in cs]
336
336
337 def closed(repo, subset, x):
337 def closed(repo, subset, x):
338 """``closed()``
338 """``closed()``
339 Changeset is closed.
339 Changeset is closed.
340 """
340 """
341 # i18n: "closed" is a keyword
341 # i18n: "closed" is a keyword
342 getargs(x, 0, 0, _("closed takes no arguments"))
342 getargs(x, 0, 0, _("closed takes no arguments"))
343 return [r for r in subset if repo[r].extra().get('close')]
343 return [r for r in subset if repo[r].extra().get('close')]
344
344
345 def contains(repo, subset, x):
345 def contains(repo, subset, x):
346 """``contains(pattern)``
346 """``contains(pattern)``
347 Revision contains a file matching pattern. See :hg:`help patterns`
347 Revision contains a file matching pattern. See :hg:`help patterns`
348 for information about file patterns.
348 for information about file patterns.
349 """
349 """
350 # i18n: "contains" is a keyword
350 # i18n: "contains" is a keyword
351 pat = getstring(x, _("contains requires a pattern"))
351 pat = getstring(x, _("contains requires a pattern"))
352 m = matchmod.match(repo.root, repo.getcwd(), [pat])
352 m = matchmod.match(repo.root, repo.getcwd(), [pat])
353 s = []
353 s = []
354 if m.files() == [pat]:
354 if m.files() == [pat]:
355 for r in subset:
355 for r in subset:
356 if pat in repo[r]:
356 if pat in repo[r]:
357 s.append(r)
357 s.append(r)
358 else:
358 else:
359 for r in subset:
359 for r in subset:
360 for f in repo[r].manifest():
360 for f in repo[r].manifest():
361 if m(f):
361 if m(f):
362 s.append(r)
362 s.append(r)
363 break
363 break
364 return s
364 return s
365
365
366 def date(repo, subset, x):
366 def date(repo, subset, x):
367 """``date(interval)``
367 """``date(interval)``
368 Changesets within the interval, see :hg:`help dates`.
368 Changesets within the interval, see :hg:`help dates`.
369 """
369 """
370 # i18n: "date" is a keyword
370 # i18n: "date" is a keyword
371 ds = getstring(x, _("date requires a string"))
371 ds = getstring(x, _("date requires a string"))
372 dm = util.matchdate(ds)
372 dm = util.matchdate(ds)
373 return [r for r in subset if dm(repo[r].date()[0])]
373 return [r for r in subset if dm(repo[r].date()[0])]
374
374
375 def desc(repo, subset, x):
375 def desc(repo, subset, x):
376 """``desc(string)``
376 """``desc(string)``
377 Search commit message for string. The match is case-insensitive.
377 Search commit message for string. The match is case-insensitive.
378 """
378 """
379 # i18n: "desc" is a keyword
379 # i18n: "desc" is a keyword
380 ds = encoding.lower(getstring(x, _("desc requires a string")))
380 ds = encoding.lower(getstring(x, _("desc requires a string")))
381 l = []
381 l = []
382 for r in subset:
382 for r in subset:
383 c = repo[r]
383 c = repo[r]
384 if ds in encoding.lower(c.description()):
384 if ds in encoding.lower(c.description()):
385 l.append(r)
385 l.append(r)
386 return l
386 return l
387
387
388 def descendants(repo, subset, x):
388 def descendants(repo, subset, x):
389 """``descendants(set)``
389 """``descendants(set)``
390 Changesets which are descendants of changesets in set.
390 Changesets which are descendants of changesets in set.
391 """
391 """
392 args = getset(repo, range(len(repo)), x)
392 args = getset(repo, range(len(repo)), x)
393 if not args:
393 if not args:
394 return []
394 return []
395 s = set(repo.changelog.descendants(*args)) | set(args)
395 s = set(repo.changelog.descendants(*args)) | set(args)
396 return [r for r in subset if r in s]
396 return [r for r in subset if r in s]
397
397
398 def draft(repo, subset, x):
398 def draft(repo, subset, x):
399 """``draft()``
399 """``draft()``
400 Changeset in draft phase."""
400 Changeset in draft phase."""
401 getargs(x, 0, 0, _("draft takes no arguments"))
401 getargs(x, 0, 0, _("draft takes no arguments"))
402 return [r for r in subset if repo._phaserev[r] == phases.draft]
402 return [r for r in subset if repo._phaserev[r] == phases.draft]
403
403
404 def filelog(repo, subset, x):
404 def filelog(repo, subset, x):
405 """``filelog(pattern)``
405 """``filelog(pattern)``
406 Changesets connected to the specified filelog.
406 Changesets connected to the specified filelog.
407 """
407 """
408
408
409 pat = getstring(x, _("filelog requires a pattern"))
409 pat = getstring(x, _("filelog requires a pattern"))
410 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
410 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
411 s = set()
411 s = set()
412
412
413 if not m.anypats():
413 if not m.anypats():
414 for f in m.files():
414 for f in m.files():
415 fl = repo.file(f)
415 fl = repo.file(f)
416 for fr in fl:
416 for fr in fl:
417 s.add(fl.linkrev(fr))
417 s.add(fl.linkrev(fr))
418 else:
418 else:
419 for f in repo[None]:
419 for f in repo[None]:
420 if m(f):
420 if m(f):
421 fl = repo.file(f)
421 fl = repo.file(f)
422 for fr in fl:
422 for fr in fl:
423 s.add(fl.linkrev(fr))
423 s.add(fl.linkrev(fr))
424
424
425 return [r for r in subset if r in s]
425 return [r for r in subset if r in s]
426
426
427 def first(repo, subset, x):
427 def first(repo, subset, x):
428 """``first(set, [n])``
428 """``first(set, [n])``
429 An alias for limit().
429 An alias for limit().
430 """
430 """
431 return limit(repo, subset, x)
431 return limit(repo, subset, x)
432
432
433 def follow(repo, subset, x):
433 def follow(repo, subset, x):
434 """``follow([file])``
434 """``follow([file])``
435 An alias for ``::.`` (ancestors of the working copy's first parent).
435 An alias for ``::.`` (ancestors of the working copy's first parent).
436 If a filename is specified, the history of the given file is followed,
436 If a filename is specified, the history of the given file is followed,
437 including copies.
437 including copies.
438 """
438 """
439 # i18n: "follow" is a keyword
439 # i18n: "follow" is a keyword
440 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
440 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
441 p = repo['.'].rev()
441 p = repo['.'].rev()
442 if l:
442 if l:
443 x = getstring(l[0], _("follow expected a filename"))
443 x = getstring(l[0], _("follow expected a filename"))
444 if x in repo['.']:
444 if x in repo['.']:
445 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
445 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
446 else:
446 else:
447 return []
447 return []
448 else:
448 else:
449 s = set(repo.changelog.ancestors(p))
449 s = set(repo.changelog.ancestors(p))
450
450
451 s |= set([p])
451 s |= set([p])
452 return [r for r in subset if r in s]
452 return [r for r in subset if r in s]
453
453
454 def followfile(repo, subset, x):
454 def followfile(repo, subset, x):
455 """``follow()``
455 """``follow()``
456 An alias for ``::.`` (ancestors of the working copy's first parent).
456 An alias for ``::.`` (ancestors of the working copy's first parent).
457 """
457 """
458 # i18n: "follow" is a keyword
458 # i18n: "follow" is a keyword
459 getargs(x, 0, 0, _("follow takes no arguments"))
459 getargs(x, 0, 0, _("follow takes no arguments"))
460 p = repo['.'].rev()
460 p = repo['.'].rev()
461 s = set(repo.changelog.ancestors(p)) | set([p])
461 s = set(repo.changelog.ancestors(p)) | set([p])
462 return [r for r in subset if r in s]
462 return [r for r in subset if r in s]
463
463
464 def getall(repo, subset, x):
464 def getall(repo, subset, x):
465 """``all()``
465 """``all()``
466 All changesets, the same as ``0:tip``.
466 All changesets, the same as ``0:tip``.
467 """
467 """
468 # i18n: "all" is a keyword
468 # i18n: "all" is a keyword
469 getargs(x, 0, 0, _("all takes no arguments"))
469 getargs(x, 0, 0, _("all takes no arguments"))
470 return subset
470 return subset
471
471
472 def grep(repo, subset, x):
472 def grep(repo, subset, x):
473 """``grep(regex)``
473 """``grep(regex)``
474 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
474 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
475 to ensure special escape characters are handled correctly. Unlike
475 to ensure special escape characters are handled correctly. Unlike
476 ``keyword(string)``, the match is case-sensitive.
476 ``keyword(string)``, the match is case-sensitive.
477 """
477 """
478 try:
478 try:
479 # i18n: "grep" is a keyword
479 # i18n: "grep" is a keyword
480 gr = re.compile(getstring(x, _("grep requires a string")))
480 gr = re.compile(getstring(x, _("grep requires a string")))
481 except re.error, e:
481 except re.error, e:
482 raise error.ParseError(_('invalid match pattern: %s') % e)
482 raise error.ParseError(_('invalid match pattern: %s') % e)
483 l = []
483 l = []
484 for r in subset:
484 for r in subset:
485 c = repo[r]
485 c = repo[r]
486 for e in c.files() + [c.user(), c.description()]:
486 for e in c.files() + [c.user(), c.description()]:
487 if gr.search(e):
487 if gr.search(e):
488 l.append(r)
488 l.append(r)
489 break
489 break
490 return l
490 return l
491
491
492 def hasfile(repo, subset, x):
492 def hasfile(repo, subset, x):
493 """``file(pattern)``
493 """``file(pattern)``
494 Changesets affecting files matched by pattern.
494 Changesets affecting files matched by pattern.
495 """
495 """
496 # i18n: "file" is a keyword
496 # i18n: "file" is a keyword
497 pat = getstring(x, _("file requires a pattern"))
497 pat = getstring(x, _("file requires a pattern"))
498 m = matchmod.match(repo.root, repo.getcwd(), [pat])
498 m = matchmod.match(repo.root, repo.getcwd(), [pat])
499 s = []
499 s = []
500 for r in subset:
500 for r in subset:
501 for f in repo[r].files():
501 for f in repo[r].files():
502 if m(f):
502 if m(f):
503 s.append(r)
503 s.append(r)
504 break
504 break
505 return s
505 return s
506
506
507 def head(repo, subset, x):
507 def head(repo, subset, x):
508 """``head()``
508 """``head()``
509 Changeset is a named branch head.
509 Changeset is a named branch head.
510 """
510 """
511 # i18n: "head" is a keyword
511 # i18n: "head" is a keyword
512 getargs(x, 0, 0, _("head takes no arguments"))
512 getargs(x, 0, 0, _("head takes no arguments"))
513 hs = set()
513 hs = set()
514 for b, ls in repo.branchmap().iteritems():
514 for b, ls in repo.branchmap().iteritems():
515 hs.update(repo[h].rev() for h in ls)
515 hs.update(repo[h].rev() for h in ls)
516 return [r for r in subset if r in hs]
516 return [r for r in subset if r in hs]
517
517
518 def heads(repo, subset, x):
518 def heads(repo, subset, x):
519 """``heads(set)``
519 """``heads(set)``
520 Members of set with no children in set.
520 Members of set with no children in set.
521 """
521 """
522 s = getset(repo, subset, x)
522 s = getset(repo, subset, x)
523 ps = set(parents(repo, subset, x))
523 ps = set(parents(repo, subset, x))
524 return [r for r in s if r not in ps]
524 return [r for r in s if r not in ps]
525
525
526 def keyword(repo, subset, x):
526 def keyword(repo, subset, x):
527 """``keyword(string)``
527 """``keyword(string)``
528 Search commit message, user name, and names of changed files for
528 Search commit message, user name, and names of changed files for
529 string. The match is case-insensitive.
529 string. The match is case-insensitive.
530 """
530 """
531 # i18n: "keyword" is a keyword
531 # i18n: "keyword" is a keyword
532 kw = encoding.lower(getstring(x, _("keyword requires a string")))
532 kw = encoding.lower(getstring(x, _("keyword requires a string")))
533 l = []
533 l = []
534 for r in subset:
534 for r in subset:
535 c = repo[r]
535 c = repo[r]
536 t = " ".join(c.files() + [c.user(), c.description()])
536 t = " ".join(c.files() + [c.user(), c.description()])
537 if kw in encoding.lower(t):
537 if kw in encoding.lower(t):
538 l.append(r)
538 l.append(r)
539 return l
539 return l
540
540
541 def limit(repo, subset, x):
541 def limit(repo, subset, x):
542 """``limit(set, [n])``
542 """``limit(set, [n])``
543 First n members of set, defaulting to 1.
543 First n members of set, defaulting to 1.
544 """
544 """
545 # i18n: "limit" is a keyword
545 # i18n: "limit" is a keyword
546 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
546 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
547 try:
547 try:
548 lim = 1
548 lim = 1
549 if len(l) == 2:
549 if len(l) == 2:
550 # i18n: "limit" is a keyword
550 # i18n: "limit" is a keyword
551 lim = int(getstring(l[1], _("limit requires a number")))
551 lim = int(getstring(l[1], _("limit requires a number")))
552 except (TypeError, ValueError):
552 except (TypeError, ValueError):
553 # i18n: "limit" is a keyword
553 # i18n: "limit" is a keyword
554 raise error.ParseError(_("limit expects a number"))
554 raise error.ParseError(_("limit expects a number"))
555 ss = set(subset)
555 ss = set(subset)
556 os = getset(repo, range(len(repo)), l[0])[:lim]
556 os = getset(repo, range(len(repo)), l[0])[:lim]
557 return [r for r in os if r in ss]
557 return [r for r in os if r in ss]
558
558
559 def last(repo, subset, x):
559 def last(repo, subset, x):
560 """``last(set, [n])``
560 """``last(set, [n])``
561 Last n members of set, defaulting to 1.
561 Last n members of set, defaulting to 1.
562 """
562 """
563 # i18n: "last" is a keyword
563 # i18n: "last" is a keyword
564 l = getargs(x, 1, 2, _("last requires one or two arguments"))
564 l = getargs(x, 1, 2, _("last requires one or two arguments"))
565 try:
565 try:
566 lim = 1
566 lim = 1
567 if len(l) == 2:
567 if len(l) == 2:
568 # i18n: "last" is a keyword
568 # i18n: "last" is a keyword
569 lim = int(getstring(l[1], _("last requires a number")))
569 lim = int(getstring(l[1], _("last requires a number")))
570 except (TypeError, ValueError):
570 except (TypeError, ValueError):
571 # i18n: "last" is a keyword
571 # i18n: "last" is a keyword
572 raise error.ParseError(_("last expects a number"))
572 raise error.ParseError(_("last expects a number"))
573 ss = set(subset)
573 ss = set(subset)
574 os = getset(repo, range(len(repo)), l[0])[-lim:]
574 os = getset(repo, range(len(repo)), l[0])[-lim:]
575 return [r for r in os if r in ss]
575 return [r for r in os if r in ss]
576
576
577 def maxrev(repo, subset, x):
577 def maxrev(repo, subset, x):
578 """``max(set)``
578 """``max(set)``
579 Changeset with highest revision number in set.
579 Changeset with highest revision number in set.
580 """
580 """
581 os = getset(repo, range(len(repo)), x)
581 os = getset(repo, range(len(repo)), x)
582 if os:
582 if os:
583 m = max(os)
583 m = max(os)
584 if m in subset:
584 if m in subset:
585 return [m]
585 return [m]
586 return []
586 return []
587
587
588 def merge(repo, subset, x):
588 def merge(repo, subset, x):
589 """``merge()``
589 """``merge()``
590 Changeset is a merge changeset.
590 Changeset is a merge changeset.
591 """
591 """
592 # i18n: "merge" is a keyword
592 # i18n: "merge" is a keyword
593 getargs(x, 0, 0, _("merge takes no arguments"))
593 getargs(x, 0, 0, _("merge takes no arguments"))
594 cl = repo.changelog
594 cl = repo.changelog
595 return [r for r in subset if cl.parentrevs(r)[1] != -1]
595 return [r for r in subset if cl.parentrevs(r)[1] != -1]
596
596
597 def minrev(repo, subset, x):
597 def minrev(repo, subset, x):
598 """``min(set)``
598 """``min(set)``
599 Changeset with lowest revision number in set.
599 Changeset with lowest revision number in set.
600 """
600 """
601 os = getset(repo, range(len(repo)), x)
601 os = getset(repo, range(len(repo)), x)
602 if os:
602 if os:
603 m = min(os)
603 m = min(os)
604 if m in subset:
604 if m in subset:
605 return [m]
605 return [m]
606 return []
606 return []
607
607
608 def modifies(repo, subset, x):
608 def modifies(repo, subset, x):
609 """``modifies(pattern)``
609 """``modifies(pattern)``
610 Changesets modifying files matched by pattern.
610 Changesets modifying files matched by pattern.
611 """
611 """
612 # i18n: "modifies" is a keyword
612 # i18n: "modifies" is a keyword
613 pat = getstring(x, _("modifies requires a pattern"))
613 pat = getstring(x, _("modifies requires a pattern"))
614 return checkstatus(repo, subset, pat, 0)
614 return checkstatus(repo, subset, pat, 0)
615
615
616 def node(repo, subset, x):
616 def node(repo, subset, x):
617 """``id(string)``
617 """``id(string)``
618 Revision non-ambiguously specified by the given hex string prefix.
618 Revision non-ambiguously specified by the given hex string prefix.
619 """
619 """
620 # i18n: "id" is a keyword
620 # i18n: "id" is a keyword
621 l = getargs(x, 1, 1, _("id requires one argument"))
621 l = getargs(x, 1, 1, _("id requires one argument"))
622 # i18n: "id" is a keyword
622 # i18n: "id" is a keyword
623 n = getstring(l[0], _("id requires a string"))
623 n = getstring(l[0], _("id requires a string"))
624 if len(n) == 40:
624 if len(n) == 40:
625 rn = repo[n].rev()
625 rn = repo[n].rev()
626 else:
626 else:
627 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
627 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
628 return [r for r in subset if r == rn]
628 return [r for r in subset if r == rn]
629
629
630 def outgoing(repo, subset, x):
630 def outgoing(repo, subset, x):
631 """``outgoing([path])``
631 """``outgoing([path])``
632 Changesets not found in the specified destination repository, or the
632 Changesets not found in the specified destination repository, or the
633 default push location.
633 default push location.
634 """
634 """
635 import hg # avoid start-up nasties
635 import hg # avoid start-up nasties
636 # i18n: "outgoing" is a keyword
636 # i18n: "outgoing" is a keyword
637 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
637 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
638 # i18n: "outgoing" is a keyword
638 # i18n: "outgoing" is a keyword
639 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
639 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
640 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
640 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
641 dest, branches = hg.parseurl(dest)
641 dest, branches = hg.parseurl(dest)
642 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
642 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
643 if revs:
643 if revs:
644 revs = [repo.lookup(rev) for rev in revs]
644 revs = [repo.lookup(rev) for rev in revs]
645 other = hg.peer(repo, {}, dest)
645 other = hg.peer(repo, {}, dest)
646 repo.ui.pushbuffer()
646 repo.ui.pushbuffer()
647 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
647 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
648 repo.ui.popbuffer()
648 repo.ui.popbuffer()
649 cl = repo.changelog
649 cl = repo.changelog
650 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
650 o = set([cl.rev(r) for r in outgoing.missing])
651 return [r for r in subset if r in o]
651 return [r for r in subset if r in o]
652
652
653 def p1(repo, subset, x):
653 def p1(repo, subset, x):
654 """``p1([set])``
654 """``p1([set])``
655 First parent of changesets in set, or the working directory.
655 First parent of changesets in set, or the working directory.
656 """
656 """
657 if x is None:
657 if x is None:
658 p = repo[x].p1().rev()
658 p = repo[x].p1().rev()
659 return [r for r in subset if r == p]
659 return [r for r in subset if r == p]
660
660
661 ps = set()
661 ps = set()
662 cl = repo.changelog
662 cl = repo.changelog
663 for r in getset(repo, range(len(repo)), x):
663 for r in getset(repo, range(len(repo)), x):
664 ps.add(cl.parentrevs(r)[0])
664 ps.add(cl.parentrevs(r)[0])
665 return [r for r in subset if r in ps]
665 return [r for r in subset if r in ps]
666
666
667 def p2(repo, subset, x):
667 def p2(repo, subset, x):
668 """``p2([set])``
668 """``p2([set])``
669 Second parent of changesets in set, or the working directory.
669 Second parent of changesets in set, or the working directory.
670 """
670 """
671 if x is None:
671 if x is None:
672 ps = repo[x].parents()
672 ps = repo[x].parents()
673 try:
673 try:
674 p = ps[1].rev()
674 p = ps[1].rev()
675 return [r for r in subset if r == p]
675 return [r for r in subset if r == p]
676 except IndexError:
676 except IndexError:
677 return []
677 return []
678
678
679 ps = set()
679 ps = set()
680 cl = repo.changelog
680 cl = repo.changelog
681 for r in getset(repo, range(len(repo)), x):
681 for r in getset(repo, range(len(repo)), x):
682 ps.add(cl.parentrevs(r)[1])
682 ps.add(cl.parentrevs(r)[1])
683 return [r for r in subset if r in ps]
683 return [r for r in subset if r in ps]
684
684
685 def parents(repo, subset, x):
685 def parents(repo, subset, x):
686 """``parents([set])``
686 """``parents([set])``
687 The set of all parents for all changesets in set, or the working directory.
687 The set of all parents for all changesets in set, or the working directory.
688 """
688 """
689 if x is None:
689 if x is None:
690 ps = tuple(p.rev() for p in repo[x].parents())
690 ps = tuple(p.rev() for p in repo[x].parents())
691 return [r for r in subset if r in ps]
691 return [r for r in subset if r in ps]
692
692
693 ps = set()
693 ps = set()
694 cl = repo.changelog
694 cl = repo.changelog
695 for r in getset(repo, range(len(repo)), x):
695 for r in getset(repo, range(len(repo)), x):
696 ps.update(cl.parentrevs(r))
696 ps.update(cl.parentrevs(r))
697 return [r for r in subset if r in ps]
697 return [r for r in subset if r in ps]
698
698
699 def parentspec(repo, subset, x, n):
699 def parentspec(repo, subset, x, n):
700 """``set^0``
700 """``set^0``
701 The set.
701 The set.
702 ``set^1`` (or ``set^``), ``set^2``
702 ``set^1`` (or ``set^``), ``set^2``
703 First or second parent, respectively, of all changesets in set.
703 First or second parent, respectively, of all changesets in set.
704 """
704 """
705 try:
705 try:
706 n = int(n[1])
706 n = int(n[1])
707 if n not in (0, 1, 2):
707 if n not in (0, 1, 2):
708 raise ValueError
708 raise ValueError
709 except (TypeError, ValueError):
709 except (TypeError, ValueError):
710 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
710 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
711 ps = set()
711 ps = set()
712 cl = repo.changelog
712 cl = repo.changelog
713 for r in getset(repo, subset, x):
713 for r in getset(repo, subset, x):
714 if n == 0:
714 if n == 0:
715 ps.add(r)
715 ps.add(r)
716 elif n == 1:
716 elif n == 1:
717 ps.add(cl.parentrevs(r)[0])
717 ps.add(cl.parentrevs(r)[0])
718 elif n == 2:
718 elif n == 2:
719 parents = cl.parentrevs(r)
719 parents = cl.parentrevs(r)
720 if len(parents) > 1:
720 if len(parents) > 1:
721 ps.add(parents[1])
721 ps.add(parents[1])
722 return [r for r in subset if r in ps]
722 return [r for r in subset if r in ps]
723
723
724 def present(repo, subset, x):
724 def present(repo, subset, x):
725 """``present(set)``
725 """``present(set)``
726 An empty set, if any revision in set isn't found; otherwise,
726 An empty set, if any revision in set isn't found; otherwise,
727 all revisions in set.
727 all revisions in set.
728 """
728 """
729 try:
729 try:
730 return getset(repo, subset, x)
730 return getset(repo, subset, x)
731 except error.RepoLookupError:
731 except error.RepoLookupError:
732 return []
732 return []
733
733
734 def public(repo, subset, x):
734 def public(repo, subset, x):
735 """``public()``
735 """``public()``
736 Changeset in public phase."""
736 Changeset in public phase."""
737 getargs(x, 0, 0, _("public takes no arguments"))
737 getargs(x, 0, 0, _("public takes no arguments"))
738 return [r for r in subset if repo._phaserev[r] == phases.public]
738 return [r for r in subset if repo._phaserev[r] == phases.public]
739
739
740 def removes(repo, subset, x):
740 def removes(repo, subset, x):
741 """``removes(pattern)``
741 """``removes(pattern)``
742 Changesets which remove files matching pattern.
742 Changesets which remove files matching pattern.
743 """
743 """
744 # i18n: "removes" is a keyword
744 # i18n: "removes" is a keyword
745 pat = getstring(x, _("removes requires a pattern"))
745 pat = getstring(x, _("removes requires a pattern"))
746 return checkstatus(repo, subset, pat, 2)
746 return checkstatus(repo, subset, pat, 2)
747
747
748 def rev(repo, subset, x):
748 def rev(repo, subset, x):
749 """``rev(number)``
749 """``rev(number)``
750 Revision with the given numeric identifier.
750 Revision with the given numeric identifier.
751 """
751 """
752 # i18n: "rev" is a keyword
752 # i18n: "rev" is a keyword
753 l = getargs(x, 1, 1, _("rev requires one argument"))
753 l = getargs(x, 1, 1, _("rev requires one argument"))
754 try:
754 try:
755 # i18n: "rev" is a keyword
755 # i18n: "rev" is a keyword
756 l = int(getstring(l[0], _("rev requires a number")))
756 l = int(getstring(l[0], _("rev requires a number")))
757 except (TypeError, ValueError):
757 except (TypeError, ValueError):
758 # i18n: "rev" is a keyword
758 # i18n: "rev" is a keyword
759 raise error.ParseError(_("rev expects a number"))
759 raise error.ParseError(_("rev expects a number"))
760 return [r for r in subset if r == l]
760 return [r for r in subset if r == l]
761
761
762 def reverse(repo, subset, x):
762 def reverse(repo, subset, x):
763 """``reverse(set)``
763 """``reverse(set)``
764 Reverse order of set.
764 Reverse order of set.
765 """
765 """
766 l = getset(repo, subset, x)
766 l = getset(repo, subset, x)
767 l.reverse()
767 l.reverse()
768 return l
768 return l
769
769
770 def roots(repo, subset, x):
770 def roots(repo, subset, x):
771 """``roots(set)``
771 """``roots(set)``
772 Changesets with no parent changeset in set.
772 Changesets with no parent changeset in set.
773 """
773 """
774 s = getset(repo, subset, x)
774 s = getset(repo, subset, x)
775 cs = set(children(repo, subset, x))
775 cs = set(children(repo, subset, x))
776 return [r for r in s if r not in cs]
776 return [r for r in s if r not in cs]
777
777
778 def secret(repo, subset, x):
778 def secret(repo, subset, x):
779 """``secret()``
779 """``secret()``
780 Changeset in secret phase."""
780 Changeset in secret phase."""
781 getargs(x, 0, 0, _("secret takes no arguments"))
781 getargs(x, 0, 0, _("secret takes no arguments"))
782 return [r for r in subset if repo._phaserev[r] == phases.secret]
782 return [r for r in subset if repo._phaserev[r] == phases.secret]
783
783
784 def sort(repo, subset, x):
784 def sort(repo, subset, x):
785 """``sort(set[, [-]key...])``
785 """``sort(set[, [-]key...])``
786 Sort set by keys. The default sort order is ascending, specify a key
786 Sort set by keys. The default sort order is ascending, specify a key
787 as ``-key`` to sort in descending order.
787 as ``-key`` to sort in descending order.
788
788
789 The keys can be:
789 The keys can be:
790
790
791 - ``rev`` for the revision number,
791 - ``rev`` for the revision number,
792 - ``branch`` for the branch name,
792 - ``branch`` for the branch name,
793 - ``desc`` for the commit message (description),
793 - ``desc`` for the commit message (description),
794 - ``user`` for user name (``author`` can be used as an alias),
794 - ``user`` for user name (``author`` can be used as an alias),
795 - ``date`` for the commit date
795 - ``date`` for the commit date
796 """
796 """
797 # i18n: "sort" is a keyword
797 # i18n: "sort" is a keyword
798 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
798 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
799 keys = "rev"
799 keys = "rev"
800 if len(l) == 2:
800 if len(l) == 2:
801 keys = getstring(l[1], _("sort spec must be a string"))
801 keys = getstring(l[1], _("sort spec must be a string"))
802
802
803 s = l[0]
803 s = l[0]
804 keys = keys.split()
804 keys = keys.split()
805 l = []
805 l = []
806 def invert(s):
806 def invert(s):
807 return "".join(chr(255 - ord(c)) for c in s)
807 return "".join(chr(255 - ord(c)) for c in s)
808 for r in getset(repo, subset, s):
808 for r in getset(repo, subset, s):
809 c = repo[r]
809 c = repo[r]
810 e = []
810 e = []
811 for k in keys:
811 for k in keys:
812 if k == 'rev':
812 if k == 'rev':
813 e.append(r)
813 e.append(r)
814 elif k == '-rev':
814 elif k == '-rev':
815 e.append(-r)
815 e.append(-r)
816 elif k == 'branch':
816 elif k == 'branch':
817 e.append(c.branch())
817 e.append(c.branch())
818 elif k == '-branch':
818 elif k == '-branch':
819 e.append(invert(c.branch()))
819 e.append(invert(c.branch()))
820 elif k == 'desc':
820 elif k == 'desc':
821 e.append(c.description())
821 e.append(c.description())
822 elif k == '-desc':
822 elif k == '-desc':
823 e.append(invert(c.description()))
823 e.append(invert(c.description()))
824 elif k in 'user author':
824 elif k in 'user author':
825 e.append(c.user())
825 e.append(c.user())
826 elif k in '-user -author':
826 elif k in '-user -author':
827 e.append(invert(c.user()))
827 e.append(invert(c.user()))
828 elif k == 'date':
828 elif k == 'date':
829 e.append(c.date()[0])
829 e.append(c.date()[0])
830 elif k == '-date':
830 elif k == '-date':
831 e.append(-c.date()[0])
831 e.append(-c.date()[0])
832 else:
832 else:
833 raise error.ParseError(_("unknown sort key %r") % k)
833 raise error.ParseError(_("unknown sort key %r") % k)
834 e.append(r)
834 e.append(r)
835 l.append(e)
835 l.append(e)
836 l.sort()
836 l.sort()
837 return [e[-1] for e in l]
837 return [e[-1] for e in l]
838
838
839 def tag(repo, subset, x):
839 def tag(repo, subset, x):
840 """``tag([name])``
840 """``tag([name])``
841 The specified tag by name, or all tagged revisions if no name is given.
841 The specified tag by name, or all tagged revisions if no name is given.
842 """
842 """
843 # i18n: "tag" is a keyword
843 # i18n: "tag" is a keyword
844 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
844 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
845 cl = repo.changelog
845 cl = repo.changelog
846 if args:
846 if args:
847 tn = getstring(args[0],
847 tn = getstring(args[0],
848 # i18n: "tag" is a keyword
848 # i18n: "tag" is a keyword
849 _('the argument to tag must be a string'))
849 _('the argument to tag must be a string'))
850 if not repo.tags().get(tn, None):
850 if not repo.tags().get(tn, None):
851 raise util.Abort(_("tag '%s' does not exist") % tn)
851 raise util.Abort(_("tag '%s' does not exist") % tn)
852 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
852 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
853 else:
853 else:
854 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
854 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
855 return [r for r in subset if r in s]
855 return [r for r in subset if r in s]
856
856
857 def tagged(repo, subset, x):
857 def tagged(repo, subset, x):
858 return tag(repo, subset, x)
858 return tag(repo, subset, x)
859
859
860 def user(repo, subset, x):
860 def user(repo, subset, x):
861 """``user(string)``
861 """``user(string)``
862 User name contains string. The match is case-insensitive.
862 User name contains string. The match is case-insensitive.
863 """
863 """
864 return author(repo, subset, x)
864 return author(repo, subset, x)
865
865
866 symbols = {
866 symbols = {
867 "adds": adds,
867 "adds": adds,
868 "all": getall,
868 "all": getall,
869 "ancestor": ancestor,
869 "ancestor": ancestor,
870 "ancestors": ancestors,
870 "ancestors": ancestors,
871 "author": author,
871 "author": author,
872 "bisect": bisect,
872 "bisect": bisect,
873 "bisected": bisected,
873 "bisected": bisected,
874 "bookmark": bookmark,
874 "bookmark": bookmark,
875 "branch": branch,
875 "branch": branch,
876 "children": children,
876 "children": children,
877 "closed": closed,
877 "closed": closed,
878 "contains": contains,
878 "contains": contains,
879 "date": date,
879 "date": date,
880 "desc": desc,
880 "desc": desc,
881 "descendants": descendants,
881 "descendants": descendants,
882 "draft": draft,
882 "draft": draft,
883 "file": hasfile,
883 "file": hasfile,
884 "filelog": filelog,
884 "filelog": filelog,
885 "first": first,
885 "first": first,
886 "follow": follow,
886 "follow": follow,
887 "grep": grep,
887 "grep": grep,
888 "head": head,
888 "head": head,
889 "heads": heads,
889 "heads": heads,
890 "id": node,
890 "id": node,
891 "keyword": keyword,
891 "keyword": keyword,
892 "last": last,
892 "last": last,
893 "limit": limit,
893 "limit": limit,
894 "max": maxrev,
894 "max": maxrev,
895 "merge": merge,
895 "merge": merge,
896 "min": minrev,
896 "min": minrev,
897 "modifies": modifies,
897 "modifies": modifies,
898 "outgoing": outgoing,
898 "outgoing": outgoing,
899 "p1": p1,
899 "p1": p1,
900 "p2": p2,
900 "p2": p2,
901 "parents": parents,
901 "parents": parents,
902 "present": present,
902 "present": present,
903 "public": public,
903 "public": public,
904 "removes": removes,
904 "removes": removes,
905 "rev": rev,
905 "rev": rev,
906 "reverse": reverse,
906 "reverse": reverse,
907 "roots": roots,
907 "roots": roots,
908 "sort": sort,
908 "sort": sort,
909 "secret": secret,
909 "secret": secret,
910 "tag": tag,
910 "tag": tag,
911 "tagged": tagged,
911 "tagged": tagged,
912 "user": user,
912 "user": user,
913 }
913 }
914
914
915 methods = {
915 methods = {
916 "range": rangeset,
916 "range": rangeset,
917 "string": stringset,
917 "string": stringset,
918 "symbol": symbolset,
918 "symbol": symbolset,
919 "and": andset,
919 "and": andset,
920 "or": orset,
920 "or": orset,
921 "not": notset,
921 "not": notset,
922 "list": listset,
922 "list": listset,
923 "func": func,
923 "func": func,
924 "ancestor": ancestorspec,
924 "ancestor": ancestorspec,
925 "parent": parentspec,
925 "parent": parentspec,
926 "parentpost": p1,
926 "parentpost": p1,
927 }
927 }
928
928
929 def optimize(x, small):
929 def optimize(x, small):
930 if x is None:
930 if x is None:
931 return 0, x
931 return 0, x
932
932
933 smallbonus = 1
933 smallbonus = 1
934 if small:
934 if small:
935 smallbonus = .5
935 smallbonus = .5
936
936
937 op = x[0]
937 op = x[0]
938 if op == 'minus':
938 if op == 'minus':
939 return optimize(('and', x[1], ('not', x[2])), small)
939 return optimize(('and', x[1], ('not', x[2])), small)
940 elif op == 'dagrange':
940 elif op == 'dagrange':
941 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
941 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
942 ('func', ('symbol', 'ancestors'), x[2])), small)
942 ('func', ('symbol', 'ancestors'), x[2])), small)
943 elif op == 'dagrangepre':
943 elif op == 'dagrangepre':
944 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
944 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
945 elif op == 'dagrangepost':
945 elif op == 'dagrangepost':
946 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
946 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
947 elif op == 'rangepre':
947 elif op == 'rangepre':
948 return optimize(('range', ('string', '0'), x[1]), small)
948 return optimize(('range', ('string', '0'), x[1]), small)
949 elif op == 'rangepost':
949 elif op == 'rangepost':
950 return optimize(('range', x[1], ('string', 'tip')), small)
950 return optimize(('range', x[1], ('string', 'tip')), small)
951 elif op == 'negate':
951 elif op == 'negate':
952 return optimize(('string',
952 return optimize(('string',
953 '-' + getstring(x[1], _("can't negate that"))), small)
953 '-' + getstring(x[1], _("can't negate that"))), small)
954 elif op in 'string symbol negate':
954 elif op in 'string symbol negate':
955 return smallbonus, x # single revisions are small
955 return smallbonus, x # single revisions are small
956 elif op == 'and' or op == 'dagrange':
956 elif op == 'and' or op == 'dagrange':
957 wa, ta = optimize(x[1], True)
957 wa, ta = optimize(x[1], True)
958 wb, tb = optimize(x[2], True)
958 wb, tb = optimize(x[2], True)
959 w = min(wa, wb)
959 w = min(wa, wb)
960 if wa > wb:
960 if wa > wb:
961 return w, (op, tb, ta)
961 return w, (op, tb, ta)
962 return w, (op, ta, tb)
962 return w, (op, ta, tb)
963 elif op == 'or':
963 elif op == 'or':
964 wa, ta = optimize(x[1], False)
964 wa, ta = optimize(x[1], False)
965 wb, tb = optimize(x[2], False)
965 wb, tb = optimize(x[2], False)
966 if wb < wa:
966 if wb < wa:
967 wb, wa = wa, wb
967 wb, wa = wa, wb
968 return max(wa, wb), (op, ta, tb)
968 return max(wa, wb), (op, ta, tb)
969 elif op == 'not':
969 elif op == 'not':
970 o = optimize(x[1], not small)
970 o = optimize(x[1], not small)
971 return o[0], (op, o[1])
971 return o[0], (op, o[1])
972 elif op == 'parentpost':
972 elif op == 'parentpost':
973 o = optimize(x[1], small)
973 o = optimize(x[1], small)
974 return o[0], (op, o[1])
974 return o[0], (op, o[1])
975 elif op == 'group':
975 elif op == 'group':
976 return optimize(x[1], small)
976 return optimize(x[1], small)
977 elif op in 'range list parent ancestorspec':
977 elif op in 'range list parent ancestorspec':
978 if op == 'parent':
978 if op == 'parent':
979 # x^:y means (x^) : y, not x ^ (:y)
979 # x^:y means (x^) : y, not x ^ (:y)
980 post = ('parentpost', x[1])
980 post = ('parentpost', x[1])
981 if x[2][0] == 'dagrangepre':
981 if x[2][0] == 'dagrangepre':
982 return optimize(('dagrange', post, x[2][1]), small)
982 return optimize(('dagrange', post, x[2][1]), small)
983 elif x[2][0] == 'rangepre':
983 elif x[2][0] == 'rangepre':
984 return optimize(('range', post, x[2][1]), small)
984 return optimize(('range', post, x[2][1]), small)
985
985
986 wa, ta = optimize(x[1], small)
986 wa, ta = optimize(x[1], small)
987 wb, tb = optimize(x[2], small)
987 wb, tb = optimize(x[2], small)
988 return wa + wb, (op, ta, tb)
988 return wa + wb, (op, ta, tb)
989 elif op == 'func':
989 elif op == 'func':
990 f = getstring(x[1], _("not a symbol"))
990 f = getstring(x[1], _("not a symbol"))
991 wa, ta = optimize(x[2], small)
991 wa, ta = optimize(x[2], small)
992 if f in ("author branch closed date desc file grep keyword "
992 if f in ("author branch closed date desc file grep keyword "
993 "outgoing user"):
993 "outgoing user"):
994 w = 10 # slow
994 w = 10 # slow
995 elif f in "modifies adds removes":
995 elif f in "modifies adds removes":
996 w = 30 # slower
996 w = 30 # slower
997 elif f == "contains":
997 elif f == "contains":
998 w = 100 # very slow
998 w = 100 # very slow
999 elif f == "ancestor":
999 elif f == "ancestor":
1000 w = 1 * smallbonus
1000 w = 1 * smallbonus
1001 elif f in "reverse limit first":
1001 elif f in "reverse limit first":
1002 w = 0
1002 w = 0
1003 elif f in "sort":
1003 elif f in "sort":
1004 w = 10 # assume most sorts look at changelog
1004 w = 10 # assume most sorts look at changelog
1005 else:
1005 else:
1006 w = 1
1006 w = 1
1007 return w + wa, (op, x[1], ta)
1007 return w + wa, (op, x[1], ta)
1008 return 1, x
1008 return 1, x
1009
1009
1010 class revsetalias(object):
1010 class revsetalias(object):
1011 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1011 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1012 args = None
1012 args = None
1013
1013
1014 def __init__(self, name, value):
1014 def __init__(self, name, value):
1015 '''Aliases like:
1015 '''Aliases like:
1016
1016
1017 h = heads(default)
1017 h = heads(default)
1018 b($1) = ancestors($1) - ancestors(default)
1018 b($1) = ancestors($1) - ancestors(default)
1019 '''
1019 '''
1020 if isinstance(name, tuple): # parameter substitution
1020 if isinstance(name, tuple): # parameter substitution
1021 self.tree = name
1021 self.tree = name
1022 self.replacement = value
1022 self.replacement = value
1023 else: # alias definition
1023 else: # alias definition
1024 m = self.funcre.search(name)
1024 m = self.funcre.search(name)
1025 if m:
1025 if m:
1026 self.tree = ('func', ('symbol', m.group(1)))
1026 self.tree = ('func', ('symbol', m.group(1)))
1027 self.args = [x.strip() for x in m.group(2).split(',')]
1027 self.args = [x.strip() for x in m.group(2).split(',')]
1028 for arg in self.args:
1028 for arg in self.args:
1029 value = value.replace(arg, repr(arg))
1029 value = value.replace(arg, repr(arg))
1030 else:
1030 else:
1031 self.tree = ('symbol', name)
1031 self.tree = ('symbol', name)
1032
1032
1033 self.replacement, pos = parse(value)
1033 self.replacement, pos = parse(value)
1034 if pos != len(value):
1034 if pos != len(value):
1035 raise error.ParseError(_('invalid token'), pos)
1035 raise error.ParseError(_('invalid token'), pos)
1036
1036
1037 def process(self, tree):
1037 def process(self, tree):
1038 if isinstance(tree, tuple):
1038 if isinstance(tree, tuple):
1039 if self.args is None:
1039 if self.args is None:
1040 if tree == self.tree:
1040 if tree == self.tree:
1041 return self.replacement
1041 return self.replacement
1042 elif tree[:2] == self.tree:
1042 elif tree[:2] == self.tree:
1043 l = getlist(tree[2])
1043 l = getlist(tree[2])
1044 if len(l) != len(self.args):
1044 if len(l) != len(self.args):
1045 raise error.ParseError(
1045 raise error.ParseError(
1046 _('invalid number of arguments: %s') % len(l))
1046 _('invalid number of arguments: %s') % len(l))
1047 result = self.replacement
1047 result = self.replacement
1048 for a, v in zip(self.args, l):
1048 for a, v in zip(self.args, l):
1049 valalias = revsetalias(('string', a), v)
1049 valalias = revsetalias(('string', a), v)
1050 result = valalias.process(result)
1050 result = valalias.process(result)
1051 return result
1051 return result
1052 return tuple(map(self.process, tree))
1052 return tuple(map(self.process, tree))
1053 return tree
1053 return tree
1054
1054
1055 def findaliases(ui, tree):
1055 def findaliases(ui, tree):
1056 for k, v in ui.configitems('revsetalias'):
1056 for k, v in ui.configitems('revsetalias'):
1057 alias = revsetalias(k, v)
1057 alias = revsetalias(k, v)
1058 tree = alias.process(tree)
1058 tree = alias.process(tree)
1059 return tree
1059 return tree
1060
1060
1061 parse = parser.parser(tokenize, elements).parse
1061 parse = parser.parser(tokenize, elements).parse
1062
1062
1063 def match(ui, spec):
1063 def match(ui, spec):
1064 if not spec:
1064 if not spec:
1065 raise error.ParseError(_("empty query"))
1065 raise error.ParseError(_("empty query"))
1066 tree, pos = parse(spec)
1066 tree, pos = parse(spec)
1067 if (pos != len(spec)):
1067 if (pos != len(spec)):
1068 raise error.ParseError(_("invalid token"), pos)
1068 raise error.ParseError(_("invalid token"), pos)
1069 if ui:
1069 if ui:
1070 tree = findaliases(ui, tree)
1070 tree = findaliases(ui, tree)
1071 weight, tree = optimize(tree, True)
1071 weight, tree = optimize(tree, True)
1072 def mfunc(repo, subset):
1072 def mfunc(repo, subset):
1073 return getset(repo, subset, tree)
1073 return getset(repo, subset, tree)
1074 return mfunc
1074 return mfunc
1075
1075
1076 def formatspec(expr, *args):
1076 def formatspec(expr, *args):
1077 '''
1077 '''
1078 This is a convenience function for using revsets internally, and
1078 This is a convenience function for using revsets internally, and
1079 escapes arguments appropriately. Aliases are intentionally ignored
1079 escapes arguments appropriately. Aliases are intentionally ignored
1080 so that intended expression behavior isn't accidentally subverted.
1080 so that intended expression behavior isn't accidentally subverted.
1081
1081
1082 Supported arguments:
1082 Supported arguments:
1083
1083
1084 %r = revset expression, parenthesized
1084 %r = revset expression, parenthesized
1085 %d = int(arg), no quoting
1085 %d = int(arg), no quoting
1086 %s = string(arg), escaped and single-quoted
1086 %s = string(arg), escaped and single-quoted
1087 %b = arg.branch(), escaped and single-quoted
1087 %b = arg.branch(), escaped and single-quoted
1088 %n = hex(arg), single-quoted
1088 %n = hex(arg), single-quoted
1089 %% = a literal '%'
1089 %% = a literal '%'
1090
1090
1091 Prefixing the type with 'l' specifies a parenthesized list of that type.
1091 Prefixing the type with 'l' specifies a parenthesized list of that type.
1092
1092
1093 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1093 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1094 '(10 or 11):: and ((this()) or (that()))'
1094 '(10 or 11):: and ((this()) or (that()))'
1095 >>> formatspec('%d:: and not %d::', 10, 20)
1095 >>> formatspec('%d:: and not %d::', 10, 20)
1096 '10:: and not 20::'
1096 '10:: and not 20::'
1097 >>> formatspec('%ld or %ld', [], [1])
1097 >>> formatspec('%ld or %ld', [], [1])
1098 '(0-0) or 1'
1098 '(0-0) or 1'
1099 >>> formatspec('keyword(%s)', 'foo\\xe9')
1099 >>> formatspec('keyword(%s)', 'foo\\xe9')
1100 "keyword('foo\\\\xe9')"
1100 "keyword('foo\\\\xe9')"
1101 >>> b = lambda: 'default'
1101 >>> b = lambda: 'default'
1102 >>> b.branch = b
1102 >>> b.branch = b
1103 >>> formatspec('branch(%b)', b)
1103 >>> formatspec('branch(%b)', b)
1104 "branch('default')"
1104 "branch('default')"
1105 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1105 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1106 "root((('a' or 'b') or ('c' or 'd')))"
1106 "root((('a' or 'b') or ('c' or 'd')))"
1107 '''
1107 '''
1108
1108
1109 def quote(s):
1109 def quote(s):
1110 return repr(str(s))
1110 return repr(str(s))
1111
1111
1112 def argtype(c, arg):
1112 def argtype(c, arg):
1113 if c == 'd':
1113 if c == 'd':
1114 return str(int(arg))
1114 return str(int(arg))
1115 elif c == 's':
1115 elif c == 's':
1116 return quote(arg)
1116 return quote(arg)
1117 elif c == 'r':
1117 elif c == 'r':
1118 parse(arg) # make sure syntax errors are confined
1118 parse(arg) # make sure syntax errors are confined
1119 return '(%s)' % arg
1119 return '(%s)' % arg
1120 elif c == 'n':
1120 elif c == 'n':
1121 return quote(nodemod.hex(arg))
1121 return quote(nodemod.hex(arg))
1122 elif c == 'b':
1122 elif c == 'b':
1123 return quote(arg.branch())
1123 return quote(arg.branch())
1124
1124
1125 def listexp(s, t):
1125 def listexp(s, t):
1126 "balance a list s of type t to limit parse tree depth"
1126 "balance a list s of type t to limit parse tree depth"
1127 l = len(s)
1127 l = len(s)
1128 if l == 0:
1128 if l == 0:
1129 return '(0-0)' # a minimal way to represent an empty set
1129 return '(0-0)' # a minimal way to represent an empty set
1130 if l == 1:
1130 if l == 1:
1131 return argtype(t, s[0])
1131 return argtype(t, s[0])
1132 m = l // 2
1132 m = l // 2
1133 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1133 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1134
1134
1135 ret = ''
1135 ret = ''
1136 pos = 0
1136 pos = 0
1137 arg = 0
1137 arg = 0
1138 while pos < len(expr):
1138 while pos < len(expr):
1139 c = expr[pos]
1139 c = expr[pos]
1140 if c == '%':
1140 if c == '%':
1141 pos += 1
1141 pos += 1
1142 d = expr[pos]
1142 d = expr[pos]
1143 if d == '%':
1143 if d == '%':
1144 ret += d
1144 ret += d
1145 elif d in 'dsnbr':
1145 elif d in 'dsnbr':
1146 ret += argtype(d, args[arg])
1146 ret += argtype(d, args[arg])
1147 arg += 1
1147 arg += 1
1148 elif d == 'l':
1148 elif d == 'l':
1149 # a list of some type
1149 # a list of some type
1150 pos += 1
1150 pos += 1
1151 d = expr[pos]
1151 d = expr[pos]
1152 ret += listexp(list(args[arg]), d)
1152 ret += listexp(list(args[arg]), d)
1153 arg += 1
1153 arg += 1
1154 else:
1154 else:
1155 raise util.Abort('unexpected revspec format character %s' % d)
1155 raise util.Abort('unexpected revspec format character %s' % d)
1156 else:
1156 else:
1157 ret += c
1157 ret += c
1158 pos += 1
1158 pos += 1
1159
1159
1160 return ret
1160 return ret
1161
1161
1162 # tell hggettext to extract docstrings from these functions:
1162 # tell hggettext to extract docstrings from these functions:
1163 i18nfunctions = symbols.values()
1163 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now