##// END OF EJS Templates
import: add --bypass option...
Patrick Mezard -
r14611:adbf5e7d default
parent child Browse files
Show More
@@ -0,0 +1,261 b''
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "purge=" >> $HGRCPATH
3 $ echo "graphlog=" >> $HGRCPATH
4
5 $ shortlog() {
6 > hg glog --template '{rev}:{node|short} {author} {date|hgdate} - {branch} - {desc|firstline}\n'
7 > }
8
9 Test --bypass with other options
10
11 $ hg init repo-options
12 $ cd repo-options
13 $ echo a > a
14 $ hg ci -Am adda
15 adding a
16 $ echo a >> a
17 $ hg branch foo
18 marked working directory as branch foo
19 $ hg ci -Am changea
20 $ hg export . > ../test.diff
21 $ hg up null
22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
23
24 Test importing an existing revision
25
26 $ hg import --bypass --exact ../test.diff
27 applying ../test.diff
28 $ shortlog
29 o 1:4e322f7ce8e3 test 0 0 - foo - changea
30 |
31 o 0:07f494440405 test 0 0 - default - adda
32
33
34 Test failure without --exact
35
36 $ hg import --bypass ../test.diff
37 applying ../test.diff
38 unable to find 'a' for patching
39 abort: patch failed to apply
40 [255]
41 $ hg st
42 $ shortlog
43 o 1:4e322f7ce8e3 test 0 0 - foo - changea
44 |
45 o 0:07f494440405 test 0 0 - default - adda
46
47
48 Test --user, --date and --message
49
50 $ hg up 0
51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 $ hg import --bypass --u test2 -d '1 0' -m patch2 ../test.diff
53 applying ../test.diff
54 $ cat .hg/last-message.txt
55 patch2 (no-eol)
56 $ shortlog
57 o 2:2e127d1da504 test2 1 0 - default - patch2
58 |
59 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
60 |/
61 @ 0:07f494440405 test 0 0 - default - adda
62
63 $ hg rollback
64 repository tip rolled back to revision 1 (undo commit)
65 working directory now based on revision 0
66
67 Test --import-branch
68
69 $ hg import --bypass --import-branch ../test.diff
70 applying ../test.diff
71 $ shortlog
72 o 1:4e322f7ce8e3 test 0 0 - foo - changea
73 |
74 @ 0:07f494440405 test 0 0 - default - adda
75
76 $ hg rollback
77 repository tip rolled back to revision 1 (undo commit)
78 working directory now based on revision 0
79
80 Test --strip
81
82 $ hg import --bypass --strip 0 - <<EOF
83 > # HG changeset patch
84 > # User test
85 > # Date 0 0
86 > # Branch foo
87 > # Node ID 4e322f7ce8e3e4203950eac9ece27bf7e45ffa6c
88 > # Parent 07f4944404050f47db2e5c5071e0e84e7a27bba9
89 > changea
90 >
91 > diff -r 07f494440405 -r 4e322f7ce8e3 a
92 > --- a Thu Jan 01 00:00:00 1970 +0000
93 > +++ a Thu Jan 01 00:00:00 1970 +0000
94 > @@ -1,1 +1,2 @@
95 > a
96 > +a
97 > EOF
98 applying patch from stdin
99 $ hg rollback
100 repository tip rolled back to revision 1 (undo commit)
101 working directory now based on revision 0
102
103 Test unsupported combinations
104
105 $ hg import --bypass --no-commit ../test.diff
106 abort: cannot use --no-commit with --bypass
107 [255]
108 $ hg import --bypass --similarity 50 ../test.diff
109 abort: cannot use --similarity with --bypass
110 [255]
111
112 Test commit editor
113
114 $ hg diff -c 1 > ../test.diff
115 $ HGEDITOR=cat hg import --bypass ../test.diff
116 applying ../test.diff
117
118
119 HG: Enter commit message. Lines beginning with 'HG:' are removed.
120 HG: Leave message empty to abort commit.
121 HG: --
122 HG: user: test
123 HG: branch 'default'
124 HG: changed a
125 abort: empty commit message
126 [255]
127
128 Test patch.eol is handled
129
130 $ python -c 'file("a", "wb").write("a\r\n")'
131 $ hg ci -m makeacrlf
132 $ hg import -m 'should fail because of eol' --bypass ../test.diff
133 applying ../test.diff
134 patching file a
135 Hunk #1 FAILED at 0
136 abort: patch failed to apply
137 [255]
138 $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff
139 applying ../test.diff
140 $ shortlog
141 o 3:d7805b4d2cb3 test 0 0 - default - test patch.eol
142 |
143 @ 2:872023de769d test 0 0 - default - makeacrlf
144 |
145 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
146 |/
147 o 0:07f494440405 test 0 0 - default - adda
148
149
150 Test applying multiple patches
151
152 $ hg up -qC 0
153 $ echo e > e
154 $ hg ci -Am adde
155 adding e
156 created new head
157 $ hg export . > ../patch1.diff
158 $ hg up -qC 1
159 $ echo f > f
160 $ hg ci -Am addf
161 adding f
162 $ hg export . > ../patch2.diff
163 $ cd ..
164 $ hg clone -r1 repo-options repo-multi1
165 adding changesets
166 adding manifests
167 adding file changes
168 added 2 changesets with 2 changes to 1 files
169 updating to branch foo
170 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ cd repo-multi1
172 $ hg up 0
173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 $ hg import --bypass ../patch1.diff ../patch2.diff
175 applying ../patch1.diff
176 applying ../patch2.diff
177 applied 16581080145e
178 $ shortlog
179 o 3:bc8ca3f8a7c4 test 0 0 - default - addf
180 |
181 o 2:16581080145e test 0 0 - default - adde
182 |
183 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
184 |/
185 @ 0:07f494440405 test 0 0 - default - adda
186
187
188 Test applying multiple patches with --exact
189
190 $ cd ..
191 $ hg clone -r1 repo-options repo-multi2
192 adding changesets
193 adding manifests
194 adding file changes
195 added 2 changesets with 2 changes to 1 files
196 updating to branch foo
197 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 $ cd repo-multi2
199 $ hg import --bypass --exact ../patch1.diff ../patch2.diff
200 applying ../patch1.diff
201 applying ../patch2.diff
202 applied 16581080145e
203 $ shortlog
204 o 3:d60cb8989666 test 0 0 - foo - addf
205 |
206 | o 2:16581080145e test 0 0 - default - adde
207 | |
208 @ | 1:4e322f7ce8e3 test 0 0 - foo - changea
209 |/
210 o 0:07f494440405 test 0 0 - default - adda
211
212
213 $ cd ..
214
215 Test complicated patch with --exact
216
217 $ hg init repo-exact
218 $ cd repo-exact
219 $ echo a > a
220 $ echo c > c
221 $ echo d > d
222 $ echo e > e
223 $ echo f > f
224 $ chmod +x f
225 $ ln -s c linkc
226 $ hg ci -Am t
227 adding a
228 adding c
229 adding d
230 adding e
231 adding f
232 adding linkc
233 $ hg cp a aa1
234 $ echo b >> a
235 $ echo b > b
236 $ hg add b
237 $ hg cp a aa2
238 $ echo aa >> aa2
239 $ chmod +x e
240 $ chmod -x f
241 $ ln -s a linka
242 $ hg rm d
243 $ hg rm linkc
244 $ hg mv c cc
245 $ hg ci -m patch
246 $ hg export --git . > ../test.diff
247 $ hg up -C null
248 0 files updated, 0 files merged, 7 files removed, 0 files unresolved
249 $ hg purge
250 $ hg st
251 $ hg import --bypass --exact ../test.diff
252 applying ../test.diff
253
254 The patch should have matched the exported revision and generated no additional
255 data. If not, diff both heads to debug it.
256
257 $ shortlog
258 o 1:2978fd5c8aa4 test 0 0 - default - patch
259 |
260 o 0:a0e19e636a43 test 0 0 - default - t
261
@@ -1,5081 +1,5123 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset, fileset
17 17 import dagparser, context, simplemerge
18 18 import random, setdiscovery, treediscovery, dagutil
19 19
20 20 table = {}
21 21
22 22 command = cmdutil.command(table)
23 23
24 24 # common command options
25 25
26 26 globalopts = [
27 27 ('R', 'repository', '',
28 28 _('repository root directory or name of overlay bundle file'),
29 29 _('REPO')),
30 30 ('', 'cwd', '',
31 31 _('change working directory'), _('DIR')),
32 32 ('y', 'noninteractive', None,
33 33 _('do not prompt, assume \'yes\' for any required answers')),
34 34 ('q', 'quiet', None, _('suppress output')),
35 35 ('v', 'verbose', None, _('enable additional output')),
36 36 ('', 'config', [],
37 37 _('set/override config option (use \'section.name=value\')'),
38 38 _('CONFIG')),
39 39 ('', 'debug', None, _('enable debugging output')),
40 40 ('', 'debugger', None, _('start debugger')),
41 41 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
42 42 _('ENCODE')),
43 43 ('', 'encodingmode', encoding.encodingmode,
44 44 _('set the charset encoding mode'), _('MODE')),
45 45 ('', 'traceback', None, _('always print a traceback on exception')),
46 46 ('', 'time', None, _('time how long the command takes')),
47 47 ('', 'profile', None, _('print command execution profile')),
48 48 ('', 'version', None, _('output version information and exit')),
49 49 ('h', 'help', None, _('display help and exit')),
50 50 ]
51 51
52 52 dryrunopts = [('n', 'dry-run', None,
53 53 _('do not perform actions, just print output'))]
54 54
55 55 remoteopts = [
56 56 ('e', 'ssh', '',
57 57 _('specify ssh command to use'), _('CMD')),
58 58 ('', 'remotecmd', '',
59 59 _('specify hg command to run on the remote side'), _('CMD')),
60 60 ('', 'insecure', None,
61 61 _('do not verify server certificate (ignoring web.cacerts config)')),
62 62 ]
63 63
64 64 walkopts = [
65 65 ('I', 'include', [],
66 66 _('include names matching the given patterns'), _('PATTERN')),
67 67 ('X', 'exclude', [],
68 68 _('exclude names matching the given patterns'), _('PATTERN')),
69 69 ]
70 70
71 71 commitopts = [
72 72 ('m', 'message', '',
73 73 _('use text as commit message'), _('TEXT')),
74 74 ('l', 'logfile', '',
75 75 _('read commit message from file'), _('FILE')),
76 76 ]
77 77
78 78 commitopts2 = [
79 79 ('d', 'date', '',
80 80 _('record the specified date as commit date'), _('DATE')),
81 81 ('u', 'user', '',
82 82 _('record the specified user as committer'), _('USER')),
83 83 ]
84 84
85 85 templateopts = [
86 86 ('', 'style', '',
87 87 _('display using template map file'), _('STYLE')),
88 88 ('', 'template', '',
89 89 _('display with template'), _('TEMPLATE')),
90 90 ]
91 91
92 92 logopts = [
93 93 ('p', 'patch', None, _('show patch')),
94 94 ('g', 'git', None, _('use git extended diff format')),
95 95 ('l', 'limit', '',
96 96 _('limit number of changes displayed'), _('NUM')),
97 97 ('M', 'no-merges', None, _('do not show merges')),
98 98 ('', 'stat', None, _('output diffstat-style summary of changes')),
99 99 ] + templateopts
100 100
101 101 diffopts = [
102 102 ('a', 'text', None, _('treat all files as text')),
103 103 ('g', 'git', None, _('use git extended diff format')),
104 104 ('', 'nodates', None, _('omit dates from diff headers'))
105 105 ]
106 106
107 107 diffopts2 = [
108 108 ('p', 'show-function', None, _('show which function each change is in')),
109 109 ('', 'reverse', None, _('produce a diff that undoes the changes')),
110 110 ('w', 'ignore-all-space', None,
111 111 _('ignore white space when comparing lines')),
112 112 ('b', 'ignore-space-change', None,
113 113 _('ignore changes in the amount of white space')),
114 114 ('B', 'ignore-blank-lines', None,
115 115 _('ignore changes whose lines are all blank')),
116 116 ('U', 'unified', '',
117 117 _('number of lines of context to show'), _('NUM')),
118 118 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 119 ]
120 120
121 121 similarityopts = [
122 122 ('s', 'similarity', '',
123 123 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
124 124 ]
125 125
126 126 subrepoopts = [
127 127 ('S', 'subrepos', None,
128 128 _('recurse into subrepositories'))
129 129 ]
130 130
131 131 # Commands start here, listed alphabetically
132 132
133 133 @command('^add',
134 134 walkopts + subrepoopts + dryrunopts,
135 135 _('[OPTION]... [FILE]...'))
136 136 def add(ui, repo, *pats, **opts):
137 137 """add the specified files on the next commit
138 138
139 139 Schedule files to be version controlled and added to the
140 140 repository.
141 141
142 142 The files will be added to the repository at the next commit. To
143 143 undo an add before that, see :hg:`forget`.
144 144
145 145 If no names are given, add all files to the repository.
146 146
147 147 .. container:: verbose
148 148
149 149 An example showing how new (unknown) files are added
150 150 automatically by :hg:`add`::
151 151
152 152 $ ls
153 153 foo.c
154 154 $ hg status
155 155 ? foo.c
156 156 $ hg add
157 157 adding foo.c
158 158 $ hg status
159 159 A foo.c
160 160
161 161 Returns 0 if all files are successfully added.
162 162 """
163 163
164 164 m = scmutil.match(repo, pats, opts)
165 165 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
166 166 opts.get('subrepos'), prefix="")
167 167 return rejected and 1 or 0
168 168
169 169 @command('addremove',
170 170 similarityopts + walkopts + dryrunopts,
171 171 _('[OPTION]... [FILE]...'))
172 172 def addremove(ui, repo, *pats, **opts):
173 173 """add all new files, delete all missing files
174 174
175 175 Add all new files and remove all missing files from the
176 176 repository.
177 177
178 178 New files are ignored if they match any of the patterns in
179 179 ``.hgignore``. As with add, these changes take effect at the next
180 180 commit.
181 181
182 182 Use the -s/--similarity option to detect renamed files. With a
183 183 parameter greater than 0, this compares every removed file with
184 184 every added file and records those similar enough as renames. This
185 185 option takes a percentage between 0 (disabled) and 100 (files must
186 186 be identical) as its parameter. Detecting renamed files this way
187 187 can be expensive. After using this option, :hg:`status -C` can be
188 188 used to check which files were identified as moved or renamed.
189 189
190 190 Returns 0 if all files are successfully added.
191 191 """
192 192 try:
193 193 sim = float(opts.get('similarity') or 100)
194 194 except ValueError:
195 195 raise util.Abort(_('similarity must be a number'))
196 196 if sim < 0 or sim > 100:
197 197 raise util.Abort(_('similarity must be between 0 and 100'))
198 198 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
199 199
200 200 @command('^annotate|blame',
201 201 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
202 202 ('', 'follow', None,
203 203 _('follow copies/renames and list the filename (DEPRECATED)')),
204 204 ('', 'no-follow', None, _("don't follow copies and renames")),
205 205 ('a', 'text', None, _('treat all files as text')),
206 206 ('u', 'user', None, _('list the author (long with -v)')),
207 207 ('f', 'file', None, _('list the filename')),
208 208 ('d', 'date', None, _('list the date (short with -q)')),
209 209 ('n', 'number', None, _('list the revision number (default)')),
210 210 ('c', 'changeset', None, _('list the changeset')),
211 211 ('l', 'line-number', None, _('show line number at the first appearance'))
212 212 ] + walkopts,
213 213 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
214 214 def annotate(ui, repo, *pats, **opts):
215 215 """show changeset information by line for each file
216 216
217 217 List changes in files, showing the revision id responsible for
218 218 each line
219 219
220 220 This command is useful for discovering when a change was made and
221 221 by whom.
222 222
223 223 Without the -a/--text option, annotate will avoid processing files
224 224 it detects as binary. With -a, annotate will annotate the file
225 225 anyway, although the results will probably be neither useful
226 226 nor desirable.
227 227
228 228 Returns 0 on success.
229 229 """
230 230 if opts.get('follow'):
231 231 # --follow is deprecated and now just an alias for -f/--file
232 232 # to mimic the behavior of Mercurial before version 1.5
233 233 opts['file'] = True
234 234
235 235 datefunc = ui.quiet and util.shortdate or util.datestr
236 236 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
237 237
238 238 if not pats:
239 239 raise util.Abort(_('at least one filename or pattern is required'))
240 240
241 241 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
242 242 ('number', ' ', lambda x: str(x[0].rev())),
243 243 ('changeset', ' ', lambda x: short(x[0].node())),
244 244 ('date', ' ', getdate),
245 245 ('file', ' ', lambda x: x[0].path()),
246 246 ('line_number', ':', lambda x: str(x[1])),
247 247 ]
248 248
249 249 if (not opts.get('user') and not opts.get('changeset')
250 250 and not opts.get('date') and not opts.get('file')):
251 251 opts['number'] = True
252 252
253 253 linenumber = opts.get('line_number') is not None
254 254 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
255 255 raise util.Abort(_('at least one of -n/-c is required for -l'))
256 256
257 257 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
258 258 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
259 259
260 260 def bad(x, y):
261 261 raise util.Abort("%s: %s" % (x, y))
262 262
263 263 ctx = scmutil.revsingle(repo, opts.get('rev'))
264 264 m = scmutil.match(repo, pats, opts)
265 265 m.bad = bad
266 266 follow = not opts.get('no_follow')
267 267 for abs in ctx.walk(m):
268 268 fctx = ctx[abs]
269 269 if not opts.get('text') and util.binary(fctx.data()):
270 270 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
271 271 continue
272 272
273 273 lines = fctx.annotate(follow=follow, linenumber=linenumber)
274 274 pieces = []
275 275
276 276 for f, sep in funcmap:
277 277 l = [f(n) for n, dummy in lines]
278 278 if l:
279 279 sized = [(x, encoding.colwidth(x)) for x in l]
280 280 ml = max([w for x, w in sized])
281 281 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
282 282 for x, w in sized])
283 283
284 284 if pieces:
285 285 for p, l in zip(zip(*pieces), lines):
286 286 ui.write("%s: %s" % ("".join(p), l[1]))
287 287
288 288 @command('archive',
289 289 [('', 'no-decode', None, _('do not pass files through decoders')),
290 290 ('p', 'prefix', '', _('directory prefix for files in archive'),
291 291 _('PREFIX')),
292 292 ('r', 'rev', '', _('revision to distribute'), _('REV')),
293 293 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
294 294 ] + subrepoopts + walkopts,
295 295 _('[OPTION]... DEST'))
296 296 def archive(ui, repo, dest, **opts):
297 297 '''create an unversioned archive of a repository revision
298 298
299 299 By default, the revision used is the parent of the working
300 300 directory; use -r/--rev to specify a different revision.
301 301
302 302 The archive type is automatically detected based on file
303 303 extension (or override using -t/--type).
304 304
305 305 Valid types are:
306 306
307 307 :``files``: a directory full of files (default)
308 308 :``tar``: tar archive, uncompressed
309 309 :``tbz2``: tar archive, compressed using bzip2
310 310 :``tgz``: tar archive, compressed using gzip
311 311 :``uzip``: zip archive, uncompressed
312 312 :``zip``: zip archive, compressed using deflate
313 313
314 314 The exact name of the destination archive or directory is given
315 315 using a format string; see :hg:`help export` for details.
316 316
317 317 Each member added to an archive file has a directory prefix
318 318 prepended. Use -p/--prefix to specify a format string for the
319 319 prefix. The default is the basename of the archive, with suffixes
320 320 removed.
321 321
322 322 Returns 0 on success.
323 323 '''
324 324
325 325 ctx = scmutil.revsingle(repo, opts.get('rev'))
326 326 if not ctx:
327 327 raise util.Abort(_('no working directory: please specify a revision'))
328 328 node = ctx.node()
329 329 dest = cmdutil.makefilename(repo, dest, node)
330 330 if os.path.realpath(dest) == repo.root:
331 331 raise util.Abort(_('repository root cannot be destination'))
332 332
333 333 kind = opts.get('type') or archival.guesskind(dest) or 'files'
334 334 prefix = opts.get('prefix')
335 335
336 336 if dest == '-':
337 337 if kind == 'files':
338 338 raise util.Abort(_('cannot archive plain files to stdout'))
339 339 dest = sys.stdout
340 340 if not prefix:
341 341 prefix = os.path.basename(repo.root) + '-%h'
342 342
343 343 prefix = cmdutil.makefilename(repo, prefix, node)
344 344 matchfn = scmutil.match(repo, [], opts)
345 345 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
346 346 matchfn, prefix, subrepos=opts.get('subrepos'))
347 347
348 348 @command('backout',
349 349 [('', 'merge', None, _('merge with old dirstate parent after backout')),
350 350 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
351 351 ('t', 'tool', '', _('specify merge tool')),
352 352 ('r', 'rev', '', _('revision to backout'), _('REV')),
353 353 ] + walkopts + commitopts + commitopts2,
354 354 _('[OPTION]... [-r] REV'))
355 355 def backout(ui, repo, node=None, rev=None, **opts):
356 356 '''reverse effect of earlier changeset
357 357
358 358 Prepare a new changeset with the effect of REV undone in the
359 359 current working directory.
360 360
361 361 If REV is the parent of the working directory, then this new changeset
362 362 is committed automatically. Otherwise, hg needs to merge the
363 363 changes and the merged result is left uncommitted.
364 364
365 365 By default, the pending changeset will have one parent,
366 366 maintaining a linear history. With --merge, the pending changeset
367 367 will instead have two parents: the old parent of the working
368 368 directory and a new child of REV that simply undoes REV.
369 369
370 370 Before version 1.7, the behavior without --merge was equivalent to
371 371 specifying --merge followed by :hg:`update --clean .` to cancel
372 372 the merge and leave the child of REV as a head to be merged
373 373 separately.
374 374
375 375 See :hg:`help dates` for a list of formats valid for -d/--date.
376 376
377 377 Returns 0 on success.
378 378 '''
379 379 if rev and node:
380 380 raise util.Abort(_("please specify just one revision"))
381 381
382 382 if not rev:
383 383 rev = node
384 384
385 385 if not rev:
386 386 raise util.Abort(_("please specify a revision to backout"))
387 387
388 388 date = opts.get('date')
389 389 if date:
390 390 opts['date'] = util.parsedate(date)
391 391
392 392 cmdutil.bailifchanged(repo)
393 393 node = scmutil.revsingle(repo, rev).node()
394 394
395 395 op1, op2 = repo.dirstate.parents()
396 396 a = repo.changelog.ancestor(op1, node)
397 397 if a != node:
398 398 raise util.Abort(_('cannot backout change on a different branch'))
399 399
400 400 p1, p2 = repo.changelog.parents(node)
401 401 if p1 == nullid:
402 402 raise util.Abort(_('cannot backout a change with no parents'))
403 403 if p2 != nullid:
404 404 if not opts.get('parent'):
405 405 raise util.Abort(_('cannot backout a merge changeset without '
406 406 '--parent'))
407 407 p = repo.lookup(opts['parent'])
408 408 if p not in (p1, p2):
409 409 raise util.Abort(_('%s is not a parent of %s') %
410 410 (short(p), short(node)))
411 411 parent = p
412 412 else:
413 413 if opts.get('parent'):
414 414 raise util.Abort(_('cannot use --parent on non-merge changeset'))
415 415 parent = p1
416 416
417 417 # the backout should appear on the same branch
418 418 branch = repo.dirstate.branch()
419 419 hg.clean(repo, node, show_stats=False)
420 420 repo.dirstate.setbranch(branch)
421 421 revert_opts = opts.copy()
422 422 revert_opts['date'] = None
423 423 revert_opts['all'] = True
424 424 revert_opts['rev'] = hex(parent)
425 425 revert_opts['no_backup'] = None
426 426 revert(ui, repo, **revert_opts)
427 427 if not opts.get('merge') and op1 != node:
428 428 try:
429 429 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
430 430 return hg.update(repo, op1)
431 431 finally:
432 432 ui.setconfig('ui', 'forcemerge', '')
433 433
434 434 commit_opts = opts.copy()
435 435 commit_opts['addremove'] = False
436 436 if not commit_opts['message'] and not commit_opts['logfile']:
437 437 # we don't translate commit messages
438 438 commit_opts['message'] = "Backed out changeset %s" % short(node)
439 439 commit_opts['force_editor'] = True
440 440 commit(ui, repo, **commit_opts)
441 441 def nice(node):
442 442 return '%d:%s' % (repo.changelog.rev(node), short(node))
443 443 ui.status(_('changeset %s backs out changeset %s\n') %
444 444 (nice(repo.changelog.tip()), nice(node)))
445 445 if opts.get('merge') and op1 != node:
446 446 hg.clean(repo, op1, show_stats=False)
447 447 ui.status(_('merging with changeset %s\n')
448 448 % nice(repo.changelog.tip()))
449 449 try:
450 450 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
451 451 return hg.merge(repo, hex(repo.changelog.tip()))
452 452 finally:
453 453 ui.setconfig('ui', 'forcemerge', '')
454 454 return 0
455 455
456 456 @command('bisect',
457 457 [('r', 'reset', False, _('reset bisect state')),
458 458 ('g', 'good', False, _('mark changeset good')),
459 459 ('b', 'bad', False, _('mark changeset bad')),
460 460 ('s', 'skip', False, _('skip testing changeset')),
461 461 ('e', 'extend', False, _('extend the bisect range')),
462 462 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
463 463 ('U', 'noupdate', False, _('do not update to target'))],
464 464 _("[-gbsr] [-U] [-c CMD] [REV]"))
465 465 def bisect(ui, repo, rev=None, extra=None, command=None,
466 466 reset=None, good=None, bad=None, skip=None, extend=None,
467 467 noupdate=None):
468 468 """subdivision search of changesets
469 469
470 470 This command helps to find changesets which introduce problems. To
471 471 use, mark the earliest changeset you know exhibits the problem as
472 472 bad, then mark the latest changeset which is free from the problem
473 473 as good. Bisect will update your working directory to a revision
474 474 for testing (unless the -U/--noupdate option is specified). Once
475 475 you have performed tests, mark the working directory as good or
476 476 bad, and bisect will either update to another candidate changeset
477 477 or announce that it has found the bad revision.
478 478
479 479 As a shortcut, you can also use the revision argument to mark a
480 480 revision as good or bad without checking it out first.
481 481
482 482 If you supply a command, it will be used for automatic bisection.
483 483 Its exit status will be used to mark revisions as good or bad:
484 484 status 0 means good, 125 means to skip the revision, 127
485 485 (command not found) will abort the bisection, and any other
486 486 non-zero exit status means the revision is bad.
487 487
488 488 Returns 0 on success.
489 489 """
490 490 def extendbisectrange(nodes, good):
491 491 # bisect is incomplete when it ends on a merge node and
492 492 # one of the parent was not checked.
493 493 parents = repo[nodes[0]].parents()
494 494 if len(parents) > 1:
495 495 side = good and state['bad'] or state['good']
496 496 num = len(set(i.node() for i in parents) & set(side))
497 497 if num == 1:
498 498 return parents[0].ancestor(parents[1])
499 499 return None
500 500
501 501 def print_result(nodes, good):
502 502 displayer = cmdutil.show_changeset(ui, repo, {})
503 503 if len(nodes) == 1:
504 504 # narrowed it down to a single revision
505 505 if good:
506 506 ui.write(_("The first good revision is:\n"))
507 507 else:
508 508 ui.write(_("The first bad revision is:\n"))
509 509 displayer.show(repo[nodes[0]])
510 510 extendnode = extendbisectrange(nodes, good)
511 511 if extendnode is not None:
512 512 ui.write(_('Not all ancestors of this changeset have been'
513 513 ' checked.\nUse bisect --extend to continue the '
514 514 'bisection from\nthe common ancestor, %s.\n')
515 515 % extendnode)
516 516 else:
517 517 # multiple possible revisions
518 518 if good:
519 519 ui.write(_("Due to skipped revisions, the first "
520 520 "good revision could be any of:\n"))
521 521 else:
522 522 ui.write(_("Due to skipped revisions, the first "
523 523 "bad revision could be any of:\n"))
524 524 for n in nodes:
525 525 displayer.show(repo[n])
526 526 displayer.close()
527 527
528 528 def check_state(state, interactive=True):
529 529 if not state['good'] or not state['bad']:
530 530 if (good or bad or skip or reset) and interactive:
531 531 return
532 532 if not state['good']:
533 533 raise util.Abort(_('cannot bisect (no known good revisions)'))
534 534 else:
535 535 raise util.Abort(_('cannot bisect (no known bad revisions)'))
536 536 return True
537 537
538 538 # backward compatibility
539 539 if rev in "good bad reset init".split():
540 540 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
541 541 cmd, rev, extra = rev, extra, None
542 542 if cmd == "good":
543 543 good = True
544 544 elif cmd == "bad":
545 545 bad = True
546 546 else:
547 547 reset = True
548 548 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
549 549 raise util.Abort(_('incompatible arguments'))
550 550
551 551 if reset:
552 552 p = repo.join("bisect.state")
553 553 if os.path.exists(p):
554 554 os.unlink(p)
555 555 return
556 556
557 557 state = hbisect.load_state(repo)
558 558
559 559 if command:
560 560 changesets = 1
561 561 try:
562 562 while changesets:
563 563 # update state
564 564 status = util.system(command)
565 565 if status == 125:
566 566 transition = "skip"
567 567 elif status == 0:
568 568 transition = "good"
569 569 # status < 0 means process was killed
570 570 elif status == 127:
571 571 raise util.Abort(_("failed to execute %s") % command)
572 572 elif status < 0:
573 573 raise util.Abort(_("%s killed") % command)
574 574 else:
575 575 transition = "bad"
576 576 ctx = scmutil.revsingle(repo, rev)
577 577 rev = None # clear for future iterations
578 578 state[transition].append(ctx.node())
579 579 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
580 580 check_state(state, interactive=False)
581 581 # bisect
582 582 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
583 583 # update to next check
584 584 cmdutil.bailifchanged(repo)
585 585 hg.clean(repo, nodes[0], show_stats=False)
586 586 finally:
587 587 hbisect.save_state(repo, state)
588 588 print_result(nodes, good)
589 589 return
590 590
591 591 # update state
592 592
593 593 if rev:
594 594 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
595 595 else:
596 596 nodes = [repo.lookup('.')]
597 597
598 598 if good or bad or skip:
599 599 if good:
600 600 state['good'] += nodes
601 601 elif bad:
602 602 state['bad'] += nodes
603 603 elif skip:
604 604 state['skip'] += nodes
605 605 hbisect.save_state(repo, state)
606 606
607 607 if not check_state(state):
608 608 return
609 609
610 610 # actually bisect
611 611 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
612 612 if extend:
613 613 if not changesets:
614 614 extendnode = extendbisectrange(nodes, good)
615 615 if extendnode is not None:
616 616 ui.write(_("Extending search to changeset %d:%s\n"
617 617 % (extendnode.rev(), extendnode)))
618 618 if noupdate:
619 619 return
620 620 cmdutil.bailifchanged(repo)
621 621 return hg.clean(repo, extendnode.node())
622 622 raise util.Abort(_("nothing to extend"))
623 623
624 624 if changesets == 0:
625 625 print_result(nodes, good)
626 626 else:
627 627 assert len(nodes) == 1 # only a single node can be tested next
628 628 node = nodes[0]
629 629 # compute the approximate number of remaining tests
630 630 tests, size = 0, 2
631 631 while size <= changesets:
632 632 tests, size = tests + 1, size * 2
633 633 rev = repo.changelog.rev(node)
634 634 ui.write(_("Testing changeset %d:%s "
635 635 "(%d changesets remaining, ~%d tests)\n")
636 636 % (rev, short(node), changesets, tests))
637 637 if not noupdate:
638 638 cmdutil.bailifchanged(repo)
639 639 return hg.clean(repo, node)
640 640
641 641 @command('bookmarks',
642 642 [('f', 'force', False, _('force')),
643 643 ('r', 'rev', '', _('revision'), _('REV')),
644 644 ('d', 'delete', False, _('delete a given bookmark')),
645 645 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
646 646 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
647 647 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
648 648 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
649 649 rename=None, inactive=False):
650 650 '''track a line of development with movable markers
651 651
652 652 Bookmarks are pointers to certain commits that move when
653 653 committing. Bookmarks are local. They can be renamed, copied and
654 654 deleted. It is possible to use bookmark names in :hg:`merge` and
655 655 :hg:`update` to merge and update respectively to a given bookmark.
656 656
657 657 You can use :hg:`bookmark NAME` to set a bookmark on the working
658 658 directory's parent revision with the given name. If you specify
659 659 a revision using -r REV (where REV may be an existing bookmark),
660 660 the bookmark is assigned to that revision.
661 661
662 662 Bookmarks can be pushed and pulled between repositories (see :hg:`help
663 663 push` and :hg:`help pull`). This requires both the local and remote
664 664 repositories to support bookmarks. For versions prior to 1.8, this means
665 665 the bookmarks extension must be enabled.
666 666 '''
667 667 hexfn = ui.debugflag and hex or short
668 668 marks = repo._bookmarks
669 669 cur = repo.changectx('.').node()
670 670
671 671 if rename:
672 672 if rename not in marks:
673 673 raise util.Abort(_("bookmark '%s' does not exist") % rename)
674 674 if mark in marks and not force:
675 675 raise util.Abort(_("bookmark '%s' already exists "
676 676 "(use -f to force)") % mark)
677 677 if mark is None:
678 678 raise util.Abort(_("new bookmark name required"))
679 679 marks[mark] = marks[rename]
680 680 if repo._bookmarkcurrent == rename and not inactive:
681 681 bookmarks.setcurrent(repo, mark)
682 682 del marks[rename]
683 683 bookmarks.write(repo)
684 684 return
685 685
686 686 if delete:
687 687 if mark is None:
688 688 raise util.Abort(_("bookmark name required"))
689 689 if mark not in marks:
690 690 raise util.Abort(_("bookmark '%s' does not exist") % mark)
691 691 if mark == repo._bookmarkcurrent:
692 692 bookmarks.setcurrent(repo, None)
693 693 del marks[mark]
694 694 bookmarks.write(repo)
695 695 return
696 696
697 697 if mark is not None:
698 698 if "\n" in mark:
699 699 raise util.Abort(_("bookmark name cannot contain newlines"))
700 700 mark = mark.strip()
701 701 if not mark:
702 702 raise util.Abort(_("bookmark names cannot consist entirely of "
703 703 "whitespace"))
704 704 if inactive and mark == repo._bookmarkcurrent:
705 705 bookmarks.setcurrent(repo, None)
706 706 return
707 707 if mark in marks and not force:
708 708 raise util.Abort(_("bookmark '%s' already exists "
709 709 "(use -f to force)") % mark)
710 710 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
711 711 and not force):
712 712 raise util.Abort(
713 713 _("a bookmark cannot have the name of an existing branch"))
714 714 if rev:
715 715 marks[mark] = repo.lookup(rev)
716 716 else:
717 717 marks[mark] = repo.changectx('.').node()
718 718 if not inactive and repo.changectx('.').node() == marks[mark]:
719 719 bookmarks.setcurrent(repo, mark)
720 720 bookmarks.write(repo)
721 721 return
722 722
723 723 if mark is None:
724 724 if rev:
725 725 raise util.Abort(_("bookmark name required"))
726 726 if len(marks) == 0:
727 727 ui.status(_("no bookmarks set\n"))
728 728 else:
729 729 for bmark, n in sorted(marks.iteritems()):
730 730 current = repo._bookmarkcurrent
731 731 if bmark == current and n == cur:
732 732 prefix, label = '*', 'bookmarks.current'
733 733 else:
734 734 prefix, label = ' ', ''
735 735
736 736 if ui.quiet:
737 737 ui.write("%s\n" % bmark, label=label)
738 738 else:
739 739 ui.write(" %s %-25s %d:%s\n" % (
740 740 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
741 741 label=label)
742 742 return
743 743
744 744 @command('branch',
745 745 [('f', 'force', None,
746 746 _('set branch name even if it shadows an existing branch')),
747 747 ('C', 'clean', None, _('reset branch name to parent branch name'))],
748 748 _('[-fC] [NAME]'))
749 749 def branch(ui, repo, label=None, **opts):
750 750 """set or show the current branch name
751 751
752 752 With no argument, show the current branch name. With one argument,
753 753 set the working directory branch name (the branch will not exist
754 754 in the repository until the next commit). Standard practice
755 755 recommends that primary development take place on the 'default'
756 756 branch.
757 757
758 758 Unless -f/--force is specified, branch will not let you set a
759 759 branch name that already exists, even if it's inactive.
760 760
761 761 Use -C/--clean to reset the working directory branch to that of
762 762 the parent of the working directory, negating a previous branch
763 763 change.
764 764
765 765 Use the command :hg:`update` to switch to an existing branch. Use
766 766 :hg:`commit --close-branch` to mark this branch as closed.
767 767
768 768 Returns 0 on success.
769 769 """
770 770
771 771 if opts.get('clean'):
772 772 label = repo[None].p1().branch()
773 773 repo.dirstate.setbranch(label)
774 774 ui.status(_('reset working directory to branch %s\n') % label)
775 775 elif label:
776 776 if not opts.get('force') and label in repo.branchtags():
777 777 if label not in [p.branch() for p in repo.parents()]:
778 778 raise util.Abort(_('a branch of the same name already exists'),
779 779 # i18n: "it" refers to an existing branch
780 780 hint=_("use 'hg update' to switch to it"))
781 781 repo.dirstate.setbranch(label)
782 782 ui.status(_('marked working directory as branch %s\n') % label)
783 783 else:
784 784 ui.write("%s\n" % repo.dirstate.branch())
785 785
786 786 @command('branches',
787 787 [('a', 'active', False, _('show only branches that have unmerged heads')),
788 788 ('c', 'closed', False, _('show normal and closed branches'))],
789 789 _('[-ac]'))
790 790 def branches(ui, repo, active=False, closed=False):
791 791 """list repository named branches
792 792
793 793 List the repository's named branches, indicating which ones are
794 794 inactive. If -c/--closed is specified, also list branches which have
795 795 been marked closed (see :hg:`commit --close-branch`).
796 796
797 797 If -a/--active is specified, only show active branches. A branch
798 798 is considered active if it contains repository heads.
799 799
800 800 Use the command :hg:`update` to switch to an existing branch.
801 801
802 802 Returns 0.
803 803 """
804 804
805 805 hexfunc = ui.debugflag and hex or short
806 806 activebranches = [repo[n].branch() for n in repo.heads()]
807 807 def testactive(tag, node):
808 808 realhead = tag in activebranches
809 809 open = node in repo.branchheads(tag, closed=False)
810 810 return realhead and open
811 811 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
812 812 for tag, node in repo.branchtags().items()],
813 813 reverse=True)
814 814
815 815 for isactive, node, tag in branches:
816 816 if (not active) or isactive:
817 817 if ui.quiet:
818 818 ui.write("%s\n" % tag)
819 819 else:
820 820 hn = repo.lookup(node)
821 821 if isactive:
822 822 label = 'branches.active'
823 823 notice = ''
824 824 elif hn not in repo.branchheads(tag, closed=False):
825 825 if not closed:
826 826 continue
827 827 label = 'branches.closed'
828 828 notice = _(' (closed)')
829 829 else:
830 830 label = 'branches.inactive'
831 831 notice = _(' (inactive)')
832 832 if tag == repo.dirstate.branch():
833 833 label = 'branches.current'
834 834 rev = str(node).rjust(31 - encoding.colwidth(tag))
835 835 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
836 836 tag = ui.label(tag, label)
837 837 ui.write("%s %s%s\n" % (tag, rev, notice))
838 838
839 839 @command('bundle',
840 840 [('f', 'force', None, _('run even when the destination is unrelated')),
841 841 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
842 842 _('REV')),
843 843 ('b', 'branch', [], _('a specific branch you would like to bundle'),
844 844 _('BRANCH')),
845 845 ('', 'base', [],
846 846 _('a base changeset assumed to be available at the destination'),
847 847 _('REV')),
848 848 ('a', 'all', None, _('bundle all changesets in the repository')),
849 849 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
850 850 ] + remoteopts,
851 851 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
852 852 def bundle(ui, repo, fname, dest=None, **opts):
853 853 """create a changegroup file
854 854
855 855 Generate a compressed changegroup file collecting changesets not
856 856 known to be in another repository.
857 857
858 858 If you omit the destination repository, then hg assumes the
859 859 destination will have all the nodes you specify with --base
860 860 parameters. To create a bundle containing all changesets, use
861 861 -a/--all (or --base null).
862 862
863 863 You can change compression method with the -t/--type option.
864 864 The available compression methods are: none, bzip2, and
865 865 gzip (by default, bundles are compressed using bzip2).
866 866
867 867 The bundle file can then be transferred using conventional means
868 868 and applied to another repository with the unbundle or pull
869 869 command. This is useful when direct push and pull are not
870 870 available or when exporting an entire repository is undesirable.
871 871
872 872 Applying bundles preserves all changeset contents including
873 873 permissions, copy/rename information, and revision history.
874 874
875 875 Returns 0 on success, 1 if no changes found.
876 876 """
877 877 revs = None
878 878 if 'rev' in opts:
879 879 revs = scmutil.revrange(repo, opts['rev'])
880 880
881 881 if opts.get('all'):
882 882 base = ['null']
883 883 else:
884 884 base = scmutil.revrange(repo, opts.get('base'))
885 885 if base:
886 886 if dest:
887 887 raise util.Abort(_("--base is incompatible with specifying "
888 888 "a destination"))
889 889 common = [repo.lookup(rev) for rev in base]
890 890 heads = revs and map(repo.lookup, revs) or revs
891 891 else:
892 892 dest = ui.expandpath(dest or 'default-push', dest or 'default')
893 893 dest, branches = hg.parseurl(dest, opts.get('branch'))
894 894 other = hg.peer(repo, opts, dest)
895 895 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
896 896 heads = revs and map(repo.lookup, revs) or revs
897 897 common, outheads = discovery.findcommonoutgoing(repo, other,
898 898 onlyheads=heads,
899 899 force=opts.get('force'))
900 900
901 901 cg = repo.getbundle('bundle', common=common, heads=heads)
902 902 if not cg:
903 903 ui.status(_("no changes found\n"))
904 904 return 1
905 905
906 906 bundletype = opts.get('type', 'bzip2').lower()
907 907 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
908 908 bundletype = btypes.get(bundletype)
909 909 if bundletype not in changegroup.bundletypes:
910 910 raise util.Abort(_('unknown bundle type specified with --type'))
911 911
912 912 changegroup.writebundle(cg, fname, bundletype)
913 913
914 914 @command('cat',
915 915 [('o', 'output', '',
916 916 _('print output to file with formatted name'), _('FORMAT')),
917 917 ('r', 'rev', '', _('print the given revision'), _('REV')),
918 918 ('', 'decode', None, _('apply any matching decode filter')),
919 919 ] + walkopts,
920 920 _('[OPTION]... FILE...'))
921 921 def cat(ui, repo, file1, *pats, **opts):
922 922 """output the current or given revision of files
923 923
924 924 Print the specified files as they were at the given revision. If
925 925 no revision is given, the parent of the working directory is used,
926 926 or tip if no revision is checked out.
927 927
928 928 Output may be to a file, in which case the name of the file is
929 929 given using a format string. The formatting rules are the same as
930 930 for the export command, with the following additions:
931 931
932 932 :``%s``: basename of file being printed
933 933 :``%d``: dirname of file being printed, or '.' if in repository root
934 934 :``%p``: root-relative path name of file being printed
935 935
936 936 Returns 0 on success.
937 937 """
938 938 ctx = scmutil.revsingle(repo, opts.get('rev'))
939 939 err = 1
940 940 m = scmutil.match(repo, (file1,) + pats, opts)
941 941 for abs in ctx.walk(m):
942 942 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
943 943 pathname=abs)
944 944 data = ctx[abs].data()
945 945 if opts.get('decode'):
946 946 data = repo.wwritedata(abs, data)
947 947 fp.write(data)
948 948 fp.close()
949 949 err = 0
950 950 return err
951 951
952 952 @command('^clone',
953 953 [('U', 'noupdate', None,
954 954 _('the clone will include an empty working copy (only a repository)')),
955 955 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
956 956 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
957 957 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
958 958 ('', 'pull', None, _('use pull protocol to copy metadata')),
959 959 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
960 960 ] + remoteopts,
961 961 _('[OPTION]... SOURCE [DEST]'))
962 962 def clone(ui, source, dest=None, **opts):
963 963 """make a copy of an existing repository
964 964
965 965 Create a copy of an existing repository in a new directory.
966 966
967 967 If no destination directory name is specified, it defaults to the
968 968 basename of the source.
969 969
970 970 The location of the source is added to the new repository's
971 971 ``.hg/hgrc`` file, as the default to be used for future pulls.
972 972
973 973 See :hg:`help urls` for valid source format details.
974 974
975 975 It is possible to specify an ``ssh://`` URL as the destination, but no
976 976 ``.hg/hgrc`` and working directory will be created on the remote side.
977 977 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
978 978
979 979 A set of changesets (tags, or branch names) to pull may be specified
980 980 by listing each changeset (tag, or branch name) with -r/--rev.
981 981 If -r/--rev is used, the cloned repository will contain only a subset
982 982 of the changesets of the source repository. Only the set of changesets
983 983 defined by all -r/--rev options (including all their ancestors)
984 984 will be pulled into the destination repository.
985 985 No subsequent changesets (including subsequent tags) will be present
986 986 in the destination.
987 987
988 988 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
989 989 local source repositories.
990 990
991 991 For efficiency, hardlinks are used for cloning whenever the source
992 992 and destination are on the same filesystem (note this applies only
993 993 to the repository data, not to the working directory). Some
994 994 filesystems, such as AFS, implement hardlinking incorrectly, but
995 995 do not report errors. In these cases, use the --pull option to
996 996 avoid hardlinking.
997 997
998 998 In some cases, you can clone repositories and the working directory
999 999 using full hardlinks with ::
1000 1000
1001 1001 $ cp -al REPO REPOCLONE
1002 1002
1003 1003 This is the fastest way to clone, but it is not always safe. The
1004 1004 operation is not atomic (making sure REPO is not modified during
1005 1005 the operation is up to you) and you have to make sure your editor
1006 1006 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1007 1007 this is not compatible with certain extensions that place their
1008 1008 metadata under the .hg directory, such as mq.
1009 1009
1010 1010 Mercurial will update the working directory to the first applicable
1011 1011 revision from this list:
1012 1012
1013 1013 a) null if -U or the source repository has no changesets
1014 1014 b) if -u . and the source repository is local, the first parent of
1015 1015 the source repository's working directory
1016 1016 c) the changeset specified with -u (if a branch name, this means the
1017 1017 latest head of that branch)
1018 1018 d) the changeset specified with -r
1019 1019 e) the tipmost head specified with -b
1020 1020 f) the tipmost head specified with the url#branch source syntax
1021 1021 g) the tipmost head of the default branch
1022 1022 h) tip
1023 1023
1024 1024 Returns 0 on success.
1025 1025 """
1026 1026 if opts.get('noupdate') and opts.get('updaterev'):
1027 1027 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1028 1028
1029 1029 r = hg.clone(ui, opts, source, dest,
1030 1030 pull=opts.get('pull'),
1031 1031 stream=opts.get('uncompressed'),
1032 1032 rev=opts.get('rev'),
1033 1033 update=opts.get('updaterev') or not opts.get('noupdate'),
1034 1034 branch=opts.get('branch'))
1035 1035
1036 1036 return r is None
1037 1037
1038 1038 @command('^commit|ci',
1039 1039 [('A', 'addremove', None,
1040 1040 _('mark new/missing files as added/removed before committing')),
1041 1041 ('', 'close-branch', None,
1042 1042 _('mark a branch as closed, hiding it from the branch list')),
1043 1043 ] + walkopts + commitopts + commitopts2,
1044 1044 _('[OPTION]... [FILE]...'))
1045 1045 def commit(ui, repo, *pats, **opts):
1046 1046 """commit the specified files or all outstanding changes
1047 1047
1048 1048 Commit changes to the given files into the repository. Unlike a
1049 1049 centralized SCM, this operation is a local operation. See
1050 1050 :hg:`push` for a way to actively distribute your changes.
1051 1051
1052 1052 If a list of files is omitted, all changes reported by :hg:`status`
1053 1053 will be committed.
1054 1054
1055 1055 If you are committing the result of a merge, do not provide any
1056 1056 filenames or -I/-X filters.
1057 1057
1058 1058 If no commit message is specified, Mercurial starts your
1059 1059 configured editor where you can enter a message. In case your
1060 1060 commit fails, you will find a backup of your message in
1061 1061 ``.hg/last-message.txt``.
1062 1062
1063 1063 See :hg:`help dates` for a list of formats valid for -d/--date.
1064 1064
1065 1065 Returns 0 on success, 1 if nothing changed.
1066 1066 """
1067 1067 extra = {}
1068 1068 if opts.get('close_branch'):
1069 1069 if repo['.'].node() not in repo.branchheads():
1070 1070 # The topo heads set is included in the branch heads set of the
1071 1071 # current branch, so it's sufficient to test branchheads
1072 1072 raise util.Abort(_('can only close branch heads'))
1073 1073 extra['close'] = 1
1074 1074 e = cmdutil.commiteditor
1075 1075 if opts.get('force_editor'):
1076 1076 e = cmdutil.commitforceeditor
1077 1077
1078 1078 def commitfunc(ui, repo, message, match, opts):
1079 1079 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1080 1080 editor=e, extra=extra)
1081 1081
1082 1082 branch = repo[None].branch()
1083 1083 bheads = repo.branchheads(branch)
1084 1084
1085 1085 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1086 1086 if not node:
1087 1087 stat = repo.status(match=scmutil.match(repo, pats, opts))
1088 1088 if stat[3]:
1089 1089 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1090 1090 % len(stat[3]))
1091 1091 else:
1092 1092 ui.status(_("nothing changed\n"))
1093 1093 return 1
1094 1094
1095 1095 ctx = repo[node]
1096 1096 parents = ctx.parents()
1097 1097
1098 1098 if bheads and not [x for x in parents
1099 1099 if x.node() in bheads and x.branch() == branch]:
1100 1100 ui.status(_('created new head\n'))
1101 1101 # The message is not printed for initial roots. For the other
1102 1102 # changesets, it is printed in the following situations:
1103 1103 #
1104 1104 # Par column: for the 2 parents with ...
1105 1105 # N: null or no parent
1106 1106 # B: parent is on another named branch
1107 1107 # C: parent is a regular non head changeset
1108 1108 # H: parent was a branch head of the current branch
1109 1109 # Msg column: whether we print "created new head" message
1110 1110 # In the following, it is assumed that there already exists some
1111 1111 # initial branch heads of the current branch, otherwise nothing is
1112 1112 # printed anyway.
1113 1113 #
1114 1114 # Par Msg Comment
1115 1115 # NN y additional topo root
1116 1116 #
1117 1117 # BN y additional branch root
1118 1118 # CN y additional topo head
1119 1119 # HN n usual case
1120 1120 #
1121 1121 # BB y weird additional branch root
1122 1122 # CB y branch merge
1123 1123 # HB n merge with named branch
1124 1124 #
1125 1125 # CC y additional head from merge
1126 1126 # CH n merge with a head
1127 1127 #
1128 1128 # HH n head merge: head count decreases
1129 1129
1130 1130 if not opts.get('close_branch'):
1131 1131 for r in parents:
1132 1132 if r.extra().get('close') and r.branch() == branch:
1133 1133 ui.status(_('reopening closed branch head %d\n') % r)
1134 1134
1135 1135 if ui.debugflag:
1136 1136 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1137 1137 elif ui.verbose:
1138 1138 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1139 1139
1140 1140 @command('copy|cp',
1141 1141 [('A', 'after', None, _('record a copy that has already occurred')),
1142 1142 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1143 1143 ] + walkopts + dryrunopts,
1144 1144 _('[OPTION]... [SOURCE]... DEST'))
1145 1145 def copy(ui, repo, *pats, **opts):
1146 1146 """mark files as copied for the next commit
1147 1147
1148 1148 Mark dest as having copies of source files. If dest is a
1149 1149 directory, copies are put in that directory. If dest is a file,
1150 1150 the source must be a single file.
1151 1151
1152 1152 By default, this command copies the contents of files as they
1153 1153 exist in the working directory. If invoked with -A/--after, the
1154 1154 operation is recorded, but no copying is performed.
1155 1155
1156 1156 This command takes effect with the next commit. To undo a copy
1157 1157 before that, see :hg:`revert`.
1158 1158
1159 1159 Returns 0 on success, 1 if errors are encountered.
1160 1160 """
1161 1161 wlock = repo.wlock(False)
1162 1162 try:
1163 1163 return cmdutil.copy(ui, repo, pats, opts)
1164 1164 finally:
1165 1165 wlock.release()
1166 1166
1167 1167 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1168 1168 def debugancestor(ui, repo, *args):
1169 1169 """find the ancestor revision of two revisions in a given index"""
1170 1170 if len(args) == 3:
1171 1171 index, rev1, rev2 = args
1172 1172 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1173 1173 lookup = r.lookup
1174 1174 elif len(args) == 2:
1175 1175 if not repo:
1176 1176 raise util.Abort(_("there is no Mercurial repository here "
1177 1177 "(.hg not found)"))
1178 1178 rev1, rev2 = args
1179 1179 r = repo.changelog
1180 1180 lookup = repo.lookup
1181 1181 else:
1182 1182 raise util.Abort(_('either two or three arguments required'))
1183 1183 a = r.ancestor(lookup(rev1), lookup(rev2))
1184 1184 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1185 1185
1186 1186 @command('debugbuilddag',
1187 1187 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1188 1188 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1189 1189 ('n', 'new-file', None, _('add new file at each rev'))],
1190 1190 _('[OPTION]... [TEXT]'))
1191 1191 def debugbuilddag(ui, repo, text=None,
1192 1192 mergeable_file=False,
1193 1193 overwritten_file=False,
1194 1194 new_file=False):
1195 1195 """builds a repo with a given DAG from scratch in the current empty repo
1196 1196
1197 1197 The description of the DAG is read from stdin if not given on the
1198 1198 command line.
1199 1199
1200 1200 Elements:
1201 1201
1202 1202 - "+n" is a linear run of n nodes based on the current default parent
1203 1203 - "." is a single node based on the current default parent
1204 1204 - "$" resets the default parent to null (implied at the start);
1205 1205 otherwise the default parent is always the last node created
1206 1206 - "<p" sets the default parent to the backref p
1207 1207 - "*p" is a fork at parent p, which is a backref
1208 1208 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1209 1209 - "/p2" is a merge of the preceding node and p2
1210 1210 - ":tag" defines a local tag for the preceding node
1211 1211 - "@branch" sets the named branch for subsequent nodes
1212 1212 - "#...\\n" is a comment up to the end of the line
1213 1213
1214 1214 Whitespace between the above elements is ignored.
1215 1215
1216 1216 A backref is either
1217 1217
1218 1218 - a number n, which references the node curr-n, where curr is the current
1219 1219 node, or
1220 1220 - the name of a local tag you placed earlier using ":tag", or
1221 1221 - empty to denote the default parent.
1222 1222
1223 1223 All string valued-elements are either strictly alphanumeric, or must
1224 1224 be enclosed in double quotes ("..."), with "\\" as escape character.
1225 1225 """
1226 1226
1227 1227 if text is None:
1228 1228 ui.status(_("reading DAG from stdin\n"))
1229 1229 text = sys.stdin.read()
1230 1230
1231 1231 cl = repo.changelog
1232 1232 if len(cl) > 0:
1233 1233 raise util.Abort(_('repository is not empty'))
1234 1234
1235 1235 # determine number of revs in DAG
1236 1236 total = 0
1237 1237 for type, data in dagparser.parsedag(text):
1238 1238 if type == 'n':
1239 1239 total += 1
1240 1240
1241 1241 if mergeable_file:
1242 1242 linesperrev = 2
1243 1243 # make a file with k lines per rev
1244 1244 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1245 1245 initialmergedlines.append("")
1246 1246
1247 1247 tags = []
1248 1248
1249 1249 tr = repo.transaction("builddag")
1250 1250 try:
1251 1251
1252 1252 at = -1
1253 1253 atbranch = 'default'
1254 1254 nodeids = []
1255 1255 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1256 1256 for type, data in dagparser.parsedag(text):
1257 1257 if type == 'n':
1258 1258 ui.note('node %s\n' % str(data))
1259 1259 id, ps = data
1260 1260
1261 1261 files = []
1262 1262 fctxs = {}
1263 1263
1264 1264 p2 = None
1265 1265 if mergeable_file:
1266 1266 fn = "mf"
1267 1267 p1 = repo[ps[0]]
1268 1268 if len(ps) > 1:
1269 1269 p2 = repo[ps[1]]
1270 1270 pa = p1.ancestor(p2)
1271 1271 base, local, other = [x[fn].data() for x in pa, p1, p2]
1272 1272 m3 = simplemerge.Merge3Text(base, local, other)
1273 1273 ml = [l.strip() for l in m3.merge_lines()]
1274 1274 ml.append("")
1275 1275 elif at > 0:
1276 1276 ml = p1[fn].data().split("\n")
1277 1277 else:
1278 1278 ml = initialmergedlines
1279 1279 ml[id * linesperrev] += " r%i" % id
1280 1280 mergedtext = "\n".join(ml)
1281 1281 files.append(fn)
1282 1282 fctxs[fn] = context.memfilectx(fn, mergedtext)
1283 1283
1284 1284 if overwritten_file:
1285 1285 fn = "of"
1286 1286 files.append(fn)
1287 1287 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1288 1288
1289 1289 if new_file:
1290 1290 fn = "nf%i" % id
1291 1291 files.append(fn)
1292 1292 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1293 1293 if len(ps) > 1:
1294 1294 if not p2:
1295 1295 p2 = repo[ps[1]]
1296 1296 for fn in p2:
1297 1297 if fn.startswith("nf"):
1298 1298 files.append(fn)
1299 1299 fctxs[fn] = p2[fn]
1300 1300
1301 1301 def fctxfn(repo, cx, path):
1302 1302 return fctxs.get(path)
1303 1303
1304 1304 if len(ps) == 0 or ps[0] < 0:
1305 1305 pars = [None, None]
1306 1306 elif len(ps) == 1:
1307 1307 pars = [nodeids[ps[0]], None]
1308 1308 else:
1309 1309 pars = [nodeids[p] for p in ps]
1310 1310 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1311 1311 date=(id, 0),
1312 1312 user="debugbuilddag",
1313 1313 extra={'branch': atbranch})
1314 1314 nodeid = repo.commitctx(cx)
1315 1315 nodeids.append(nodeid)
1316 1316 at = id
1317 1317 elif type == 'l':
1318 1318 id, name = data
1319 1319 ui.note('tag %s\n' % name)
1320 1320 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1321 1321 elif type == 'a':
1322 1322 ui.note('branch %s\n' % data)
1323 1323 atbranch = data
1324 1324 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1325 1325 tr.close()
1326 1326 finally:
1327 1327 ui.progress(_('building'), None)
1328 1328 tr.release()
1329 1329
1330 1330 if tags:
1331 1331 repo.opener.write("localtags", "".join(tags))
1332 1332
1333 1333 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1334 1334 def debugbundle(ui, bundlepath, all=None, **opts):
1335 1335 """lists the contents of a bundle"""
1336 1336 f = url.open(ui, bundlepath)
1337 1337 try:
1338 1338 gen = changegroup.readbundle(f, bundlepath)
1339 1339 if all:
1340 1340 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1341 1341
1342 1342 def showchunks(named):
1343 1343 ui.write("\n%s\n" % named)
1344 1344 chain = None
1345 1345 while True:
1346 1346 chunkdata = gen.deltachunk(chain)
1347 1347 if not chunkdata:
1348 1348 break
1349 1349 node = chunkdata['node']
1350 1350 p1 = chunkdata['p1']
1351 1351 p2 = chunkdata['p2']
1352 1352 cs = chunkdata['cs']
1353 1353 deltabase = chunkdata['deltabase']
1354 1354 delta = chunkdata['delta']
1355 1355 ui.write("%s %s %s %s %s %s\n" %
1356 1356 (hex(node), hex(p1), hex(p2),
1357 1357 hex(cs), hex(deltabase), len(delta)))
1358 1358 chain = node
1359 1359
1360 1360 chunkdata = gen.changelogheader()
1361 1361 showchunks("changelog")
1362 1362 chunkdata = gen.manifestheader()
1363 1363 showchunks("manifest")
1364 1364 while True:
1365 1365 chunkdata = gen.filelogheader()
1366 1366 if not chunkdata:
1367 1367 break
1368 1368 fname = chunkdata['filename']
1369 1369 showchunks(fname)
1370 1370 else:
1371 1371 chunkdata = gen.changelogheader()
1372 1372 chain = None
1373 1373 while True:
1374 1374 chunkdata = gen.deltachunk(chain)
1375 1375 if not chunkdata:
1376 1376 break
1377 1377 node = chunkdata['node']
1378 1378 ui.write("%s\n" % hex(node))
1379 1379 chain = node
1380 1380 finally:
1381 1381 f.close()
1382 1382
1383 1383 @command('debugcheckstate', [], '')
1384 1384 def debugcheckstate(ui, repo):
1385 1385 """validate the correctness of the current dirstate"""
1386 1386 parent1, parent2 = repo.dirstate.parents()
1387 1387 m1 = repo[parent1].manifest()
1388 1388 m2 = repo[parent2].manifest()
1389 1389 errors = 0
1390 1390 for f in repo.dirstate:
1391 1391 state = repo.dirstate[f]
1392 1392 if state in "nr" and f not in m1:
1393 1393 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1394 1394 errors += 1
1395 1395 if state in "a" and f in m1:
1396 1396 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1397 1397 errors += 1
1398 1398 if state in "m" and f not in m1 and f not in m2:
1399 1399 ui.warn(_("%s in state %s, but not in either manifest\n") %
1400 1400 (f, state))
1401 1401 errors += 1
1402 1402 for f in m1:
1403 1403 state = repo.dirstate[f]
1404 1404 if state not in "nrm":
1405 1405 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1406 1406 errors += 1
1407 1407 if errors:
1408 1408 error = _(".hg/dirstate inconsistent with current parent's manifest")
1409 1409 raise util.Abort(error)
1410 1410
1411 1411 @command('debugcommands', [], _('[COMMAND]'))
1412 1412 def debugcommands(ui, cmd='', *args):
1413 1413 """list all available commands and options"""
1414 1414 for cmd, vals in sorted(table.iteritems()):
1415 1415 cmd = cmd.split('|')[0].strip('^')
1416 1416 opts = ', '.join([i[1] for i in vals[1]])
1417 1417 ui.write('%s: %s\n' % (cmd, opts))
1418 1418
1419 1419 @command('debugcomplete',
1420 1420 [('o', 'options', None, _('show the command options'))],
1421 1421 _('[-o] CMD'))
1422 1422 def debugcomplete(ui, cmd='', **opts):
1423 1423 """returns the completion list associated with the given command"""
1424 1424
1425 1425 if opts.get('options'):
1426 1426 options = []
1427 1427 otables = [globalopts]
1428 1428 if cmd:
1429 1429 aliases, entry = cmdutil.findcmd(cmd, table, False)
1430 1430 otables.append(entry[1])
1431 1431 for t in otables:
1432 1432 for o in t:
1433 1433 if "(DEPRECATED)" in o[3]:
1434 1434 continue
1435 1435 if o[0]:
1436 1436 options.append('-%s' % o[0])
1437 1437 options.append('--%s' % o[1])
1438 1438 ui.write("%s\n" % "\n".join(options))
1439 1439 return
1440 1440
1441 1441 cmdlist = cmdutil.findpossible(cmd, table)
1442 1442 if ui.verbose:
1443 1443 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1444 1444 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1445 1445
1446 1446 @command('debugdag',
1447 1447 [('t', 'tags', None, _('use tags as labels')),
1448 1448 ('b', 'branches', None, _('annotate with branch names')),
1449 1449 ('', 'dots', None, _('use dots for runs')),
1450 1450 ('s', 'spaces', None, _('separate elements by spaces'))],
1451 1451 _('[OPTION]... [FILE [REV]...]'))
1452 1452 def debugdag(ui, repo, file_=None, *revs, **opts):
1453 1453 """format the changelog or an index DAG as a concise textual description
1454 1454
1455 1455 If you pass a revlog index, the revlog's DAG is emitted. If you list
1456 1456 revision numbers, they get labelled in the output as rN.
1457 1457
1458 1458 Otherwise, the changelog DAG of the current repo is emitted.
1459 1459 """
1460 1460 spaces = opts.get('spaces')
1461 1461 dots = opts.get('dots')
1462 1462 if file_:
1463 1463 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1464 1464 revs = set((int(r) for r in revs))
1465 1465 def events():
1466 1466 for r in rlog:
1467 1467 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1468 1468 if r in revs:
1469 1469 yield 'l', (r, "r%i" % r)
1470 1470 elif repo:
1471 1471 cl = repo.changelog
1472 1472 tags = opts.get('tags')
1473 1473 branches = opts.get('branches')
1474 1474 if tags:
1475 1475 labels = {}
1476 1476 for l, n in repo.tags().items():
1477 1477 labels.setdefault(cl.rev(n), []).append(l)
1478 1478 def events():
1479 1479 b = "default"
1480 1480 for r in cl:
1481 1481 if branches:
1482 1482 newb = cl.read(cl.node(r))[5]['branch']
1483 1483 if newb != b:
1484 1484 yield 'a', newb
1485 1485 b = newb
1486 1486 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1487 1487 if tags:
1488 1488 ls = labels.get(r)
1489 1489 if ls:
1490 1490 for l in ls:
1491 1491 yield 'l', (r, l)
1492 1492 else:
1493 1493 raise util.Abort(_('need repo for changelog dag'))
1494 1494
1495 1495 for line in dagparser.dagtextlines(events(),
1496 1496 addspaces=spaces,
1497 1497 wraplabels=True,
1498 1498 wrapannotations=True,
1499 1499 wrapnonlinear=dots,
1500 1500 usedots=dots,
1501 1501 maxlinewidth=70):
1502 1502 ui.write(line)
1503 1503 ui.write("\n")
1504 1504
1505 1505 @command('debugdata',
1506 1506 [('c', 'changelog', False, _('open changelog')),
1507 1507 ('m', 'manifest', False, _('open manifest'))],
1508 1508 _('-c|-m|FILE REV'))
1509 1509 def debugdata(ui, repo, file_, rev = None, **opts):
1510 1510 """dump the contents of a data file revision"""
1511 1511 if opts.get('changelog') or opts.get('manifest'):
1512 1512 file_, rev = None, file_
1513 1513 elif rev is None:
1514 1514 raise error.CommandError('debugdata', _('invalid arguments'))
1515 1515 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1516 1516 try:
1517 1517 ui.write(r.revision(r.lookup(rev)))
1518 1518 except KeyError:
1519 1519 raise util.Abort(_('invalid revision identifier %s') % rev)
1520 1520
1521 1521 @command('debugdate',
1522 1522 [('e', 'extended', None, _('try extended date formats'))],
1523 1523 _('[-e] DATE [RANGE]'))
1524 1524 def debugdate(ui, date, range=None, **opts):
1525 1525 """parse and display a date"""
1526 1526 if opts["extended"]:
1527 1527 d = util.parsedate(date, util.extendeddateformats)
1528 1528 else:
1529 1529 d = util.parsedate(date)
1530 1530 ui.write("internal: %s %s\n" % d)
1531 1531 ui.write("standard: %s\n" % util.datestr(d))
1532 1532 if range:
1533 1533 m = util.matchdate(range)
1534 1534 ui.write("match: %s\n" % m(d[0]))
1535 1535
1536 1536 @command('debugdiscovery',
1537 1537 [('', 'old', None, _('use old-style discovery')),
1538 1538 ('', 'nonheads', None,
1539 1539 _('use old-style discovery with non-heads included')),
1540 1540 ] + remoteopts,
1541 1541 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1542 1542 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1543 1543 """runs the changeset discovery protocol in isolation"""
1544 1544 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1545 1545 remote = hg.peer(repo, opts, remoteurl)
1546 1546 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1547 1547
1548 1548 # make sure tests are repeatable
1549 1549 random.seed(12323)
1550 1550
1551 1551 def doit(localheads, remoteheads):
1552 1552 if opts.get('old'):
1553 1553 if localheads:
1554 1554 raise util.Abort('cannot use localheads with old style discovery')
1555 1555 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1556 1556 force=True)
1557 1557 common = set(common)
1558 1558 if not opts.get('nonheads'):
1559 1559 ui.write("unpruned common: %s\n" % " ".join([short(n)
1560 1560 for n in common]))
1561 1561 dag = dagutil.revlogdag(repo.changelog)
1562 1562 all = dag.ancestorset(dag.internalizeall(common))
1563 1563 common = dag.externalizeall(dag.headsetofconnecteds(all))
1564 1564 else:
1565 1565 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1566 1566 common = set(common)
1567 1567 rheads = set(hds)
1568 1568 lheads = set(repo.heads())
1569 1569 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1570 1570 if lheads <= common:
1571 1571 ui.write("local is subset\n")
1572 1572 elif rheads <= common:
1573 1573 ui.write("remote is subset\n")
1574 1574
1575 1575 serverlogs = opts.get('serverlog')
1576 1576 if serverlogs:
1577 1577 for filename in serverlogs:
1578 1578 logfile = open(filename, 'r')
1579 1579 try:
1580 1580 line = logfile.readline()
1581 1581 while line:
1582 1582 parts = line.strip().split(';')
1583 1583 op = parts[1]
1584 1584 if op == 'cg':
1585 1585 pass
1586 1586 elif op == 'cgss':
1587 1587 doit(parts[2].split(' '), parts[3].split(' '))
1588 1588 elif op == 'unb':
1589 1589 doit(parts[3].split(' '), parts[2].split(' '))
1590 1590 line = logfile.readline()
1591 1591 finally:
1592 1592 logfile.close()
1593 1593
1594 1594 else:
1595 1595 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1596 1596 opts.get('remote_head'))
1597 1597 localrevs = opts.get('local_head')
1598 1598 doit(localrevs, remoterevs)
1599 1599
1600 1600 @command('debugfileset', [], ('REVSPEC'))
1601 1601 def debugfileset(ui, repo, expr):
1602 1602 '''parse and apply a fileset specification'''
1603 1603 if ui.verbose:
1604 1604 tree = fileset.parse(expr)[0]
1605 1605 ui.note(tree, "\n")
1606 1606 matcher = lambda x: scmutil.match(repo, x, default='glob')
1607 1607
1608 1608 for f in fileset.getfileset(repo[None], matcher, expr):
1609 1609 ui.write("%s\n" % f)
1610 1610
1611 1611 @command('debugfsinfo', [], _('[PATH]'))
1612 1612 def debugfsinfo(ui, path = "."):
1613 1613 """show information detected about current filesystem"""
1614 1614 util.writefile('.debugfsinfo', '')
1615 1615 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1616 1616 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1617 1617 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1618 1618 and 'yes' or 'no'))
1619 1619 os.unlink('.debugfsinfo')
1620 1620
1621 1621 @command('debuggetbundle',
1622 1622 [('H', 'head', [], _('id of head node'), _('ID')),
1623 1623 ('C', 'common', [], _('id of common node'), _('ID')),
1624 1624 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1625 1625 _('REPO FILE [-H|-C ID]...'))
1626 1626 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1627 1627 """retrieves a bundle from a repo
1628 1628
1629 1629 Every ID must be a full-length hex node id string. Saves the bundle to the
1630 1630 given file.
1631 1631 """
1632 1632 repo = hg.peer(ui, opts, repopath)
1633 1633 if not repo.capable('getbundle'):
1634 1634 raise util.Abort("getbundle() not supported by target repository")
1635 1635 args = {}
1636 1636 if common:
1637 1637 args['common'] = [bin(s) for s in common]
1638 1638 if head:
1639 1639 args['heads'] = [bin(s) for s in head]
1640 1640 bundle = repo.getbundle('debug', **args)
1641 1641
1642 1642 bundletype = opts.get('type', 'bzip2').lower()
1643 1643 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1644 1644 bundletype = btypes.get(bundletype)
1645 1645 if bundletype not in changegroup.bundletypes:
1646 1646 raise util.Abort(_('unknown bundle type specified with --type'))
1647 1647 changegroup.writebundle(bundle, bundlepath, bundletype)
1648 1648
1649 1649 @command('debugignore', [], '')
1650 1650 def debugignore(ui, repo, *values, **opts):
1651 1651 """display the combined ignore pattern"""
1652 1652 ignore = repo.dirstate._ignore
1653 1653 if hasattr(ignore, 'includepat'):
1654 1654 ui.write("%s\n" % ignore.includepat)
1655 1655 else:
1656 1656 raise util.Abort(_("no ignore patterns found"))
1657 1657
1658 1658 @command('debugindex',
1659 1659 [('c', 'changelog', False, _('open changelog')),
1660 1660 ('m', 'manifest', False, _('open manifest')),
1661 1661 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1662 1662 _('[-f FORMAT] -c|-m|FILE'))
1663 1663 def debugindex(ui, repo, file_ = None, **opts):
1664 1664 """dump the contents of an index file"""
1665 1665 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1666 1666 format = opts.get('format', 0)
1667 1667 if format not in (0, 1):
1668 1668 raise util.Abort(_("unknown format %d") % format)
1669 1669
1670 1670 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1671 1671 if generaldelta:
1672 1672 basehdr = ' delta'
1673 1673 else:
1674 1674 basehdr = ' base'
1675 1675
1676 1676 if format == 0:
1677 1677 ui.write(" rev offset length " + basehdr + " linkrev"
1678 1678 " nodeid p1 p2\n")
1679 1679 elif format == 1:
1680 1680 ui.write(" rev flag offset length"
1681 1681 " size " + basehdr + " link p1 p2 nodeid\n")
1682 1682
1683 1683 for i in r:
1684 1684 node = r.node(i)
1685 1685 if generaldelta:
1686 1686 base = r.deltaparent(i)
1687 1687 else:
1688 1688 base = r.chainbase(i)
1689 1689 if format == 0:
1690 1690 try:
1691 1691 pp = r.parents(node)
1692 1692 except:
1693 1693 pp = [nullid, nullid]
1694 1694 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1695 1695 i, r.start(i), r.length(i), base, r.linkrev(i),
1696 1696 short(node), short(pp[0]), short(pp[1])))
1697 1697 elif format == 1:
1698 1698 pr = r.parentrevs(i)
1699 1699 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1700 1700 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1701 1701 base, r.linkrev(i), pr[0], pr[1], short(node)))
1702 1702
1703 1703 @command('debugindexdot', [], _('FILE'))
1704 1704 def debugindexdot(ui, repo, file_):
1705 1705 """dump an index DAG as a graphviz dot file"""
1706 1706 r = None
1707 1707 if repo:
1708 1708 filelog = repo.file(file_)
1709 1709 if len(filelog):
1710 1710 r = filelog
1711 1711 if not r:
1712 1712 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1713 1713 ui.write("digraph G {\n")
1714 1714 for i in r:
1715 1715 node = r.node(i)
1716 1716 pp = r.parents(node)
1717 1717 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1718 1718 if pp[1] != nullid:
1719 1719 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1720 1720 ui.write("}\n")
1721 1721
1722 1722 @command('debuginstall', [], '')
1723 1723 def debuginstall(ui):
1724 1724 '''test Mercurial installation
1725 1725
1726 1726 Returns 0 on success.
1727 1727 '''
1728 1728
1729 1729 def writetemp(contents):
1730 1730 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1731 1731 f = os.fdopen(fd, "wb")
1732 1732 f.write(contents)
1733 1733 f.close()
1734 1734 return name
1735 1735
1736 1736 problems = 0
1737 1737
1738 1738 # encoding
1739 1739 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1740 1740 try:
1741 1741 encoding.fromlocal("test")
1742 1742 except util.Abort, inst:
1743 1743 ui.write(" %s\n" % inst)
1744 1744 ui.write(_(" (check that your locale is properly set)\n"))
1745 1745 problems += 1
1746 1746
1747 1747 # compiled modules
1748 1748 ui.status(_("Checking installed modules (%s)...\n")
1749 1749 % os.path.dirname(__file__))
1750 1750 try:
1751 1751 import bdiff, mpatch, base85, osutil
1752 1752 except Exception, inst:
1753 1753 ui.write(" %s\n" % inst)
1754 1754 ui.write(_(" One or more extensions could not be found"))
1755 1755 ui.write(_(" (check that you compiled the extensions)\n"))
1756 1756 problems += 1
1757 1757
1758 1758 # templates
1759 1759 ui.status(_("Checking templates...\n"))
1760 1760 try:
1761 1761 import templater
1762 1762 templater.templater(templater.templatepath("map-cmdline.default"))
1763 1763 except Exception, inst:
1764 1764 ui.write(" %s\n" % inst)
1765 1765 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1766 1766 problems += 1
1767 1767
1768 1768 # editor
1769 1769 ui.status(_("Checking commit editor...\n"))
1770 1770 editor = ui.geteditor()
1771 1771 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1772 1772 if not cmdpath:
1773 1773 if editor == 'vi':
1774 1774 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1775 1775 ui.write(_(" (specify a commit editor in your configuration"
1776 1776 " file)\n"))
1777 1777 else:
1778 1778 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1779 1779 ui.write(_(" (specify a commit editor in your configuration"
1780 1780 " file)\n"))
1781 1781 problems += 1
1782 1782
1783 1783 # check username
1784 1784 ui.status(_("Checking username...\n"))
1785 1785 try:
1786 1786 ui.username()
1787 1787 except util.Abort, e:
1788 1788 ui.write(" %s\n" % e)
1789 1789 ui.write(_(" (specify a username in your configuration file)\n"))
1790 1790 problems += 1
1791 1791
1792 1792 if not problems:
1793 1793 ui.status(_("No problems detected\n"))
1794 1794 else:
1795 1795 ui.write(_("%s problems detected,"
1796 1796 " please check your install!\n") % problems)
1797 1797
1798 1798 return problems
1799 1799
1800 1800 @command('debugknown', [], _('REPO ID...'))
1801 1801 def debugknown(ui, repopath, *ids, **opts):
1802 1802 """test whether node ids are known to a repo
1803 1803
1804 1804 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1805 1805 indicating unknown/known.
1806 1806 """
1807 1807 repo = hg.peer(ui, opts, repopath)
1808 1808 if not repo.capable('known'):
1809 1809 raise util.Abort("known() not supported by target repository")
1810 1810 flags = repo.known([bin(s) for s in ids])
1811 1811 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1812 1812
1813 1813 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1814 1814 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1815 1815 '''access the pushkey key/value protocol
1816 1816
1817 1817 With two args, list the keys in the given namespace.
1818 1818
1819 1819 With five args, set a key to new if it currently is set to old.
1820 1820 Reports success or failure.
1821 1821 '''
1822 1822
1823 1823 target = hg.peer(ui, {}, repopath)
1824 1824 if keyinfo:
1825 1825 key, old, new = keyinfo
1826 1826 r = target.pushkey(namespace, key, old, new)
1827 1827 ui.status(str(r) + '\n')
1828 1828 return not r
1829 1829 else:
1830 1830 for k, v in target.listkeys(namespace).iteritems():
1831 1831 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1832 1832 v.encode('string-escape')))
1833 1833
1834 1834 @command('debugrebuildstate',
1835 1835 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1836 1836 _('[-r REV] [REV]'))
1837 1837 def debugrebuildstate(ui, repo, rev="tip"):
1838 1838 """rebuild the dirstate as it would look like for the given revision"""
1839 1839 ctx = scmutil.revsingle(repo, rev)
1840 1840 wlock = repo.wlock()
1841 1841 try:
1842 1842 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1843 1843 finally:
1844 1844 wlock.release()
1845 1845
1846 1846 @command('debugrename',
1847 1847 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1848 1848 _('[-r REV] FILE'))
1849 1849 def debugrename(ui, repo, file1, *pats, **opts):
1850 1850 """dump rename information"""
1851 1851
1852 1852 ctx = scmutil.revsingle(repo, opts.get('rev'))
1853 1853 m = scmutil.match(repo, (file1,) + pats, opts)
1854 1854 for abs in ctx.walk(m):
1855 1855 fctx = ctx[abs]
1856 1856 o = fctx.filelog().renamed(fctx.filenode())
1857 1857 rel = m.rel(abs)
1858 1858 if o:
1859 1859 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1860 1860 else:
1861 1861 ui.write(_("%s not renamed\n") % rel)
1862 1862
1863 1863 @command('debugrevlog',
1864 1864 [('c', 'changelog', False, _('open changelog')),
1865 1865 ('m', 'manifest', False, _('open manifest')),
1866 1866 ('d', 'dump', False, _('dump index data'))],
1867 1867 _('-c|-m|FILE'))
1868 1868 def debugrevlog(ui, repo, file_ = None, **opts):
1869 1869 """show data and statistics about a revlog"""
1870 1870 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1871 1871
1872 1872 if opts.get("dump"):
1873 1873 numrevs = len(r)
1874 1874 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1875 1875 " rawsize totalsize compression heads\n")
1876 1876 ts = 0
1877 1877 heads = set()
1878 1878 for rev in xrange(numrevs):
1879 1879 dbase = r.deltaparent(rev)
1880 1880 if dbase == -1:
1881 1881 dbase = rev
1882 1882 cbase = r.chainbase(rev)
1883 1883 p1, p2 = r.parentrevs(rev)
1884 1884 rs = r.rawsize(rev)
1885 1885 ts = ts + rs
1886 1886 heads -= set(r.parentrevs(rev))
1887 1887 heads.add(rev)
1888 1888 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1889 1889 (rev, p1, p2, r.start(rev), r.end(rev),
1890 1890 r.start(dbase), r.start(cbase),
1891 1891 r.start(p1), r.start(p2),
1892 1892 rs, ts, ts / r.end(rev), len(heads)))
1893 1893 return 0
1894 1894
1895 1895 v = r.version
1896 1896 format = v & 0xFFFF
1897 1897 flags = []
1898 1898 gdelta = False
1899 1899 if v & revlog.REVLOGNGINLINEDATA:
1900 1900 flags.append('inline')
1901 1901 if v & revlog.REVLOGGENERALDELTA:
1902 1902 gdelta = True
1903 1903 flags.append('generaldelta')
1904 1904 if not flags:
1905 1905 flags = ['(none)']
1906 1906
1907 1907 nummerges = 0
1908 1908 numfull = 0
1909 1909 numprev = 0
1910 1910 nump1 = 0
1911 1911 nump2 = 0
1912 1912 numother = 0
1913 1913 nump1prev = 0
1914 1914 nump2prev = 0
1915 1915 chainlengths = []
1916 1916
1917 1917 datasize = [None, 0, 0L]
1918 1918 fullsize = [None, 0, 0L]
1919 1919 deltasize = [None, 0, 0L]
1920 1920
1921 1921 def addsize(size, l):
1922 1922 if l[0] is None or size < l[0]:
1923 1923 l[0] = size
1924 1924 if size > l[1]:
1925 1925 l[1] = size
1926 1926 l[2] += size
1927 1927
1928 1928 numrevs = len(r)
1929 1929 for rev in xrange(numrevs):
1930 1930 p1, p2 = r.parentrevs(rev)
1931 1931 delta = r.deltaparent(rev)
1932 1932 if format > 0:
1933 1933 addsize(r.rawsize(rev), datasize)
1934 1934 if p2 != nullrev:
1935 1935 nummerges += 1
1936 1936 size = r.length(rev)
1937 1937 if delta == nullrev:
1938 1938 chainlengths.append(0)
1939 1939 numfull += 1
1940 1940 addsize(size, fullsize)
1941 1941 else:
1942 1942 chainlengths.append(chainlengths[delta] + 1)
1943 1943 addsize(size, deltasize)
1944 1944 if delta == rev - 1:
1945 1945 numprev += 1
1946 1946 if delta == p1:
1947 1947 nump1prev += 1
1948 1948 elif delta == p2:
1949 1949 nump2prev += 1
1950 1950 elif delta == p1:
1951 1951 nump1 += 1
1952 1952 elif delta == p2:
1953 1953 nump2 += 1
1954 1954 elif delta != nullrev:
1955 1955 numother += 1
1956 1956
1957 1957 numdeltas = numrevs - numfull
1958 1958 numoprev = numprev - nump1prev - nump2prev
1959 1959 totalrawsize = datasize[2]
1960 1960 datasize[2] /= numrevs
1961 1961 fulltotal = fullsize[2]
1962 1962 fullsize[2] /= numfull
1963 1963 deltatotal = deltasize[2]
1964 1964 deltasize[2] /= numrevs - numfull
1965 1965 totalsize = fulltotal + deltatotal
1966 1966 avgchainlen = sum(chainlengths) / numrevs
1967 1967 compratio = totalrawsize / totalsize
1968 1968
1969 1969 basedfmtstr = '%%%dd\n'
1970 1970 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1971 1971
1972 1972 def dfmtstr(max):
1973 1973 return basedfmtstr % len(str(max))
1974 1974 def pcfmtstr(max, padding=0):
1975 1975 return basepcfmtstr % (len(str(max)), ' ' * padding)
1976 1976
1977 1977 def pcfmt(value, total):
1978 1978 return (value, 100 * float(value) / total)
1979 1979
1980 1980 ui.write('format : %d\n' % format)
1981 1981 ui.write('flags : %s\n' % ', '.join(flags))
1982 1982
1983 1983 ui.write('\n')
1984 1984 fmt = pcfmtstr(totalsize)
1985 1985 fmt2 = dfmtstr(totalsize)
1986 1986 ui.write('revisions : ' + fmt2 % numrevs)
1987 1987 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1988 1988 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1989 1989 ui.write('revisions : ' + fmt2 % numrevs)
1990 1990 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1991 1991 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1992 1992 ui.write('revision size : ' + fmt2 % totalsize)
1993 1993 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1994 1994 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
1995 1995
1996 1996 ui.write('\n')
1997 1997 fmt = dfmtstr(max(avgchainlen, compratio))
1998 1998 ui.write('avg chain length : ' + fmt % avgchainlen)
1999 1999 ui.write('compression ratio : ' + fmt % compratio)
2000 2000
2001 2001 if format > 0:
2002 2002 ui.write('\n')
2003 2003 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2004 2004 % tuple(datasize))
2005 2005 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2006 2006 % tuple(fullsize))
2007 2007 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2008 2008 % tuple(deltasize))
2009 2009
2010 2010 if numdeltas > 0:
2011 2011 ui.write('\n')
2012 2012 fmt = pcfmtstr(numdeltas)
2013 2013 fmt2 = pcfmtstr(numdeltas, 4)
2014 2014 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2015 2015 if numprev > 0:
2016 2016 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2017 2017 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2018 2018 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2019 2019 if gdelta:
2020 2020 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2021 2021 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2022 2022 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2023 2023
2024 2024 @command('debugrevspec', [], ('REVSPEC'))
2025 2025 def debugrevspec(ui, repo, expr):
2026 2026 '''parse and apply a revision specification'''
2027 2027 if ui.verbose:
2028 2028 tree = revset.parse(expr)[0]
2029 2029 ui.note(tree, "\n")
2030 2030 newtree = revset.findaliases(ui, tree)
2031 2031 if newtree != tree:
2032 2032 ui.note(newtree, "\n")
2033 2033 func = revset.match(ui, expr)
2034 2034 for c in func(repo, range(len(repo))):
2035 2035 ui.write("%s\n" % c)
2036 2036
2037 2037 @command('debugsetparents', [], _('REV1 [REV2]'))
2038 2038 def debugsetparents(ui, repo, rev1, rev2=None):
2039 2039 """manually set the parents of the current working directory
2040 2040
2041 2041 This is useful for writing repository conversion tools, but should
2042 2042 be used with care.
2043 2043
2044 2044 Returns 0 on success.
2045 2045 """
2046 2046
2047 2047 r1 = scmutil.revsingle(repo, rev1).node()
2048 2048 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2049 2049
2050 2050 wlock = repo.wlock()
2051 2051 try:
2052 2052 repo.dirstate.setparents(r1, r2)
2053 2053 finally:
2054 2054 wlock.release()
2055 2055
2056 2056 @command('debugstate',
2057 2057 [('', 'nodates', None, _('do not display the saved mtime')),
2058 2058 ('', 'datesort', None, _('sort by saved mtime'))],
2059 2059 _('[OPTION]...'))
2060 2060 def debugstate(ui, repo, nodates=None, datesort=None):
2061 2061 """show the contents of the current dirstate"""
2062 2062 timestr = ""
2063 2063 showdate = not nodates
2064 2064 if datesort:
2065 2065 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2066 2066 else:
2067 2067 keyfunc = None # sort by filename
2068 2068 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2069 2069 if showdate:
2070 2070 if ent[3] == -1:
2071 2071 # Pad or slice to locale representation
2072 2072 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2073 2073 time.localtime(0)))
2074 2074 timestr = 'unset'
2075 2075 timestr = (timestr[:locale_len] +
2076 2076 ' ' * (locale_len - len(timestr)))
2077 2077 else:
2078 2078 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2079 2079 time.localtime(ent[3]))
2080 2080 if ent[1] & 020000:
2081 2081 mode = 'lnk'
2082 2082 else:
2083 2083 mode = '%3o' % (ent[1] & 0777)
2084 2084 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2085 2085 for f in repo.dirstate.copies():
2086 2086 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2087 2087
2088 2088 @command('debugsub',
2089 2089 [('r', 'rev', '',
2090 2090 _('revision to check'), _('REV'))],
2091 2091 _('[-r REV] [REV]'))
2092 2092 def debugsub(ui, repo, rev=None):
2093 2093 ctx = scmutil.revsingle(repo, rev, None)
2094 2094 for k, v in sorted(ctx.substate.items()):
2095 2095 ui.write('path %s\n' % k)
2096 2096 ui.write(' source %s\n' % v[0])
2097 2097 ui.write(' revision %s\n' % v[1])
2098 2098
2099 2099 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2100 2100 def debugwalk(ui, repo, *pats, **opts):
2101 2101 """show how files match on given patterns"""
2102 2102 m = scmutil.match(repo, pats, opts)
2103 2103 items = list(repo.walk(m))
2104 2104 if not items:
2105 2105 return
2106 2106 fmt = 'f %%-%ds %%-%ds %%s' % (
2107 2107 max([len(abs) for abs in items]),
2108 2108 max([len(m.rel(abs)) for abs in items]))
2109 2109 for abs in items:
2110 2110 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2111 2111 ui.write("%s\n" % line.rstrip())
2112 2112
2113 2113 @command('debugwireargs',
2114 2114 [('', 'three', '', 'three'),
2115 2115 ('', 'four', '', 'four'),
2116 2116 ('', 'five', '', 'five'),
2117 2117 ] + remoteopts,
2118 2118 _('REPO [OPTIONS]... [ONE [TWO]]'))
2119 2119 def debugwireargs(ui, repopath, *vals, **opts):
2120 2120 repo = hg.peer(ui, opts, repopath)
2121 2121 for opt in remoteopts:
2122 2122 del opts[opt[1]]
2123 2123 args = {}
2124 2124 for k, v in opts.iteritems():
2125 2125 if v:
2126 2126 args[k] = v
2127 2127 # run twice to check that we don't mess up the stream for the next command
2128 2128 res1 = repo.debugwireargs(*vals, **args)
2129 2129 res2 = repo.debugwireargs(*vals, **args)
2130 2130 ui.write("%s\n" % res1)
2131 2131 if res1 != res2:
2132 2132 ui.warn("%s\n" % res2)
2133 2133
2134 2134 @command('^diff',
2135 2135 [('r', 'rev', [], _('revision'), _('REV')),
2136 2136 ('c', 'change', '', _('change made by revision'), _('REV'))
2137 2137 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2138 2138 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2139 2139 def diff(ui, repo, *pats, **opts):
2140 2140 """diff repository (or selected files)
2141 2141
2142 2142 Show differences between revisions for the specified files.
2143 2143
2144 2144 Differences between files are shown using the unified diff format.
2145 2145
2146 2146 .. note::
2147 2147 diff may generate unexpected results for merges, as it will
2148 2148 default to comparing against the working directory's first
2149 2149 parent changeset if no revisions are specified.
2150 2150
2151 2151 When two revision arguments are given, then changes are shown
2152 2152 between those revisions. If only one revision is specified then
2153 2153 that revision is compared to the working directory, and, when no
2154 2154 revisions are specified, the working directory files are compared
2155 2155 to its parent.
2156 2156
2157 2157 Alternatively you can specify -c/--change with a revision to see
2158 2158 the changes in that changeset relative to its first parent.
2159 2159
2160 2160 Without the -a/--text option, diff will avoid generating diffs of
2161 2161 files it detects as binary. With -a, diff will generate a diff
2162 2162 anyway, probably with undesirable results.
2163 2163
2164 2164 Use the -g/--git option to generate diffs in the git extended diff
2165 2165 format. For more information, read :hg:`help diffs`.
2166 2166
2167 2167 Returns 0 on success.
2168 2168 """
2169 2169
2170 2170 revs = opts.get('rev')
2171 2171 change = opts.get('change')
2172 2172 stat = opts.get('stat')
2173 2173 reverse = opts.get('reverse')
2174 2174
2175 2175 if revs and change:
2176 2176 msg = _('cannot specify --rev and --change at the same time')
2177 2177 raise util.Abort(msg)
2178 2178 elif change:
2179 2179 node2 = scmutil.revsingle(repo, change, None).node()
2180 2180 node1 = repo[node2].p1().node()
2181 2181 else:
2182 2182 node1, node2 = scmutil.revpair(repo, revs)
2183 2183
2184 2184 if reverse:
2185 2185 node1, node2 = node2, node1
2186 2186
2187 2187 diffopts = patch.diffopts(ui, opts)
2188 2188 m = scmutil.match(repo, pats, opts)
2189 2189 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2190 2190 listsubrepos=opts.get('subrepos'))
2191 2191
2192 2192 @command('^export',
2193 2193 [('o', 'output', '',
2194 2194 _('print output to file with formatted name'), _('FORMAT')),
2195 2195 ('', 'switch-parent', None, _('diff against the second parent')),
2196 2196 ('r', 'rev', [], _('revisions to export'), _('REV')),
2197 2197 ] + diffopts,
2198 2198 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2199 2199 def export(ui, repo, *changesets, **opts):
2200 2200 """dump the header and diffs for one or more changesets
2201 2201
2202 2202 Print the changeset header and diffs for one or more revisions.
2203 2203
2204 2204 The information shown in the changeset header is: author, date,
2205 2205 branch name (if non-default), changeset hash, parent(s) and commit
2206 2206 comment.
2207 2207
2208 2208 .. note::
2209 2209 export may generate unexpected diff output for merge
2210 2210 changesets, as it will compare the merge changeset against its
2211 2211 first parent only.
2212 2212
2213 2213 Output may be to a file, in which case the name of the file is
2214 2214 given using a format string. The formatting rules are as follows:
2215 2215
2216 2216 :``%%``: literal "%" character
2217 2217 :``%H``: changeset hash (40 hexadecimal digits)
2218 2218 :``%N``: number of patches being generated
2219 2219 :``%R``: changeset revision number
2220 2220 :``%b``: basename of the exporting repository
2221 2221 :``%h``: short-form changeset hash (12 hexadecimal digits)
2222 2222 :``%n``: zero-padded sequence number, starting at 1
2223 2223 :``%r``: zero-padded changeset revision number
2224 2224
2225 2225 Without the -a/--text option, export will avoid generating diffs
2226 2226 of files it detects as binary. With -a, export will generate a
2227 2227 diff anyway, probably with undesirable results.
2228 2228
2229 2229 Use the -g/--git option to generate diffs in the git extended diff
2230 2230 format. See :hg:`help diffs` for more information.
2231 2231
2232 2232 With the --switch-parent option, the diff will be against the
2233 2233 second parent. It can be useful to review a merge.
2234 2234
2235 2235 Returns 0 on success.
2236 2236 """
2237 2237 changesets += tuple(opts.get('rev', []))
2238 2238 if not changesets:
2239 2239 raise util.Abort(_("export requires at least one changeset"))
2240 2240 revs = scmutil.revrange(repo, changesets)
2241 2241 if len(revs) > 1:
2242 2242 ui.note(_('exporting patches:\n'))
2243 2243 else:
2244 2244 ui.note(_('exporting patch:\n'))
2245 2245 cmdutil.export(repo, revs, template=opts.get('output'),
2246 2246 switch_parent=opts.get('switch_parent'),
2247 2247 opts=patch.diffopts(ui, opts))
2248 2248
2249 2249 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2250 2250 def forget(ui, repo, *pats, **opts):
2251 2251 """forget the specified files on the next commit
2252 2252
2253 2253 Mark the specified files so they will no longer be tracked
2254 2254 after the next commit.
2255 2255
2256 2256 This only removes files from the current branch, not from the
2257 2257 entire project history, and it does not delete them from the
2258 2258 working directory.
2259 2259
2260 2260 To undo a forget before the next commit, see :hg:`add`.
2261 2261
2262 2262 Returns 0 on success.
2263 2263 """
2264 2264
2265 2265 if not pats:
2266 2266 raise util.Abort(_('no files specified'))
2267 2267
2268 2268 m = scmutil.match(repo, pats, opts)
2269 2269 s = repo.status(match=m, clean=True)
2270 2270 forget = sorted(s[0] + s[1] + s[3] + s[6])
2271 2271 errs = 0
2272 2272
2273 2273 for f in m.files():
2274 2274 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2275 2275 if os.path.exists(m.rel(f)):
2276 2276 ui.warn(_('not removing %s: file is already untracked\n')
2277 2277 % m.rel(f))
2278 2278 errs = 1
2279 2279
2280 2280 for f in forget:
2281 2281 if ui.verbose or not m.exact(f):
2282 2282 ui.status(_('removing %s\n') % m.rel(f))
2283 2283
2284 2284 repo[None].forget(forget)
2285 2285 return errs
2286 2286
2287 2287 @command('grep',
2288 2288 [('0', 'print0', None, _('end fields with NUL')),
2289 2289 ('', 'all', None, _('print all revisions that match')),
2290 2290 ('a', 'text', None, _('treat all files as text')),
2291 2291 ('f', 'follow', None,
2292 2292 _('follow changeset history,'
2293 2293 ' or file history across copies and renames')),
2294 2294 ('i', 'ignore-case', None, _('ignore case when matching')),
2295 2295 ('l', 'files-with-matches', None,
2296 2296 _('print only filenames and revisions that match')),
2297 2297 ('n', 'line-number', None, _('print matching line numbers')),
2298 2298 ('r', 'rev', [],
2299 2299 _('only search files changed within revision range'), _('REV')),
2300 2300 ('u', 'user', None, _('list the author (long with -v)')),
2301 2301 ('d', 'date', None, _('list the date (short with -q)')),
2302 2302 ] + walkopts,
2303 2303 _('[OPTION]... PATTERN [FILE]...'))
2304 2304 def grep(ui, repo, pattern, *pats, **opts):
2305 2305 """search for a pattern in specified files and revisions
2306 2306
2307 2307 Search revisions of files for a regular expression.
2308 2308
2309 2309 This command behaves differently than Unix grep. It only accepts
2310 2310 Python/Perl regexps. It searches repository history, not the
2311 2311 working directory. It always prints the revision number in which a
2312 2312 match appears.
2313 2313
2314 2314 By default, grep only prints output for the first revision of a
2315 2315 file in which it finds a match. To get it to print every revision
2316 2316 that contains a change in match status ("-" for a match that
2317 2317 becomes a non-match, or "+" for a non-match that becomes a match),
2318 2318 use the --all flag.
2319 2319
2320 2320 Returns 0 if a match is found, 1 otherwise.
2321 2321 """
2322 2322 reflags = 0
2323 2323 if opts.get('ignore_case'):
2324 2324 reflags |= re.I
2325 2325 try:
2326 2326 regexp = re.compile(pattern, reflags)
2327 2327 except re.error, inst:
2328 2328 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2329 2329 return 1
2330 2330 sep, eol = ':', '\n'
2331 2331 if opts.get('print0'):
2332 2332 sep = eol = '\0'
2333 2333
2334 2334 getfile = util.lrucachefunc(repo.file)
2335 2335
2336 2336 def matchlines(body):
2337 2337 begin = 0
2338 2338 linenum = 0
2339 2339 while True:
2340 2340 match = regexp.search(body, begin)
2341 2341 if not match:
2342 2342 break
2343 2343 mstart, mend = match.span()
2344 2344 linenum += body.count('\n', begin, mstart) + 1
2345 2345 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2346 2346 begin = body.find('\n', mend) + 1 or len(body)
2347 2347 lend = begin - 1
2348 2348 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2349 2349
2350 2350 class linestate(object):
2351 2351 def __init__(self, line, linenum, colstart, colend):
2352 2352 self.line = line
2353 2353 self.linenum = linenum
2354 2354 self.colstart = colstart
2355 2355 self.colend = colend
2356 2356
2357 2357 def __hash__(self):
2358 2358 return hash((self.linenum, self.line))
2359 2359
2360 2360 def __eq__(self, other):
2361 2361 return self.line == other.line
2362 2362
2363 2363 matches = {}
2364 2364 copies = {}
2365 2365 def grepbody(fn, rev, body):
2366 2366 matches[rev].setdefault(fn, [])
2367 2367 m = matches[rev][fn]
2368 2368 for lnum, cstart, cend, line in matchlines(body):
2369 2369 s = linestate(line, lnum, cstart, cend)
2370 2370 m.append(s)
2371 2371
2372 2372 def difflinestates(a, b):
2373 2373 sm = difflib.SequenceMatcher(None, a, b)
2374 2374 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2375 2375 if tag == 'insert':
2376 2376 for i in xrange(blo, bhi):
2377 2377 yield ('+', b[i])
2378 2378 elif tag == 'delete':
2379 2379 for i in xrange(alo, ahi):
2380 2380 yield ('-', a[i])
2381 2381 elif tag == 'replace':
2382 2382 for i in xrange(alo, ahi):
2383 2383 yield ('-', a[i])
2384 2384 for i in xrange(blo, bhi):
2385 2385 yield ('+', b[i])
2386 2386
2387 2387 def display(fn, ctx, pstates, states):
2388 2388 rev = ctx.rev()
2389 2389 datefunc = ui.quiet and util.shortdate or util.datestr
2390 2390 found = False
2391 2391 filerevmatches = {}
2392 2392 def binary():
2393 2393 flog = getfile(fn)
2394 2394 return util.binary(flog.read(ctx.filenode(fn)))
2395 2395
2396 2396 if opts.get('all'):
2397 2397 iter = difflinestates(pstates, states)
2398 2398 else:
2399 2399 iter = [('', l) for l in states]
2400 2400 for change, l in iter:
2401 2401 cols = [fn, str(rev)]
2402 2402 before, match, after = None, None, None
2403 2403 if opts.get('line_number'):
2404 2404 cols.append(str(l.linenum))
2405 2405 if opts.get('all'):
2406 2406 cols.append(change)
2407 2407 if opts.get('user'):
2408 2408 cols.append(ui.shortuser(ctx.user()))
2409 2409 if opts.get('date'):
2410 2410 cols.append(datefunc(ctx.date()))
2411 2411 if opts.get('files_with_matches'):
2412 2412 c = (fn, rev)
2413 2413 if c in filerevmatches:
2414 2414 continue
2415 2415 filerevmatches[c] = 1
2416 2416 else:
2417 2417 before = l.line[:l.colstart]
2418 2418 match = l.line[l.colstart:l.colend]
2419 2419 after = l.line[l.colend:]
2420 2420 ui.write(sep.join(cols))
2421 2421 if before is not None:
2422 2422 if not opts.get('text') and binary():
2423 2423 ui.write(sep + " Binary file matches")
2424 2424 else:
2425 2425 ui.write(sep + before)
2426 2426 ui.write(match, label='grep.match')
2427 2427 ui.write(after)
2428 2428 ui.write(eol)
2429 2429 found = True
2430 2430 return found
2431 2431
2432 2432 skip = {}
2433 2433 revfiles = {}
2434 2434 matchfn = scmutil.match(repo, pats, opts)
2435 2435 found = False
2436 2436 follow = opts.get('follow')
2437 2437
2438 2438 def prep(ctx, fns):
2439 2439 rev = ctx.rev()
2440 2440 pctx = ctx.p1()
2441 2441 parent = pctx.rev()
2442 2442 matches.setdefault(rev, {})
2443 2443 matches.setdefault(parent, {})
2444 2444 files = revfiles.setdefault(rev, [])
2445 2445 for fn in fns:
2446 2446 flog = getfile(fn)
2447 2447 try:
2448 2448 fnode = ctx.filenode(fn)
2449 2449 except error.LookupError:
2450 2450 continue
2451 2451
2452 2452 copied = flog.renamed(fnode)
2453 2453 copy = follow and copied and copied[0]
2454 2454 if copy:
2455 2455 copies.setdefault(rev, {})[fn] = copy
2456 2456 if fn in skip:
2457 2457 if copy:
2458 2458 skip[copy] = True
2459 2459 continue
2460 2460 files.append(fn)
2461 2461
2462 2462 if fn not in matches[rev]:
2463 2463 grepbody(fn, rev, flog.read(fnode))
2464 2464
2465 2465 pfn = copy or fn
2466 2466 if pfn not in matches[parent]:
2467 2467 try:
2468 2468 fnode = pctx.filenode(pfn)
2469 2469 grepbody(pfn, parent, flog.read(fnode))
2470 2470 except error.LookupError:
2471 2471 pass
2472 2472
2473 2473 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2474 2474 rev = ctx.rev()
2475 2475 parent = ctx.p1().rev()
2476 2476 for fn in sorted(revfiles.get(rev, [])):
2477 2477 states = matches[rev][fn]
2478 2478 copy = copies.get(rev, {}).get(fn)
2479 2479 if fn in skip:
2480 2480 if copy:
2481 2481 skip[copy] = True
2482 2482 continue
2483 2483 pstates = matches.get(parent, {}).get(copy or fn, [])
2484 2484 if pstates or states:
2485 2485 r = display(fn, ctx, pstates, states)
2486 2486 found = found or r
2487 2487 if r and not opts.get('all'):
2488 2488 skip[fn] = True
2489 2489 if copy:
2490 2490 skip[copy] = True
2491 2491 del matches[rev]
2492 2492 del revfiles[rev]
2493 2493
2494 2494 return not found
2495 2495
2496 2496 @command('heads',
2497 2497 [('r', 'rev', '',
2498 2498 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2499 2499 ('t', 'topo', False, _('show topological heads only')),
2500 2500 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2501 2501 ('c', 'closed', False, _('show normal and closed branch heads')),
2502 2502 ] + templateopts,
2503 2503 _('[-ac] [-r STARTREV] [REV]...'))
2504 2504 def heads(ui, repo, *branchrevs, **opts):
2505 2505 """show current repository heads or show branch heads
2506 2506
2507 2507 With no arguments, show all repository branch heads.
2508 2508
2509 2509 Repository "heads" are changesets with no child changesets. They are
2510 2510 where development generally takes place and are the usual targets
2511 2511 for update and merge operations. Branch heads are changesets that have
2512 2512 no child changeset on the same branch.
2513 2513
2514 2514 If one or more REVs are given, only branch heads on the branches
2515 2515 associated with the specified changesets are shown.
2516 2516
2517 2517 If -c/--closed is specified, also show branch heads marked closed
2518 2518 (see :hg:`commit --close-branch`).
2519 2519
2520 2520 If STARTREV is specified, only those heads that are descendants of
2521 2521 STARTREV will be displayed.
2522 2522
2523 2523 If -t/--topo is specified, named branch mechanics will be ignored and only
2524 2524 changesets without children will be shown.
2525 2525
2526 2526 Returns 0 if matching heads are found, 1 if not.
2527 2527 """
2528 2528
2529 2529 start = None
2530 2530 if 'rev' in opts:
2531 2531 start = scmutil.revsingle(repo, opts['rev'], None).node()
2532 2532
2533 2533 if opts.get('topo'):
2534 2534 heads = [repo[h] for h in repo.heads(start)]
2535 2535 else:
2536 2536 heads = []
2537 2537 for branch in repo.branchmap():
2538 2538 heads += repo.branchheads(branch, start, opts.get('closed'))
2539 2539 heads = [repo[h] for h in heads]
2540 2540
2541 2541 if branchrevs:
2542 2542 branches = set(repo[br].branch() for br in branchrevs)
2543 2543 heads = [h for h in heads if h.branch() in branches]
2544 2544
2545 2545 if opts.get('active') and branchrevs:
2546 2546 dagheads = repo.heads(start)
2547 2547 heads = [h for h in heads if h.node() in dagheads]
2548 2548
2549 2549 if branchrevs:
2550 2550 haveheads = set(h.branch() for h in heads)
2551 2551 if branches - haveheads:
2552 2552 headless = ', '.join(b for b in branches - haveheads)
2553 2553 msg = _('no open branch heads found on branches %s')
2554 2554 if opts.get('rev'):
2555 2555 msg += _(' (started at %s)' % opts['rev'])
2556 2556 ui.warn((msg + '\n') % headless)
2557 2557
2558 2558 if not heads:
2559 2559 return 1
2560 2560
2561 2561 heads = sorted(heads, key=lambda x: -x.rev())
2562 2562 displayer = cmdutil.show_changeset(ui, repo, opts)
2563 2563 for ctx in heads:
2564 2564 displayer.show(ctx)
2565 2565 displayer.close()
2566 2566
2567 2567 @command('help',
2568 2568 [('e', 'extension', None, _('show only help for extensions')),
2569 2569 ('c', 'command', None, _('show only help for commands'))],
2570 2570 _('[-ec] [TOPIC]'))
2571 2571 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2572 2572 """show help for a given topic or a help overview
2573 2573
2574 2574 With no arguments, print a list of commands with short help messages.
2575 2575
2576 2576 Given a topic, extension, or command name, print help for that
2577 2577 topic.
2578 2578
2579 2579 Returns 0 if successful.
2580 2580 """
2581 2581 option_lists = []
2582 2582 textwidth = min(ui.termwidth(), 80) - 2
2583 2583
2584 2584 def addglobalopts(aliases):
2585 2585 if ui.verbose:
2586 2586 option_lists.append((_("global options:"), globalopts))
2587 2587 if name == 'shortlist':
2588 2588 option_lists.append((_('use "hg help" for the full list '
2589 2589 'of commands'), ()))
2590 2590 else:
2591 2591 if name == 'shortlist':
2592 2592 msg = _('use "hg help" for the full list of commands '
2593 2593 'or "hg -v" for details')
2594 2594 elif name and not full:
2595 2595 msg = _('use "hg help %s" to show the full help text' % name)
2596 2596 elif aliases:
2597 2597 msg = _('use "hg -v help%s" to show builtin aliases and '
2598 2598 'global options') % (name and " " + name or "")
2599 2599 else:
2600 2600 msg = _('use "hg -v help %s" to show global options') % name
2601 2601 option_lists.append((msg, ()))
2602 2602
2603 2603 def helpcmd(name):
2604 2604 if with_version:
2605 2605 version_(ui)
2606 2606 ui.write('\n')
2607 2607
2608 2608 try:
2609 2609 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2610 2610 except error.AmbiguousCommand, inst:
2611 2611 # py3k fix: except vars can't be used outside the scope of the
2612 2612 # except block, nor can be used inside a lambda. python issue4617
2613 2613 prefix = inst.args[0]
2614 2614 select = lambda c: c.lstrip('^').startswith(prefix)
2615 2615 helplist(_('list of commands:\n\n'), select)
2616 2616 return
2617 2617
2618 2618 # check if it's an invalid alias and display its error if it is
2619 2619 if getattr(entry[0], 'badalias', False):
2620 2620 if not unknowncmd:
2621 2621 entry[0](ui)
2622 2622 return
2623 2623
2624 2624 # synopsis
2625 2625 if len(entry) > 2:
2626 2626 if entry[2].startswith('hg'):
2627 2627 ui.write("%s\n" % entry[2])
2628 2628 else:
2629 2629 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2630 2630 else:
2631 2631 ui.write('hg %s\n' % aliases[0])
2632 2632
2633 2633 # aliases
2634 2634 if full and not ui.quiet and len(aliases) > 1:
2635 2635 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2636 2636
2637 2637 # description
2638 2638 doc = gettext(entry[0].__doc__)
2639 2639 if not doc:
2640 2640 doc = _("(no help text available)")
2641 2641 if hasattr(entry[0], 'definition'): # aliased command
2642 2642 if entry[0].definition.startswith('!'): # shell alias
2643 2643 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2644 2644 else:
2645 2645 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2646 2646 if ui.quiet or not full:
2647 2647 doc = doc.splitlines()[0]
2648 2648 keep = ui.verbose and ['verbose'] or []
2649 2649 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2650 2650 ui.write("\n%s\n" % formatted)
2651 2651 if pruned:
2652 2652 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2653 2653
2654 2654 if not ui.quiet:
2655 2655 # options
2656 2656 if entry[1]:
2657 2657 option_lists.append((_("options:\n"), entry[1]))
2658 2658
2659 2659 addglobalopts(False)
2660 2660
2661 2661 # check if this command shadows a non-trivial (multi-line)
2662 2662 # extension help text
2663 2663 try:
2664 2664 mod = extensions.find(name)
2665 2665 doc = gettext(mod.__doc__) or ''
2666 2666 if '\n' in doc.strip():
2667 2667 msg = _('use "hg help -e %s" to show help for '
2668 2668 'the %s extension') % (name, name)
2669 2669 ui.write('\n%s\n' % msg)
2670 2670 except KeyError:
2671 2671 pass
2672 2672
2673 2673 def helplist(header, select=None):
2674 2674 h = {}
2675 2675 cmds = {}
2676 2676 for c, e in table.iteritems():
2677 2677 f = c.split("|", 1)[0]
2678 2678 if select and not select(f):
2679 2679 continue
2680 2680 if (not select and name != 'shortlist' and
2681 2681 e[0].__module__ != __name__):
2682 2682 continue
2683 2683 if name == "shortlist" and not f.startswith("^"):
2684 2684 continue
2685 2685 f = f.lstrip("^")
2686 2686 if not ui.debugflag and f.startswith("debug"):
2687 2687 continue
2688 2688 doc = e[0].__doc__
2689 2689 if doc and 'DEPRECATED' in doc and not ui.verbose:
2690 2690 continue
2691 2691 doc = gettext(doc)
2692 2692 if not doc:
2693 2693 doc = _("(no help text available)")
2694 2694 h[f] = doc.splitlines()[0].rstrip()
2695 2695 cmds[f] = c.lstrip("^")
2696 2696
2697 2697 if not h:
2698 2698 ui.status(_('no commands defined\n'))
2699 2699 return
2700 2700
2701 2701 ui.status(header)
2702 2702 fns = sorted(h)
2703 2703 m = max(map(len, fns))
2704 2704 for f in fns:
2705 2705 if ui.verbose:
2706 2706 commands = cmds[f].replace("|",", ")
2707 2707 ui.write(" %s:\n %s\n"%(commands, h[f]))
2708 2708 else:
2709 2709 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2710 2710 initindent=' %-*s ' % (m, f),
2711 2711 hangindent=' ' * (m + 4))))
2712 2712
2713 2713 if not ui.quiet:
2714 2714 addglobalopts(True)
2715 2715
2716 2716 def helptopic(name):
2717 2717 for names, header, doc in help.helptable:
2718 2718 if name in names:
2719 2719 break
2720 2720 else:
2721 2721 raise error.UnknownCommand(name)
2722 2722
2723 2723 # description
2724 2724 if not doc:
2725 2725 doc = _("(no help text available)")
2726 2726 if hasattr(doc, '__call__'):
2727 2727 doc = doc()
2728 2728
2729 2729 ui.write("%s\n\n" % header)
2730 2730 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2731 2731 try:
2732 2732 cmdutil.findcmd(name, table)
2733 2733 ui.write(_('\nuse "hg help -c %s" to see help for '
2734 2734 'the %s command\n') % (name, name))
2735 2735 except error.UnknownCommand:
2736 2736 pass
2737 2737
2738 2738 def helpext(name):
2739 2739 try:
2740 2740 mod = extensions.find(name)
2741 2741 doc = gettext(mod.__doc__) or _('no help text available')
2742 2742 except KeyError:
2743 2743 mod = None
2744 2744 doc = extensions.disabledext(name)
2745 2745 if not doc:
2746 2746 raise error.UnknownCommand(name)
2747 2747
2748 2748 if '\n' not in doc:
2749 2749 head, tail = doc, ""
2750 2750 else:
2751 2751 head, tail = doc.split('\n', 1)
2752 2752 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2753 2753 if tail:
2754 2754 ui.write(minirst.format(tail, textwidth))
2755 2755 ui.status('\n\n')
2756 2756
2757 2757 if mod:
2758 2758 try:
2759 2759 ct = mod.cmdtable
2760 2760 except AttributeError:
2761 2761 ct = {}
2762 2762 modcmds = set([c.split('|', 1)[0] for c in ct])
2763 2763 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2764 2764 else:
2765 2765 ui.write(_('use "hg help extensions" for information on enabling '
2766 2766 'extensions\n'))
2767 2767
2768 2768 def helpextcmd(name):
2769 2769 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2770 2770 doc = gettext(mod.__doc__).splitlines()[0]
2771 2771
2772 2772 msg = help.listexts(_("'%s' is provided by the following "
2773 2773 "extension:") % cmd, {ext: doc}, indent=4)
2774 2774 ui.write(minirst.format(msg, textwidth))
2775 2775 ui.write('\n\n')
2776 2776 ui.write(_('use "hg help extensions" for information on enabling '
2777 2777 'extensions\n'))
2778 2778
2779 2779 if name and name != 'shortlist':
2780 2780 i = None
2781 2781 if unknowncmd:
2782 2782 queries = (helpextcmd,)
2783 2783 elif opts.get('extension'):
2784 2784 queries = (helpext,)
2785 2785 elif opts.get('command'):
2786 2786 queries = (helpcmd,)
2787 2787 else:
2788 2788 queries = (helptopic, helpcmd, helpext, helpextcmd)
2789 2789 for f in queries:
2790 2790 try:
2791 2791 f(name)
2792 2792 i = None
2793 2793 break
2794 2794 except error.UnknownCommand, inst:
2795 2795 i = inst
2796 2796 if i:
2797 2797 raise i
2798 2798
2799 2799 else:
2800 2800 # program name
2801 2801 if ui.verbose or with_version:
2802 2802 version_(ui)
2803 2803 else:
2804 2804 ui.status(_("Mercurial Distributed SCM\n"))
2805 2805 ui.status('\n')
2806 2806
2807 2807 # list of commands
2808 2808 if name == "shortlist":
2809 2809 header = _('basic commands:\n\n')
2810 2810 else:
2811 2811 header = _('list of commands:\n\n')
2812 2812
2813 2813 helplist(header)
2814 2814 if name != 'shortlist':
2815 2815 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2816 2816 if text:
2817 2817 ui.write("\n%s\n" % minirst.format(text, textwidth))
2818 2818
2819 2819 # list all option lists
2820 2820 opt_output = []
2821 2821 multioccur = False
2822 2822 for title, options in option_lists:
2823 2823 opt_output.append(("\n%s" % title, None))
2824 2824 for option in options:
2825 2825 if len(option) == 5:
2826 2826 shortopt, longopt, default, desc, optlabel = option
2827 2827 else:
2828 2828 shortopt, longopt, default, desc = option
2829 2829 optlabel = _("VALUE") # default label
2830 2830
2831 2831 if _("DEPRECATED") in desc and not ui.verbose:
2832 2832 continue
2833 2833 if isinstance(default, list):
2834 2834 numqualifier = " %s [+]" % optlabel
2835 2835 multioccur = True
2836 2836 elif (default is not None) and not isinstance(default, bool):
2837 2837 numqualifier = " %s" % optlabel
2838 2838 else:
2839 2839 numqualifier = ""
2840 2840 opt_output.append(("%2s%s" %
2841 2841 (shortopt and "-%s" % shortopt,
2842 2842 longopt and " --%s%s" %
2843 2843 (longopt, numqualifier)),
2844 2844 "%s%s" % (desc,
2845 2845 default
2846 2846 and _(" (default: %s)") % default
2847 2847 or "")))
2848 2848 if multioccur:
2849 2849 msg = _("\n[+] marked option can be specified multiple times")
2850 2850 if ui.verbose and name != 'shortlist':
2851 2851 opt_output.append((msg, None))
2852 2852 else:
2853 2853 opt_output.insert(-1, (msg, None))
2854 2854
2855 2855 if not name:
2856 2856 ui.write(_("\nadditional help topics:\n\n"))
2857 2857 topics = []
2858 2858 for names, header, doc in help.helptable:
2859 2859 topics.append((sorted(names, key=len, reverse=True)[0], header))
2860 2860 topics_len = max([len(s[0]) for s in topics])
2861 2861 for t, desc in topics:
2862 2862 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2863 2863
2864 2864 if opt_output:
2865 2865 colwidth = encoding.colwidth
2866 2866 # normalize: (opt or message, desc or None, width of opt)
2867 2867 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2868 2868 for opt, desc in opt_output]
2869 2869 hanging = max([e[2] for e in entries])
2870 2870 for opt, desc, width in entries:
2871 2871 if desc:
2872 2872 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2873 2873 hangindent = ' ' * (hanging + 3)
2874 2874 ui.write('%s\n' % (util.wrap(desc, textwidth,
2875 2875 initindent=initindent,
2876 2876 hangindent=hangindent)))
2877 2877 else:
2878 2878 ui.write("%s\n" % opt)
2879 2879
2880 2880 @command('identify|id',
2881 2881 [('r', 'rev', '',
2882 2882 _('identify the specified revision'), _('REV')),
2883 2883 ('n', 'num', None, _('show local revision number')),
2884 2884 ('i', 'id', None, _('show global revision id')),
2885 2885 ('b', 'branch', None, _('show branch')),
2886 2886 ('t', 'tags', None, _('show tags')),
2887 2887 ('B', 'bookmarks', None, _('show bookmarks'))],
2888 2888 _('[-nibtB] [-r REV] [SOURCE]'))
2889 2889 def identify(ui, repo, source=None, rev=None,
2890 2890 num=None, id=None, branch=None, tags=None, bookmarks=None):
2891 2891 """identify the working copy or specified revision
2892 2892
2893 2893 Print a summary identifying the repository state at REV using one or
2894 2894 two parent hash identifiers, followed by a "+" if the working
2895 2895 directory has uncommitted changes, the branch name (if not default),
2896 2896 a list of tags, and a list of bookmarks.
2897 2897
2898 2898 When REV is not given, print a summary of the current state of the
2899 2899 repository.
2900 2900
2901 2901 Specifying a path to a repository root or Mercurial bundle will
2902 2902 cause lookup to operate on that repository/bundle.
2903 2903
2904 2904 Returns 0 if successful.
2905 2905 """
2906 2906
2907 2907 if not repo and not source:
2908 2908 raise util.Abort(_("there is no Mercurial repository here "
2909 2909 "(.hg not found)"))
2910 2910
2911 2911 hexfunc = ui.debugflag and hex or short
2912 2912 default = not (num or id or branch or tags or bookmarks)
2913 2913 output = []
2914 2914 revs = []
2915 2915
2916 2916 if source:
2917 2917 source, branches = hg.parseurl(ui.expandpath(source))
2918 2918 repo = hg.peer(ui, {}, source)
2919 2919 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2920 2920
2921 2921 if not repo.local():
2922 2922 if num or branch or tags:
2923 2923 raise util.Abort(
2924 2924 _("can't query remote revision number, branch, or tags"))
2925 2925 if not rev and revs:
2926 2926 rev = revs[0]
2927 2927 if not rev:
2928 2928 rev = "tip"
2929 2929
2930 2930 remoterev = repo.lookup(rev)
2931 2931 if default or id:
2932 2932 output = [hexfunc(remoterev)]
2933 2933
2934 2934 def getbms():
2935 2935 bms = []
2936 2936
2937 2937 if 'bookmarks' in repo.listkeys('namespaces'):
2938 2938 hexremoterev = hex(remoterev)
2939 2939 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2940 2940 if bmr == hexremoterev]
2941 2941
2942 2942 return bms
2943 2943
2944 2944 if bookmarks:
2945 2945 output.extend(getbms())
2946 2946 elif default and not ui.quiet:
2947 2947 # multiple bookmarks for a single parent separated by '/'
2948 2948 bm = '/'.join(getbms())
2949 2949 if bm:
2950 2950 output.append(bm)
2951 2951 else:
2952 2952 if not rev:
2953 2953 ctx = repo[None]
2954 2954 parents = ctx.parents()
2955 2955 changed = ""
2956 2956 if default or id or num:
2957 2957 changed = util.any(repo.status()) and "+" or ""
2958 2958 if default or id:
2959 2959 output = ["%s%s" %
2960 2960 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2961 2961 if num:
2962 2962 output.append("%s%s" %
2963 2963 ('+'.join([str(p.rev()) for p in parents]), changed))
2964 2964 else:
2965 2965 ctx = scmutil.revsingle(repo, rev)
2966 2966 if default or id:
2967 2967 output = [hexfunc(ctx.node())]
2968 2968 if num:
2969 2969 output.append(str(ctx.rev()))
2970 2970
2971 2971 if default and not ui.quiet:
2972 2972 b = ctx.branch()
2973 2973 if b != 'default':
2974 2974 output.append("(%s)" % b)
2975 2975
2976 2976 # multiple tags for a single parent separated by '/'
2977 2977 t = '/'.join(ctx.tags())
2978 2978 if t:
2979 2979 output.append(t)
2980 2980
2981 2981 # multiple bookmarks for a single parent separated by '/'
2982 2982 bm = '/'.join(ctx.bookmarks())
2983 2983 if bm:
2984 2984 output.append(bm)
2985 2985 else:
2986 2986 if branch:
2987 2987 output.append(ctx.branch())
2988 2988
2989 2989 if tags:
2990 2990 output.extend(ctx.tags())
2991 2991
2992 2992 if bookmarks:
2993 2993 output.extend(ctx.bookmarks())
2994 2994
2995 2995 ui.write("%s\n" % ' '.join(output))
2996 2996
2997 2997 @command('import|patch',
2998 2998 [('p', 'strip', 1,
2999 2999 _('directory strip option for patch. This has the same '
3000 3000 'meaning as the corresponding patch option'), _('NUM')),
3001 3001 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3002 3002 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3003 3003 ('', 'no-commit', None,
3004 3004 _("don't commit, just update the working directory")),
3005 ('', 'bypass', None,
3006 _("apply patch without touching the working directory")),
3005 3007 ('', 'exact', None,
3006 3008 _('apply patch to the nodes from which it was generated')),
3007 3009 ('', 'import-branch', None,
3008 3010 _('use any branch information in patch (implied by --exact)'))] +
3009 3011 commitopts + commitopts2 + similarityopts,
3010 3012 _('[OPTION]... PATCH...'))
3011 3013 def import_(ui, repo, patch1, *patches, **opts):
3012 3014 """import an ordered set of patches
3013 3015
3014 3016 Import a list of patches and commit them individually (unless
3015 3017 --no-commit is specified).
3016 3018
3017 3019 If there are outstanding changes in the working directory, import
3018 3020 will abort unless given the -f/--force flag.
3019 3021
3020 3022 You can import a patch straight from a mail message. Even patches
3021 3023 as attachments work (to use the body part, it must have type
3022 3024 text/plain or text/x-patch). From and Subject headers of email
3023 3025 message are used as default committer and commit message. All
3024 3026 text/plain body parts before first diff are added to commit
3025 3027 message.
3026 3028
3027 3029 If the imported patch was generated by :hg:`export`, user and
3028 3030 description from patch override values from message headers and
3029 3031 body. Values given on command line with -m/--message and -u/--user
3030 3032 override these.
3031 3033
3032 3034 If --exact is specified, import will set the working directory to
3033 3035 the parent of each patch before applying it, and will abort if the
3034 3036 resulting changeset has a different ID than the one recorded in
3035 3037 the patch. This may happen due to character set problems or other
3036 3038 deficiencies in the text patch format.
3037 3039
3040 Use --bypass to apply and commit patches directly to the
3041 repository, not touching the working directory. Without --exact,
3042 patches will be applied on top of the working directory parent
3043 revision.
3044
3038 3045 With -s/--similarity, hg will attempt to discover renames and
3039 3046 copies in the patch in the same way as 'addremove'.
3040 3047
3041 3048 To read a patch from standard input, use "-" as the patch name. If
3042 3049 a URL is specified, the patch will be downloaded from it.
3043 3050 See :hg:`help dates` for a list of formats valid for -d/--date.
3044 3051
3045 3052 Returns 0 on success.
3046 3053 """
3047 3054 patches = (patch1,) + patches
3048 3055
3049 3056 date = opts.get('date')
3050 3057 if date:
3051 3058 opts['date'] = util.parsedate(date)
3052 3059
3060 update = not opts.get('bypass')
3061 if not update and opts.get('no_commit'):
3062 raise util.Abort(_('cannot use --no-commit with --bypass'))
3053 3063 try:
3054 3064 sim = float(opts.get('similarity') or 0)
3055 3065 except ValueError:
3056 3066 raise util.Abort(_('similarity must be a number'))
3057 3067 if sim < 0 or sim > 100:
3058 3068 raise util.Abort(_('similarity must be between 0 and 100'))
3059
3060 if opts.get('exact') or not opts.get('force'):
3069 if sim and not update:
3070 raise util.Abort(_('cannot use --similarity with --bypass'))
3071
3072 if (opts.get('exact') or not opts.get('force')) and update:
3061 3073 cmdutil.bailifchanged(repo)
3062 3074
3063 3075 d = opts["base"]
3064 3076 strip = opts["strip"]
3065 3077 wlock = lock = None
3066 3078 msgs = []
3067 3079
3068 def tryone(ui, hunk):
3080 def checkexact(repo, n, nodeid):
3081 if opts.get('exact') and hex(n) != nodeid:
3082 repo.rollback()
3083 raise util.Abort(_('patch is damaged or loses information'))
3084
3085 def tryone(ui, hunk, parents):
3069 3086 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3070 3087 patch.extract(ui, hunk)
3071 3088
3072 3089 if not tmpname:
3073 3090 return None
3074 3091 commitid = _('to working directory')
3075 3092
3076 3093 try:
3077 3094 cmdline_message = cmdutil.logmessage(opts)
3078 3095 if cmdline_message:
3079 3096 # pickup the cmdline msg
3080 3097 message = cmdline_message
3081 3098 elif message:
3082 3099 # pickup the patch msg
3083 3100 message = message.strip()
3084 3101 else:
3085 3102 # launch the editor
3086 3103 message = None
3087 3104 ui.debug('message:\n%s\n' % message)
3088 3105
3089 wp = repo.parents()
3090 if len(wp) == 1:
3091 wp.append(repo[nullid])
3106 if len(parents) == 1:
3107 parents.append(repo[nullid])
3092 3108 if opts.get('exact'):
3093 3109 if not nodeid or not p1:
3094 3110 raise util.Abort(_('not a Mercurial patch'))
3095 3111 p1 = repo[p1]
3096 3112 p2 = repo[p2 or nullid]
3097 3113 elif p2:
3098 3114 try:
3099 3115 p1 = repo[p1]
3100 3116 p2 = repo[p2]
3101 3117 except error.RepoError:
3102 p1, p2 = wp
3118 p1, p2 = parents
3103 3119 else:
3104 p1, p2 = wp
3105
3106 if opts.get('exact') and p1 != wp[0]:
3107 hg.clean(repo, p1.node())
3108 if p1 != wp[0] and p2 != wp[1]:
3109 repo.dirstate.setparents(p1.node(), p2.node())
3110
3111 if opts.get('exact') or opts.get('import_branch'):
3112 repo.dirstate.setbranch(branch or 'default')
3113
3114 files = set()
3115 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3116 eolmode=None, similarity=sim / 100.0)
3117 files = list(files)
3118 if opts.get('no_commit'):
3119 if message:
3120 msgs.append(message)
3120 p1, p2 = parents
3121
3122 n = None
3123 if update:
3124 if opts.get('exact') and p1 != parents[0]:
3125 hg.clean(repo, p1.node())
3126 if p1 != parents[0] and p2 != parents[1]:
3127 repo.dirstate.setparents(p1.node(), p2.node())
3128
3129 if opts.get('exact') or opts.get('import_branch'):
3130 repo.dirstate.setbranch(branch or 'default')
3131
3132 files = set()
3133 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3134 eolmode=None, similarity=sim / 100.0)
3135 files = list(files)
3136 if opts.get('no_commit'):
3137 if message:
3138 msgs.append(message)
3139 else:
3140 if opts.get('exact'):
3141 m = None
3142 else:
3143 m = scmutil.matchfiles(repo, files or [])
3144 n = repo.commit(message, opts.get('user') or user,
3145 opts.get('date') or date, match=m,
3146 editor=cmdutil.commiteditor)
3147 checkexact(repo, n, nodeid)
3148 # Force a dirstate write so that the next transaction
3149 # backups an up-to-date file.
3150 repo.dirstate.write()
3121 3151 else:
3122 if opts.get('exact'):
3123 m = None
3152 if opts.get('exact') or opts.get('import_branch'):
3153 branch = branch or 'default'
3124 3154 else:
3125 m = scmutil.matchfiles(repo, files or [])
3126 n = repo.commit(message, opts.get('user') or user,
3127 opts.get('date') or date, match=m,
3128 editor=cmdutil.commiteditor)
3129 if opts.get('exact'):
3130 if hex(n) != nodeid:
3131 repo.rollback()
3132 raise util.Abort(_('patch is damaged'
3133 ' or loses information'))
3134 # Force a dirstate write so that the next transaction
3135 # backups an up-do-date file.
3136 repo.dirstate.write()
3137 if n:
3138 commitid = short(n)
3139
3155 branch = p1.branch()
3156 store = patch.filestore()
3157 try:
3158 files = set()
3159 try:
3160 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3161 files, eolmode=None)
3162 except patch.PatchError, e:
3163 raise util.Abort(str(e))
3164 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3165 message,
3166 opts.get('user') or user,
3167 opts.get('date') or date,
3168 branch, files, store,
3169 editor=cmdutil.commiteditor)
3170 repo.savecommitmessage(memctx.description())
3171 n = memctx.commit()
3172 checkexact(repo, n, nodeid)
3173 finally:
3174 store.close()
3175 if n:
3176 commitid = short(n)
3140 3177 return commitid
3141 3178 finally:
3142 3179 os.unlink(tmpname)
3143 3180
3144 3181 try:
3145 3182 wlock = repo.wlock()
3146 3183 lock = repo.lock()
3184 parents = repo.parents()
3147 3185 lastcommit = None
3148 3186 for p in patches:
3149 3187 pf = os.path.join(d, p)
3150 3188
3151 3189 if pf == '-':
3152 3190 ui.status(_("applying patch from stdin\n"))
3153 3191 pf = sys.stdin
3154 3192 else:
3155 3193 ui.status(_("applying %s\n") % p)
3156 3194 pf = url.open(ui, pf)
3157 3195
3158 3196 haspatch = False
3159 3197 for hunk in patch.split(pf):
3160 commitid = tryone(ui, hunk)
3198 commitid = tryone(ui, hunk, parents)
3161 3199 if commitid:
3162 3200 haspatch = True
3163 3201 if lastcommit:
3164 3202 ui.status(_('applied %s\n') % lastcommit)
3165 3203 lastcommit = commitid
3204 if update or opts.get('exact'):
3205 parents = repo.parents()
3206 else:
3207 parents = [repo[commitid]]
3166 3208
3167 3209 if not haspatch:
3168 3210 raise util.Abort(_('no diffs found'))
3169 3211
3170 3212 if msgs:
3171 3213 repo.savecommitmessage('\n* * *\n'.join(msgs))
3172 3214 finally:
3173 3215 release(lock, wlock)
3174 3216
3175 3217 @command('incoming|in',
3176 3218 [('f', 'force', None,
3177 3219 _('run even if remote repository is unrelated')),
3178 3220 ('n', 'newest-first', None, _('show newest record first')),
3179 3221 ('', 'bundle', '',
3180 3222 _('file to store the bundles into'), _('FILE')),
3181 3223 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3182 3224 ('B', 'bookmarks', False, _("compare bookmarks")),
3183 3225 ('b', 'branch', [],
3184 3226 _('a specific branch you would like to pull'), _('BRANCH')),
3185 3227 ] + logopts + remoteopts + subrepoopts,
3186 3228 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3187 3229 def incoming(ui, repo, source="default", **opts):
3188 3230 """show new changesets found in source
3189 3231
3190 3232 Show new changesets found in the specified path/URL or the default
3191 3233 pull location. These are the changesets that would have been pulled
3192 3234 if a pull at the time you issued this command.
3193 3235
3194 3236 For remote repository, using --bundle avoids downloading the
3195 3237 changesets twice if the incoming is followed by a pull.
3196 3238
3197 3239 See pull for valid source format details.
3198 3240
3199 3241 Returns 0 if there are incoming changes, 1 otherwise.
3200 3242 """
3201 3243 if opts.get('bundle') and opts.get('subrepos'):
3202 3244 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3203 3245
3204 3246 if opts.get('bookmarks'):
3205 3247 source, branches = hg.parseurl(ui.expandpath(source),
3206 3248 opts.get('branch'))
3207 3249 other = hg.peer(repo, opts, source)
3208 3250 if 'bookmarks' not in other.listkeys('namespaces'):
3209 3251 ui.warn(_("remote doesn't support bookmarks\n"))
3210 3252 return 0
3211 3253 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3212 3254 return bookmarks.diff(ui, repo, other)
3213 3255
3214 3256 repo._subtoppath = ui.expandpath(source)
3215 3257 try:
3216 3258 return hg.incoming(ui, repo, source, opts)
3217 3259 finally:
3218 3260 del repo._subtoppath
3219 3261
3220 3262
3221 3263 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3222 3264 def init(ui, dest=".", **opts):
3223 3265 """create a new repository in the given directory
3224 3266
3225 3267 Initialize a new repository in the given directory. If the given
3226 3268 directory does not exist, it will be created.
3227 3269
3228 3270 If no directory is given, the current directory is used.
3229 3271
3230 3272 It is possible to specify an ``ssh://`` URL as the destination.
3231 3273 See :hg:`help urls` for more information.
3232 3274
3233 3275 Returns 0 on success.
3234 3276 """
3235 3277 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3236 3278
3237 3279 @command('locate',
3238 3280 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3239 3281 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3240 3282 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3241 3283 ] + walkopts,
3242 3284 _('[OPTION]... [PATTERN]...'))
3243 3285 def locate(ui, repo, *pats, **opts):
3244 3286 """locate files matching specific patterns
3245 3287
3246 3288 Print files under Mercurial control in the working directory whose
3247 3289 names match the given patterns.
3248 3290
3249 3291 By default, this command searches all directories in the working
3250 3292 directory. To search just the current directory and its
3251 3293 subdirectories, use "--include .".
3252 3294
3253 3295 If no patterns are given to match, this command prints the names
3254 3296 of all files under Mercurial control in the working directory.
3255 3297
3256 3298 If you want to feed the output of this command into the "xargs"
3257 3299 command, use the -0 option to both this command and "xargs". This
3258 3300 will avoid the problem of "xargs" treating single filenames that
3259 3301 contain whitespace as multiple filenames.
3260 3302
3261 3303 Returns 0 if a match is found, 1 otherwise.
3262 3304 """
3263 3305 end = opts.get('print0') and '\0' or '\n'
3264 3306 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3265 3307
3266 3308 ret = 1
3267 3309 m = scmutil.match(repo, pats, opts, default='relglob')
3268 3310 m.bad = lambda x, y: False
3269 3311 for abs in repo[rev].walk(m):
3270 3312 if not rev and abs not in repo.dirstate:
3271 3313 continue
3272 3314 if opts.get('fullpath'):
3273 3315 ui.write(repo.wjoin(abs), end)
3274 3316 else:
3275 3317 ui.write(((pats and m.rel(abs)) or abs), end)
3276 3318 ret = 0
3277 3319
3278 3320 return ret
3279 3321
3280 3322 @command('^log|history',
3281 3323 [('f', 'follow', None,
3282 3324 _('follow changeset history, or file history across copies and renames')),
3283 3325 ('', 'follow-first', None,
3284 3326 _('only follow the first parent of merge changesets')),
3285 3327 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3286 3328 ('C', 'copies', None, _('show copied files')),
3287 3329 ('k', 'keyword', [],
3288 3330 _('do case-insensitive search for a given text'), _('TEXT')),
3289 3331 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3290 3332 ('', 'removed', None, _('include revisions where files were removed')),
3291 3333 ('m', 'only-merges', None, _('show only merges')),
3292 3334 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3293 3335 ('', 'only-branch', [],
3294 3336 _('show only changesets within the given named branch (DEPRECATED)'),
3295 3337 _('BRANCH')),
3296 3338 ('b', 'branch', [],
3297 3339 _('show changesets within the given named branch'), _('BRANCH')),
3298 3340 ('P', 'prune', [],
3299 3341 _('do not display revision or any of its ancestors'), _('REV')),
3300 3342 ] + logopts + walkopts,
3301 3343 _('[OPTION]... [FILE]'))
3302 3344 def log(ui, repo, *pats, **opts):
3303 3345 """show revision history of entire repository or files
3304 3346
3305 3347 Print the revision history of the specified files or the entire
3306 3348 project.
3307 3349
3308 3350 File history is shown without following rename or copy history of
3309 3351 files. Use -f/--follow with a filename to follow history across
3310 3352 renames and copies. --follow without a filename will only show
3311 3353 ancestors or descendants of the starting revision. --follow-first
3312 3354 only follows the first parent of merge revisions.
3313 3355
3314 3356 If no revision range is specified, the default is ``tip:0`` unless
3315 3357 --follow is set, in which case the working directory parent is
3316 3358 used as the starting revision. You can specify a revision set for
3317 3359 log, see :hg:`help revsets` for more information.
3318 3360
3319 3361 See :hg:`help dates` for a list of formats valid for -d/--date.
3320 3362
3321 3363 By default this command prints revision number and changeset id,
3322 3364 tags, non-trivial parents, user, date and time, and a summary for
3323 3365 each commit. When the -v/--verbose switch is used, the list of
3324 3366 changed files and full commit message are shown.
3325 3367
3326 3368 .. note::
3327 3369 log -p/--patch may generate unexpected diff output for merge
3328 3370 changesets, as it will only compare the merge changeset against
3329 3371 its first parent. Also, only files different from BOTH parents
3330 3372 will appear in files:.
3331 3373
3332 3374 Returns 0 on success.
3333 3375 """
3334 3376
3335 3377 matchfn = scmutil.match(repo, pats, opts)
3336 3378 limit = cmdutil.loglimit(opts)
3337 3379 count = 0
3338 3380
3339 3381 endrev = None
3340 3382 if opts.get('copies') and opts.get('rev'):
3341 3383 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3342 3384
3343 3385 df = False
3344 3386 if opts["date"]:
3345 3387 df = util.matchdate(opts["date"])
3346 3388
3347 3389 branches = opts.get('branch', []) + opts.get('only_branch', [])
3348 3390 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3349 3391
3350 3392 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3351 3393 def prep(ctx, fns):
3352 3394 rev = ctx.rev()
3353 3395 parents = [p for p in repo.changelog.parentrevs(rev)
3354 3396 if p != nullrev]
3355 3397 if opts.get('no_merges') and len(parents) == 2:
3356 3398 return
3357 3399 if opts.get('only_merges') and len(parents) != 2:
3358 3400 return
3359 3401 if opts.get('branch') and ctx.branch() not in opts['branch']:
3360 3402 return
3361 3403 if df and not df(ctx.date()[0]):
3362 3404 return
3363 3405 if opts['user'] and not [k for k in opts['user']
3364 3406 if k.lower() in ctx.user().lower()]:
3365 3407 return
3366 3408 if opts.get('keyword'):
3367 3409 for k in [kw.lower() for kw in opts['keyword']]:
3368 3410 if (k in ctx.user().lower() or
3369 3411 k in ctx.description().lower() or
3370 3412 k in " ".join(ctx.files()).lower()):
3371 3413 break
3372 3414 else:
3373 3415 return
3374 3416
3375 3417 copies = None
3376 3418 if opts.get('copies') and rev:
3377 3419 copies = []
3378 3420 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3379 3421 for fn in ctx.files():
3380 3422 rename = getrenamed(fn, rev)
3381 3423 if rename:
3382 3424 copies.append((fn, rename[0]))
3383 3425
3384 3426 revmatchfn = None
3385 3427 if opts.get('patch') or opts.get('stat'):
3386 3428 if opts.get('follow') or opts.get('follow_first'):
3387 3429 # note: this might be wrong when following through merges
3388 3430 revmatchfn = scmutil.match(repo, fns, default='path')
3389 3431 else:
3390 3432 revmatchfn = matchfn
3391 3433
3392 3434 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3393 3435
3394 3436 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3395 3437 if count == limit:
3396 3438 break
3397 3439 if displayer.flush(ctx.rev()):
3398 3440 count += 1
3399 3441 displayer.close()
3400 3442
3401 3443 @command('manifest',
3402 3444 [('r', 'rev', '', _('revision to display'), _('REV')),
3403 3445 ('', 'all', False, _("list files from all revisions"))],
3404 3446 _('[-r REV]'))
3405 3447 def manifest(ui, repo, node=None, rev=None, **opts):
3406 3448 """output the current or given revision of the project manifest
3407 3449
3408 3450 Print a list of version controlled files for the given revision.
3409 3451 If no revision is given, the first parent of the working directory
3410 3452 is used, or the null revision if no revision is checked out.
3411 3453
3412 3454 With -v, print file permissions, symlink and executable bits.
3413 3455 With --debug, print file revision hashes.
3414 3456
3415 3457 If option --all is specified, the list of all files from all revisions
3416 3458 is printed. This includes deleted and renamed files.
3417 3459
3418 3460 Returns 0 on success.
3419 3461 """
3420 3462 if opts.get('all'):
3421 3463 if rev or node:
3422 3464 raise util.Abort(_("can't specify a revision with --all"))
3423 3465
3424 3466 res = []
3425 3467 prefix = "data/"
3426 3468 suffix = ".i"
3427 3469 plen = len(prefix)
3428 3470 slen = len(suffix)
3429 3471 lock = repo.lock()
3430 3472 try:
3431 3473 for fn, b, size in repo.store.datafiles():
3432 3474 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3433 3475 res.append(fn[plen:-slen])
3434 3476 finally:
3435 3477 lock.release()
3436 3478 for f in sorted(res):
3437 3479 ui.write("%s\n" % f)
3438 3480 return
3439 3481
3440 3482 if rev and node:
3441 3483 raise util.Abort(_("please specify just one revision"))
3442 3484
3443 3485 if not node:
3444 3486 node = rev
3445 3487
3446 3488 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3447 3489 ctx = scmutil.revsingle(repo, node)
3448 3490 for f in ctx:
3449 3491 if ui.debugflag:
3450 3492 ui.write("%40s " % hex(ctx.manifest()[f]))
3451 3493 if ui.verbose:
3452 3494 ui.write(decor[ctx.flags(f)])
3453 3495 ui.write("%s\n" % f)
3454 3496
3455 3497 @command('^merge',
3456 3498 [('f', 'force', None, _('force a merge with outstanding changes')),
3457 3499 ('t', 'tool', '', _('specify merge tool')),
3458 3500 ('r', 'rev', '', _('revision to merge'), _('REV')),
3459 3501 ('P', 'preview', None,
3460 3502 _('review revisions to merge (no merge is performed)'))],
3461 3503 _('[-P] [-f] [[-r] REV]'))
3462 3504 def merge(ui, repo, node=None, **opts):
3463 3505 """merge working directory with another revision
3464 3506
3465 3507 The current working directory is updated with all changes made in
3466 3508 the requested revision since the last common predecessor revision.
3467 3509
3468 3510 Files that changed between either parent are marked as changed for
3469 3511 the next commit and a commit must be performed before any further
3470 3512 updates to the repository are allowed. The next commit will have
3471 3513 two parents.
3472 3514
3473 3515 ``--tool`` can be used to specify the merge tool used for file
3474 3516 merges. It overrides the HGMERGE environment variable and your
3475 3517 configuration files. See :hg:`help merge-tools` for options.
3476 3518
3477 3519 If no revision is specified, the working directory's parent is a
3478 3520 head revision, and the current branch contains exactly one other
3479 3521 head, the other head is merged with by default. Otherwise, an
3480 3522 explicit revision with which to merge with must be provided.
3481 3523
3482 3524 :hg:`resolve` must be used to resolve unresolved files.
3483 3525
3484 3526 To undo an uncommitted merge, use :hg:`update --clean .` which
3485 3527 will check out a clean copy of the original merge parent, losing
3486 3528 all changes.
3487 3529
3488 3530 Returns 0 on success, 1 if there are unresolved files.
3489 3531 """
3490 3532
3491 3533 if opts.get('rev') and node:
3492 3534 raise util.Abort(_("please specify just one revision"))
3493 3535 if not node:
3494 3536 node = opts.get('rev')
3495 3537
3496 3538 if not node:
3497 3539 branch = repo[None].branch()
3498 3540 bheads = repo.branchheads(branch)
3499 3541 if len(bheads) > 2:
3500 3542 raise util.Abort(_("branch '%s' has %d heads - "
3501 3543 "please merge with an explicit rev")
3502 3544 % (branch, len(bheads)),
3503 3545 hint=_("run 'hg heads .' to see heads"))
3504 3546
3505 3547 parent = repo.dirstate.p1()
3506 3548 if len(bheads) == 1:
3507 3549 if len(repo.heads()) > 1:
3508 3550 raise util.Abort(_("branch '%s' has one head - "
3509 3551 "please merge with an explicit rev")
3510 3552 % branch,
3511 3553 hint=_("run 'hg heads' to see all heads"))
3512 3554 msg = _('there is nothing to merge')
3513 3555 if parent != repo.lookup(repo[None].branch()):
3514 3556 msg = _('%s - use "hg update" instead') % msg
3515 3557 raise util.Abort(msg)
3516 3558
3517 3559 if parent not in bheads:
3518 3560 raise util.Abort(_('working directory not at a head revision'),
3519 3561 hint=_("use 'hg update' or merge with an "
3520 3562 "explicit revision"))
3521 3563 node = parent == bheads[0] and bheads[-1] or bheads[0]
3522 3564 else:
3523 3565 node = scmutil.revsingle(repo, node).node()
3524 3566
3525 3567 if opts.get('preview'):
3526 3568 # find nodes that are ancestors of p2 but not of p1
3527 3569 p1 = repo.lookup('.')
3528 3570 p2 = repo.lookup(node)
3529 3571 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3530 3572
3531 3573 displayer = cmdutil.show_changeset(ui, repo, opts)
3532 3574 for node in nodes:
3533 3575 displayer.show(repo[node])
3534 3576 displayer.close()
3535 3577 return 0
3536 3578
3537 3579 try:
3538 3580 # ui.forcemerge is an internal variable, do not document
3539 3581 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3540 3582 return hg.merge(repo, node, force=opts.get('force'))
3541 3583 finally:
3542 3584 ui.setconfig('ui', 'forcemerge', '')
3543 3585
3544 3586 @command('outgoing|out',
3545 3587 [('f', 'force', None, _('run even when the destination is unrelated')),
3546 3588 ('r', 'rev', [],
3547 3589 _('a changeset intended to be included in the destination'), _('REV')),
3548 3590 ('n', 'newest-first', None, _('show newest record first')),
3549 3591 ('B', 'bookmarks', False, _('compare bookmarks')),
3550 3592 ('b', 'branch', [], _('a specific branch you would like to push'),
3551 3593 _('BRANCH')),
3552 3594 ] + logopts + remoteopts + subrepoopts,
3553 3595 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3554 3596 def outgoing(ui, repo, dest=None, **opts):
3555 3597 """show changesets not found in the destination
3556 3598
3557 3599 Show changesets not found in the specified destination repository
3558 3600 or the default push location. These are the changesets that would
3559 3601 be pushed if a push was requested.
3560 3602
3561 3603 See pull for details of valid destination formats.
3562 3604
3563 3605 Returns 0 if there are outgoing changes, 1 otherwise.
3564 3606 """
3565 3607
3566 3608 if opts.get('bookmarks'):
3567 3609 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3568 3610 dest, branches = hg.parseurl(dest, opts.get('branch'))
3569 3611 other = hg.peer(repo, opts, dest)
3570 3612 if 'bookmarks' not in other.listkeys('namespaces'):
3571 3613 ui.warn(_("remote doesn't support bookmarks\n"))
3572 3614 return 0
3573 3615 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3574 3616 return bookmarks.diff(ui, other, repo)
3575 3617
3576 3618 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3577 3619 try:
3578 3620 return hg.outgoing(ui, repo, dest, opts)
3579 3621 finally:
3580 3622 del repo._subtoppath
3581 3623
3582 3624 @command('parents',
3583 3625 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3584 3626 ] + templateopts,
3585 3627 _('[-r REV] [FILE]'))
3586 3628 def parents(ui, repo, file_=None, **opts):
3587 3629 """show the parents of the working directory or revision
3588 3630
3589 3631 Print the working directory's parent revisions. If a revision is
3590 3632 given via -r/--rev, the parent of that revision will be printed.
3591 3633 If a file argument is given, the revision in which the file was
3592 3634 last changed (before the working directory revision or the
3593 3635 argument to --rev if given) is printed.
3594 3636
3595 3637 Returns 0 on success.
3596 3638 """
3597 3639
3598 3640 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3599 3641
3600 3642 if file_:
3601 3643 m = scmutil.match(repo, (file_,), opts)
3602 3644 if m.anypats() or len(m.files()) != 1:
3603 3645 raise util.Abort(_('can only specify an explicit filename'))
3604 3646 file_ = m.files()[0]
3605 3647 filenodes = []
3606 3648 for cp in ctx.parents():
3607 3649 if not cp:
3608 3650 continue
3609 3651 try:
3610 3652 filenodes.append(cp.filenode(file_))
3611 3653 except error.LookupError:
3612 3654 pass
3613 3655 if not filenodes:
3614 3656 raise util.Abort(_("'%s' not found in manifest!") % file_)
3615 3657 fl = repo.file(file_)
3616 3658 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3617 3659 else:
3618 3660 p = [cp.node() for cp in ctx.parents()]
3619 3661
3620 3662 displayer = cmdutil.show_changeset(ui, repo, opts)
3621 3663 for n in p:
3622 3664 if n != nullid:
3623 3665 displayer.show(repo[n])
3624 3666 displayer.close()
3625 3667
3626 3668 @command('paths', [], _('[NAME]'))
3627 3669 def paths(ui, repo, search=None):
3628 3670 """show aliases for remote repositories
3629 3671
3630 3672 Show definition of symbolic path name NAME. If no name is given,
3631 3673 show definition of all available names.
3632 3674
3633 3675 Option -q/--quiet suppresses all output when searching for NAME
3634 3676 and shows only the path names when listing all definitions.
3635 3677
3636 3678 Path names are defined in the [paths] section of your
3637 3679 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3638 3680 repository, ``.hg/hgrc`` is used, too.
3639 3681
3640 3682 The path names ``default`` and ``default-push`` have a special
3641 3683 meaning. When performing a push or pull operation, they are used
3642 3684 as fallbacks if no location is specified on the command-line.
3643 3685 When ``default-push`` is set, it will be used for push and
3644 3686 ``default`` will be used for pull; otherwise ``default`` is used
3645 3687 as the fallback for both. When cloning a repository, the clone
3646 3688 source is written as ``default`` in ``.hg/hgrc``. Note that
3647 3689 ``default`` and ``default-push`` apply to all inbound (e.g.
3648 3690 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3649 3691 :hg:`bundle`) operations.
3650 3692
3651 3693 See :hg:`help urls` for more information.
3652 3694
3653 3695 Returns 0 on success.
3654 3696 """
3655 3697 if search:
3656 3698 for name, path in ui.configitems("paths"):
3657 3699 if name == search:
3658 3700 ui.status("%s\n" % util.hidepassword(path))
3659 3701 return
3660 3702 if not ui.quiet:
3661 3703 ui.warn(_("not found!\n"))
3662 3704 return 1
3663 3705 else:
3664 3706 for name, path in ui.configitems("paths"):
3665 3707 if ui.quiet:
3666 3708 ui.write("%s\n" % name)
3667 3709 else:
3668 3710 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3669 3711
3670 3712 def postincoming(ui, repo, modheads, optupdate, checkout):
3671 3713 if modheads == 0:
3672 3714 return
3673 3715 if optupdate:
3674 3716 try:
3675 3717 return hg.update(repo, checkout)
3676 3718 except util.Abort, inst:
3677 3719 ui.warn(_("not updating: %s\n" % str(inst)))
3678 3720 return 0
3679 3721 if modheads > 1:
3680 3722 currentbranchheads = len(repo.branchheads())
3681 3723 if currentbranchheads == modheads:
3682 3724 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3683 3725 elif currentbranchheads > 1:
3684 3726 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3685 3727 else:
3686 3728 ui.status(_("(run 'hg heads' to see heads)\n"))
3687 3729 else:
3688 3730 ui.status(_("(run 'hg update' to get a working copy)\n"))
3689 3731
3690 3732 @command('^pull',
3691 3733 [('u', 'update', None,
3692 3734 _('update to new branch head if changesets were pulled')),
3693 3735 ('f', 'force', None, _('run even when remote repository is unrelated')),
3694 3736 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3695 3737 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3696 3738 ('b', 'branch', [], _('a specific branch you would like to pull'),
3697 3739 _('BRANCH')),
3698 3740 ] + remoteopts,
3699 3741 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3700 3742 def pull(ui, repo, source="default", **opts):
3701 3743 """pull changes from the specified source
3702 3744
3703 3745 Pull changes from a remote repository to a local one.
3704 3746
3705 3747 This finds all changes from the repository at the specified path
3706 3748 or URL and adds them to a local repository (the current one unless
3707 3749 -R is specified). By default, this does not update the copy of the
3708 3750 project in the working directory.
3709 3751
3710 3752 Use :hg:`incoming` if you want to see what would have been added
3711 3753 by a pull at the time you issued this command. If you then decide
3712 3754 to add those changes to the repository, you should use :hg:`pull
3713 3755 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3714 3756
3715 3757 If SOURCE is omitted, the 'default' path will be used.
3716 3758 See :hg:`help urls` for more information.
3717 3759
3718 3760 Returns 0 on success, 1 if an update had unresolved files.
3719 3761 """
3720 3762 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3721 3763 other = hg.peer(repo, opts, source)
3722 3764 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3723 3765 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3724 3766
3725 3767 if opts.get('bookmark'):
3726 3768 if not revs:
3727 3769 revs = []
3728 3770 rb = other.listkeys('bookmarks')
3729 3771 for b in opts['bookmark']:
3730 3772 if b not in rb:
3731 3773 raise util.Abort(_('remote bookmark %s not found!') % b)
3732 3774 revs.append(rb[b])
3733 3775
3734 3776 if revs:
3735 3777 try:
3736 3778 revs = [other.lookup(rev) for rev in revs]
3737 3779 except error.CapabilityError:
3738 3780 err = _("other repository doesn't support revision lookup, "
3739 3781 "so a rev cannot be specified.")
3740 3782 raise util.Abort(err)
3741 3783
3742 3784 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3743 3785 bookmarks.updatefromremote(ui, repo, other)
3744 3786 if checkout:
3745 3787 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3746 3788 repo._subtoppath = source
3747 3789 try:
3748 3790 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3749 3791
3750 3792 finally:
3751 3793 del repo._subtoppath
3752 3794
3753 3795 # update specified bookmarks
3754 3796 if opts.get('bookmark'):
3755 3797 for b in opts['bookmark']:
3756 3798 # explicit pull overrides local bookmark if any
3757 3799 ui.status(_("importing bookmark %s\n") % b)
3758 3800 repo._bookmarks[b] = repo[rb[b]].node()
3759 3801 bookmarks.write(repo)
3760 3802
3761 3803 return ret
3762 3804
3763 3805 @command('^push',
3764 3806 [('f', 'force', None, _('force push')),
3765 3807 ('r', 'rev', [],
3766 3808 _('a changeset intended to be included in the destination'),
3767 3809 _('REV')),
3768 3810 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3769 3811 ('b', 'branch', [],
3770 3812 _('a specific branch you would like to push'), _('BRANCH')),
3771 3813 ('', 'new-branch', False, _('allow pushing a new branch')),
3772 3814 ] + remoteopts,
3773 3815 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3774 3816 def push(ui, repo, dest=None, **opts):
3775 3817 """push changes to the specified destination
3776 3818
3777 3819 Push changesets from the local repository to the specified
3778 3820 destination.
3779 3821
3780 3822 This operation is symmetrical to pull: it is identical to a pull
3781 3823 in the destination repository from the current one.
3782 3824
3783 3825 By default, push will not allow creation of new heads at the
3784 3826 destination, since multiple heads would make it unclear which head
3785 3827 to use. In this situation, it is recommended to pull and merge
3786 3828 before pushing.
3787 3829
3788 3830 Use --new-branch if you want to allow push to create a new named
3789 3831 branch that is not present at the destination. This allows you to
3790 3832 only create a new branch without forcing other changes.
3791 3833
3792 3834 Use -f/--force to override the default behavior and push all
3793 3835 changesets on all branches.
3794 3836
3795 3837 If -r/--rev is used, the specified revision and all its ancestors
3796 3838 will be pushed to the remote repository.
3797 3839
3798 3840 Please see :hg:`help urls` for important details about ``ssh://``
3799 3841 URLs. If DESTINATION is omitted, a default path will be used.
3800 3842
3801 3843 Returns 0 if push was successful, 1 if nothing to push.
3802 3844 """
3803 3845
3804 3846 if opts.get('bookmark'):
3805 3847 for b in opts['bookmark']:
3806 3848 # translate -B options to -r so changesets get pushed
3807 3849 if b in repo._bookmarks:
3808 3850 opts.setdefault('rev', []).append(b)
3809 3851 else:
3810 3852 # if we try to push a deleted bookmark, translate it to null
3811 3853 # this lets simultaneous -r, -b options continue working
3812 3854 opts.setdefault('rev', []).append("null")
3813 3855
3814 3856 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3815 3857 dest, branches = hg.parseurl(dest, opts.get('branch'))
3816 3858 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3817 3859 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3818 3860 other = hg.peer(repo, opts, dest)
3819 3861 if revs:
3820 3862 revs = [repo.lookup(rev) for rev in revs]
3821 3863
3822 3864 repo._subtoppath = dest
3823 3865 try:
3824 3866 # push subrepos depth-first for coherent ordering
3825 3867 c = repo['']
3826 3868 subs = c.substate # only repos that are committed
3827 3869 for s in sorted(subs):
3828 3870 if not c.sub(s).push(opts.get('force')):
3829 3871 return False
3830 3872 finally:
3831 3873 del repo._subtoppath
3832 3874 result = repo.push(other, opts.get('force'), revs=revs,
3833 3875 newbranch=opts.get('new_branch'))
3834 3876
3835 3877 result = (result == 0)
3836 3878
3837 3879 if opts.get('bookmark'):
3838 3880 rb = other.listkeys('bookmarks')
3839 3881 for b in opts['bookmark']:
3840 3882 # explicit push overrides remote bookmark if any
3841 3883 if b in repo._bookmarks:
3842 3884 ui.status(_("exporting bookmark %s\n") % b)
3843 3885 new = repo[b].hex()
3844 3886 elif b in rb:
3845 3887 ui.status(_("deleting remote bookmark %s\n") % b)
3846 3888 new = '' # delete
3847 3889 else:
3848 3890 ui.warn(_('bookmark %s does not exist on the local '
3849 3891 'or remote repository!\n') % b)
3850 3892 return 2
3851 3893 old = rb.get(b, '')
3852 3894 r = other.pushkey('bookmarks', b, old, new)
3853 3895 if not r:
3854 3896 ui.warn(_('updating bookmark %s failed!\n') % b)
3855 3897 if not result:
3856 3898 result = 2
3857 3899
3858 3900 return result
3859 3901
3860 3902 @command('recover', [])
3861 3903 def recover(ui, repo):
3862 3904 """roll back an interrupted transaction
3863 3905
3864 3906 Recover from an interrupted commit or pull.
3865 3907
3866 3908 This command tries to fix the repository status after an
3867 3909 interrupted operation. It should only be necessary when Mercurial
3868 3910 suggests it.
3869 3911
3870 3912 Returns 0 if successful, 1 if nothing to recover or verify fails.
3871 3913 """
3872 3914 if repo.recover():
3873 3915 return hg.verify(repo)
3874 3916 return 1
3875 3917
3876 3918 @command('^remove|rm',
3877 3919 [('A', 'after', None, _('record delete for missing files')),
3878 3920 ('f', 'force', None,
3879 3921 _('remove (and delete) file even if added or modified')),
3880 3922 ] + walkopts,
3881 3923 _('[OPTION]... FILE...'))
3882 3924 def remove(ui, repo, *pats, **opts):
3883 3925 """remove the specified files on the next commit
3884 3926
3885 3927 Schedule the indicated files for removal from the repository.
3886 3928
3887 3929 This only removes files from the current branch, not from the
3888 3930 entire project history. -A/--after can be used to remove only
3889 3931 files that have already been deleted, -f/--force can be used to
3890 3932 force deletion, and -Af can be used to remove files from the next
3891 3933 revision without deleting them from the working directory.
3892 3934
3893 3935 The following table details the behavior of remove for different
3894 3936 file states (columns) and option combinations (rows). The file
3895 3937 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3896 3938 reported by :hg:`status`). The actions are Warn, Remove (from
3897 3939 branch) and Delete (from disk)::
3898 3940
3899 3941 A C M !
3900 3942 none W RD W R
3901 3943 -f R RD RD R
3902 3944 -A W W W R
3903 3945 -Af R R R R
3904 3946
3905 3947 Note that remove never deletes files in Added [A] state from the
3906 3948 working directory, not even if option --force is specified.
3907 3949
3908 3950 This command schedules the files to be removed at the next commit.
3909 3951 To undo a remove before that, see :hg:`revert`.
3910 3952
3911 3953 Returns 0 on success, 1 if any warnings encountered.
3912 3954 """
3913 3955
3914 3956 ret = 0
3915 3957 after, force = opts.get('after'), opts.get('force')
3916 3958 if not pats and not after:
3917 3959 raise util.Abort(_('no files specified'))
3918 3960
3919 3961 m = scmutil.match(repo, pats, opts)
3920 3962 s = repo.status(match=m, clean=True)
3921 3963 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3922 3964
3923 3965 for f in m.files():
3924 3966 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3925 3967 if os.path.exists(m.rel(f)):
3926 3968 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3927 3969 ret = 1
3928 3970
3929 3971 if force:
3930 3972 list = modified + deleted + clean + added
3931 3973 elif after:
3932 3974 list = deleted
3933 3975 for f in modified + added + clean:
3934 3976 ui.warn(_('not removing %s: file still exists (use -f'
3935 3977 ' to force removal)\n') % m.rel(f))
3936 3978 ret = 1
3937 3979 else:
3938 3980 list = deleted + clean
3939 3981 for f in modified:
3940 3982 ui.warn(_('not removing %s: file is modified (use -f'
3941 3983 ' to force removal)\n') % m.rel(f))
3942 3984 ret = 1
3943 3985 for f in added:
3944 3986 ui.warn(_('not removing %s: file has been marked for add (use -f'
3945 3987 ' to force removal)\n') % m.rel(f))
3946 3988 ret = 1
3947 3989
3948 3990 for f in sorted(list):
3949 3991 if ui.verbose or not m.exact(f):
3950 3992 ui.status(_('removing %s\n') % m.rel(f))
3951 3993
3952 3994 wlock = repo.wlock()
3953 3995 try:
3954 3996 if not after:
3955 3997 for f in list:
3956 3998 if f in added:
3957 3999 continue # we never unlink added files on remove
3958 4000 try:
3959 4001 util.unlinkpath(repo.wjoin(f))
3960 4002 except OSError, inst:
3961 4003 if inst.errno != errno.ENOENT:
3962 4004 raise
3963 4005 repo[None].forget(list)
3964 4006 finally:
3965 4007 wlock.release()
3966 4008
3967 4009 return ret
3968 4010
3969 4011 @command('rename|move|mv',
3970 4012 [('A', 'after', None, _('record a rename that has already occurred')),
3971 4013 ('f', 'force', None, _('forcibly copy over an existing managed file')),
3972 4014 ] + walkopts + dryrunopts,
3973 4015 _('[OPTION]... SOURCE... DEST'))
3974 4016 def rename(ui, repo, *pats, **opts):
3975 4017 """rename files; equivalent of copy + remove
3976 4018
3977 4019 Mark dest as copies of sources; mark sources for deletion. If dest
3978 4020 is a directory, copies are put in that directory. If dest is a
3979 4021 file, there can only be one source.
3980 4022
3981 4023 By default, this command copies the contents of files as they
3982 4024 exist in the working directory. If invoked with -A/--after, the
3983 4025 operation is recorded, but no copying is performed.
3984 4026
3985 4027 This command takes effect at the next commit. To undo a rename
3986 4028 before that, see :hg:`revert`.
3987 4029
3988 4030 Returns 0 on success, 1 if errors are encountered.
3989 4031 """
3990 4032 wlock = repo.wlock(False)
3991 4033 try:
3992 4034 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3993 4035 finally:
3994 4036 wlock.release()
3995 4037
3996 4038 @command('resolve',
3997 4039 [('a', 'all', None, _('select all unresolved files')),
3998 4040 ('l', 'list', None, _('list state of files needing merge')),
3999 4041 ('m', 'mark', None, _('mark files as resolved')),
4000 4042 ('u', 'unmark', None, _('mark files as unresolved')),
4001 4043 ('t', 'tool', '', _('specify merge tool')),
4002 4044 ('n', 'no-status', None, _('hide status prefix'))]
4003 4045 + walkopts,
4004 4046 _('[OPTION]... [FILE]...'))
4005 4047 def resolve(ui, repo, *pats, **opts):
4006 4048 """redo merges or set/view the merge status of files
4007 4049
4008 4050 Merges with unresolved conflicts are often the result of
4009 4051 non-interactive merging using the ``internal:merge`` configuration
4010 4052 setting, or a command-line merge tool like ``diff3``. The resolve
4011 4053 command is used to manage the files involved in a merge, after
4012 4054 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4013 4055 working directory must have two parents).
4014 4056
4015 4057 The resolve command can be used in the following ways:
4016 4058
4017 4059 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4018 4060 files, discarding any previous merge attempts. Re-merging is not
4019 4061 performed for files already marked as resolved. Use ``--all/-a``
4020 4062 to selects all unresolved files. ``--tool`` can be used to specify
4021 4063 the merge tool used for the given files. It overrides the HGMERGE
4022 4064 environment variable and your configuration files.
4023 4065
4024 4066 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4025 4067 (e.g. after having manually fixed-up the files). The default is
4026 4068 to mark all unresolved files.
4027 4069
4028 4070 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4029 4071 default is to mark all resolved files.
4030 4072
4031 4073 - :hg:`resolve -l`: list files which had or still have conflicts.
4032 4074 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4033 4075
4034 4076 Note that Mercurial will not let you commit files with unresolved
4035 4077 merge conflicts. You must use :hg:`resolve -m ...` before you can
4036 4078 commit after a conflicting merge.
4037 4079
4038 4080 Returns 0 on success, 1 if any files fail a resolve attempt.
4039 4081 """
4040 4082
4041 4083 all, mark, unmark, show, nostatus = \
4042 4084 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4043 4085
4044 4086 if (show and (mark or unmark)) or (mark and unmark):
4045 4087 raise util.Abort(_("too many options specified"))
4046 4088 if pats and all:
4047 4089 raise util.Abort(_("can't specify --all and patterns"))
4048 4090 if not (all or pats or show or mark or unmark):
4049 4091 raise util.Abort(_('no files or directories specified; '
4050 4092 'use --all to remerge all files'))
4051 4093
4052 4094 ms = mergemod.mergestate(repo)
4053 4095 m = scmutil.match(repo, pats, opts)
4054 4096 ret = 0
4055 4097
4056 4098 for f in ms:
4057 4099 if m(f):
4058 4100 if show:
4059 4101 if nostatus:
4060 4102 ui.write("%s\n" % f)
4061 4103 else:
4062 4104 ui.write("%s %s\n" % (ms[f].upper(), f),
4063 4105 label='resolve.' +
4064 4106 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4065 4107 elif mark:
4066 4108 ms.mark(f, "r")
4067 4109 elif unmark:
4068 4110 ms.mark(f, "u")
4069 4111 else:
4070 4112 wctx = repo[None]
4071 4113 mctx = wctx.parents()[-1]
4072 4114
4073 4115 # backup pre-resolve (merge uses .orig for its own purposes)
4074 4116 a = repo.wjoin(f)
4075 4117 util.copyfile(a, a + ".resolve")
4076 4118
4077 4119 try:
4078 4120 # resolve file
4079 4121 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4080 4122 if ms.resolve(f, wctx, mctx):
4081 4123 ret = 1
4082 4124 finally:
4083 4125 ui.setconfig('ui', 'forcemerge', '')
4084 4126
4085 4127 # replace filemerge's .orig file with our resolve file
4086 4128 util.rename(a + ".resolve", a + ".orig")
4087 4129
4088 4130 ms.commit()
4089 4131 return ret
4090 4132
4091 4133 @command('revert',
4092 4134 [('a', 'all', None, _('revert all changes when no arguments given')),
4093 4135 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4094 4136 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4095 4137 ('', 'no-backup', None, _('do not save backup copies of files')),
4096 4138 ] + walkopts + dryrunopts,
4097 4139 _('[OPTION]... [-r REV] [NAME]...'))
4098 4140 def revert(ui, repo, *pats, **opts):
4099 4141 """restore files to their checkout state
4100 4142
4101 4143 .. note::
4102 4144 To check out earlier revisions, you should use :hg:`update REV`.
4103 4145 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4104 4146
4105 4147 With no revision specified, revert the specified files or directories
4106 4148 to the state they had in the first parent of the working directory.
4107 4149 This restores the contents of files to an unmodified
4108 4150 state and unschedules adds, removes, copies, and renames.
4109 4151
4110 4152 Using the -r/--rev or -d/--date options, revert the given files or
4111 4153 directories to their states as of a specific revision. Because
4112 4154 revert does not change the working directory parents, this will
4113 4155 cause these files to appear modified. This can be helpful to "back
4114 4156 out" some or all of an earlier change. See :hg:`backout` for a
4115 4157 related method.
4116 4158
4117 4159 Modified files are saved with a .orig suffix before reverting.
4118 4160 To disable these backups, use --no-backup.
4119 4161
4120 4162 See :hg:`help dates` for a list of formats valid for -d/--date.
4121 4163
4122 4164 Returns 0 on success.
4123 4165 """
4124 4166
4125 4167 if opts.get("date"):
4126 4168 if opts.get("rev"):
4127 4169 raise util.Abort(_("you can't specify a revision and a date"))
4128 4170 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4129 4171
4130 4172 parent, p2 = repo.dirstate.parents()
4131 4173
4132 4174 if not pats and not opts.get('all'):
4133 4175 raise util.Abort(_('no files or directories specified'),
4134 4176 hint=_('use --all to revert all files'))
4135 4177
4136 4178 ctx = scmutil.revsingle(repo, opts.get('rev'))
4137 4179 node = ctx.node()
4138 4180 mf = ctx.manifest()
4139 4181 if node == parent:
4140 4182 pmf = mf
4141 4183 else:
4142 4184 pmf = None
4143 4185
4144 4186 # need all matching names in dirstate and manifest of target rev,
4145 4187 # so have to walk both. do not print errors if files exist in one
4146 4188 # but not other.
4147 4189
4148 4190 names = {}
4149 4191
4150 4192 wlock = repo.wlock()
4151 4193 try:
4152 4194 # walk dirstate.
4153 4195
4154 4196 m = scmutil.match(repo, pats, opts)
4155 4197 m.bad = lambda x, y: False
4156 4198 for abs in repo.walk(m):
4157 4199 names[abs] = m.rel(abs), m.exact(abs)
4158 4200
4159 4201 # walk target manifest.
4160 4202
4161 4203 def badfn(path, msg):
4162 4204 if path in names:
4163 4205 return
4164 4206 path_ = path + '/'
4165 4207 for f in names:
4166 4208 if f.startswith(path_):
4167 4209 return
4168 4210 ui.warn("%s: %s\n" % (m.rel(path), msg))
4169 4211
4170 4212 m = scmutil.match(repo, pats, opts)
4171 4213 m.bad = badfn
4172 4214 for abs in repo[node].walk(m):
4173 4215 if abs not in names:
4174 4216 names[abs] = m.rel(abs), m.exact(abs)
4175 4217
4176 4218 m = scmutil.matchfiles(repo, names)
4177 4219 changes = repo.status(match=m)[:4]
4178 4220 modified, added, removed, deleted = map(set, changes)
4179 4221
4180 4222 # if f is a rename, also revert the source
4181 4223 cwd = repo.getcwd()
4182 4224 for f in added:
4183 4225 src = repo.dirstate.copied(f)
4184 4226 if src and src not in names and repo.dirstate[src] == 'r':
4185 4227 removed.add(src)
4186 4228 names[src] = (repo.pathto(src, cwd), True)
4187 4229
4188 4230 def removeforget(abs):
4189 4231 if repo.dirstate[abs] == 'a':
4190 4232 return _('forgetting %s\n')
4191 4233 return _('removing %s\n')
4192 4234
4193 4235 revert = ([], _('reverting %s\n'))
4194 4236 add = ([], _('adding %s\n'))
4195 4237 remove = ([], removeforget)
4196 4238 undelete = ([], _('undeleting %s\n'))
4197 4239
4198 4240 disptable = (
4199 4241 # dispatch table:
4200 4242 # file state
4201 4243 # action if in target manifest
4202 4244 # action if not in target manifest
4203 4245 # make backup if in target manifest
4204 4246 # make backup if not in target manifest
4205 4247 (modified, revert, remove, True, True),
4206 4248 (added, revert, remove, True, False),
4207 4249 (removed, undelete, None, False, False),
4208 4250 (deleted, revert, remove, False, False),
4209 4251 )
4210 4252
4211 4253 for abs, (rel, exact) in sorted(names.items()):
4212 4254 mfentry = mf.get(abs)
4213 4255 target = repo.wjoin(abs)
4214 4256 def handle(xlist, dobackup):
4215 4257 xlist[0].append(abs)
4216 4258 if (dobackup and not opts.get('no_backup') and
4217 4259 os.path.lexists(target)):
4218 4260 bakname = "%s.orig" % rel
4219 4261 ui.note(_('saving current version of %s as %s\n') %
4220 4262 (rel, bakname))
4221 4263 if not opts.get('dry_run'):
4222 4264 util.rename(target, bakname)
4223 4265 if ui.verbose or not exact:
4224 4266 msg = xlist[1]
4225 4267 if not isinstance(msg, basestring):
4226 4268 msg = msg(abs)
4227 4269 ui.status(msg % rel)
4228 4270 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4229 4271 if abs not in table:
4230 4272 continue
4231 4273 # file has changed in dirstate
4232 4274 if mfentry:
4233 4275 handle(hitlist, backuphit)
4234 4276 elif misslist is not None:
4235 4277 handle(misslist, backupmiss)
4236 4278 break
4237 4279 else:
4238 4280 if abs not in repo.dirstate:
4239 4281 if mfentry:
4240 4282 handle(add, True)
4241 4283 elif exact:
4242 4284 ui.warn(_('file not managed: %s\n') % rel)
4243 4285 continue
4244 4286 # file has not changed in dirstate
4245 4287 if node == parent:
4246 4288 if exact:
4247 4289 ui.warn(_('no changes needed to %s\n') % rel)
4248 4290 continue
4249 4291 if pmf is None:
4250 4292 # only need parent manifest in this unlikely case,
4251 4293 # so do not read by default
4252 4294 pmf = repo[parent].manifest()
4253 4295 if abs in pmf:
4254 4296 if mfentry:
4255 4297 # if version of file is same in parent and target
4256 4298 # manifests, do nothing
4257 4299 if (pmf[abs] != mfentry or
4258 4300 pmf.flags(abs) != mf.flags(abs)):
4259 4301 handle(revert, False)
4260 4302 else:
4261 4303 handle(remove, False)
4262 4304
4263 4305 if not opts.get('dry_run'):
4264 4306 def checkout(f):
4265 4307 fc = ctx[f]
4266 4308 repo.wwrite(f, fc.data(), fc.flags())
4267 4309
4268 4310 audit_path = scmutil.pathauditor(repo.root)
4269 4311 for f in remove[0]:
4270 4312 if repo.dirstate[f] == 'a':
4271 4313 repo.dirstate.drop(f)
4272 4314 continue
4273 4315 audit_path(f)
4274 4316 try:
4275 4317 util.unlinkpath(repo.wjoin(f))
4276 4318 except OSError:
4277 4319 pass
4278 4320 repo.dirstate.remove(f)
4279 4321
4280 4322 normal = None
4281 4323 if node == parent:
4282 4324 # We're reverting to our parent. If possible, we'd like status
4283 4325 # to report the file as clean. We have to use normallookup for
4284 4326 # merges to avoid losing information about merged/dirty files.
4285 4327 if p2 != nullid:
4286 4328 normal = repo.dirstate.normallookup
4287 4329 else:
4288 4330 normal = repo.dirstate.normal
4289 4331 for f in revert[0]:
4290 4332 checkout(f)
4291 4333 if normal:
4292 4334 normal(f)
4293 4335
4294 4336 for f in add[0]:
4295 4337 checkout(f)
4296 4338 repo.dirstate.add(f)
4297 4339
4298 4340 normal = repo.dirstate.normallookup
4299 4341 if node == parent and p2 == nullid:
4300 4342 normal = repo.dirstate.normal
4301 4343 for f in undelete[0]:
4302 4344 checkout(f)
4303 4345 normal(f)
4304 4346
4305 4347 finally:
4306 4348 wlock.release()
4307 4349
4308 4350 @command('rollback', dryrunopts)
4309 4351 def rollback(ui, repo, **opts):
4310 4352 """roll back the last transaction (dangerous)
4311 4353
4312 4354 This command should be used with care. There is only one level of
4313 4355 rollback, and there is no way to undo a rollback. It will also
4314 4356 restore the dirstate at the time of the last transaction, losing
4315 4357 any dirstate changes since that time. This command does not alter
4316 4358 the working directory.
4317 4359
4318 4360 Transactions are used to encapsulate the effects of all commands
4319 4361 that create new changesets or propagate existing changesets into a
4320 4362 repository. For example, the following commands are transactional,
4321 4363 and their effects can be rolled back:
4322 4364
4323 4365 - commit
4324 4366 - import
4325 4367 - pull
4326 4368 - push (with this repository as the destination)
4327 4369 - unbundle
4328 4370
4329 4371 This command is not intended for use on public repositories. Once
4330 4372 changes are visible for pull by other users, rolling a transaction
4331 4373 back locally is ineffective (someone else may already have pulled
4332 4374 the changes). Furthermore, a race is possible with readers of the
4333 4375 repository; for example an in-progress pull from the repository
4334 4376 may fail if a rollback is performed.
4335 4377
4336 4378 Returns 0 on success, 1 if no rollback data is available.
4337 4379 """
4338 4380 return repo.rollback(opts.get('dry_run'))
4339 4381
4340 4382 @command('root', [])
4341 4383 def root(ui, repo):
4342 4384 """print the root (top) of the current working directory
4343 4385
4344 4386 Print the root directory of the current repository.
4345 4387
4346 4388 Returns 0 on success.
4347 4389 """
4348 4390 ui.write(repo.root + "\n")
4349 4391
4350 4392 @command('^serve',
4351 4393 [('A', 'accesslog', '', _('name of access log file to write to'),
4352 4394 _('FILE')),
4353 4395 ('d', 'daemon', None, _('run server in background')),
4354 4396 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4355 4397 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4356 4398 # use string type, then we can check if something was passed
4357 4399 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4358 4400 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4359 4401 _('ADDR')),
4360 4402 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4361 4403 _('PREFIX')),
4362 4404 ('n', 'name', '',
4363 4405 _('name to show in web pages (default: working directory)'), _('NAME')),
4364 4406 ('', 'web-conf', '',
4365 4407 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4366 4408 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4367 4409 _('FILE')),
4368 4410 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4369 4411 ('', 'stdio', None, _('for remote clients')),
4370 4412 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4371 4413 ('', 'style', '', _('template style to use'), _('STYLE')),
4372 4414 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4373 4415 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4374 4416 _('[OPTION]...'))
4375 4417 def serve(ui, repo, **opts):
4376 4418 """start stand-alone webserver
4377 4419
4378 4420 Start a local HTTP repository browser and pull server. You can use
4379 4421 this for ad-hoc sharing and browsing of repositories. It is
4380 4422 recommended to use a real web server to serve a repository for
4381 4423 longer periods of time.
4382 4424
4383 4425 Please note that the server does not implement access control.
4384 4426 This means that, by default, anybody can read from the server and
4385 4427 nobody can write to it by default. Set the ``web.allow_push``
4386 4428 option to ``*`` to allow everybody to push to the server. You
4387 4429 should use a real web server if you need to authenticate users.
4388 4430
4389 4431 By default, the server logs accesses to stdout and errors to
4390 4432 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4391 4433 files.
4392 4434
4393 4435 To have the server choose a free port number to listen on, specify
4394 4436 a port number of 0; in this case, the server will print the port
4395 4437 number it uses.
4396 4438
4397 4439 Returns 0 on success.
4398 4440 """
4399 4441
4400 4442 if opts["stdio"]:
4401 4443 if repo is None:
4402 4444 raise error.RepoError(_("There is no Mercurial repository here"
4403 4445 " (.hg not found)"))
4404 4446 s = sshserver.sshserver(ui, repo)
4405 4447 s.serve_forever()
4406 4448
4407 4449 # this way we can check if something was given in the command-line
4408 4450 if opts.get('port'):
4409 4451 opts['port'] = util.getport(opts.get('port'))
4410 4452
4411 4453 baseui = repo and repo.baseui or ui
4412 4454 optlist = ("name templates style address port prefix ipv6"
4413 4455 " accesslog errorlog certificate encoding")
4414 4456 for o in optlist.split():
4415 4457 val = opts.get(o, '')
4416 4458 if val in (None, ''): # should check against default options instead
4417 4459 continue
4418 4460 baseui.setconfig("web", o, val)
4419 4461 if repo and repo.ui != baseui:
4420 4462 repo.ui.setconfig("web", o, val)
4421 4463
4422 4464 o = opts.get('web_conf') or opts.get('webdir_conf')
4423 4465 if not o:
4424 4466 if not repo:
4425 4467 raise error.RepoError(_("There is no Mercurial repository"
4426 4468 " here (.hg not found)"))
4427 4469 o = repo.root
4428 4470
4429 4471 app = hgweb.hgweb(o, baseui=ui)
4430 4472
4431 4473 class service(object):
4432 4474 def init(self):
4433 4475 util.setsignalhandler()
4434 4476 self.httpd = hgweb.server.create_server(ui, app)
4435 4477
4436 4478 if opts['port'] and not ui.verbose:
4437 4479 return
4438 4480
4439 4481 if self.httpd.prefix:
4440 4482 prefix = self.httpd.prefix.strip('/') + '/'
4441 4483 else:
4442 4484 prefix = ''
4443 4485
4444 4486 port = ':%d' % self.httpd.port
4445 4487 if port == ':80':
4446 4488 port = ''
4447 4489
4448 4490 bindaddr = self.httpd.addr
4449 4491 if bindaddr == '0.0.0.0':
4450 4492 bindaddr = '*'
4451 4493 elif ':' in bindaddr: # IPv6
4452 4494 bindaddr = '[%s]' % bindaddr
4453 4495
4454 4496 fqaddr = self.httpd.fqaddr
4455 4497 if ':' in fqaddr:
4456 4498 fqaddr = '[%s]' % fqaddr
4457 4499 if opts['port']:
4458 4500 write = ui.status
4459 4501 else:
4460 4502 write = ui.write
4461 4503 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4462 4504 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4463 4505
4464 4506 def run(self):
4465 4507 self.httpd.serve_forever()
4466 4508
4467 4509 service = service()
4468 4510
4469 4511 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4470 4512
4471 4513 @command('showconfig|debugconfig',
4472 4514 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4473 4515 _('[-u] [NAME]...'))
4474 4516 def showconfig(ui, repo, *values, **opts):
4475 4517 """show combined config settings from all hgrc files
4476 4518
4477 4519 With no arguments, print names and values of all config items.
4478 4520
4479 4521 With one argument of the form section.name, print just the value
4480 4522 of that config item.
4481 4523
4482 4524 With multiple arguments, print names and values of all config
4483 4525 items with matching section names.
4484 4526
4485 4527 With --debug, the source (filename and line number) is printed
4486 4528 for each config item.
4487 4529
4488 4530 Returns 0 on success.
4489 4531 """
4490 4532
4491 4533 for f in scmutil.rcpath():
4492 4534 ui.debug(_('read config from: %s\n') % f)
4493 4535 untrusted = bool(opts.get('untrusted'))
4494 4536 if values:
4495 4537 sections = [v for v in values if '.' not in v]
4496 4538 items = [v for v in values if '.' in v]
4497 4539 if len(items) > 1 or items and sections:
4498 4540 raise util.Abort(_('only one config item permitted'))
4499 4541 for section, name, value in ui.walkconfig(untrusted=untrusted):
4500 4542 value = str(value).replace('\n', '\\n')
4501 4543 sectname = section + '.' + name
4502 4544 if values:
4503 4545 for v in values:
4504 4546 if v == section:
4505 4547 ui.debug('%s: ' %
4506 4548 ui.configsource(section, name, untrusted))
4507 4549 ui.write('%s=%s\n' % (sectname, value))
4508 4550 elif v == sectname:
4509 4551 ui.debug('%s: ' %
4510 4552 ui.configsource(section, name, untrusted))
4511 4553 ui.write(value, '\n')
4512 4554 else:
4513 4555 ui.debug('%s: ' %
4514 4556 ui.configsource(section, name, untrusted))
4515 4557 ui.write('%s=%s\n' % (sectname, value))
4516 4558
4517 4559 @command('^status|st',
4518 4560 [('A', 'all', None, _('show status of all files')),
4519 4561 ('m', 'modified', None, _('show only modified files')),
4520 4562 ('a', 'added', None, _('show only added files')),
4521 4563 ('r', 'removed', None, _('show only removed files')),
4522 4564 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4523 4565 ('c', 'clean', None, _('show only files without changes')),
4524 4566 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4525 4567 ('i', 'ignored', None, _('show only ignored files')),
4526 4568 ('n', 'no-status', None, _('hide status prefix')),
4527 4569 ('C', 'copies', None, _('show source of copied files')),
4528 4570 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4529 4571 ('', 'rev', [], _('show difference from revision'), _('REV')),
4530 4572 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4531 4573 ] + walkopts + subrepoopts,
4532 4574 _('[OPTION]... [FILE]...'))
4533 4575 def status(ui, repo, *pats, **opts):
4534 4576 """show changed files in the working directory
4535 4577
4536 4578 Show status of files in the repository. If names are given, only
4537 4579 files that match are shown. Files that are clean or ignored or
4538 4580 the source of a copy/move operation, are not listed unless
4539 4581 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4540 4582 Unless options described with "show only ..." are given, the
4541 4583 options -mardu are used.
4542 4584
4543 4585 Option -q/--quiet hides untracked (unknown and ignored) files
4544 4586 unless explicitly requested with -u/--unknown or -i/--ignored.
4545 4587
4546 4588 .. note::
4547 4589 status may appear to disagree with diff if permissions have
4548 4590 changed or a merge has occurred. The standard diff format does
4549 4591 not report permission changes and diff only reports changes
4550 4592 relative to one merge parent.
4551 4593
4552 4594 If one revision is given, it is used as the base revision.
4553 4595 If two revisions are given, the differences between them are
4554 4596 shown. The --change option can also be used as a shortcut to list
4555 4597 the changed files of a revision from its first parent.
4556 4598
4557 4599 The codes used to show the status of files are::
4558 4600
4559 4601 M = modified
4560 4602 A = added
4561 4603 R = removed
4562 4604 C = clean
4563 4605 ! = missing (deleted by non-hg command, but still tracked)
4564 4606 ? = not tracked
4565 4607 I = ignored
4566 4608 = origin of the previous file listed as A (added)
4567 4609
4568 4610 Returns 0 on success.
4569 4611 """
4570 4612
4571 4613 revs = opts.get('rev')
4572 4614 change = opts.get('change')
4573 4615
4574 4616 if revs and change:
4575 4617 msg = _('cannot specify --rev and --change at the same time')
4576 4618 raise util.Abort(msg)
4577 4619 elif change:
4578 4620 node2 = repo.lookup(change)
4579 4621 node1 = repo[node2].p1().node()
4580 4622 else:
4581 4623 node1, node2 = scmutil.revpair(repo, revs)
4582 4624
4583 4625 cwd = (pats and repo.getcwd()) or ''
4584 4626 end = opts.get('print0') and '\0' or '\n'
4585 4627 copy = {}
4586 4628 states = 'modified added removed deleted unknown ignored clean'.split()
4587 4629 show = [k for k in states if opts.get(k)]
4588 4630 if opts.get('all'):
4589 4631 show += ui.quiet and (states[:4] + ['clean']) or states
4590 4632 if not show:
4591 4633 show = ui.quiet and states[:4] or states[:5]
4592 4634
4593 4635 stat = repo.status(node1, node2, scmutil.match(repo, pats, opts),
4594 4636 'ignored' in show, 'clean' in show, 'unknown' in show,
4595 4637 opts.get('subrepos'))
4596 4638 changestates = zip(states, 'MAR!?IC', stat)
4597 4639
4598 4640 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4599 4641 ctxn = repo[nullid]
4600 4642 ctx1 = repo[node1]
4601 4643 ctx2 = repo[node2]
4602 4644 added = stat[1]
4603 4645 if node2 is None:
4604 4646 added = stat[0] + stat[1] # merged?
4605 4647
4606 4648 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4607 4649 if k in added:
4608 4650 copy[k] = v
4609 4651 elif v in added:
4610 4652 copy[v] = k
4611 4653
4612 4654 for state, char, files in changestates:
4613 4655 if state in show:
4614 4656 format = "%s %%s%s" % (char, end)
4615 4657 if opts.get('no_status'):
4616 4658 format = "%%s%s" % end
4617 4659
4618 4660 for f in files:
4619 4661 ui.write(format % repo.pathto(f, cwd),
4620 4662 label='status.' + state)
4621 4663 if f in copy:
4622 4664 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4623 4665 label='status.copied')
4624 4666
4625 4667 @command('^summary|sum',
4626 4668 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4627 4669 def summary(ui, repo, **opts):
4628 4670 """summarize working directory state
4629 4671
4630 4672 This generates a brief summary of the working directory state,
4631 4673 including parents, branch, commit status, and available updates.
4632 4674
4633 4675 With the --remote option, this will check the default paths for
4634 4676 incoming and outgoing changes. This can be time-consuming.
4635 4677
4636 4678 Returns 0 on success.
4637 4679 """
4638 4680
4639 4681 ctx = repo[None]
4640 4682 parents = ctx.parents()
4641 4683 pnode = parents[0].node()
4642 4684
4643 4685 for p in parents:
4644 4686 # label with log.changeset (instead of log.parent) since this
4645 4687 # shows a working directory parent *changeset*:
4646 4688 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4647 4689 label='log.changeset')
4648 4690 ui.write(' '.join(p.tags()), label='log.tag')
4649 4691 if p.bookmarks():
4650 4692 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4651 4693 if p.rev() == -1:
4652 4694 if not len(repo):
4653 4695 ui.write(_(' (empty repository)'))
4654 4696 else:
4655 4697 ui.write(_(' (no revision checked out)'))
4656 4698 ui.write('\n')
4657 4699 if p.description():
4658 4700 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4659 4701 label='log.summary')
4660 4702
4661 4703 branch = ctx.branch()
4662 4704 bheads = repo.branchheads(branch)
4663 4705 m = _('branch: %s\n') % branch
4664 4706 if branch != 'default':
4665 4707 ui.write(m, label='log.branch')
4666 4708 else:
4667 4709 ui.status(m, label='log.branch')
4668 4710
4669 4711 st = list(repo.status(unknown=True))[:6]
4670 4712
4671 4713 c = repo.dirstate.copies()
4672 4714 copied, renamed = [], []
4673 4715 for d, s in c.iteritems():
4674 4716 if s in st[2]:
4675 4717 st[2].remove(s)
4676 4718 renamed.append(d)
4677 4719 else:
4678 4720 copied.append(d)
4679 4721 if d in st[1]:
4680 4722 st[1].remove(d)
4681 4723 st.insert(3, renamed)
4682 4724 st.insert(4, copied)
4683 4725
4684 4726 ms = mergemod.mergestate(repo)
4685 4727 st.append([f for f in ms if ms[f] == 'u'])
4686 4728
4687 4729 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4688 4730 st.append(subs)
4689 4731
4690 4732 labels = [ui.label(_('%d modified'), 'status.modified'),
4691 4733 ui.label(_('%d added'), 'status.added'),
4692 4734 ui.label(_('%d removed'), 'status.removed'),
4693 4735 ui.label(_('%d renamed'), 'status.copied'),
4694 4736 ui.label(_('%d copied'), 'status.copied'),
4695 4737 ui.label(_('%d deleted'), 'status.deleted'),
4696 4738 ui.label(_('%d unknown'), 'status.unknown'),
4697 4739 ui.label(_('%d ignored'), 'status.ignored'),
4698 4740 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4699 4741 ui.label(_('%d subrepos'), 'status.modified')]
4700 4742 t = []
4701 4743 for s, l in zip(st, labels):
4702 4744 if s:
4703 4745 t.append(l % len(s))
4704 4746
4705 4747 t = ', '.join(t)
4706 4748 cleanworkdir = False
4707 4749
4708 4750 if len(parents) > 1:
4709 4751 t += _(' (merge)')
4710 4752 elif branch != parents[0].branch():
4711 4753 t += _(' (new branch)')
4712 4754 elif (parents[0].extra().get('close') and
4713 4755 pnode in repo.branchheads(branch, closed=True)):
4714 4756 t += _(' (head closed)')
4715 4757 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4716 4758 t += _(' (clean)')
4717 4759 cleanworkdir = True
4718 4760 elif pnode not in bheads:
4719 4761 t += _(' (new branch head)')
4720 4762
4721 4763 if cleanworkdir:
4722 4764 ui.status(_('commit: %s\n') % t.strip())
4723 4765 else:
4724 4766 ui.write(_('commit: %s\n') % t.strip())
4725 4767
4726 4768 # all ancestors of branch heads - all ancestors of parent = new csets
4727 4769 new = [0] * len(repo)
4728 4770 cl = repo.changelog
4729 4771 for a in [cl.rev(n) for n in bheads]:
4730 4772 new[a] = 1
4731 4773 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4732 4774 new[a] = 1
4733 4775 for a in [p.rev() for p in parents]:
4734 4776 if a >= 0:
4735 4777 new[a] = 0
4736 4778 for a in cl.ancestors(*[p.rev() for p in parents]):
4737 4779 new[a] = 0
4738 4780 new = sum(new)
4739 4781
4740 4782 if new == 0:
4741 4783 ui.status(_('update: (current)\n'))
4742 4784 elif pnode not in bheads:
4743 4785 ui.write(_('update: %d new changesets (update)\n') % new)
4744 4786 else:
4745 4787 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4746 4788 (new, len(bheads)))
4747 4789
4748 4790 if opts.get('remote'):
4749 4791 t = []
4750 4792 source, branches = hg.parseurl(ui.expandpath('default'))
4751 4793 other = hg.peer(repo, {}, source)
4752 4794 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4753 4795 ui.debug('comparing with %s\n' % util.hidepassword(source))
4754 4796 repo.ui.pushbuffer()
4755 4797 commoninc = discovery.findcommonincoming(repo, other)
4756 4798 _common, incoming, _rheads = commoninc
4757 4799 repo.ui.popbuffer()
4758 4800 if incoming:
4759 4801 t.append(_('1 or more incoming'))
4760 4802
4761 4803 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4762 4804 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4763 4805 if source != dest:
4764 4806 other = hg.peer(repo, {}, dest)
4765 4807 commoninc = None
4766 4808 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4767 4809 repo.ui.pushbuffer()
4768 4810 common, outheads = discovery.findcommonoutgoing(repo, other,
4769 4811 commoninc=commoninc)
4770 4812 repo.ui.popbuffer()
4771 4813 o = repo.changelog.findmissing(common=common, heads=outheads)
4772 4814 if o:
4773 4815 t.append(_('%d outgoing') % len(o))
4774 4816 if 'bookmarks' in other.listkeys('namespaces'):
4775 4817 lmarks = repo.listkeys('bookmarks')
4776 4818 rmarks = other.listkeys('bookmarks')
4777 4819 diff = set(rmarks) - set(lmarks)
4778 4820 if len(diff) > 0:
4779 4821 t.append(_('%d incoming bookmarks') % len(diff))
4780 4822 diff = set(lmarks) - set(rmarks)
4781 4823 if len(diff) > 0:
4782 4824 t.append(_('%d outgoing bookmarks') % len(diff))
4783 4825
4784 4826 if t:
4785 4827 ui.write(_('remote: %s\n') % (', '.join(t)))
4786 4828 else:
4787 4829 ui.status(_('remote: (synced)\n'))
4788 4830
4789 4831 @command('tag',
4790 4832 [('f', 'force', None, _('force tag')),
4791 4833 ('l', 'local', None, _('make the tag local')),
4792 4834 ('r', 'rev', '', _('revision to tag'), _('REV')),
4793 4835 ('', 'remove', None, _('remove a tag')),
4794 4836 # -l/--local is already there, commitopts cannot be used
4795 4837 ('e', 'edit', None, _('edit commit message')),
4796 4838 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4797 4839 ] + commitopts2,
4798 4840 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4799 4841 def tag(ui, repo, name1, *names, **opts):
4800 4842 """add one or more tags for the current or given revision
4801 4843
4802 4844 Name a particular revision using <name>.
4803 4845
4804 4846 Tags are used to name particular revisions of the repository and are
4805 4847 very useful to compare different revisions, to go back to significant
4806 4848 earlier versions or to mark branch points as releases, etc. Changing
4807 4849 an existing tag is normally disallowed; use -f/--force to override.
4808 4850
4809 4851 If no revision is given, the parent of the working directory is
4810 4852 used, or tip if no revision is checked out.
4811 4853
4812 4854 To facilitate version control, distribution, and merging of tags,
4813 4855 they are stored as a file named ".hgtags" which is managed similarly
4814 4856 to other project files and can be hand-edited if necessary. This
4815 4857 also means that tagging creates a new commit. The file
4816 4858 ".hg/localtags" is used for local tags (not shared among
4817 4859 repositories).
4818 4860
4819 4861 Tag commits are usually made at the head of a branch. If the parent
4820 4862 of the working directory is not a branch head, :hg:`tag` aborts; use
4821 4863 -f/--force to force the tag commit to be based on a non-head
4822 4864 changeset.
4823 4865
4824 4866 See :hg:`help dates` for a list of formats valid for -d/--date.
4825 4867
4826 4868 Since tag names have priority over branch names during revision
4827 4869 lookup, using an existing branch name as a tag name is discouraged.
4828 4870
4829 4871 Returns 0 on success.
4830 4872 """
4831 4873
4832 4874 rev_ = "."
4833 4875 names = [t.strip() for t in (name1,) + names]
4834 4876 if len(names) != len(set(names)):
4835 4877 raise util.Abort(_('tag names must be unique'))
4836 4878 for n in names:
4837 4879 if n in ['tip', '.', 'null']:
4838 4880 raise util.Abort(_("the name '%s' is reserved") % n)
4839 4881 if not n:
4840 4882 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4841 4883 if opts.get('rev') and opts.get('remove'):
4842 4884 raise util.Abort(_("--rev and --remove are incompatible"))
4843 4885 if opts.get('rev'):
4844 4886 rev_ = opts['rev']
4845 4887 message = opts.get('message')
4846 4888 if opts.get('remove'):
4847 4889 expectedtype = opts.get('local') and 'local' or 'global'
4848 4890 for n in names:
4849 4891 if not repo.tagtype(n):
4850 4892 raise util.Abort(_("tag '%s' does not exist") % n)
4851 4893 if repo.tagtype(n) != expectedtype:
4852 4894 if expectedtype == 'global':
4853 4895 raise util.Abort(_("tag '%s' is not a global tag") % n)
4854 4896 else:
4855 4897 raise util.Abort(_("tag '%s' is not a local tag") % n)
4856 4898 rev_ = nullid
4857 4899 if not message:
4858 4900 # we don't translate commit messages
4859 4901 message = 'Removed tag %s' % ', '.join(names)
4860 4902 elif not opts.get('force'):
4861 4903 for n in names:
4862 4904 if n in repo.tags():
4863 4905 raise util.Abort(_("tag '%s' already exists "
4864 4906 "(use -f to force)") % n)
4865 4907 if not opts.get('local'):
4866 4908 p1, p2 = repo.dirstate.parents()
4867 4909 if p2 != nullid:
4868 4910 raise util.Abort(_('uncommitted merge'))
4869 4911 bheads = repo.branchheads()
4870 4912 if not opts.get('force') and bheads and p1 not in bheads:
4871 4913 raise util.Abort(_('not at a branch head (use -f to force)'))
4872 4914 r = scmutil.revsingle(repo, rev_).node()
4873 4915
4874 4916 if not message:
4875 4917 # we don't translate commit messages
4876 4918 message = ('Added tag %s for changeset %s' %
4877 4919 (', '.join(names), short(r)))
4878 4920
4879 4921 date = opts.get('date')
4880 4922 if date:
4881 4923 date = util.parsedate(date)
4882 4924
4883 4925 if opts.get('edit'):
4884 4926 message = ui.edit(message, ui.username())
4885 4927
4886 4928 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4887 4929
4888 4930 @command('tags', [], '')
4889 4931 def tags(ui, repo):
4890 4932 """list repository tags
4891 4933
4892 4934 This lists both regular and local tags. When the -v/--verbose
4893 4935 switch is used, a third column "local" is printed for local tags.
4894 4936
4895 4937 Returns 0 on success.
4896 4938 """
4897 4939
4898 4940 hexfunc = ui.debugflag and hex or short
4899 4941 tagtype = ""
4900 4942
4901 4943 for t, n in reversed(repo.tagslist()):
4902 4944 if ui.quiet:
4903 4945 ui.write("%s\n" % t)
4904 4946 continue
4905 4947
4906 4948 hn = hexfunc(n)
4907 4949 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4908 4950 spaces = " " * (30 - encoding.colwidth(t))
4909 4951
4910 4952 if ui.verbose:
4911 4953 if repo.tagtype(t) == 'local':
4912 4954 tagtype = " local"
4913 4955 else:
4914 4956 tagtype = ""
4915 4957 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4916 4958
4917 4959 @command('tip',
4918 4960 [('p', 'patch', None, _('show patch')),
4919 4961 ('g', 'git', None, _('use git extended diff format')),
4920 4962 ] + templateopts,
4921 4963 _('[-p] [-g]'))
4922 4964 def tip(ui, repo, **opts):
4923 4965 """show the tip revision
4924 4966
4925 4967 The tip revision (usually just called the tip) is the changeset
4926 4968 most recently added to the repository (and therefore the most
4927 4969 recently changed head).
4928 4970
4929 4971 If you have just made a commit, that commit will be the tip. If
4930 4972 you have just pulled changes from another repository, the tip of
4931 4973 that repository becomes the current tip. The "tip" tag is special
4932 4974 and cannot be renamed or assigned to a different changeset.
4933 4975
4934 4976 Returns 0 on success.
4935 4977 """
4936 4978 displayer = cmdutil.show_changeset(ui, repo, opts)
4937 4979 displayer.show(repo[len(repo) - 1])
4938 4980 displayer.close()
4939 4981
4940 4982 @command('unbundle',
4941 4983 [('u', 'update', None,
4942 4984 _('update to new branch head if changesets were unbundled'))],
4943 4985 _('[-u] FILE...'))
4944 4986 def unbundle(ui, repo, fname1, *fnames, **opts):
4945 4987 """apply one or more changegroup files
4946 4988
4947 4989 Apply one or more compressed changegroup files generated by the
4948 4990 bundle command.
4949 4991
4950 4992 Returns 0 on success, 1 if an update has unresolved files.
4951 4993 """
4952 4994 fnames = (fname1,) + fnames
4953 4995
4954 4996 lock = repo.lock()
4955 4997 wc = repo['.']
4956 4998 try:
4957 4999 for fname in fnames:
4958 5000 f = url.open(ui, fname)
4959 5001 gen = changegroup.readbundle(f, fname)
4960 5002 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4961 5003 lock=lock)
4962 5004 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4963 5005 finally:
4964 5006 lock.release()
4965 5007 return postincoming(ui, repo, modheads, opts.get('update'), None)
4966 5008
4967 5009 @command('^update|up|checkout|co',
4968 5010 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4969 5011 ('c', 'check', None,
4970 5012 _('update across branches if no uncommitted changes')),
4971 5013 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4972 5014 ('r', 'rev', '', _('revision'), _('REV'))],
4973 5015 _('[-c] [-C] [-d DATE] [[-r] REV]'))
4974 5016 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4975 5017 """update working directory (or switch revisions)
4976 5018
4977 5019 Update the repository's working directory to the specified
4978 5020 changeset. If no changeset is specified, update to the tip of the
4979 5021 current named branch.
4980 5022
4981 5023 If the changeset is not a descendant of the working directory's
4982 5024 parent, the update is aborted. With the -c/--check option, the
4983 5025 working directory is checked for uncommitted changes; if none are
4984 5026 found, the working directory is updated to the specified
4985 5027 changeset.
4986 5028
4987 5029 The following rules apply when the working directory contains
4988 5030 uncommitted changes:
4989 5031
4990 5032 1. If neither -c/--check nor -C/--clean is specified, and if
4991 5033 the requested changeset is an ancestor or descendant of
4992 5034 the working directory's parent, the uncommitted changes
4993 5035 are merged into the requested changeset and the merged
4994 5036 result is left uncommitted. If the requested changeset is
4995 5037 not an ancestor or descendant (that is, it is on another
4996 5038 branch), the update is aborted and the uncommitted changes
4997 5039 are preserved.
4998 5040
4999 5041 2. With the -c/--check option, the update is aborted and the
5000 5042 uncommitted changes are preserved.
5001 5043
5002 5044 3. With the -C/--clean option, uncommitted changes are discarded and
5003 5045 the working directory is updated to the requested changeset.
5004 5046
5005 5047 Use null as the changeset to remove the working directory (like
5006 5048 :hg:`clone -U`).
5007 5049
5008 5050 If you want to update just one file to an older changeset, use
5009 5051 :hg:`revert`.
5010 5052
5011 5053 See :hg:`help dates` for a list of formats valid for -d/--date.
5012 5054
5013 5055 Returns 0 on success, 1 if there are unresolved files.
5014 5056 """
5015 5057 if rev and node:
5016 5058 raise util.Abort(_("please specify just one revision"))
5017 5059
5018 5060 if rev is None or rev == '':
5019 5061 rev = node
5020 5062
5021 5063 # if we defined a bookmark, we have to remember the original bookmark name
5022 5064 brev = rev
5023 5065 rev = scmutil.revsingle(repo, rev, rev).rev()
5024 5066
5025 5067 if check and clean:
5026 5068 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5027 5069
5028 5070 if check:
5029 5071 # we could use dirty() but we can ignore merge and branch trivia
5030 5072 c = repo[None]
5031 5073 if c.modified() or c.added() or c.removed():
5032 5074 raise util.Abort(_("uncommitted local changes"))
5033 5075
5034 5076 if date:
5035 5077 if rev is not None:
5036 5078 raise util.Abort(_("you can't specify a revision and a date"))
5037 5079 rev = cmdutil.finddate(ui, repo, date)
5038 5080
5039 5081 if clean or check:
5040 5082 ret = hg.clean(repo, rev)
5041 5083 else:
5042 5084 ret = hg.update(repo, rev)
5043 5085
5044 5086 if brev in repo._bookmarks:
5045 5087 bookmarks.setcurrent(repo, brev)
5046 5088
5047 5089 return ret
5048 5090
5049 5091 @command('verify', [])
5050 5092 def verify(ui, repo):
5051 5093 """verify the integrity of the repository
5052 5094
5053 5095 Verify the integrity of the current repository.
5054 5096
5055 5097 This will perform an extensive check of the repository's
5056 5098 integrity, validating the hashes and checksums of each entry in
5057 5099 the changelog, manifest, and tracked files, as well as the
5058 5100 integrity of their crosslinks and indices.
5059 5101
5060 5102 Returns 0 on success, 1 if errors are encountered.
5061 5103 """
5062 5104 return hg.verify(repo)
5063 5105
5064 5106 @command('version', [])
5065 5107 def version_(ui):
5066 5108 """output version and copyright information"""
5067 5109 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5068 5110 % util.version())
5069 5111 ui.status(_(
5070 5112 "(see http://mercurial.selenic.com for more information)\n"
5071 5113 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5072 5114 "This is free software; see the source for copying conditions. "
5073 5115 "There is NO\nwarranty; "
5074 5116 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5075 5117 ))
5076 5118
5077 5119 norepo = ("clone init version help debugcommands debugcomplete"
5078 5120 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5079 5121 " debugknown debuggetbundle debugbundle")
5080 5122 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5081 5123 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,1783 +1,1848 b''
1 1 # patch.py - patch file parsing routines
2 2 #
3 3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import cStringIO, email.Parser, os, errno, re
10 10 import tempfile, zlib, shutil
11 11
12 12 from i18n import _
13 13 from node import hex, nullid, short
14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
15 import context
15 16
16 17 gitre = re.compile('diff --git a/(.*) b/(.*)')
17 18
18 19 class PatchError(Exception):
19 20 pass
20 21
21 22
22 23 # public functions
23 24
24 25 def split(stream):
25 26 '''return an iterator of individual patches from a stream'''
26 27 def isheader(line, inheader):
27 28 if inheader and line[0] in (' ', '\t'):
28 29 # continuation
29 30 return True
30 31 if line[0] in (' ', '-', '+'):
31 32 # diff line - don't check for header pattern in there
32 33 return False
33 34 l = line.split(': ', 1)
34 35 return len(l) == 2 and ' ' not in l[0]
35 36
36 37 def chunk(lines):
37 38 return cStringIO.StringIO(''.join(lines))
38 39
39 40 def hgsplit(stream, cur):
40 41 inheader = True
41 42
42 43 for line in stream:
43 44 if not line.strip():
44 45 inheader = False
45 46 if not inheader and line.startswith('# HG changeset patch'):
46 47 yield chunk(cur)
47 48 cur = []
48 49 inheader = True
49 50
50 51 cur.append(line)
51 52
52 53 if cur:
53 54 yield chunk(cur)
54 55
55 56 def mboxsplit(stream, cur):
56 57 for line in stream:
57 58 if line.startswith('From '):
58 59 for c in split(chunk(cur[1:])):
59 60 yield c
60 61 cur = []
61 62
62 63 cur.append(line)
63 64
64 65 if cur:
65 66 for c in split(chunk(cur[1:])):
66 67 yield c
67 68
68 69 def mimesplit(stream, cur):
69 70 def msgfp(m):
70 71 fp = cStringIO.StringIO()
71 72 g = email.Generator.Generator(fp, mangle_from_=False)
72 73 g.flatten(m)
73 74 fp.seek(0)
74 75 return fp
75 76
76 77 for line in stream:
77 78 cur.append(line)
78 79 c = chunk(cur)
79 80
80 81 m = email.Parser.Parser().parse(c)
81 82 if not m.is_multipart():
82 83 yield msgfp(m)
83 84 else:
84 85 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
85 86 for part in m.walk():
86 87 ct = part.get_content_type()
87 88 if ct not in ok_types:
88 89 continue
89 90 yield msgfp(part)
90 91
91 92 def headersplit(stream, cur):
92 93 inheader = False
93 94
94 95 for line in stream:
95 96 if not inheader and isheader(line, inheader):
96 97 yield chunk(cur)
97 98 cur = []
98 99 inheader = True
99 100 if inheader and not isheader(line, inheader):
100 101 inheader = False
101 102
102 103 cur.append(line)
103 104
104 105 if cur:
105 106 yield chunk(cur)
106 107
107 108 def remainder(cur):
108 109 yield chunk(cur)
109 110
110 111 class fiter(object):
111 112 def __init__(self, fp):
112 113 self.fp = fp
113 114
114 115 def __iter__(self):
115 116 return self
116 117
117 118 def next(self):
118 119 l = self.fp.readline()
119 120 if not l:
120 121 raise StopIteration
121 122 return l
122 123
123 124 inheader = False
124 125 cur = []
125 126
126 127 mimeheaders = ['content-type']
127 128
128 129 if not hasattr(stream, 'next'):
129 130 # http responses, for example, have readline but not next
130 131 stream = fiter(stream)
131 132
132 133 for line in stream:
133 134 cur.append(line)
134 135 if line.startswith('# HG changeset patch'):
135 136 return hgsplit(stream, cur)
136 137 elif line.startswith('From '):
137 138 return mboxsplit(stream, cur)
138 139 elif isheader(line, inheader):
139 140 inheader = True
140 141 if line.split(':', 1)[0].lower() in mimeheaders:
141 142 # let email parser handle this
142 143 return mimesplit(stream, cur)
143 144 elif line.startswith('--- ') and inheader:
144 145 # No evil headers seen by diff start, split by hand
145 146 return headersplit(stream, cur)
146 147 # Not enough info, keep reading
147 148
148 149 # if we are here, we have a very plain patch
149 150 return remainder(cur)
150 151
151 152 def extract(ui, fileobj):
152 153 '''extract patch from data read from fileobj.
153 154
154 155 patch can be a normal patch or contained in an email message.
155 156
156 157 return tuple (filename, message, user, date, branch, node, p1, p2).
157 158 Any item in the returned tuple can be None. If filename is None,
158 159 fileobj did not contain a patch. Caller must unlink filename when done.'''
159 160
160 161 # attempt to detect the start of a patch
161 162 # (this heuristic is borrowed from quilt)
162 163 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
163 164 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
164 165 r'---[ \t].*?^\+\+\+[ \t]|'
165 166 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
166 167
167 168 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
168 169 tmpfp = os.fdopen(fd, 'w')
169 170 try:
170 171 msg = email.Parser.Parser().parse(fileobj)
171 172
172 173 subject = msg['Subject']
173 174 user = msg['From']
174 175 if not subject and not user:
175 176 # Not an email, restore parsed headers if any
176 177 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
177 178
178 179 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
179 180 # should try to parse msg['Date']
180 181 date = None
181 182 nodeid = None
182 183 branch = None
183 184 parents = []
184 185
185 186 if subject:
186 187 if subject.startswith('[PATCH'):
187 188 pend = subject.find(']')
188 189 if pend >= 0:
189 190 subject = subject[pend + 1:].lstrip()
190 191 subject = subject.replace('\n\t', ' ')
191 192 ui.debug('Subject: %s\n' % subject)
192 193 if user:
193 194 ui.debug('From: %s\n' % user)
194 195 diffs_seen = 0
195 196 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
196 197 message = ''
197 198 for part in msg.walk():
198 199 content_type = part.get_content_type()
199 200 ui.debug('Content-Type: %s\n' % content_type)
200 201 if content_type not in ok_types:
201 202 continue
202 203 payload = part.get_payload(decode=True)
203 204 m = diffre.search(payload)
204 205 if m:
205 206 hgpatch = False
206 207 hgpatchheader = False
207 208 ignoretext = False
208 209
209 210 ui.debug('found patch at byte %d\n' % m.start(0))
210 211 diffs_seen += 1
211 212 cfp = cStringIO.StringIO()
212 213 for line in payload[:m.start(0)].splitlines():
213 214 if line.startswith('# HG changeset patch') and not hgpatch:
214 215 ui.debug('patch generated by hg export\n')
215 216 hgpatch = True
216 217 hgpatchheader = True
217 218 # drop earlier commit message content
218 219 cfp.seek(0)
219 220 cfp.truncate()
220 221 subject = None
221 222 elif hgpatchheader:
222 223 if line.startswith('# User '):
223 224 user = line[7:]
224 225 ui.debug('From: %s\n' % user)
225 226 elif line.startswith("# Date "):
226 227 date = line[7:]
227 228 elif line.startswith("# Branch "):
228 229 branch = line[9:]
229 230 elif line.startswith("# Node ID "):
230 231 nodeid = line[10:]
231 232 elif line.startswith("# Parent "):
232 233 parents.append(line[10:])
233 234 elif not line.startswith("# "):
234 235 hgpatchheader = False
235 236 elif line == '---' and gitsendmail:
236 237 ignoretext = True
237 238 if not hgpatchheader and not ignoretext:
238 239 cfp.write(line)
239 240 cfp.write('\n')
240 241 message = cfp.getvalue()
241 242 if tmpfp:
242 243 tmpfp.write(payload)
243 244 if not payload.endswith('\n'):
244 245 tmpfp.write('\n')
245 246 elif not diffs_seen and message and content_type == 'text/plain':
246 247 message += '\n' + payload
247 248 except:
248 249 tmpfp.close()
249 250 os.unlink(tmpname)
250 251 raise
251 252
252 253 if subject and not message.startswith(subject):
253 254 message = '%s\n%s' % (subject, message)
254 255 tmpfp.close()
255 256 if not diffs_seen:
256 257 os.unlink(tmpname)
257 258 return None, message, user, date, branch, None, None, None
258 259 p1 = parents and parents.pop(0) or None
259 260 p2 = parents and parents.pop(0) or None
260 261 return tmpname, message, user, date, branch, nodeid, p1, p2
261 262
262 263 class patchmeta(object):
263 264 """Patched file metadata
264 265
265 266 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
266 267 or COPY. 'path' is patched file path. 'oldpath' is set to the
267 268 origin file when 'op' is either COPY or RENAME, None otherwise. If
268 269 file mode is changed, 'mode' is a tuple (islink, isexec) where
269 270 'islink' is True if the file is a symlink and 'isexec' is True if
270 271 the file is executable. Otherwise, 'mode' is None.
271 272 """
272 273 def __init__(self, path):
273 274 self.path = path
274 275 self.oldpath = None
275 276 self.mode = None
276 277 self.op = 'MODIFY'
277 278 self.binary = False
278 279
279 280 def setmode(self, mode):
280 281 islink = mode & 020000
281 282 isexec = mode & 0100
282 283 self.mode = (islink, isexec)
283 284
284 285 def copy(self):
285 286 other = patchmeta(self.path)
286 287 other.oldpath = self.oldpath
287 288 other.mode = self.mode
288 289 other.op = self.op
289 290 other.binary = self.binary
290 291 return other
291 292
292 293 def __repr__(self):
293 294 return "<patchmeta %s %r>" % (self.op, self.path)
294 295
295 296 def readgitpatch(lr):
296 297 """extract git-style metadata about patches from <patchname>"""
297 298
298 299 # Filter patch for git information
299 300 gp = None
300 301 gitpatches = []
301 302 for line in lr:
302 303 line = line.rstrip(' \r\n')
303 304 if line.startswith('diff --git'):
304 305 m = gitre.match(line)
305 306 if m:
306 307 if gp:
307 308 gitpatches.append(gp)
308 309 dst = m.group(2)
309 310 gp = patchmeta(dst)
310 311 elif gp:
311 312 if line.startswith('--- '):
312 313 gitpatches.append(gp)
313 314 gp = None
314 315 continue
315 316 if line.startswith('rename from '):
316 317 gp.op = 'RENAME'
317 318 gp.oldpath = line[12:]
318 319 elif line.startswith('rename to '):
319 320 gp.path = line[10:]
320 321 elif line.startswith('copy from '):
321 322 gp.op = 'COPY'
322 323 gp.oldpath = line[10:]
323 324 elif line.startswith('copy to '):
324 325 gp.path = line[8:]
325 326 elif line.startswith('deleted file'):
326 327 gp.op = 'DELETE'
327 328 elif line.startswith('new file mode '):
328 329 gp.op = 'ADD'
329 330 gp.setmode(int(line[-6:], 8))
330 331 elif line.startswith('new mode '):
331 332 gp.setmode(int(line[-6:], 8))
332 333 elif line.startswith('GIT binary patch'):
333 334 gp.binary = True
334 335 if gp:
335 336 gitpatches.append(gp)
336 337
337 338 return gitpatches
338 339
339 340 class linereader(object):
340 341 # simple class to allow pushing lines back into the input stream
341 342 def __init__(self, fp):
342 343 self.fp = fp
343 344 self.buf = []
344 345
345 346 def push(self, line):
346 347 if line is not None:
347 348 self.buf.append(line)
348 349
349 350 def readline(self):
350 351 if self.buf:
351 352 l = self.buf[0]
352 353 del self.buf[0]
353 354 return l
354 355 return self.fp.readline()
355 356
356 357 def __iter__(self):
357 358 while True:
358 359 l = self.readline()
359 360 if not l:
360 361 break
361 362 yield l
362 363
363 364 class abstractbackend(object):
364 365 def __init__(self, ui):
365 366 self.ui = ui
366 367
367 368 def getfile(self, fname):
368 369 """Return target file data and flags as a (data, (islink,
369 370 isexec)) tuple.
370 371 """
371 372 raise NotImplementedError
372 373
373 374 def setfile(self, fname, data, mode, copysource):
374 375 """Write data to target file fname and set its mode. mode is a
375 376 (islink, isexec) tuple. If data is None, the file content should
376 377 be left unchanged. If the file is modified after being copied,
377 378 copysource is set to the original file name.
378 379 """
379 380 raise NotImplementedError
380 381
381 382 def unlink(self, fname):
382 383 """Unlink target file."""
383 384 raise NotImplementedError
384 385
385 386 def writerej(self, fname, failed, total, lines):
386 387 """Write rejected lines for fname. total is the number of hunks
387 388 which failed to apply and total the total number of hunks for this
388 389 files.
389 390 """
390 391 pass
391 392
392 393 def exists(self, fname):
393 394 raise NotImplementedError
394 395
395 396 class fsbackend(abstractbackend):
396 397 def __init__(self, ui, basedir):
397 398 super(fsbackend, self).__init__(ui)
398 399 self.opener = scmutil.opener(basedir)
399 400
400 401 def _join(self, f):
401 402 return os.path.join(self.opener.base, f)
402 403
403 404 def getfile(self, fname):
404 405 path = self._join(fname)
405 406 if os.path.islink(path):
406 407 return (os.readlink(path), (True, False))
407 408 isexec = False
408 409 try:
409 410 isexec = os.lstat(path).st_mode & 0100 != 0
410 411 except OSError, e:
411 412 if e.errno != errno.ENOENT:
412 413 raise
413 414 return (self.opener.read(fname), (False, isexec))
414 415
415 416 def setfile(self, fname, data, mode, copysource):
416 417 islink, isexec = mode
417 418 if data is None:
418 419 util.setflags(self._join(fname), islink, isexec)
419 420 return
420 421 if islink:
421 422 self.opener.symlink(data, fname)
422 423 else:
423 424 self.opener.write(fname, data)
424 425 if isexec:
425 426 util.setflags(self._join(fname), False, True)
426 427
427 428 def unlink(self, fname):
428 429 try:
429 430 util.unlinkpath(self._join(fname))
430 431 except OSError, inst:
431 432 if inst.errno != errno.ENOENT:
432 433 raise
433 434
434 435 def writerej(self, fname, failed, total, lines):
435 436 fname = fname + ".rej"
436 437 self.ui.warn(
437 438 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
438 439 (failed, total, fname))
439 440 fp = self.opener(fname, 'w')
440 441 fp.writelines(lines)
441 442 fp.close()
442 443
443 444 def exists(self, fname):
444 445 return os.path.lexists(self._join(fname))
445 446
446 447 class workingbackend(fsbackend):
447 448 def __init__(self, ui, repo, similarity):
448 449 super(workingbackend, self).__init__(ui, repo.root)
449 450 self.repo = repo
450 451 self.similarity = similarity
451 452 self.removed = set()
452 453 self.changed = set()
453 454 self.copied = []
454 455
455 456 def _checkknown(self, fname):
456 457 if self.repo.dirstate[fname] == '?' and self.exists(fname):
457 458 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
458 459
459 460 def setfile(self, fname, data, mode, copysource):
460 461 self._checkknown(fname)
461 462 super(workingbackend, self).setfile(fname, data, mode, copysource)
462 463 if copysource is not None:
463 464 self.copied.append((copysource, fname))
464 465 self.changed.add(fname)
465 466
466 467 def unlink(self, fname):
467 468 self._checkknown(fname)
468 469 super(workingbackend, self).unlink(fname)
469 470 self.removed.add(fname)
470 471 self.changed.add(fname)
471 472
472 473 def close(self):
473 474 wctx = self.repo[None]
474 475 addremoved = set(self.changed)
475 476 for src, dst in self.copied:
476 477 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
477 478 addremoved.discard(src)
478 479 if (not self.similarity) and self.removed:
479 480 wctx.forget(sorted(self.removed))
480 481 if addremoved:
481 482 cwd = self.repo.getcwd()
482 483 if cwd:
483 484 addremoved = [util.pathto(self.repo.root, cwd, f)
484 485 for f in addremoved]
485 486 scmutil.addremove(self.repo, addremoved, similarity=self.similarity)
486 487 return sorted(self.changed)
487 488
488 489 class filestore(object):
489 490 def __init__(self):
490 491 self.opener = None
491 492 self.files = {}
492 493 self.created = 0
493 494
494 495 def setfile(self, fname, data, mode, copied=None):
495 496 if self.opener is None:
496 497 root = tempfile.mkdtemp(prefix='hg-patch-')
497 498 self.opener = scmutil.opener(root)
498 499 # Avoid filename issues with these simple names
499 500 fn = str(self.created)
500 501 self.opener.write(fn, data)
501 502 self.created += 1
502 503 self.files[fname] = (fn, mode, copied)
503 504
504 505 def getfile(self, fname):
505 506 if fname not in self.files:
506 507 raise IOError()
507 508 fn, mode, copied = self.files[fname]
508 509 return self.opener.read(fn), mode, copied
509 510
510 511 def close(self):
511 512 if self.opener:
512 513 shutil.rmtree(self.opener.base)
513 514
515 class repobackend(abstractbackend):
516 def __init__(self, ui, repo, ctx, store):
517 super(repobackend, self).__init__(ui)
518 self.repo = repo
519 self.ctx = ctx
520 self.store = store
521 self.changed = set()
522 self.removed = set()
523 self.copied = {}
524
525 def _checkknown(self, fname):
526 if fname not in self.ctx:
527 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
528
529 def getfile(self, fname):
530 try:
531 fctx = self.ctx[fname]
532 except error.LookupError:
533 raise IOError()
534 flags = fctx.flags()
535 return fctx.data(), ('l' in flags, 'x' in flags)
536
537 def setfile(self, fname, data, mode, copysource):
538 if copysource:
539 self._checkknown(copysource)
540 if data is None:
541 data = self.ctx[fname].data()
542 self.store.setfile(fname, data, mode, copysource)
543 self.changed.add(fname)
544 if copysource:
545 self.copied[fname] = copysource
546
547 def unlink(self, fname):
548 self._checkknown(fname)
549 self.removed.add(fname)
550
551 def exists(self, fname):
552 return fname in self.ctx
553
554 def close(self):
555 return self.changed | self.removed
556
514 557 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
515 558 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
516 559 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
517 560 eolmodes = ['strict', 'crlf', 'lf', 'auto']
518 561
519 562 class patchfile(object):
520 563 def __init__(self, ui, gp, backend, store, eolmode='strict'):
521 564 self.fname = gp.path
522 565 self.eolmode = eolmode
523 566 self.eol = None
524 567 self.backend = backend
525 568 self.ui = ui
526 569 self.lines = []
527 570 self.exists = False
528 571 self.missing = True
529 572 self.mode = gp.mode
530 573 self.copysource = gp.oldpath
531 574 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
532 575 self.remove = gp.op == 'DELETE'
533 576 try:
534 577 if self.copysource is None:
535 578 data, mode = backend.getfile(self.fname)
536 579 self.exists = True
537 580 else:
538 581 data, mode = store.getfile(self.copysource)[:2]
539 582 self.exists = backend.exists(self.fname)
540 583 self.missing = False
541 584 if data:
542 585 self.lines = data.splitlines(True)
543 586 if self.mode is None:
544 587 self.mode = mode
545 588 if self.lines:
546 589 # Normalize line endings
547 590 if self.lines[0].endswith('\r\n'):
548 591 self.eol = '\r\n'
549 592 elif self.lines[0].endswith('\n'):
550 593 self.eol = '\n'
551 594 if eolmode != 'strict':
552 595 nlines = []
553 596 for l in self.lines:
554 597 if l.endswith('\r\n'):
555 598 l = l[:-2] + '\n'
556 599 nlines.append(l)
557 600 self.lines = nlines
558 601 except IOError:
559 602 if self.create:
560 603 self.missing = False
561 604 if self.mode is None:
562 605 self.mode = (False, False)
563 606 if self.missing:
564 607 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
565 608
566 609 self.hash = {}
567 610 self.dirty = 0
568 611 self.offset = 0
569 612 self.skew = 0
570 613 self.rej = []
571 614 self.fileprinted = False
572 615 self.printfile(False)
573 616 self.hunks = 0
574 617
575 618 def writelines(self, fname, lines, mode):
576 619 if self.eolmode == 'auto':
577 620 eol = self.eol
578 621 elif self.eolmode == 'crlf':
579 622 eol = '\r\n'
580 623 else:
581 624 eol = '\n'
582 625
583 626 if self.eolmode != 'strict' and eol and eol != '\n':
584 627 rawlines = []
585 628 for l in lines:
586 629 if l and l[-1] == '\n':
587 630 l = l[:-1] + eol
588 631 rawlines.append(l)
589 632 lines = rawlines
590 633
591 634 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
592 635
593 636 def printfile(self, warn):
594 637 if self.fileprinted:
595 638 return
596 639 if warn or self.ui.verbose:
597 640 self.fileprinted = True
598 641 s = _("patching file %s\n") % self.fname
599 642 if warn:
600 643 self.ui.warn(s)
601 644 else:
602 645 self.ui.note(s)
603 646
604 647
605 648 def findlines(self, l, linenum):
606 649 # looks through the hash and finds candidate lines. The
607 650 # result is a list of line numbers sorted based on distance
608 651 # from linenum
609 652
610 653 cand = self.hash.get(l, [])
611 654 if len(cand) > 1:
612 655 # resort our list of potentials forward then back.
613 656 cand.sort(key=lambda x: abs(x - linenum))
614 657 return cand
615 658
616 659 def write_rej(self):
617 660 # our rejects are a little different from patch(1). This always
618 661 # creates rejects in the same form as the original patch. A file
619 662 # header is inserted so that you can run the reject through patch again
620 663 # without having to type the filename.
621 664 if not self.rej:
622 665 return
623 666 base = os.path.basename(self.fname)
624 667 lines = ["--- %s\n+++ %s\n" % (base, base)]
625 668 for x in self.rej:
626 669 for l in x.hunk:
627 670 lines.append(l)
628 671 if l[-1] != '\n':
629 672 lines.append("\n\ No newline at end of file\n")
630 673 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
631 674
632 675 def apply(self, h):
633 676 if not h.complete():
634 677 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
635 678 (h.number, h.desc, len(h.a), h.lena, len(h.b),
636 679 h.lenb))
637 680
638 681 self.hunks += 1
639 682
640 683 if self.missing:
641 684 self.rej.append(h)
642 685 return -1
643 686
644 687 if self.exists and self.create:
645 688 if self.copysource:
646 689 self.ui.warn(_("cannot create %s: destination already "
647 690 "exists\n" % self.fname))
648 691 else:
649 692 self.ui.warn(_("file %s already exists\n") % self.fname)
650 693 self.rej.append(h)
651 694 return -1
652 695
653 696 if isinstance(h, binhunk):
654 697 if self.remove:
655 698 self.backend.unlink(self.fname)
656 699 else:
657 700 self.lines[:] = h.new()
658 701 self.offset += len(h.new())
659 702 self.dirty = True
660 703 return 0
661 704
662 705 horig = h
663 706 if (self.eolmode in ('crlf', 'lf')
664 707 or self.eolmode == 'auto' and self.eol):
665 708 # If new eols are going to be normalized, then normalize
666 709 # hunk data before patching. Otherwise, preserve input
667 710 # line-endings.
668 711 h = h.getnormalized()
669 712
670 713 # fast case first, no offsets, no fuzz
671 714 old = h.old()
672 715 # patch starts counting at 1 unless we are adding the file
673 716 if h.starta == 0:
674 717 start = 0
675 718 else:
676 719 start = h.starta + self.offset - 1
677 720 orig_start = start
678 721 # if there's skew we want to emit the "(offset %d lines)" even
679 722 # when the hunk cleanly applies at start + skew, so skip the
680 723 # fast case code
681 724 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
682 725 if self.remove:
683 726 self.backend.unlink(self.fname)
684 727 else:
685 728 self.lines[start : start + h.lena] = h.new()
686 729 self.offset += h.lenb - h.lena
687 730 self.dirty = True
688 731 return 0
689 732
690 733 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
691 734 self.hash = {}
692 735 for x, s in enumerate(self.lines):
693 736 self.hash.setdefault(s, []).append(x)
694 737 if h.hunk[-1][0] != ' ':
695 738 # if the hunk tried to put something at the bottom of the file
696 739 # override the start line and use eof here
697 740 search_start = len(self.lines)
698 741 else:
699 742 search_start = orig_start + self.skew
700 743
701 744 for fuzzlen in xrange(3):
702 745 for toponly in [True, False]:
703 746 old = h.old(fuzzlen, toponly)
704 747
705 748 cand = self.findlines(old[0][1:], search_start)
706 749 for l in cand:
707 750 if diffhelpers.testhunk(old, self.lines, l) == 0:
708 751 newlines = h.new(fuzzlen, toponly)
709 752 self.lines[l : l + len(old)] = newlines
710 753 self.offset += len(newlines) - len(old)
711 754 self.skew = l - orig_start
712 755 self.dirty = True
713 756 offset = l - orig_start - fuzzlen
714 757 if fuzzlen:
715 758 msg = _("Hunk #%d succeeded at %d "
716 759 "with fuzz %d "
717 760 "(offset %d lines).\n")
718 761 self.printfile(True)
719 762 self.ui.warn(msg %
720 763 (h.number, l + 1, fuzzlen, offset))
721 764 else:
722 765 msg = _("Hunk #%d succeeded at %d "
723 766 "(offset %d lines).\n")
724 767 self.ui.note(msg % (h.number, l + 1, offset))
725 768 return fuzzlen
726 769 self.printfile(True)
727 770 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
728 771 self.rej.append(horig)
729 772 return -1
730 773
731 774 def close(self):
732 775 if self.dirty:
733 776 self.writelines(self.fname, self.lines, self.mode)
734 777 self.write_rej()
735 778 return len(self.rej)
736 779
737 780 class hunk(object):
738 781 def __init__(self, desc, num, lr, context):
739 782 self.number = num
740 783 self.desc = desc
741 784 self.hunk = [desc]
742 785 self.a = []
743 786 self.b = []
744 787 self.starta = self.lena = None
745 788 self.startb = self.lenb = None
746 789 if lr is not None:
747 790 if context:
748 791 self.read_context_hunk(lr)
749 792 else:
750 793 self.read_unified_hunk(lr)
751 794
752 795 def getnormalized(self):
753 796 """Return a copy with line endings normalized to LF."""
754 797
755 798 def normalize(lines):
756 799 nlines = []
757 800 for line in lines:
758 801 if line.endswith('\r\n'):
759 802 line = line[:-2] + '\n'
760 803 nlines.append(line)
761 804 return nlines
762 805
763 806 # Dummy object, it is rebuilt manually
764 807 nh = hunk(self.desc, self.number, None, None)
765 808 nh.number = self.number
766 809 nh.desc = self.desc
767 810 nh.hunk = self.hunk
768 811 nh.a = normalize(self.a)
769 812 nh.b = normalize(self.b)
770 813 nh.starta = self.starta
771 814 nh.startb = self.startb
772 815 nh.lena = self.lena
773 816 nh.lenb = self.lenb
774 817 return nh
775 818
776 819 def read_unified_hunk(self, lr):
777 820 m = unidesc.match(self.desc)
778 821 if not m:
779 822 raise PatchError(_("bad hunk #%d") % self.number)
780 823 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
781 824 if self.lena is None:
782 825 self.lena = 1
783 826 else:
784 827 self.lena = int(self.lena)
785 828 if self.lenb is None:
786 829 self.lenb = 1
787 830 else:
788 831 self.lenb = int(self.lenb)
789 832 self.starta = int(self.starta)
790 833 self.startb = int(self.startb)
791 834 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
792 835 # if we hit eof before finishing out the hunk, the last line will
793 836 # be zero length. Lets try to fix it up.
794 837 while len(self.hunk[-1]) == 0:
795 838 del self.hunk[-1]
796 839 del self.a[-1]
797 840 del self.b[-1]
798 841 self.lena -= 1
799 842 self.lenb -= 1
800 843 self._fixnewline(lr)
801 844
802 845 def read_context_hunk(self, lr):
803 846 self.desc = lr.readline()
804 847 m = contextdesc.match(self.desc)
805 848 if not m:
806 849 raise PatchError(_("bad hunk #%d") % self.number)
807 850 foo, self.starta, foo2, aend, foo3 = m.groups()
808 851 self.starta = int(self.starta)
809 852 if aend is None:
810 853 aend = self.starta
811 854 self.lena = int(aend) - self.starta
812 855 if self.starta:
813 856 self.lena += 1
814 857 for x in xrange(self.lena):
815 858 l = lr.readline()
816 859 if l.startswith('---'):
817 860 # lines addition, old block is empty
818 861 lr.push(l)
819 862 break
820 863 s = l[2:]
821 864 if l.startswith('- ') or l.startswith('! '):
822 865 u = '-' + s
823 866 elif l.startswith(' '):
824 867 u = ' ' + s
825 868 else:
826 869 raise PatchError(_("bad hunk #%d old text line %d") %
827 870 (self.number, x))
828 871 self.a.append(u)
829 872 self.hunk.append(u)
830 873
831 874 l = lr.readline()
832 875 if l.startswith('\ '):
833 876 s = self.a[-1][:-1]
834 877 self.a[-1] = s
835 878 self.hunk[-1] = s
836 879 l = lr.readline()
837 880 m = contextdesc.match(l)
838 881 if not m:
839 882 raise PatchError(_("bad hunk #%d") % self.number)
840 883 foo, self.startb, foo2, bend, foo3 = m.groups()
841 884 self.startb = int(self.startb)
842 885 if bend is None:
843 886 bend = self.startb
844 887 self.lenb = int(bend) - self.startb
845 888 if self.startb:
846 889 self.lenb += 1
847 890 hunki = 1
848 891 for x in xrange(self.lenb):
849 892 l = lr.readline()
850 893 if l.startswith('\ '):
851 894 # XXX: the only way to hit this is with an invalid line range.
852 895 # The no-eol marker is not counted in the line range, but I
853 896 # guess there are diff(1) out there which behave differently.
854 897 s = self.b[-1][:-1]
855 898 self.b[-1] = s
856 899 self.hunk[hunki - 1] = s
857 900 continue
858 901 if not l:
859 902 # line deletions, new block is empty and we hit EOF
860 903 lr.push(l)
861 904 break
862 905 s = l[2:]
863 906 if l.startswith('+ ') or l.startswith('! '):
864 907 u = '+' + s
865 908 elif l.startswith(' '):
866 909 u = ' ' + s
867 910 elif len(self.b) == 0:
868 911 # line deletions, new block is empty
869 912 lr.push(l)
870 913 break
871 914 else:
872 915 raise PatchError(_("bad hunk #%d old text line %d") %
873 916 (self.number, x))
874 917 self.b.append(s)
875 918 while True:
876 919 if hunki >= len(self.hunk):
877 920 h = ""
878 921 else:
879 922 h = self.hunk[hunki]
880 923 hunki += 1
881 924 if h == u:
882 925 break
883 926 elif h.startswith('-'):
884 927 continue
885 928 else:
886 929 self.hunk.insert(hunki - 1, u)
887 930 break
888 931
889 932 if not self.a:
890 933 # this happens when lines were only added to the hunk
891 934 for x in self.hunk:
892 935 if x.startswith('-') or x.startswith(' '):
893 936 self.a.append(x)
894 937 if not self.b:
895 938 # this happens when lines were only deleted from the hunk
896 939 for x in self.hunk:
897 940 if x.startswith('+') or x.startswith(' '):
898 941 self.b.append(x[1:])
899 942 # @@ -start,len +start,len @@
900 943 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
901 944 self.startb, self.lenb)
902 945 self.hunk[0] = self.desc
903 946 self._fixnewline(lr)
904 947
905 948 def _fixnewline(self, lr):
906 949 l = lr.readline()
907 950 if l.startswith('\ '):
908 951 diffhelpers.fix_newline(self.hunk, self.a, self.b)
909 952 else:
910 953 lr.push(l)
911 954
912 955 def complete(self):
913 956 return len(self.a) == self.lena and len(self.b) == self.lenb
914 957
915 958 def fuzzit(self, l, fuzz, toponly):
916 959 # this removes context lines from the top and bottom of list 'l'. It
917 960 # checks the hunk to make sure only context lines are removed, and then
918 961 # returns a new shortened list of lines.
919 962 fuzz = min(fuzz, len(l)-1)
920 963 if fuzz:
921 964 top = 0
922 965 bot = 0
923 966 hlen = len(self.hunk)
924 967 for x in xrange(hlen - 1):
925 968 # the hunk starts with the @@ line, so use x+1
926 969 if self.hunk[x + 1][0] == ' ':
927 970 top += 1
928 971 else:
929 972 break
930 973 if not toponly:
931 974 for x in xrange(hlen - 1):
932 975 if self.hunk[hlen - bot - 1][0] == ' ':
933 976 bot += 1
934 977 else:
935 978 break
936 979
937 980 # top and bot now count context in the hunk
938 981 # adjust them if either one is short
939 982 context = max(top, bot, 3)
940 983 if bot < context:
941 984 bot = max(0, fuzz - (context - bot))
942 985 else:
943 986 bot = min(fuzz, bot)
944 987 if top < context:
945 988 top = max(0, fuzz - (context - top))
946 989 else:
947 990 top = min(fuzz, top)
948 991
949 992 return l[top:len(l)-bot]
950 993 return l
951 994
952 995 def old(self, fuzz=0, toponly=False):
953 996 return self.fuzzit(self.a, fuzz, toponly)
954 997
955 998 def new(self, fuzz=0, toponly=False):
956 999 return self.fuzzit(self.b, fuzz, toponly)
957 1000
958 1001 class binhunk:
959 1002 'A binary patch file. Only understands literals so far.'
960 1003 def __init__(self, lr):
961 1004 self.text = None
962 1005 self.hunk = ['GIT binary patch\n']
963 1006 self._read(lr)
964 1007
965 1008 def complete(self):
966 1009 return self.text is not None
967 1010
968 1011 def new(self):
969 1012 return [self.text]
970 1013
971 1014 def _read(self, lr):
972 1015 line = lr.readline()
973 1016 self.hunk.append(line)
974 1017 while line and not line.startswith('literal '):
975 1018 line = lr.readline()
976 1019 self.hunk.append(line)
977 1020 if not line:
978 1021 raise PatchError(_('could not extract binary patch'))
979 1022 size = int(line[8:].rstrip())
980 1023 dec = []
981 1024 line = lr.readline()
982 1025 self.hunk.append(line)
983 1026 while len(line) > 1:
984 1027 l = line[0]
985 1028 if l <= 'Z' and l >= 'A':
986 1029 l = ord(l) - ord('A') + 1
987 1030 else:
988 1031 l = ord(l) - ord('a') + 27
989 1032 dec.append(base85.b85decode(line[1:-1])[:l])
990 1033 line = lr.readline()
991 1034 self.hunk.append(line)
992 1035 text = zlib.decompress(''.join(dec))
993 1036 if len(text) != size:
994 1037 raise PatchError(_('binary patch is %d bytes, not %d') %
995 1038 len(text), size)
996 1039 self.text = text
997 1040
998 1041 def parsefilename(str):
999 1042 # --- filename \t|space stuff
1000 1043 s = str[4:].rstrip('\r\n')
1001 1044 i = s.find('\t')
1002 1045 if i < 0:
1003 1046 i = s.find(' ')
1004 1047 if i < 0:
1005 1048 return s
1006 1049 return s[:i]
1007 1050
1008 1051 def pathstrip(path, strip):
1009 1052 pathlen = len(path)
1010 1053 i = 0
1011 1054 if strip == 0:
1012 1055 return '', path.rstrip()
1013 1056 count = strip
1014 1057 while count > 0:
1015 1058 i = path.find('/', i)
1016 1059 if i == -1:
1017 1060 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1018 1061 (count, strip, path))
1019 1062 i += 1
1020 1063 # consume '//' in the path
1021 1064 while i < pathlen - 1 and path[i] == '/':
1022 1065 i += 1
1023 1066 count -= 1
1024 1067 return path[:i].lstrip(), path[i:].rstrip()
1025 1068
1026 1069 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
1027 1070 nulla = afile_orig == "/dev/null"
1028 1071 nullb = bfile_orig == "/dev/null"
1029 1072 create = nulla and hunk.starta == 0 and hunk.lena == 0
1030 1073 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1031 1074 abase, afile = pathstrip(afile_orig, strip)
1032 1075 gooda = not nulla and backend.exists(afile)
1033 1076 bbase, bfile = pathstrip(bfile_orig, strip)
1034 1077 if afile == bfile:
1035 1078 goodb = gooda
1036 1079 else:
1037 1080 goodb = not nullb and backend.exists(bfile)
1038 1081 missing = not goodb and not gooda and not create
1039 1082
1040 1083 # some diff programs apparently produce patches where the afile is
1041 1084 # not /dev/null, but afile starts with bfile
1042 1085 abasedir = afile[:afile.rfind('/') + 1]
1043 1086 bbasedir = bfile[:bfile.rfind('/') + 1]
1044 1087 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1045 1088 and hunk.starta == 0 and hunk.lena == 0):
1046 1089 create = True
1047 1090 missing = False
1048 1091
1049 1092 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1050 1093 # diff is between a file and its backup. In this case, the original
1051 1094 # file should be patched (see original mpatch code).
1052 1095 isbackup = (abase == bbase and bfile.startswith(afile))
1053 1096 fname = None
1054 1097 if not missing:
1055 1098 if gooda and goodb:
1056 1099 fname = isbackup and afile or bfile
1057 1100 elif gooda:
1058 1101 fname = afile
1059 1102
1060 1103 if not fname:
1061 1104 if not nullb:
1062 1105 fname = isbackup and afile or bfile
1063 1106 elif not nulla:
1064 1107 fname = afile
1065 1108 else:
1066 1109 raise PatchError(_("undefined source and destination files"))
1067 1110
1068 1111 gp = patchmeta(fname)
1069 1112 if create:
1070 1113 gp.op = 'ADD'
1071 1114 elif remove:
1072 1115 gp.op = 'DELETE'
1073 1116 return gp
1074 1117
1075 1118 def scangitpatch(lr, firstline):
1076 1119 """
1077 1120 Git patches can emit:
1078 1121 - rename a to b
1079 1122 - change b
1080 1123 - copy a to c
1081 1124 - change c
1082 1125
1083 1126 We cannot apply this sequence as-is, the renamed 'a' could not be
1084 1127 found for it would have been renamed already. And we cannot copy
1085 1128 from 'b' instead because 'b' would have been changed already. So
1086 1129 we scan the git patch for copy and rename commands so we can
1087 1130 perform the copies ahead of time.
1088 1131 """
1089 1132 pos = 0
1090 1133 try:
1091 1134 pos = lr.fp.tell()
1092 1135 fp = lr.fp
1093 1136 except IOError:
1094 1137 fp = cStringIO.StringIO(lr.fp.read())
1095 1138 gitlr = linereader(fp)
1096 1139 gitlr.push(firstline)
1097 1140 gitpatches = readgitpatch(gitlr)
1098 1141 fp.seek(pos)
1099 1142 return gitpatches
1100 1143
1101 1144 def iterhunks(fp):
1102 1145 """Read a patch and yield the following events:
1103 1146 - ("file", afile, bfile, firsthunk): select a new target file.
1104 1147 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1105 1148 "file" event.
1106 1149 - ("git", gitchanges): current diff is in git format, gitchanges
1107 1150 maps filenames to gitpatch records. Unique event.
1108 1151 """
1109 1152 afile = ""
1110 1153 bfile = ""
1111 1154 state = None
1112 1155 hunknum = 0
1113 1156 emitfile = newfile = False
1114 1157 gitpatches = None
1115 1158
1116 1159 # our states
1117 1160 BFILE = 1
1118 1161 context = None
1119 1162 lr = linereader(fp)
1120 1163
1121 1164 while True:
1122 1165 x = lr.readline()
1123 1166 if not x:
1124 1167 break
1125 1168 if state == BFILE and (
1126 1169 (not context and x[0] == '@')
1127 1170 or (context is not False and x.startswith('***************'))
1128 1171 or x.startswith('GIT binary patch')):
1129 1172 gp = None
1130 1173 if (gitpatches and
1131 1174 (gitpatches[-1][0] == afile or gitpatches[-1][1] == bfile)):
1132 1175 gp = gitpatches.pop()[2]
1133 1176 if x.startswith('GIT binary patch'):
1134 1177 h = binhunk(lr)
1135 1178 else:
1136 1179 if context is None and x.startswith('***************'):
1137 1180 context = True
1138 1181 h = hunk(x, hunknum + 1, lr, context)
1139 1182 hunknum += 1
1140 1183 if emitfile:
1141 1184 emitfile = False
1142 1185 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1143 1186 yield 'hunk', h
1144 1187 elif x.startswith('diff --git'):
1145 1188 m = gitre.match(x)
1146 1189 if not m:
1147 1190 continue
1148 1191 if gitpatches is None:
1149 1192 # scan whole input for git metadata
1150 1193 gitpatches = [('a/' + gp.path, 'b/' + gp.path, gp) for gp
1151 1194 in scangitpatch(lr, x)]
1152 1195 yield 'git', [g[2].copy() for g in gitpatches
1153 1196 if g[2].op in ('COPY', 'RENAME')]
1154 1197 gitpatches.reverse()
1155 1198 afile = 'a/' + m.group(1)
1156 1199 bfile = 'b/' + m.group(2)
1157 1200 while afile != gitpatches[-1][0] and bfile != gitpatches[-1][1]:
1158 1201 gp = gitpatches.pop()[2]
1159 1202 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1160 1203 gp = gitpatches[-1][2]
1161 1204 # copy/rename + modify should modify target, not source
1162 1205 if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode:
1163 1206 afile = bfile
1164 1207 newfile = True
1165 1208 elif x.startswith('---'):
1166 1209 # check for a unified diff
1167 1210 l2 = lr.readline()
1168 1211 if not l2.startswith('+++'):
1169 1212 lr.push(l2)
1170 1213 continue
1171 1214 newfile = True
1172 1215 context = False
1173 1216 afile = parsefilename(x)
1174 1217 bfile = parsefilename(l2)
1175 1218 elif x.startswith('***'):
1176 1219 # check for a context diff
1177 1220 l2 = lr.readline()
1178 1221 if not l2.startswith('---'):
1179 1222 lr.push(l2)
1180 1223 continue
1181 1224 l3 = lr.readline()
1182 1225 lr.push(l3)
1183 1226 if not l3.startswith("***************"):
1184 1227 lr.push(l2)
1185 1228 continue
1186 1229 newfile = True
1187 1230 context = True
1188 1231 afile = parsefilename(x)
1189 1232 bfile = parsefilename(l2)
1190 1233
1191 1234 if newfile:
1192 1235 newfile = False
1193 1236 emitfile = True
1194 1237 state = BFILE
1195 1238 hunknum = 0
1196 1239
1197 1240 while gitpatches:
1198 1241 gp = gitpatches.pop()[2]
1199 1242 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1200 1243
1201 1244 def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
1202 1245 """Reads a patch from fp and tries to apply it.
1203 1246
1204 1247 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1205 1248 there was any fuzz.
1206 1249
1207 1250 If 'eolmode' is 'strict', the patch content and patched file are
1208 1251 read in binary mode. Otherwise, line endings are ignored when
1209 1252 patching then normalized according to 'eolmode'.
1210 1253 """
1211 1254 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1212 1255 eolmode=eolmode)
1213 1256
1214 1257 def _applydiff(ui, fp, patcher, backend, store, strip=1,
1215 1258 eolmode='strict'):
1216 1259
1217 1260 def pstrip(p):
1218 1261 return pathstrip(p, strip - 1)[1]
1219 1262
1220 1263 rejects = 0
1221 1264 err = 0
1222 1265 current_file = None
1223 1266
1224 1267 for state, values in iterhunks(fp):
1225 1268 if state == 'hunk':
1226 1269 if not current_file:
1227 1270 continue
1228 1271 ret = current_file.apply(values)
1229 1272 if ret > 0:
1230 1273 err = 1
1231 1274 elif state == 'file':
1232 1275 if current_file:
1233 1276 rejects += current_file.close()
1234 1277 current_file = None
1235 1278 afile, bfile, first_hunk, gp = values
1236 1279 if gp:
1237 1280 path = pstrip(gp.path)
1238 1281 gp.path = pstrip(gp.path)
1239 1282 if gp.oldpath:
1240 1283 gp.oldpath = pstrip(gp.oldpath)
1241 1284 else:
1242 1285 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1243 1286 if gp.op == 'RENAME':
1244 1287 backend.unlink(gp.oldpath)
1245 1288 if not first_hunk:
1246 1289 if gp.op == 'DELETE':
1247 1290 backend.unlink(gp.path)
1248 1291 continue
1249 1292 data, mode = None, None
1250 1293 if gp.op in ('RENAME', 'COPY'):
1251 1294 data, mode = store.getfile(gp.oldpath)[:2]
1252 1295 if gp.mode:
1253 1296 mode = gp.mode
1254 1297 if gp.op == 'ADD':
1255 1298 # Added files without content have no hunk and
1256 1299 # must be created
1257 1300 data = ''
1258 1301 if data or mode:
1259 1302 if (gp.op in ('ADD', 'RENAME', 'COPY')
1260 1303 and backend.exists(gp.path)):
1261 1304 raise PatchError(_("cannot create %s: destination "
1262 1305 "already exists") % gp.path)
1263 1306 backend.setfile(gp.path, data, mode, gp.oldpath)
1264 1307 continue
1265 1308 try:
1266 1309 current_file = patcher(ui, gp, backend, store,
1267 1310 eolmode=eolmode)
1268 1311 except PatchError, inst:
1269 1312 ui.warn(str(inst) + '\n')
1270 1313 current_file = None
1271 1314 rejects += 1
1272 1315 continue
1273 1316 elif state == 'git':
1274 1317 for gp in values:
1275 1318 path = pstrip(gp.oldpath)
1276 1319 data, mode = backend.getfile(path)
1277 1320 store.setfile(path, data, mode)
1278 1321 else:
1279 1322 raise util.Abort(_('unsupported parser state: %s') % state)
1280 1323
1281 1324 if current_file:
1282 1325 rejects += current_file.close()
1283 1326
1284 1327 if rejects:
1285 1328 return -1
1286 1329 return err
1287 1330
1288 1331 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1289 1332 similarity):
1290 1333 """use <patcher> to apply <patchname> to the working directory.
1291 1334 returns whether patch was applied with fuzz factor."""
1292 1335
1293 1336 fuzz = False
1294 1337 args = []
1295 1338 cwd = repo.root
1296 1339 if cwd:
1297 1340 args.append('-d %s' % util.shellquote(cwd))
1298 1341 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1299 1342 util.shellquote(patchname)))
1300 1343 try:
1301 1344 for line in fp:
1302 1345 line = line.rstrip()
1303 1346 ui.note(line + '\n')
1304 1347 if line.startswith('patching file '):
1305 1348 pf = util.parsepatchoutput(line)
1306 1349 printed_file = False
1307 1350 files.add(pf)
1308 1351 elif line.find('with fuzz') >= 0:
1309 1352 fuzz = True
1310 1353 if not printed_file:
1311 1354 ui.warn(pf + '\n')
1312 1355 printed_file = True
1313 1356 ui.warn(line + '\n')
1314 1357 elif line.find('saving rejects to file') >= 0:
1315 1358 ui.warn(line + '\n')
1316 1359 elif line.find('FAILED') >= 0:
1317 1360 if not printed_file:
1318 1361 ui.warn(pf + '\n')
1319 1362 printed_file = True
1320 1363 ui.warn(line + '\n')
1321 1364 finally:
1322 1365 if files:
1323 1366 cfiles = list(files)
1324 1367 cwd = repo.getcwd()
1325 1368 if cwd:
1326 1369 cfiles = [util.pathto(repo.root, cwd, f)
1327 1370 for f in cfile]
1328 1371 scmutil.addremove(repo, cfiles, similarity=similarity)
1329 1372 code = fp.close()
1330 1373 if code:
1331 1374 raise PatchError(_("patch command failed: %s") %
1332 1375 util.explainexit(code)[0])
1333 1376 return fuzz
1334 1377
1335 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1336 similarity=0):
1337 """use builtin patch to apply <patchobj> to the working directory.
1338 returns whether patch was applied with fuzz factor."""
1339
1378 def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
1340 1379 if files is None:
1341 1380 files = set()
1342 1381 if eolmode is None:
1343 1382 eolmode = ui.config('patch', 'eol', 'strict')
1344 1383 if eolmode.lower() not in eolmodes:
1345 1384 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1346 1385 eolmode = eolmode.lower()
1347 1386
1348 1387 store = filestore()
1349 backend = workingbackend(ui, repo, similarity)
1350 1388 try:
1351 1389 fp = open(patchobj, 'rb')
1352 1390 except TypeError:
1353 1391 fp = patchobj
1354 1392 try:
1355 1393 ret = applydiff(ui, fp, backend, store, strip=strip,
1356 1394 eolmode=eolmode)
1357 1395 finally:
1358 1396 if fp != patchobj:
1359 1397 fp.close()
1360 1398 files.update(backend.close())
1361 1399 store.close()
1362 1400 if ret < 0:
1363 1401 raise PatchError(_('patch failed to apply'))
1364 1402 return ret > 0
1365 1403
1404 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1405 similarity=0):
1406 """use builtin patch to apply <patchobj> to the working directory.
1407 returns whether patch was applied with fuzz factor."""
1408 backend = workingbackend(ui, repo, similarity)
1409 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1410
1411 def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
1412 eolmode='strict'):
1413 backend = repobackend(ui, repo, ctx, store)
1414 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1415
1416 def makememctx(repo, parents, text, user, date, branch, files, store,
1417 editor=None):
1418 def getfilectx(repo, memctx, path):
1419 data, (islink, isexec), copied = store.getfile(path)
1420 return context.memfilectx(path, data, islink=islink, isexec=isexec,
1421 copied=copied)
1422 extra = {}
1423 if branch:
1424 extra['branch'] = encoding.fromlocal(branch)
1425 ctx = context.memctx(repo, parents, text, files, getfilectx, user,
1426 date, extra)
1427 if editor:
1428 ctx._text = editor(repo, ctx, [])
1429 return ctx
1430
1366 1431 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1367 1432 similarity=0):
1368 1433 """Apply <patchname> to the working directory.
1369 1434
1370 1435 'eolmode' specifies how end of lines should be handled. It can be:
1371 1436 - 'strict': inputs are read in binary mode, EOLs are preserved
1372 1437 - 'crlf': EOLs are ignored when patching and reset to CRLF
1373 1438 - 'lf': EOLs are ignored when patching and reset to LF
1374 1439 - None: get it from user settings, default to 'strict'
1375 1440 'eolmode' is ignored when using an external patcher program.
1376 1441
1377 1442 Returns whether patch was applied with fuzz factor.
1378 1443 """
1379 1444 patcher = ui.config('ui', 'patch')
1380 1445 if files is None:
1381 1446 files = set()
1382 1447 try:
1383 1448 if patcher:
1384 1449 return _externalpatch(ui, repo, patcher, patchname, strip,
1385 1450 files, similarity)
1386 1451 return internalpatch(ui, repo, patchname, strip, files, eolmode,
1387 1452 similarity)
1388 1453 except PatchError, err:
1389 1454 raise util.Abort(str(err))
1390 1455
1391 1456 def changedfiles(ui, repo, patchpath, strip=1):
1392 1457 backend = fsbackend(ui, repo.root)
1393 1458 fp = open(patchpath, 'rb')
1394 1459 try:
1395 1460 changed = set()
1396 1461 for state, values in iterhunks(fp):
1397 1462 if state == 'file':
1398 1463 afile, bfile, first_hunk, gp = values
1399 1464 if gp:
1400 1465 gp.path = pathstrip(gp.path, strip - 1)[1]
1401 1466 if gp.oldpath:
1402 1467 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1403 1468 else:
1404 1469 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1405 1470 changed.add(gp.path)
1406 1471 if gp.op == 'RENAME':
1407 1472 changed.add(gp.oldpath)
1408 1473 elif state not in ('hunk', 'git'):
1409 1474 raise util.Abort(_('unsupported parser state: %s') % state)
1410 1475 return changed
1411 1476 finally:
1412 1477 fp.close()
1413 1478
1414 1479 def b85diff(to, tn):
1415 1480 '''print base85-encoded binary diff'''
1416 1481 def gitindex(text):
1417 1482 if not text:
1418 1483 return hex(nullid)
1419 1484 l = len(text)
1420 1485 s = util.sha1('blob %d\0' % l)
1421 1486 s.update(text)
1422 1487 return s.hexdigest()
1423 1488
1424 1489 def fmtline(line):
1425 1490 l = len(line)
1426 1491 if l <= 26:
1427 1492 l = chr(ord('A') + l - 1)
1428 1493 else:
1429 1494 l = chr(l - 26 + ord('a') - 1)
1430 1495 return '%c%s\n' % (l, base85.b85encode(line, True))
1431 1496
1432 1497 def chunk(text, csize=52):
1433 1498 l = len(text)
1434 1499 i = 0
1435 1500 while i < l:
1436 1501 yield text[i:i + csize]
1437 1502 i += csize
1438 1503
1439 1504 tohash = gitindex(to)
1440 1505 tnhash = gitindex(tn)
1441 1506 if tohash == tnhash:
1442 1507 return ""
1443 1508
1444 1509 # TODO: deltas
1445 1510 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1446 1511 (tohash, tnhash, len(tn))]
1447 1512 for l in chunk(zlib.compress(tn)):
1448 1513 ret.append(fmtline(l))
1449 1514 ret.append('\n')
1450 1515 return ''.join(ret)
1451 1516
1452 1517 class GitDiffRequired(Exception):
1453 1518 pass
1454 1519
1455 1520 def diffopts(ui, opts=None, untrusted=False):
1456 1521 def get(key, name=None, getter=ui.configbool):
1457 1522 return ((opts and opts.get(key)) or
1458 1523 getter('diff', name or key, None, untrusted=untrusted))
1459 1524 return mdiff.diffopts(
1460 1525 text=opts and opts.get('text'),
1461 1526 git=get('git'),
1462 1527 nodates=get('nodates'),
1463 1528 showfunc=get('show_function', 'showfunc'),
1464 1529 ignorews=get('ignore_all_space', 'ignorews'),
1465 1530 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1466 1531 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1467 1532 context=get('unified', getter=ui.config))
1468 1533
1469 1534 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1470 1535 losedatafn=None, prefix=''):
1471 1536 '''yields diff of changes to files between two nodes, or node and
1472 1537 working directory.
1473 1538
1474 1539 if node1 is None, use first dirstate parent instead.
1475 1540 if node2 is None, compare node1 with working directory.
1476 1541
1477 1542 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1478 1543 every time some change cannot be represented with the current
1479 1544 patch format. Return False to upgrade to git patch format, True to
1480 1545 accept the loss or raise an exception to abort the diff. It is
1481 1546 called with the name of current file being diffed as 'fn'. If set
1482 1547 to None, patches will always be upgraded to git format when
1483 1548 necessary.
1484 1549
1485 1550 prefix is a filename prefix that is prepended to all filenames on
1486 1551 display (used for subrepos).
1487 1552 '''
1488 1553
1489 1554 if opts is None:
1490 1555 opts = mdiff.defaultopts
1491 1556
1492 1557 if not node1 and not node2:
1493 1558 node1 = repo.dirstate.p1()
1494 1559
1495 1560 def lrugetfilectx():
1496 1561 cache = {}
1497 1562 order = []
1498 1563 def getfilectx(f, ctx):
1499 1564 fctx = ctx.filectx(f, filelog=cache.get(f))
1500 1565 if f not in cache:
1501 1566 if len(cache) > 20:
1502 1567 del cache[order.pop(0)]
1503 1568 cache[f] = fctx.filelog()
1504 1569 else:
1505 1570 order.remove(f)
1506 1571 order.append(f)
1507 1572 return fctx
1508 1573 return getfilectx
1509 1574 getfilectx = lrugetfilectx()
1510 1575
1511 1576 ctx1 = repo[node1]
1512 1577 ctx2 = repo[node2]
1513 1578
1514 1579 if not changes:
1515 1580 changes = repo.status(ctx1, ctx2, match=match)
1516 1581 modified, added, removed = changes[:3]
1517 1582
1518 1583 if not modified and not added and not removed:
1519 1584 return []
1520 1585
1521 1586 revs = None
1522 1587 if not repo.ui.quiet:
1523 1588 hexfunc = repo.ui.debugflag and hex or short
1524 1589 revs = [hexfunc(node) for node in [node1, node2] if node]
1525 1590
1526 1591 copy = {}
1527 1592 if opts.git or opts.upgrade:
1528 1593 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1529 1594
1530 1595 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1531 1596 modified, added, removed, copy, getfilectx, opts, losedata, prefix)
1532 1597 if opts.upgrade and not opts.git:
1533 1598 try:
1534 1599 def losedata(fn):
1535 1600 if not losedatafn or not losedatafn(fn=fn):
1536 1601 raise GitDiffRequired()
1537 1602 # Buffer the whole output until we are sure it can be generated
1538 1603 return list(difffn(opts.copy(git=False), losedata))
1539 1604 except GitDiffRequired:
1540 1605 return difffn(opts.copy(git=True), None)
1541 1606 else:
1542 1607 return difffn(opts, None)
1543 1608
1544 1609 def difflabel(func, *args, **kw):
1545 1610 '''yields 2-tuples of (output, label) based on the output of func()'''
1546 1611 prefixes = [('diff', 'diff.diffline'),
1547 1612 ('copy', 'diff.extended'),
1548 1613 ('rename', 'diff.extended'),
1549 1614 ('old', 'diff.extended'),
1550 1615 ('new', 'diff.extended'),
1551 1616 ('deleted', 'diff.extended'),
1552 1617 ('---', 'diff.file_a'),
1553 1618 ('+++', 'diff.file_b'),
1554 1619 ('@@', 'diff.hunk'),
1555 1620 ('-', 'diff.deleted'),
1556 1621 ('+', 'diff.inserted')]
1557 1622
1558 1623 for chunk in func(*args, **kw):
1559 1624 lines = chunk.split('\n')
1560 1625 for i, line in enumerate(lines):
1561 1626 if i != 0:
1562 1627 yield ('\n', '')
1563 1628 stripline = line
1564 1629 if line and line[0] in '+-':
1565 1630 # highlight trailing whitespace, but only in changed lines
1566 1631 stripline = line.rstrip()
1567 1632 for prefix, label in prefixes:
1568 1633 if stripline.startswith(prefix):
1569 1634 yield (stripline, label)
1570 1635 break
1571 1636 else:
1572 1637 yield (line, '')
1573 1638 if line != stripline:
1574 1639 yield (line[len(stripline):], 'diff.trailingwhitespace')
1575 1640
1576 1641 def diffui(*args, **kw):
1577 1642 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1578 1643 return difflabel(diff, *args, **kw)
1579 1644
1580 1645
1581 1646 def _addmodehdr(header, omode, nmode):
1582 1647 if omode != nmode:
1583 1648 header.append('old mode %s\n' % omode)
1584 1649 header.append('new mode %s\n' % nmode)
1585 1650
1586 1651 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1587 1652 copy, getfilectx, opts, losedatafn, prefix):
1588 1653
1589 1654 def join(f):
1590 1655 return os.path.join(prefix, f)
1591 1656
1592 1657 date1 = util.datestr(ctx1.date())
1593 1658 man1 = ctx1.manifest()
1594 1659
1595 1660 gone = set()
1596 1661 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1597 1662
1598 1663 copyto = dict([(v, k) for k, v in copy.items()])
1599 1664
1600 1665 if opts.git:
1601 1666 revs = None
1602 1667
1603 1668 for f in sorted(modified + added + removed):
1604 1669 to = None
1605 1670 tn = None
1606 1671 dodiff = True
1607 1672 header = []
1608 1673 if f in man1:
1609 1674 to = getfilectx(f, ctx1).data()
1610 1675 if f not in removed:
1611 1676 tn = getfilectx(f, ctx2).data()
1612 1677 a, b = f, f
1613 1678 if opts.git or losedatafn:
1614 1679 if f in added:
1615 1680 mode = gitmode[ctx2.flags(f)]
1616 1681 if f in copy or f in copyto:
1617 1682 if opts.git:
1618 1683 if f in copy:
1619 1684 a = copy[f]
1620 1685 else:
1621 1686 a = copyto[f]
1622 1687 omode = gitmode[man1.flags(a)]
1623 1688 _addmodehdr(header, omode, mode)
1624 1689 if a in removed and a not in gone:
1625 1690 op = 'rename'
1626 1691 gone.add(a)
1627 1692 else:
1628 1693 op = 'copy'
1629 1694 header.append('%s from %s\n' % (op, join(a)))
1630 1695 header.append('%s to %s\n' % (op, join(f)))
1631 1696 to = getfilectx(a, ctx1).data()
1632 1697 else:
1633 1698 losedatafn(f)
1634 1699 else:
1635 1700 if opts.git:
1636 1701 header.append('new file mode %s\n' % mode)
1637 1702 elif ctx2.flags(f):
1638 1703 losedatafn(f)
1639 1704 # In theory, if tn was copied or renamed we should check
1640 1705 # if the source is binary too but the copy record already
1641 1706 # forces git mode.
1642 1707 if util.binary(tn):
1643 1708 if opts.git:
1644 1709 dodiff = 'binary'
1645 1710 else:
1646 1711 losedatafn(f)
1647 1712 if not opts.git and not tn:
1648 1713 # regular diffs cannot represent new empty file
1649 1714 losedatafn(f)
1650 1715 elif f in removed:
1651 1716 if opts.git:
1652 1717 # have we already reported a copy above?
1653 1718 if ((f in copy and copy[f] in added
1654 1719 and copyto[copy[f]] == f) or
1655 1720 (f in copyto and copyto[f] in added
1656 1721 and copy[copyto[f]] == f)):
1657 1722 dodiff = False
1658 1723 else:
1659 1724 header.append('deleted file mode %s\n' %
1660 1725 gitmode[man1.flags(f)])
1661 1726 elif not to or util.binary(to):
1662 1727 # regular diffs cannot represent empty file deletion
1663 1728 losedatafn(f)
1664 1729 else:
1665 1730 oflag = man1.flags(f)
1666 1731 nflag = ctx2.flags(f)
1667 1732 binary = util.binary(to) or util.binary(tn)
1668 1733 if opts.git:
1669 1734 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1670 1735 if binary:
1671 1736 dodiff = 'binary'
1672 1737 elif binary or nflag != oflag:
1673 1738 losedatafn(f)
1674 1739 if opts.git:
1675 1740 header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
1676 1741
1677 1742 if dodiff:
1678 1743 if dodiff == 'binary':
1679 1744 text = b85diff(to, tn)
1680 1745 else:
1681 1746 text = mdiff.unidiff(to, date1,
1682 1747 # ctx2 date may be dynamic
1683 1748 tn, util.datestr(ctx2.date()),
1684 1749 join(a), join(b), revs, opts=opts)
1685 1750 if header and (text or len(header) > 1):
1686 1751 yield ''.join(header)
1687 1752 if text:
1688 1753 yield text
1689 1754
1690 1755 def diffstatsum(stats):
1691 1756 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
1692 1757 for f, a, r, b in stats:
1693 1758 maxfile = max(maxfile, encoding.colwidth(f))
1694 1759 maxtotal = max(maxtotal, a + r)
1695 1760 addtotal += a
1696 1761 removetotal += r
1697 1762 binary = binary or b
1698 1763
1699 1764 return maxfile, maxtotal, addtotal, removetotal, binary
1700 1765
1701 1766 def diffstatdata(lines):
1702 1767 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
1703 1768
1704 1769 results = []
1705 1770 filename, adds, removes = None, 0, 0
1706 1771
1707 1772 def addresult():
1708 1773 if filename:
1709 1774 isbinary = adds == 0 and removes == 0
1710 1775 results.append((filename, adds, removes, isbinary))
1711 1776
1712 1777 for line in lines:
1713 1778 if line.startswith('diff'):
1714 1779 addresult()
1715 1780 # set numbers to 0 anyway when starting new file
1716 1781 adds, removes = 0, 0
1717 1782 if line.startswith('diff --git'):
1718 1783 filename = gitre.search(line).group(1)
1719 1784 elif line.startswith('diff -r'):
1720 1785 # format: "diff -r ... -r ... filename"
1721 1786 filename = diffre.search(line).group(1)
1722 1787 elif line.startswith('+') and not line.startswith('+++'):
1723 1788 adds += 1
1724 1789 elif line.startswith('-') and not line.startswith('---'):
1725 1790 removes += 1
1726 1791 addresult()
1727 1792 return results
1728 1793
1729 1794 def diffstat(lines, width=80, git=False):
1730 1795 output = []
1731 1796 stats = diffstatdata(lines)
1732 1797 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
1733 1798
1734 1799 countwidth = len(str(maxtotal))
1735 1800 if hasbinary and countwidth < 3:
1736 1801 countwidth = 3
1737 1802 graphwidth = width - countwidth - maxname - 6
1738 1803 if graphwidth < 10:
1739 1804 graphwidth = 10
1740 1805
1741 1806 def scale(i):
1742 1807 if maxtotal <= graphwidth:
1743 1808 return i
1744 1809 # If diffstat runs out of room it doesn't print anything,
1745 1810 # which isn't very useful, so always print at least one + or -
1746 1811 # if there were at least some changes.
1747 1812 return max(i * graphwidth // maxtotal, int(bool(i)))
1748 1813
1749 1814 for filename, adds, removes, isbinary in stats:
1750 1815 if git and isbinary:
1751 1816 count = 'Bin'
1752 1817 else:
1753 1818 count = adds + removes
1754 1819 pluses = '+' * scale(adds)
1755 1820 minuses = '-' * scale(removes)
1756 1821 output.append(' %s%s | %*s %s%s\n' %
1757 1822 (filename, ' ' * (maxname - encoding.colwidth(filename)),
1758 1823 countwidth, count, pluses, minuses))
1759 1824
1760 1825 if stats:
1761 1826 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1762 1827 % (len(stats), totaladds, totalremoves))
1763 1828
1764 1829 return ''.join(output)
1765 1830
1766 1831 def diffstatui(*args, **kw):
1767 1832 '''like diffstat(), but yields 2-tuples of (output, label) for
1768 1833 ui.write()
1769 1834 '''
1770 1835
1771 1836 for line in diffstat(*args, **kw).splitlines():
1772 1837 if line and line[-1] in '+-':
1773 1838 name, graph = line.rsplit(' ', 1)
1774 1839 yield (name + ' ', '')
1775 1840 m = re.search(r'\++', graph)
1776 1841 if m:
1777 1842 yield (m.group(0), 'diffstat.inserted')
1778 1843 m = re.search(r'-+', graph)
1779 1844 if m:
1780 1845 yield (m.group(0), 'diffstat.deleted')
1781 1846 else:
1782 1847 yield (line, '')
1783 1848 yield ('\n', '')
@@ -1,267 +1,267 b''
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 add
4 4 addremove
5 5 annotate
6 6 archive
7 7 backout
8 8 bisect
9 9 bookmarks
10 10 branch
11 11 branches
12 12 bundle
13 13 cat
14 14 clone
15 15 commit
16 16 copy
17 17 diff
18 18 export
19 19 forget
20 20 grep
21 21 heads
22 22 help
23 23 identify
24 24 import
25 25 incoming
26 26 init
27 27 locate
28 28 log
29 29 manifest
30 30 merge
31 31 outgoing
32 32 parents
33 33 paths
34 34 pull
35 35 push
36 36 recover
37 37 remove
38 38 rename
39 39 resolve
40 40 revert
41 41 rollback
42 42 root
43 43 serve
44 44 showconfig
45 45 status
46 46 summary
47 47 tag
48 48 tags
49 49 tip
50 50 unbundle
51 51 update
52 52 verify
53 53 version
54 54
55 55 Show all commands that start with "a"
56 56 $ hg debugcomplete a
57 57 add
58 58 addremove
59 59 annotate
60 60 archive
61 61
62 62 Do not show debug commands if there are other candidates
63 63 $ hg debugcomplete d
64 64 diff
65 65
66 66 Show debug commands if there are no other candidates
67 67 $ hg debugcomplete debug
68 68 debugancestor
69 69 debugbuilddag
70 70 debugbundle
71 71 debugcheckstate
72 72 debugcommands
73 73 debugcomplete
74 74 debugconfig
75 75 debugdag
76 76 debugdata
77 77 debugdate
78 78 debugdiscovery
79 79 debugfileset
80 80 debugfsinfo
81 81 debuggetbundle
82 82 debugignore
83 83 debugindex
84 84 debugindexdot
85 85 debuginstall
86 86 debugknown
87 87 debugpushkey
88 88 debugrebuildstate
89 89 debugrename
90 90 debugrevlog
91 91 debugrevspec
92 92 debugsetparents
93 93 debugstate
94 94 debugsub
95 95 debugwalk
96 96 debugwireargs
97 97
98 98 Do not show the alias of a debug command if there are other candidates
99 99 (this should hide rawcommit)
100 100 $ hg debugcomplete r
101 101 recover
102 102 remove
103 103 rename
104 104 resolve
105 105 revert
106 106 rollback
107 107 root
108 108 Show the alias of a debug command if there are no other candidates
109 109 $ hg debugcomplete rawc
110 110
111 111
112 112 Show the global options
113 113 $ hg debugcomplete --options | sort
114 114 --config
115 115 --cwd
116 116 --debug
117 117 --debugger
118 118 --encoding
119 119 --encodingmode
120 120 --help
121 121 --noninteractive
122 122 --profile
123 123 --quiet
124 124 --repository
125 125 --time
126 126 --traceback
127 127 --verbose
128 128 --version
129 129 -R
130 130 -h
131 131 -q
132 132 -v
133 133 -y
134 134
135 135 Show the options for the "serve" command
136 136 $ hg debugcomplete --options serve | sort
137 137 --accesslog
138 138 --address
139 139 --certificate
140 140 --config
141 141 --cwd
142 142 --daemon
143 143 --daemon-pipefds
144 144 --debug
145 145 --debugger
146 146 --encoding
147 147 --encodingmode
148 148 --errorlog
149 149 --help
150 150 --ipv6
151 151 --name
152 152 --noninteractive
153 153 --pid-file
154 154 --port
155 155 --prefix
156 156 --profile
157 157 --quiet
158 158 --repository
159 159 --stdio
160 160 --style
161 161 --templates
162 162 --time
163 163 --traceback
164 164 --verbose
165 165 --version
166 166 --web-conf
167 167 -6
168 168 -A
169 169 -E
170 170 -R
171 171 -a
172 172 -d
173 173 -h
174 174 -n
175 175 -p
176 176 -q
177 177 -t
178 178 -v
179 179 -y
180 180
181 181 Show an error if we use --options with an ambiguous abbreviation
182 182 $ hg debugcomplete --options s
183 183 hg: command 's' is ambiguous:
184 184 serve showconfig status summary
185 185 [255]
186 186
187 187 Show all commands + options
188 188 $ hg debugcommands
189 189 add: include, exclude, subrepos, dry-run
190 190 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
191 191 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
192 192 commit: addremove, close-branch, include, exclude, message, logfile, date, user
193 193 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
194 194 export: output, switch-parent, rev, text, git, nodates
195 195 forget: include, exclude
196 196 init: ssh, remotecmd, insecure
197 197 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
198 198 merge: force, tool, rev, preview
199 199 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
200 200 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
201 201 remove: after, force, include, exclude
202 202 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
203 203 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
204 204 summary: remote
205 205 update: clean, check, date, rev
206 206 addremove: similarity, include, exclude, dry-run
207 207 archive: no-decode, prefix, rev, type, subrepos, include, exclude
208 208 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
209 209 bisect: reset, good, bad, skip, extend, command, noupdate
210 210 bookmarks: force, rev, delete, rename, inactive
211 211 branch: force, clean
212 212 branches: active, closed
213 213 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
214 214 cat: output, rev, decode, include, exclude
215 215 copy: after, force, include, exclude, dry-run
216 216 debugancestor:
217 217 debugbuilddag: mergeable-file, overwritten-file, new-file
218 218 debugbundle: all
219 219 debugcheckstate:
220 220 debugcommands:
221 221 debugcomplete: options
222 222 debugdag: tags, branches, dots, spaces
223 223 debugdata: changelog, manifest
224 224 debugdate: extended
225 225 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
226 226 debugfileset:
227 227 debugfsinfo:
228 228 debuggetbundle: head, common, type
229 229 debugignore:
230 230 debugindex: changelog, manifest, format
231 231 debugindexdot:
232 232 debuginstall:
233 233 debugknown:
234 234 debugpushkey:
235 235 debugrebuildstate: rev
236 236 debugrename: rev
237 237 debugrevlog: changelog, manifest, dump
238 238 debugrevspec:
239 239 debugsetparents:
240 240 debugstate: nodates, datesort
241 241 debugsub: rev
242 242 debugwalk: include, exclude
243 243 debugwireargs: three, four, five, ssh, remotecmd, insecure
244 244 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
245 245 heads: rev, topo, active, closed, style, template
246 246 help: extension, command
247 247 identify: rev, num, id, branch, tags, bookmarks
248 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
248 import: strip, base, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity
249 249 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
250 250 locate: rev, print0, fullpath, include, exclude
251 251 manifest: rev, all
252 252 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
253 253 parents: rev, style, template
254 254 paths:
255 255 recover:
256 256 rename: after, force, include, exclude, dry-run
257 257 resolve: all, list, mark, unmark, tool, no-status, include, exclude
258 258 revert: all, date, rev, no-backup, include, exclude, dry-run
259 259 rollback: dry-run
260 260 root:
261 261 showconfig: untrusted
262 262 tag: force, local, rev, remove, edit, message, date, user
263 263 tags:
264 264 tip: patch, git, style, template
265 265 unbundle: update
266 266 verify:
267 267 version:
General Comments 0
You need to be logged in to leave comments. Login now