##// 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 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile, errno
11 import os, re, sys, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset, fileset
16 import minirst, revset, fileset
17 import dagparser, context, simplemerge
17 import dagparser, context, simplemerge
18 import random, setdiscovery, treediscovery, dagutil
18 import random, setdiscovery, treediscovery, dagutil
19
19
20 table = {}
20 table = {}
21
21
22 command = cmdutil.command(table)
22 command = cmdutil.command(table)
23
23
24 # common command options
24 # common command options
25
25
26 globalopts = [
26 globalopts = [
27 ('R', 'repository', '',
27 ('R', 'repository', '',
28 _('repository root directory or name of overlay bundle file'),
28 _('repository root directory or name of overlay bundle file'),
29 _('REPO')),
29 _('REPO')),
30 ('', 'cwd', '',
30 ('', 'cwd', '',
31 _('change working directory'), _('DIR')),
31 _('change working directory'), _('DIR')),
32 ('y', 'noninteractive', None,
32 ('y', 'noninteractive', None,
33 _('do not prompt, assume \'yes\' for any required answers')),
33 _('do not prompt, assume \'yes\' for any required answers')),
34 ('q', 'quiet', None, _('suppress output')),
34 ('q', 'quiet', None, _('suppress output')),
35 ('v', 'verbose', None, _('enable additional output')),
35 ('v', 'verbose', None, _('enable additional output')),
36 ('', 'config', [],
36 ('', 'config', [],
37 _('set/override config option (use \'section.name=value\')'),
37 _('set/override config option (use \'section.name=value\')'),
38 _('CONFIG')),
38 _('CONFIG')),
39 ('', 'debug', None, _('enable debugging output')),
39 ('', 'debug', None, _('enable debugging output')),
40 ('', 'debugger', None, _('start debugger')),
40 ('', 'debugger', None, _('start debugger')),
41 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
41 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
42 _('ENCODE')),
42 _('ENCODE')),
43 ('', 'encodingmode', encoding.encodingmode,
43 ('', 'encodingmode', encoding.encodingmode,
44 _('set the charset encoding mode'), _('MODE')),
44 _('set the charset encoding mode'), _('MODE')),
45 ('', 'traceback', None, _('always print a traceback on exception')),
45 ('', 'traceback', None, _('always print a traceback on exception')),
46 ('', 'time', None, _('time how long the command takes')),
46 ('', 'time', None, _('time how long the command takes')),
47 ('', 'profile', None, _('print command execution profile')),
47 ('', 'profile', None, _('print command execution profile')),
48 ('', 'version', None, _('output version information and exit')),
48 ('', 'version', None, _('output version information and exit')),
49 ('h', 'help', None, _('display help and exit')),
49 ('h', 'help', None, _('display help and exit')),
50 ]
50 ]
51
51
52 dryrunopts = [('n', 'dry-run', None,
52 dryrunopts = [('n', 'dry-run', None,
53 _('do not perform actions, just print output'))]
53 _('do not perform actions, just print output'))]
54
54
55 remoteopts = [
55 remoteopts = [
56 ('e', 'ssh', '',
56 ('e', 'ssh', '',
57 _('specify ssh command to use'), _('CMD')),
57 _('specify ssh command to use'), _('CMD')),
58 ('', 'remotecmd', '',
58 ('', 'remotecmd', '',
59 _('specify hg command to run on the remote side'), _('CMD')),
59 _('specify hg command to run on the remote side'), _('CMD')),
60 ('', 'insecure', None,
60 ('', 'insecure', None,
61 _('do not verify server certificate (ignoring web.cacerts config)')),
61 _('do not verify server certificate (ignoring web.cacerts config)')),
62 ]
62 ]
63
63
64 walkopts = [
64 walkopts = [
65 ('I', 'include', [],
65 ('I', 'include', [],
66 _('include names matching the given patterns'), _('PATTERN')),
66 _('include names matching the given patterns'), _('PATTERN')),
67 ('X', 'exclude', [],
67 ('X', 'exclude', [],
68 _('exclude names matching the given patterns'), _('PATTERN')),
68 _('exclude names matching the given patterns'), _('PATTERN')),
69 ]
69 ]
70
70
71 commitopts = [
71 commitopts = [
72 ('m', 'message', '',
72 ('m', 'message', '',
73 _('use text as commit message'), _('TEXT')),
73 _('use text as commit message'), _('TEXT')),
74 ('l', 'logfile', '',
74 ('l', 'logfile', '',
75 _('read commit message from file'), _('FILE')),
75 _('read commit message from file'), _('FILE')),
76 ]
76 ]
77
77
78 commitopts2 = [
78 commitopts2 = [
79 ('d', 'date', '',
79 ('d', 'date', '',
80 _('record the specified date as commit date'), _('DATE')),
80 _('record the specified date as commit date'), _('DATE')),
81 ('u', 'user', '',
81 ('u', 'user', '',
82 _('record the specified user as committer'), _('USER')),
82 _('record the specified user as committer'), _('USER')),
83 ]
83 ]
84
84
85 templateopts = [
85 templateopts = [
86 ('', 'style', '',
86 ('', 'style', '',
87 _('display using template map file'), _('STYLE')),
87 _('display using template map file'), _('STYLE')),
88 ('', 'template', '',
88 ('', 'template', '',
89 _('display with template'), _('TEMPLATE')),
89 _('display with template'), _('TEMPLATE')),
90 ]
90 ]
91
91
92 logopts = [
92 logopts = [
93 ('p', 'patch', None, _('show patch')),
93 ('p', 'patch', None, _('show patch')),
94 ('g', 'git', None, _('use git extended diff format')),
94 ('g', 'git', None, _('use git extended diff format')),
95 ('l', 'limit', '',
95 ('l', 'limit', '',
96 _('limit number of changes displayed'), _('NUM')),
96 _('limit number of changes displayed'), _('NUM')),
97 ('M', 'no-merges', None, _('do not show merges')),
97 ('M', 'no-merges', None, _('do not show merges')),
98 ('', 'stat', None, _('output diffstat-style summary of changes')),
98 ('', 'stat', None, _('output diffstat-style summary of changes')),
99 ] + templateopts
99 ] + templateopts
100
100
101 diffopts = [
101 diffopts = [
102 ('a', 'text', None, _('treat all files as text')),
102 ('a', 'text', None, _('treat all files as text')),
103 ('g', 'git', None, _('use git extended diff format')),
103 ('g', 'git', None, _('use git extended diff format')),
104 ('', 'nodates', None, _('omit dates from diff headers'))
104 ('', 'nodates', None, _('omit dates from diff headers'))
105 ]
105 ]
106
106
107 diffopts2 = [
107 diffopts2 = [
108 ('p', 'show-function', None, _('show which function each change is in')),
108 ('p', 'show-function', None, _('show which function each change is in')),
109 ('', 'reverse', None, _('produce a diff that undoes the changes')),
109 ('', 'reverse', None, _('produce a diff that undoes the changes')),
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ('U', 'unified', '',
116 ('U', 'unified', '',
117 _('number of lines of context to show'), _('NUM')),
117 _('number of lines of context to show'), _('NUM')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ]
119 ]
120
120
121 similarityopts = [
121 similarityopts = [
122 ('s', 'similarity', '',
122 ('s', 'similarity', '',
123 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
123 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
124 ]
124 ]
125
125
126 subrepoopts = [
126 subrepoopts = [
127 ('S', 'subrepos', None,
127 ('S', 'subrepos', None,
128 _('recurse into subrepositories'))
128 _('recurse into subrepositories'))
129 ]
129 ]
130
130
131 # Commands start here, listed alphabetically
131 # Commands start here, listed alphabetically
132
132
133 @command('^add',
133 @command('^add',
134 walkopts + subrepoopts + dryrunopts,
134 walkopts + subrepoopts + dryrunopts,
135 _('[OPTION]... [FILE]...'))
135 _('[OPTION]... [FILE]...'))
136 def add(ui, repo, *pats, **opts):
136 def add(ui, repo, *pats, **opts):
137 """add the specified files on the next commit
137 """add the specified files on the next commit
138
138
139 Schedule files to be version controlled and added to the
139 Schedule files to be version controlled and added to the
140 repository.
140 repository.
141
141
142 The files will be added to the repository at the next commit. To
142 The files will be added to the repository at the next commit. To
143 undo an add before that, see :hg:`forget`.
143 undo an add before that, see :hg:`forget`.
144
144
145 If no names are given, add all files to the repository.
145 If no names are given, add all files to the repository.
146
146
147 .. container:: verbose
147 .. container:: verbose
148
148
149 An example showing how new (unknown) files are added
149 An example showing how new (unknown) files are added
150 automatically by :hg:`add`::
150 automatically by :hg:`add`::
151
151
152 $ ls
152 $ ls
153 foo.c
153 foo.c
154 $ hg status
154 $ hg status
155 ? foo.c
155 ? foo.c
156 $ hg add
156 $ hg add
157 adding foo.c
157 adding foo.c
158 $ hg status
158 $ hg status
159 A foo.c
159 A foo.c
160
160
161 Returns 0 if all files are successfully added.
161 Returns 0 if all files are successfully added.
162 """
162 """
163
163
164 m = scmutil.match(repo, pats, opts)
164 m = scmutil.match(repo, pats, opts)
165 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
165 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
166 opts.get('subrepos'), prefix="")
166 opts.get('subrepos'), prefix="")
167 return rejected and 1 or 0
167 return rejected and 1 or 0
168
168
169 @command('addremove',
169 @command('addremove',
170 similarityopts + walkopts + dryrunopts,
170 similarityopts + walkopts + dryrunopts,
171 _('[OPTION]... [FILE]...'))
171 _('[OPTION]... [FILE]...'))
172 def addremove(ui, repo, *pats, **opts):
172 def addremove(ui, repo, *pats, **opts):
173 """add all new files, delete all missing files
173 """add all new files, delete all missing files
174
174
175 Add all new files and remove all missing files from the
175 Add all new files and remove all missing files from the
176 repository.
176 repository.
177
177
178 New files are ignored if they match any of the patterns in
178 New files are ignored if they match any of the patterns in
179 ``.hgignore``. As with add, these changes take effect at the next
179 ``.hgignore``. As with add, these changes take effect at the next
180 commit.
180 commit.
181
181
182 Use the -s/--similarity option to detect renamed files. With a
182 Use the -s/--similarity option to detect renamed files. With a
183 parameter greater than 0, this compares every removed file with
183 parameter greater than 0, this compares every removed file with
184 every added file and records those similar enough as renames. This
184 every added file and records those similar enough as renames. This
185 option takes a percentage between 0 (disabled) and 100 (files must
185 option takes a percentage between 0 (disabled) and 100 (files must
186 be identical) as its parameter. Detecting renamed files this way
186 be identical) as its parameter. Detecting renamed files this way
187 can be expensive. After using this option, :hg:`status -C` can be
187 can be expensive. After using this option, :hg:`status -C` can be
188 used to check which files were identified as moved or renamed.
188 used to check which files were identified as moved or renamed.
189
189
190 Returns 0 if all files are successfully added.
190 Returns 0 if all files are successfully added.
191 """
191 """
192 try:
192 try:
193 sim = float(opts.get('similarity') or 100)
193 sim = float(opts.get('similarity') or 100)
194 except ValueError:
194 except ValueError:
195 raise util.Abort(_('similarity must be a number'))
195 raise util.Abort(_('similarity must be a number'))
196 if sim < 0 or sim > 100:
196 if sim < 0 or sim > 100:
197 raise util.Abort(_('similarity must be between 0 and 100'))
197 raise util.Abort(_('similarity must be between 0 and 100'))
198 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
198 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
199
199
200 @command('^annotate|blame',
200 @command('^annotate|blame',
201 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
201 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
202 ('', 'follow', None,
202 ('', 'follow', None,
203 _('follow copies/renames and list the filename (DEPRECATED)')),
203 _('follow copies/renames and list the filename (DEPRECATED)')),
204 ('', 'no-follow', None, _("don't follow copies and renames")),
204 ('', 'no-follow', None, _("don't follow copies and renames")),
205 ('a', 'text', None, _('treat all files as text')),
205 ('a', 'text', None, _('treat all files as text')),
206 ('u', 'user', None, _('list the author (long with -v)')),
206 ('u', 'user', None, _('list the author (long with -v)')),
207 ('f', 'file', None, _('list the filename')),
207 ('f', 'file', None, _('list the filename')),
208 ('d', 'date', None, _('list the date (short with -q)')),
208 ('d', 'date', None, _('list the date (short with -q)')),
209 ('n', 'number', None, _('list the revision number (default)')),
209 ('n', 'number', None, _('list the revision number (default)')),
210 ('c', 'changeset', None, _('list the changeset')),
210 ('c', 'changeset', None, _('list the changeset')),
211 ('l', 'line-number', None, _('show line number at the first appearance'))
211 ('l', 'line-number', None, _('show line number at the first appearance'))
212 ] + walkopts,
212 ] + walkopts,
213 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
213 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
214 def annotate(ui, repo, *pats, **opts):
214 def annotate(ui, repo, *pats, **opts):
215 """show changeset information by line for each file
215 """show changeset information by line for each file
216
216
217 List changes in files, showing the revision id responsible for
217 List changes in files, showing the revision id responsible for
218 each line
218 each line
219
219
220 This command is useful for discovering when a change was made and
220 This command is useful for discovering when a change was made and
221 by whom.
221 by whom.
222
222
223 Without the -a/--text option, annotate will avoid processing files
223 Without the -a/--text option, annotate will avoid processing files
224 it detects as binary. With -a, annotate will annotate the file
224 it detects as binary. With -a, annotate will annotate the file
225 anyway, although the results will probably be neither useful
225 anyway, although the results will probably be neither useful
226 nor desirable.
226 nor desirable.
227
227
228 Returns 0 on success.
228 Returns 0 on success.
229 """
229 """
230 if opts.get('follow'):
230 if opts.get('follow'):
231 # --follow is deprecated and now just an alias for -f/--file
231 # --follow is deprecated and now just an alias for -f/--file
232 # to mimic the behavior of Mercurial before version 1.5
232 # to mimic the behavior of Mercurial before version 1.5
233 opts['file'] = True
233 opts['file'] = True
234
234
235 datefunc = ui.quiet and util.shortdate or util.datestr
235 datefunc = ui.quiet and util.shortdate or util.datestr
236 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
236 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
237
237
238 if not pats:
238 if not pats:
239 raise util.Abort(_('at least one filename or pattern is required'))
239 raise util.Abort(_('at least one filename or pattern is required'))
240
240
241 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
241 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
242 ('number', ' ', lambda x: str(x[0].rev())),
242 ('number', ' ', lambda x: str(x[0].rev())),
243 ('changeset', ' ', lambda x: short(x[0].node())),
243 ('changeset', ' ', lambda x: short(x[0].node())),
244 ('date', ' ', getdate),
244 ('date', ' ', getdate),
245 ('file', ' ', lambda x: x[0].path()),
245 ('file', ' ', lambda x: x[0].path()),
246 ('line_number', ':', lambda x: str(x[1])),
246 ('line_number', ':', lambda x: str(x[1])),
247 ]
247 ]
248
248
249 if (not opts.get('user') and not opts.get('changeset')
249 if (not opts.get('user') and not opts.get('changeset')
250 and not opts.get('date') and not opts.get('file')):
250 and not opts.get('date') and not opts.get('file')):
251 opts['number'] = True
251 opts['number'] = True
252
252
253 linenumber = opts.get('line_number') is not None
253 linenumber = opts.get('line_number') is not None
254 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
254 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
255 raise util.Abort(_('at least one of -n/-c is required for -l'))
255 raise util.Abort(_('at least one of -n/-c is required for -l'))
256
256
257 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
257 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
258 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
258 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
259
259
260 def bad(x, y):
260 def bad(x, y):
261 raise util.Abort("%s: %s" % (x, y))
261 raise util.Abort("%s: %s" % (x, y))
262
262
263 ctx = scmutil.revsingle(repo, opts.get('rev'))
263 ctx = scmutil.revsingle(repo, opts.get('rev'))
264 m = scmutil.match(repo, pats, opts)
264 m = scmutil.match(repo, pats, opts)
265 m.bad = bad
265 m.bad = bad
266 follow = not opts.get('no_follow')
266 follow = not opts.get('no_follow')
267 for abs in ctx.walk(m):
267 for abs in ctx.walk(m):
268 fctx = ctx[abs]
268 fctx = ctx[abs]
269 if not opts.get('text') and util.binary(fctx.data()):
269 if not opts.get('text') and util.binary(fctx.data()):
270 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
270 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
271 continue
271 continue
272
272
273 lines = fctx.annotate(follow=follow, linenumber=linenumber)
273 lines = fctx.annotate(follow=follow, linenumber=linenumber)
274 pieces = []
274 pieces = []
275
275
276 for f, sep in funcmap:
276 for f, sep in funcmap:
277 l = [f(n) for n, dummy in lines]
277 l = [f(n) for n, dummy in lines]
278 if l:
278 if l:
279 sized = [(x, encoding.colwidth(x)) for x in l]
279 sized = [(x, encoding.colwidth(x)) for x in l]
280 ml = max([w for x, w in sized])
280 ml = max([w for x, w in sized])
281 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
281 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
282 for x, w in sized])
282 for x, w in sized])
283
283
284 if pieces:
284 if pieces:
285 for p, l in zip(zip(*pieces), lines):
285 for p, l in zip(zip(*pieces), lines):
286 ui.write("%s: %s" % ("".join(p), l[1]))
286 ui.write("%s: %s" % ("".join(p), l[1]))
287
287
288 @command('archive',
288 @command('archive',
289 [('', 'no-decode', None, _('do not pass files through decoders')),
289 [('', 'no-decode', None, _('do not pass files through decoders')),
290 ('p', 'prefix', '', _('directory prefix for files in archive'),
290 ('p', 'prefix', '', _('directory prefix for files in archive'),
291 _('PREFIX')),
291 _('PREFIX')),
292 ('r', 'rev', '', _('revision to distribute'), _('REV')),
292 ('r', 'rev', '', _('revision to distribute'), _('REV')),
293 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
293 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
294 ] + subrepoopts + walkopts,
294 ] + subrepoopts + walkopts,
295 _('[OPTION]... DEST'))
295 _('[OPTION]... DEST'))
296 def archive(ui, repo, dest, **opts):
296 def archive(ui, repo, dest, **opts):
297 '''create an unversioned archive of a repository revision
297 '''create an unversioned archive of a repository revision
298
298
299 By default, the revision used is the parent of the working
299 By default, the revision used is the parent of the working
300 directory; use -r/--rev to specify a different revision.
300 directory; use -r/--rev to specify a different revision.
301
301
302 The archive type is automatically detected based on file
302 The archive type is automatically detected based on file
303 extension (or override using -t/--type).
303 extension (or override using -t/--type).
304
304
305 Valid types are:
305 Valid types are:
306
306
307 :``files``: a directory full of files (default)
307 :``files``: a directory full of files (default)
308 :``tar``: tar archive, uncompressed
308 :``tar``: tar archive, uncompressed
309 :``tbz2``: tar archive, compressed using bzip2
309 :``tbz2``: tar archive, compressed using bzip2
310 :``tgz``: tar archive, compressed using gzip
310 :``tgz``: tar archive, compressed using gzip
311 :``uzip``: zip archive, uncompressed
311 :``uzip``: zip archive, uncompressed
312 :``zip``: zip archive, compressed using deflate
312 :``zip``: zip archive, compressed using deflate
313
313
314 The exact name of the destination archive or directory is given
314 The exact name of the destination archive or directory is given
315 using a format string; see :hg:`help export` for details.
315 using a format string; see :hg:`help export` for details.
316
316
317 Each member added to an archive file has a directory prefix
317 Each member added to an archive file has a directory prefix
318 prepended. Use -p/--prefix to specify a format string for the
318 prepended. Use -p/--prefix to specify a format string for the
319 prefix. The default is the basename of the archive, with suffixes
319 prefix. The default is the basename of the archive, with suffixes
320 removed.
320 removed.
321
321
322 Returns 0 on success.
322 Returns 0 on success.
323 '''
323 '''
324
324
325 ctx = scmutil.revsingle(repo, opts.get('rev'))
325 ctx = scmutil.revsingle(repo, opts.get('rev'))
326 if not ctx:
326 if not ctx:
327 raise util.Abort(_('no working directory: please specify a revision'))
327 raise util.Abort(_('no working directory: please specify a revision'))
328 node = ctx.node()
328 node = ctx.node()
329 dest = cmdutil.makefilename(repo, dest, node)
329 dest = cmdutil.makefilename(repo, dest, node)
330 if os.path.realpath(dest) == repo.root:
330 if os.path.realpath(dest) == repo.root:
331 raise util.Abort(_('repository root cannot be destination'))
331 raise util.Abort(_('repository root cannot be destination'))
332
332
333 kind = opts.get('type') or archival.guesskind(dest) or 'files'
333 kind = opts.get('type') or archival.guesskind(dest) or 'files'
334 prefix = opts.get('prefix')
334 prefix = opts.get('prefix')
335
335
336 if dest == '-':
336 if dest == '-':
337 if kind == 'files':
337 if kind == 'files':
338 raise util.Abort(_('cannot archive plain files to stdout'))
338 raise util.Abort(_('cannot archive plain files to stdout'))
339 dest = sys.stdout
339 dest = sys.stdout
340 if not prefix:
340 if not prefix:
341 prefix = os.path.basename(repo.root) + '-%h'
341 prefix = os.path.basename(repo.root) + '-%h'
342
342
343 prefix = cmdutil.makefilename(repo, prefix, node)
343 prefix = cmdutil.makefilename(repo, prefix, node)
344 matchfn = scmutil.match(repo, [], opts)
344 matchfn = scmutil.match(repo, [], opts)
345 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
345 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
346 matchfn, prefix, subrepos=opts.get('subrepos'))
346 matchfn, prefix, subrepos=opts.get('subrepos'))
347
347
348 @command('backout',
348 @command('backout',
349 [('', 'merge', None, _('merge with old dirstate parent after backout')),
349 [('', 'merge', None, _('merge with old dirstate parent after backout')),
350 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
350 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
351 ('t', 'tool', '', _('specify merge tool')),
351 ('t', 'tool', '', _('specify merge tool')),
352 ('r', 'rev', '', _('revision to backout'), _('REV')),
352 ('r', 'rev', '', _('revision to backout'), _('REV')),
353 ] + walkopts + commitopts + commitopts2,
353 ] + walkopts + commitopts + commitopts2,
354 _('[OPTION]... [-r] REV'))
354 _('[OPTION]... [-r] REV'))
355 def backout(ui, repo, node=None, rev=None, **opts):
355 def backout(ui, repo, node=None, rev=None, **opts):
356 '''reverse effect of earlier changeset
356 '''reverse effect of earlier changeset
357
357
358 Prepare a new changeset with the effect of REV undone in the
358 Prepare a new changeset with the effect of REV undone in the
359 current working directory.
359 current working directory.
360
360
361 If REV is the parent of the working directory, then this new changeset
361 If REV is the parent of the working directory, then this new changeset
362 is committed automatically. Otherwise, hg needs to merge the
362 is committed automatically. Otherwise, hg needs to merge the
363 changes and the merged result is left uncommitted.
363 changes and the merged result is left uncommitted.
364
364
365 By default, the pending changeset will have one parent,
365 By default, the pending changeset will have one parent,
366 maintaining a linear history. With --merge, the pending changeset
366 maintaining a linear history. With --merge, the pending changeset
367 will instead have two parents: the old parent of the working
367 will instead have two parents: the old parent of the working
368 directory and a new child of REV that simply undoes REV.
368 directory and a new child of REV that simply undoes REV.
369
369
370 Before version 1.7, the behavior without --merge was equivalent to
370 Before version 1.7, the behavior without --merge was equivalent to
371 specifying --merge followed by :hg:`update --clean .` to cancel
371 specifying --merge followed by :hg:`update --clean .` to cancel
372 the merge and leave the child of REV as a head to be merged
372 the merge and leave the child of REV as a head to be merged
373 separately.
373 separately.
374
374
375 See :hg:`help dates` for a list of formats valid for -d/--date.
375 See :hg:`help dates` for a list of formats valid for -d/--date.
376
376
377 Returns 0 on success.
377 Returns 0 on success.
378 '''
378 '''
379 if rev and node:
379 if rev and node:
380 raise util.Abort(_("please specify just one revision"))
380 raise util.Abort(_("please specify just one revision"))
381
381
382 if not rev:
382 if not rev:
383 rev = node
383 rev = node
384
384
385 if not rev:
385 if not rev:
386 raise util.Abort(_("please specify a revision to backout"))
386 raise util.Abort(_("please specify a revision to backout"))
387
387
388 date = opts.get('date')
388 date = opts.get('date')
389 if date:
389 if date:
390 opts['date'] = util.parsedate(date)
390 opts['date'] = util.parsedate(date)
391
391
392 cmdutil.bailifchanged(repo)
392 cmdutil.bailifchanged(repo)
393 node = scmutil.revsingle(repo, rev).node()
393 node = scmutil.revsingle(repo, rev).node()
394
394
395 op1, op2 = repo.dirstate.parents()
395 op1, op2 = repo.dirstate.parents()
396 a = repo.changelog.ancestor(op1, node)
396 a = repo.changelog.ancestor(op1, node)
397 if a != node:
397 if a != node:
398 raise util.Abort(_('cannot backout change on a different branch'))
398 raise util.Abort(_('cannot backout change on a different branch'))
399
399
400 p1, p2 = repo.changelog.parents(node)
400 p1, p2 = repo.changelog.parents(node)
401 if p1 == nullid:
401 if p1 == nullid:
402 raise util.Abort(_('cannot backout a change with no parents'))
402 raise util.Abort(_('cannot backout a change with no parents'))
403 if p2 != nullid:
403 if p2 != nullid:
404 if not opts.get('parent'):
404 if not opts.get('parent'):
405 raise util.Abort(_('cannot backout a merge changeset without '
405 raise util.Abort(_('cannot backout a merge changeset without '
406 '--parent'))
406 '--parent'))
407 p = repo.lookup(opts['parent'])
407 p = repo.lookup(opts['parent'])
408 if p not in (p1, p2):
408 if p not in (p1, p2):
409 raise util.Abort(_('%s is not a parent of %s') %
409 raise util.Abort(_('%s is not a parent of %s') %
410 (short(p), short(node)))
410 (short(p), short(node)))
411 parent = p
411 parent = p
412 else:
412 else:
413 if opts.get('parent'):
413 if opts.get('parent'):
414 raise util.Abort(_('cannot use --parent on non-merge changeset'))
414 raise util.Abort(_('cannot use --parent on non-merge changeset'))
415 parent = p1
415 parent = p1
416
416
417 # the backout should appear on the same branch
417 # the backout should appear on the same branch
418 branch = repo.dirstate.branch()
418 branch = repo.dirstate.branch()
419 hg.clean(repo, node, show_stats=False)
419 hg.clean(repo, node, show_stats=False)
420 repo.dirstate.setbranch(branch)
420 repo.dirstate.setbranch(branch)
421 revert_opts = opts.copy()
421 revert_opts = opts.copy()
422 revert_opts['date'] = None
422 revert_opts['date'] = None
423 revert_opts['all'] = True
423 revert_opts['all'] = True
424 revert_opts['rev'] = hex(parent)
424 revert_opts['rev'] = hex(parent)
425 revert_opts['no_backup'] = None
425 revert_opts['no_backup'] = None
426 revert(ui, repo, **revert_opts)
426 revert(ui, repo, **revert_opts)
427 if not opts.get('merge') and op1 != node:
427 if not opts.get('merge') and op1 != node:
428 try:
428 try:
429 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
429 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
430 return hg.update(repo, op1)
430 return hg.update(repo, op1)
431 finally:
431 finally:
432 ui.setconfig('ui', 'forcemerge', '')
432 ui.setconfig('ui', 'forcemerge', '')
433
433
434 commit_opts = opts.copy()
434 commit_opts = opts.copy()
435 commit_opts['addremove'] = False
435 commit_opts['addremove'] = False
436 if not commit_opts['message'] and not commit_opts['logfile']:
436 if not commit_opts['message'] and not commit_opts['logfile']:
437 # we don't translate commit messages
437 # we don't translate commit messages
438 commit_opts['message'] = "Backed out changeset %s" % short(node)
438 commit_opts['message'] = "Backed out changeset %s" % short(node)
439 commit_opts['force_editor'] = True
439 commit_opts['force_editor'] = True
440 commit(ui, repo, **commit_opts)
440 commit(ui, repo, **commit_opts)
441 def nice(node):
441 def nice(node):
442 return '%d:%s' % (repo.changelog.rev(node), short(node))
442 return '%d:%s' % (repo.changelog.rev(node), short(node))
443 ui.status(_('changeset %s backs out changeset %s\n') %
443 ui.status(_('changeset %s backs out changeset %s\n') %
444 (nice(repo.changelog.tip()), nice(node)))
444 (nice(repo.changelog.tip()), nice(node)))
445 if opts.get('merge') and op1 != node:
445 if opts.get('merge') and op1 != node:
446 hg.clean(repo, op1, show_stats=False)
446 hg.clean(repo, op1, show_stats=False)
447 ui.status(_('merging with changeset %s\n')
447 ui.status(_('merging with changeset %s\n')
448 % nice(repo.changelog.tip()))
448 % nice(repo.changelog.tip()))
449 try:
449 try:
450 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
450 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
451 return hg.merge(repo, hex(repo.changelog.tip()))
451 return hg.merge(repo, hex(repo.changelog.tip()))
452 finally:
452 finally:
453 ui.setconfig('ui', 'forcemerge', '')
453 ui.setconfig('ui', 'forcemerge', '')
454 return 0
454 return 0
455
455
456 @command('bisect',
456 @command('bisect',
457 [('r', 'reset', False, _('reset bisect state')),
457 [('r', 'reset', False, _('reset bisect state')),
458 ('g', 'good', False, _('mark changeset good')),
458 ('g', 'good', False, _('mark changeset good')),
459 ('b', 'bad', False, _('mark changeset bad')),
459 ('b', 'bad', False, _('mark changeset bad')),
460 ('s', 'skip', False, _('skip testing changeset')),
460 ('s', 'skip', False, _('skip testing changeset')),
461 ('e', 'extend', False, _('extend the bisect range')),
461 ('e', 'extend', False, _('extend the bisect range')),
462 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
462 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
463 ('U', 'noupdate', False, _('do not update to target'))],
463 ('U', 'noupdate', False, _('do not update to target'))],
464 _("[-gbsr] [-U] [-c CMD] [REV]"))
464 _("[-gbsr] [-U] [-c CMD] [REV]"))
465 def bisect(ui, repo, rev=None, extra=None, command=None,
465 def bisect(ui, repo, rev=None, extra=None, command=None,
466 reset=None, good=None, bad=None, skip=None, extend=None,
466 reset=None, good=None, bad=None, skip=None, extend=None,
467 noupdate=None):
467 noupdate=None):
468 """subdivision search of changesets
468 """subdivision search of changesets
469
469
470 This command helps to find changesets which introduce problems. To
470 This command helps to find changesets which introduce problems. To
471 use, mark the earliest changeset you know exhibits the problem as
471 use, mark the earliest changeset you know exhibits the problem as
472 bad, then mark the latest changeset which is free from the problem
472 bad, then mark the latest changeset which is free from the problem
473 as good. Bisect will update your working directory to a revision
473 as good. Bisect will update your working directory to a revision
474 for testing (unless the -U/--noupdate option is specified). Once
474 for testing (unless the -U/--noupdate option is specified). Once
475 you have performed tests, mark the working directory as good or
475 you have performed tests, mark the working directory as good or
476 bad, and bisect will either update to another candidate changeset
476 bad, and bisect will either update to another candidate changeset
477 or announce that it has found the bad revision.
477 or announce that it has found the bad revision.
478
478
479 As a shortcut, you can also use the revision argument to mark a
479 As a shortcut, you can also use the revision argument to mark a
480 revision as good or bad without checking it out first.
480 revision as good or bad without checking it out first.
481
481
482 If you supply a command, it will be used for automatic bisection.
482 If you supply a command, it will be used for automatic bisection.
483 Its exit status will be used to mark revisions as good or bad:
483 Its exit status will be used to mark revisions as good or bad:
484 status 0 means good, 125 means to skip the revision, 127
484 status 0 means good, 125 means to skip the revision, 127
485 (command not found) will abort the bisection, and any other
485 (command not found) will abort the bisection, and any other
486 non-zero exit status means the revision is bad.
486 non-zero exit status means the revision is bad.
487
487
488 Returns 0 on success.
488 Returns 0 on success.
489 """
489 """
490 def extendbisectrange(nodes, good):
490 def extendbisectrange(nodes, good):
491 # bisect is incomplete when it ends on a merge node and
491 # bisect is incomplete when it ends on a merge node and
492 # one of the parent was not checked.
492 # one of the parent was not checked.
493 parents = repo[nodes[0]].parents()
493 parents = repo[nodes[0]].parents()
494 if len(parents) > 1:
494 if len(parents) > 1:
495 side = good and state['bad'] or state['good']
495 side = good and state['bad'] or state['good']
496 num = len(set(i.node() for i in parents) & set(side))
496 num = len(set(i.node() for i in parents) & set(side))
497 if num == 1:
497 if num == 1:
498 return parents[0].ancestor(parents[1])
498 return parents[0].ancestor(parents[1])
499 return None
499 return None
500
500
501 def print_result(nodes, good):
501 def print_result(nodes, good):
502 displayer = cmdutil.show_changeset(ui, repo, {})
502 displayer = cmdutil.show_changeset(ui, repo, {})
503 if len(nodes) == 1:
503 if len(nodes) == 1:
504 # narrowed it down to a single revision
504 # narrowed it down to a single revision
505 if good:
505 if good:
506 ui.write(_("The first good revision is:\n"))
506 ui.write(_("The first good revision is:\n"))
507 else:
507 else:
508 ui.write(_("The first bad revision is:\n"))
508 ui.write(_("The first bad revision is:\n"))
509 displayer.show(repo[nodes[0]])
509 displayer.show(repo[nodes[0]])
510 extendnode = extendbisectrange(nodes, good)
510 extendnode = extendbisectrange(nodes, good)
511 if extendnode is not None:
511 if extendnode is not None:
512 ui.write(_('Not all ancestors of this changeset have been'
512 ui.write(_('Not all ancestors of this changeset have been'
513 ' checked.\nUse bisect --extend to continue the '
513 ' checked.\nUse bisect --extend to continue the '
514 'bisection from\nthe common ancestor, %s.\n')
514 'bisection from\nthe common ancestor, %s.\n')
515 % extendnode)
515 % extendnode)
516 else:
516 else:
517 # multiple possible revisions
517 # multiple possible revisions
518 if good:
518 if good:
519 ui.write(_("Due to skipped revisions, the first "
519 ui.write(_("Due to skipped revisions, the first "
520 "good revision could be any of:\n"))
520 "good revision could be any of:\n"))
521 else:
521 else:
522 ui.write(_("Due to skipped revisions, the first "
522 ui.write(_("Due to skipped revisions, the first "
523 "bad revision could be any of:\n"))
523 "bad revision could be any of:\n"))
524 for n in nodes:
524 for n in nodes:
525 displayer.show(repo[n])
525 displayer.show(repo[n])
526 displayer.close()
526 displayer.close()
527
527
528 def check_state(state, interactive=True):
528 def check_state(state, interactive=True):
529 if not state['good'] or not state['bad']:
529 if not state['good'] or not state['bad']:
530 if (good or bad or skip or reset) and interactive:
530 if (good or bad or skip or reset) and interactive:
531 return
531 return
532 if not state['good']:
532 if not state['good']:
533 raise util.Abort(_('cannot bisect (no known good revisions)'))
533 raise util.Abort(_('cannot bisect (no known good revisions)'))
534 else:
534 else:
535 raise util.Abort(_('cannot bisect (no known bad revisions)'))
535 raise util.Abort(_('cannot bisect (no known bad revisions)'))
536 return True
536 return True
537
537
538 # backward compatibility
538 # backward compatibility
539 if rev in "good bad reset init".split():
539 if rev in "good bad reset init".split():
540 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
540 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
541 cmd, rev, extra = rev, extra, None
541 cmd, rev, extra = rev, extra, None
542 if cmd == "good":
542 if cmd == "good":
543 good = True
543 good = True
544 elif cmd == "bad":
544 elif cmd == "bad":
545 bad = True
545 bad = True
546 else:
546 else:
547 reset = True
547 reset = True
548 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
548 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
549 raise util.Abort(_('incompatible arguments'))
549 raise util.Abort(_('incompatible arguments'))
550
550
551 if reset:
551 if reset:
552 p = repo.join("bisect.state")
552 p = repo.join("bisect.state")
553 if os.path.exists(p):
553 if os.path.exists(p):
554 os.unlink(p)
554 os.unlink(p)
555 return
555 return
556
556
557 state = hbisect.load_state(repo)
557 state = hbisect.load_state(repo)
558
558
559 if command:
559 if command:
560 changesets = 1
560 changesets = 1
561 try:
561 try:
562 while changesets:
562 while changesets:
563 # update state
563 # update state
564 status = util.system(command)
564 status = util.system(command)
565 if status == 125:
565 if status == 125:
566 transition = "skip"
566 transition = "skip"
567 elif status == 0:
567 elif status == 0:
568 transition = "good"
568 transition = "good"
569 # status < 0 means process was killed
569 # status < 0 means process was killed
570 elif status == 127:
570 elif status == 127:
571 raise util.Abort(_("failed to execute %s") % command)
571 raise util.Abort(_("failed to execute %s") % command)
572 elif status < 0:
572 elif status < 0:
573 raise util.Abort(_("%s killed") % command)
573 raise util.Abort(_("%s killed") % command)
574 else:
574 else:
575 transition = "bad"
575 transition = "bad"
576 ctx = scmutil.revsingle(repo, rev)
576 ctx = scmutil.revsingle(repo, rev)
577 rev = None # clear for future iterations
577 rev = None # clear for future iterations
578 state[transition].append(ctx.node())
578 state[transition].append(ctx.node())
579 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
579 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
580 check_state(state, interactive=False)
580 check_state(state, interactive=False)
581 # bisect
581 # bisect
582 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
582 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
583 # update to next check
583 # update to next check
584 cmdutil.bailifchanged(repo)
584 cmdutil.bailifchanged(repo)
585 hg.clean(repo, nodes[0], show_stats=False)
585 hg.clean(repo, nodes[0], show_stats=False)
586 finally:
586 finally:
587 hbisect.save_state(repo, state)
587 hbisect.save_state(repo, state)
588 print_result(nodes, good)
588 print_result(nodes, good)
589 return
589 return
590
590
591 # update state
591 # update state
592
592
593 if rev:
593 if rev:
594 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
594 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
595 else:
595 else:
596 nodes = [repo.lookup('.')]
596 nodes = [repo.lookup('.')]
597
597
598 if good or bad or skip:
598 if good or bad or skip:
599 if good:
599 if good:
600 state['good'] += nodes
600 state['good'] += nodes
601 elif bad:
601 elif bad:
602 state['bad'] += nodes
602 state['bad'] += nodes
603 elif skip:
603 elif skip:
604 state['skip'] += nodes
604 state['skip'] += nodes
605 hbisect.save_state(repo, state)
605 hbisect.save_state(repo, state)
606
606
607 if not check_state(state):
607 if not check_state(state):
608 return
608 return
609
609
610 # actually bisect
610 # actually bisect
611 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
611 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
612 if extend:
612 if extend:
613 if not changesets:
613 if not changesets:
614 extendnode = extendbisectrange(nodes, good)
614 extendnode = extendbisectrange(nodes, good)
615 if extendnode is not None:
615 if extendnode is not None:
616 ui.write(_("Extending search to changeset %d:%s\n"
616 ui.write(_("Extending search to changeset %d:%s\n"
617 % (extendnode.rev(), extendnode)))
617 % (extendnode.rev(), extendnode)))
618 if noupdate:
618 if noupdate:
619 return
619 return
620 cmdutil.bailifchanged(repo)
620 cmdutil.bailifchanged(repo)
621 return hg.clean(repo, extendnode.node())
621 return hg.clean(repo, extendnode.node())
622 raise util.Abort(_("nothing to extend"))
622 raise util.Abort(_("nothing to extend"))
623
623
624 if changesets == 0:
624 if changesets == 0:
625 print_result(nodes, good)
625 print_result(nodes, good)
626 else:
626 else:
627 assert len(nodes) == 1 # only a single node can be tested next
627 assert len(nodes) == 1 # only a single node can be tested next
628 node = nodes[0]
628 node = nodes[0]
629 # compute the approximate number of remaining tests
629 # compute the approximate number of remaining tests
630 tests, size = 0, 2
630 tests, size = 0, 2
631 while size <= changesets:
631 while size <= changesets:
632 tests, size = tests + 1, size * 2
632 tests, size = tests + 1, size * 2
633 rev = repo.changelog.rev(node)
633 rev = repo.changelog.rev(node)
634 ui.write(_("Testing changeset %d:%s "
634 ui.write(_("Testing changeset %d:%s "
635 "(%d changesets remaining, ~%d tests)\n")
635 "(%d changesets remaining, ~%d tests)\n")
636 % (rev, short(node), changesets, tests))
636 % (rev, short(node), changesets, tests))
637 if not noupdate:
637 if not noupdate:
638 cmdutil.bailifchanged(repo)
638 cmdutil.bailifchanged(repo)
639 return hg.clean(repo, node)
639 return hg.clean(repo, node)
640
640
641 @command('bookmarks',
641 @command('bookmarks',
642 [('f', 'force', False, _('force')),
642 [('f', 'force', False, _('force')),
643 ('r', 'rev', '', _('revision'), _('REV')),
643 ('r', 'rev', '', _('revision'), _('REV')),
644 ('d', 'delete', False, _('delete a given bookmark')),
644 ('d', 'delete', False, _('delete a given bookmark')),
645 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
645 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
646 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
646 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
647 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
647 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
648 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
648 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
649 rename=None, inactive=False):
649 rename=None, inactive=False):
650 '''track a line of development with movable markers
650 '''track a line of development with movable markers
651
651
652 Bookmarks are pointers to certain commits that move when
652 Bookmarks are pointers to certain commits that move when
653 committing. Bookmarks are local. They can be renamed, copied and
653 committing. Bookmarks are local. They can be renamed, copied and
654 deleted. It is possible to use bookmark names in :hg:`merge` and
654 deleted. It is possible to use bookmark names in :hg:`merge` and
655 :hg:`update` to merge and update respectively to a given bookmark.
655 :hg:`update` to merge and update respectively to a given bookmark.
656
656
657 You can use :hg:`bookmark NAME` to set a bookmark on the working
657 You can use :hg:`bookmark NAME` to set a bookmark on the working
658 directory's parent revision with the given name. If you specify
658 directory's parent revision with the given name. If you specify
659 a revision using -r REV (where REV may be an existing bookmark),
659 a revision using -r REV (where REV may be an existing bookmark),
660 the bookmark is assigned to that revision.
660 the bookmark is assigned to that revision.
661
661
662 Bookmarks can be pushed and pulled between repositories (see :hg:`help
662 Bookmarks can be pushed and pulled between repositories (see :hg:`help
663 push` and :hg:`help pull`). This requires both the local and remote
663 push` and :hg:`help pull`). This requires both the local and remote
664 repositories to support bookmarks. For versions prior to 1.8, this means
664 repositories to support bookmarks. For versions prior to 1.8, this means
665 the bookmarks extension must be enabled.
665 the bookmarks extension must be enabled.
666 '''
666 '''
667 hexfn = ui.debugflag and hex or short
667 hexfn = ui.debugflag and hex or short
668 marks = repo._bookmarks
668 marks = repo._bookmarks
669 cur = repo.changectx('.').node()
669 cur = repo.changectx('.').node()
670
670
671 if rename:
671 if rename:
672 if rename not in marks:
672 if rename not in marks:
673 raise util.Abort(_("bookmark '%s' does not exist") % rename)
673 raise util.Abort(_("bookmark '%s' does not exist") % rename)
674 if mark in marks and not force:
674 if mark in marks and not force:
675 raise util.Abort(_("bookmark '%s' already exists "
675 raise util.Abort(_("bookmark '%s' already exists "
676 "(use -f to force)") % mark)
676 "(use -f to force)") % mark)
677 if mark is None:
677 if mark is None:
678 raise util.Abort(_("new bookmark name required"))
678 raise util.Abort(_("new bookmark name required"))
679 marks[mark] = marks[rename]
679 marks[mark] = marks[rename]
680 if repo._bookmarkcurrent == rename and not inactive:
680 if repo._bookmarkcurrent == rename and not inactive:
681 bookmarks.setcurrent(repo, mark)
681 bookmarks.setcurrent(repo, mark)
682 del marks[rename]
682 del marks[rename]
683 bookmarks.write(repo)
683 bookmarks.write(repo)
684 return
684 return
685
685
686 if delete:
686 if delete:
687 if mark is None:
687 if mark is None:
688 raise util.Abort(_("bookmark name required"))
688 raise util.Abort(_("bookmark name required"))
689 if mark not in marks:
689 if mark not in marks:
690 raise util.Abort(_("bookmark '%s' does not exist") % mark)
690 raise util.Abort(_("bookmark '%s' does not exist") % mark)
691 if mark == repo._bookmarkcurrent:
691 if mark == repo._bookmarkcurrent:
692 bookmarks.setcurrent(repo, None)
692 bookmarks.setcurrent(repo, None)
693 del marks[mark]
693 del marks[mark]
694 bookmarks.write(repo)
694 bookmarks.write(repo)
695 return
695 return
696
696
697 if mark is not None:
697 if mark is not None:
698 if "\n" in mark:
698 if "\n" in mark:
699 raise util.Abort(_("bookmark name cannot contain newlines"))
699 raise util.Abort(_("bookmark name cannot contain newlines"))
700 mark = mark.strip()
700 mark = mark.strip()
701 if not mark:
701 if not mark:
702 raise util.Abort(_("bookmark names cannot consist entirely of "
702 raise util.Abort(_("bookmark names cannot consist entirely of "
703 "whitespace"))
703 "whitespace"))
704 if inactive and mark == repo._bookmarkcurrent:
704 if inactive and mark == repo._bookmarkcurrent:
705 bookmarks.setcurrent(repo, None)
705 bookmarks.setcurrent(repo, None)
706 return
706 return
707 if mark in marks and not force:
707 if mark in marks and not force:
708 raise util.Abort(_("bookmark '%s' already exists "
708 raise util.Abort(_("bookmark '%s' already exists "
709 "(use -f to force)") % mark)
709 "(use -f to force)") % mark)
710 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
710 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
711 and not force):
711 and not force):
712 raise util.Abort(
712 raise util.Abort(
713 _("a bookmark cannot have the name of an existing branch"))
713 _("a bookmark cannot have the name of an existing branch"))
714 if rev:
714 if rev:
715 marks[mark] = repo.lookup(rev)
715 marks[mark] = repo.lookup(rev)
716 else:
716 else:
717 marks[mark] = repo.changectx('.').node()
717 marks[mark] = repo.changectx('.').node()
718 if not inactive and repo.changectx('.').node() == marks[mark]:
718 if not inactive and repo.changectx('.').node() == marks[mark]:
719 bookmarks.setcurrent(repo, mark)
719 bookmarks.setcurrent(repo, mark)
720 bookmarks.write(repo)
720 bookmarks.write(repo)
721 return
721 return
722
722
723 if mark is None:
723 if mark is None:
724 if rev:
724 if rev:
725 raise util.Abort(_("bookmark name required"))
725 raise util.Abort(_("bookmark name required"))
726 if len(marks) == 0:
726 if len(marks) == 0:
727 ui.status(_("no bookmarks set\n"))
727 ui.status(_("no bookmarks set\n"))
728 else:
728 else:
729 for bmark, n in sorted(marks.iteritems()):
729 for bmark, n in sorted(marks.iteritems()):
730 current = repo._bookmarkcurrent
730 current = repo._bookmarkcurrent
731 if bmark == current and n == cur:
731 if bmark == current and n == cur:
732 prefix, label = '*', 'bookmarks.current'
732 prefix, label = '*', 'bookmarks.current'
733 else:
733 else:
734 prefix, label = ' ', ''
734 prefix, label = ' ', ''
735
735
736 if ui.quiet:
736 if ui.quiet:
737 ui.write("%s\n" % bmark, label=label)
737 ui.write("%s\n" % bmark, label=label)
738 else:
738 else:
739 ui.write(" %s %-25s %d:%s\n" % (
739 ui.write(" %s %-25s %d:%s\n" % (
740 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
740 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
741 label=label)
741 label=label)
742 return
742 return
743
743
744 @command('branch',
744 @command('branch',
745 [('f', 'force', None,
745 [('f', 'force', None,
746 _('set branch name even if it shadows an existing branch')),
746 _('set branch name even if it shadows an existing branch')),
747 ('C', 'clean', None, _('reset branch name to parent branch name'))],
747 ('C', 'clean', None, _('reset branch name to parent branch name'))],
748 _('[-fC] [NAME]'))
748 _('[-fC] [NAME]'))
749 def branch(ui, repo, label=None, **opts):
749 def branch(ui, repo, label=None, **opts):
750 """set or show the current branch name
750 """set or show the current branch name
751
751
752 With no argument, show the current branch name. With one argument,
752 With no argument, show the current branch name. With one argument,
753 set the working directory branch name (the branch will not exist
753 set the working directory branch name (the branch will not exist
754 in the repository until the next commit). Standard practice
754 in the repository until the next commit). Standard practice
755 recommends that primary development take place on the 'default'
755 recommends that primary development take place on the 'default'
756 branch.
756 branch.
757
757
758 Unless -f/--force is specified, branch will not let you set a
758 Unless -f/--force is specified, branch will not let you set a
759 branch name that already exists, even if it's inactive.
759 branch name that already exists, even if it's inactive.
760
760
761 Use -C/--clean to reset the working directory branch to that of
761 Use -C/--clean to reset the working directory branch to that of
762 the parent of the working directory, negating a previous branch
762 the parent of the working directory, negating a previous branch
763 change.
763 change.
764
764
765 Use the command :hg:`update` to switch to an existing branch. Use
765 Use the command :hg:`update` to switch to an existing branch. Use
766 :hg:`commit --close-branch` to mark this branch as closed.
766 :hg:`commit --close-branch` to mark this branch as closed.
767
767
768 Returns 0 on success.
768 Returns 0 on success.
769 """
769 """
770
770
771 if opts.get('clean'):
771 if opts.get('clean'):
772 label = repo[None].p1().branch()
772 label = repo[None].p1().branch()
773 repo.dirstate.setbranch(label)
773 repo.dirstate.setbranch(label)
774 ui.status(_('reset working directory to branch %s\n') % label)
774 ui.status(_('reset working directory to branch %s\n') % label)
775 elif label:
775 elif label:
776 if not opts.get('force') and label in repo.branchtags():
776 if not opts.get('force') and label in repo.branchtags():
777 if label not in [p.branch() for p in repo.parents()]:
777 if label not in [p.branch() for p in repo.parents()]:
778 raise util.Abort(_('a branch of the same name already exists'),
778 raise util.Abort(_('a branch of the same name already exists'),
779 # i18n: "it" refers to an existing branch
779 # i18n: "it" refers to an existing branch
780 hint=_("use 'hg update' to switch to it"))
780 hint=_("use 'hg update' to switch to it"))
781 repo.dirstate.setbranch(label)
781 repo.dirstate.setbranch(label)
782 ui.status(_('marked working directory as branch %s\n') % label)
782 ui.status(_('marked working directory as branch %s\n') % label)
783 else:
783 else:
784 ui.write("%s\n" % repo.dirstate.branch())
784 ui.write("%s\n" % repo.dirstate.branch())
785
785
786 @command('branches',
786 @command('branches',
787 [('a', 'active', False, _('show only branches that have unmerged heads')),
787 [('a', 'active', False, _('show only branches that have unmerged heads')),
788 ('c', 'closed', False, _('show normal and closed branches'))],
788 ('c', 'closed', False, _('show normal and closed branches'))],
789 _('[-ac]'))
789 _('[-ac]'))
790 def branches(ui, repo, active=False, closed=False):
790 def branches(ui, repo, active=False, closed=False):
791 """list repository named branches
791 """list repository named branches
792
792
793 List the repository's named branches, indicating which ones are
793 List the repository's named branches, indicating which ones are
794 inactive. If -c/--closed is specified, also list branches which have
794 inactive. If -c/--closed is specified, also list branches which have
795 been marked closed (see :hg:`commit --close-branch`).
795 been marked closed (see :hg:`commit --close-branch`).
796
796
797 If -a/--active is specified, only show active branches. A branch
797 If -a/--active is specified, only show active branches. A branch
798 is considered active if it contains repository heads.
798 is considered active if it contains repository heads.
799
799
800 Use the command :hg:`update` to switch to an existing branch.
800 Use the command :hg:`update` to switch to an existing branch.
801
801
802 Returns 0.
802 Returns 0.
803 """
803 """
804
804
805 hexfunc = ui.debugflag and hex or short
805 hexfunc = ui.debugflag and hex or short
806 activebranches = [repo[n].branch() for n in repo.heads()]
806 activebranches = [repo[n].branch() for n in repo.heads()]
807 def testactive(tag, node):
807 def testactive(tag, node):
808 realhead = tag in activebranches
808 realhead = tag in activebranches
809 open = node in repo.branchheads(tag, closed=False)
809 open = node in repo.branchheads(tag, closed=False)
810 return realhead and open
810 return realhead and open
811 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
811 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
812 for tag, node in repo.branchtags().items()],
812 for tag, node in repo.branchtags().items()],
813 reverse=True)
813 reverse=True)
814
814
815 for isactive, node, tag in branches:
815 for isactive, node, tag in branches:
816 if (not active) or isactive:
816 if (not active) or isactive:
817 if ui.quiet:
817 if ui.quiet:
818 ui.write("%s\n" % tag)
818 ui.write("%s\n" % tag)
819 else:
819 else:
820 hn = repo.lookup(node)
820 hn = repo.lookup(node)
821 if isactive:
821 if isactive:
822 label = 'branches.active'
822 label = 'branches.active'
823 notice = ''
823 notice = ''
824 elif hn not in repo.branchheads(tag, closed=False):
824 elif hn not in repo.branchheads(tag, closed=False):
825 if not closed:
825 if not closed:
826 continue
826 continue
827 label = 'branches.closed'
827 label = 'branches.closed'
828 notice = _(' (closed)')
828 notice = _(' (closed)')
829 else:
829 else:
830 label = 'branches.inactive'
830 label = 'branches.inactive'
831 notice = _(' (inactive)')
831 notice = _(' (inactive)')
832 if tag == repo.dirstate.branch():
832 if tag == repo.dirstate.branch():
833 label = 'branches.current'
833 label = 'branches.current'
834 rev = str(node).rjust(31 - encoding.colwidth(tag))
834 rev = str(node).rjust(31 - encoding.colwidth(tag))
835 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
835 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
836 tag = ui.label(tag, label)
836 tag = ui.label(tag, label)
837 ui.write("%s %s%s\n" % (tag, rev, notice))
837 ui.write("%s %s%s\n" % (tag, rev, notice))
838
838
839 @command('bundle',
839 @command('bundle',
840 [('f', 'force', None, _('run even when the destination is unrelated')),
840 [('f', 'force', None, _('run even when the destination is unrelated')),
841 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
841 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
842 _('REV')),
842 _('REV')),
843 ('b', 'branch', [], _('a specific branch you would like to bundle'),
843 ('b', 'branch', [], _('a specific branch you would like to bundle'),
844 _('BRANCH')),
844 _('BRANCH')),
845 ('', 'base', [],
845 ('', 'base', [],
846 _('a base changeset assumed to be available at the destination'),
846 _('a base changeset assumed to be available at the destination'),
847 _('REV')),
847 _('REV')),
848 ('a', 'all', None, _('bundle all changesets in the repository')),
848 ('a', 'all', None, _('bundle all changesets in the repository')),
849 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
849 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
850 ] + remoteopts,
850 ] + remoteopts,
851 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
851 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
852 def bundle(ui, repo, fname, dest=None, **opts):
852 def bundle(ui, repo, fname, dest=None, **opts):
853 """create a changegroup file
853 """create a changegroup file
854
854
855 Generate a compressed changegroup file collecting changesets not
855 Generate a compressed changegroup file collecting changesets not
856 known to be in another repository.
856 known to be in another repository.
857
857
858 If you omit the destination repository, then hg assumes the
858 If you omit the destination repository, then hg assumes the
859 destination will have all the nodes you specify with --base
859 destination will have all the nodes you specify with --base
860 parameters. To create a bundle containing all changesets, use
860 parameters. To create a bundle containing all changesets, use
861 -a/--all (or --base null).
861 -a/--all (or --base null).
862
862
863 You can change compression method with the -t/--type option.
863 You can change compression method with the -t/--type option.
864 The available compression methods are: none, bzip2, and
864 The available compression methods are: none, bzip2, and
865 gzip (by default, bundles are compressed using bzip2).
865 gzip (by default, bundles are compressed using bzip2).
866
866
867 The bundle file can then be transferred using conventional means
867 The bundle file can then be transferred using conventional means
868 and applied to another repository with the unbundle or pull
868 and applied to another repository with the unbundle or pull
869 command. This is useful when direct push and pull are not
869 command. This is useful when direct push and pull are not
870 available or when exporting an entire repository is undesirable.
870 available or when exporting an entire repository is undesirable.
871
871
872 Applying bundles preserves all changeset contents including
872 Applying bundles preserves all changeset contents including
873 permissions, copy/rename information, and revision history.
873 permissions, copy/rename information, and revision history.
874
874
875 Returns 0 on success, 1 if no changes found.
875 Returns 0 on success, 1 if no changes found.
876 """
876 """
877 revs = None
877 revs = None
878 if 'rev' in opts:
878 if 'rev' in opts:
879 revs = scmutil.revrange(repo, opts['rev'])
879 revs = scmutil.revrange(repo, opts['rev'])
880
880
881 if opts.get('all'):
881 if opts.get('all'):
882 base = ['null']
882 base = ['null']
883 else:
883 else:
884 base = scmutil.revrange(repo, opts.get('base'))
884 base = scmutil.revrange(repo, opts.get('base'))
885 if base:
885 if base:
886 if dest:
886 if dest:
887 raise util.Abort(_("--base is incompatible with specifying "
887 raise util.Abort(_("--base is incompatible with specifying "
888 "a destination"))
888 "a destination"))
889 common = [repo.lookup(rev) for rev in base]
889 common = [repo.lookup(rev) for rev in base]
890 heads = revs and map(repo.lookup, revs) or revs
890 heads = revs and map(repo.lookup, revs) or revs
891 else:
891 else:
892 dest = ui.expandpath(dest or 'default-push', dest or 'default')
892 dest = ui.expandpath(dest or 'default-push', dest or 'default')
893 dest, branches = hg.parseurl(dest, opts.get('branch'))
893 dest, branches = hg.parseurl(dest, opts.get('branch'))
894 other = hg.peer(repo, opts, dest)
894 other = hg.peer(repo, opts, dest)
895 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
895 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
896 heads = revs and map(repo.lookup, revs) or revs
896 heads = revs and map(repo.lookup, revs) or revs
897 common, outheads = discovery.findcommonoutgoing(repo, other,
897 common, outheads = discovery.findcommonoutgoing(repo, other,
898 onlyheads=heads,
898 onlyheads=heads,
899 force=opts.get('force'))
899 force=opts.get('force'))
900
900
901 cg = repo.getbundle('bundle', common=common, heads=heads)
901 cg = repo.getbundle('bundle', common=common, heads=heads)
902 if not cg:
902 if not cg:
903 ui.status(_("no changes found\n"))
903 ui.status(_("no changes found\n"))
904 return 1
904 return 1
905
905
906 bundletype = opts.get('type', 'bzip2').lower()
906 bundletype = opts.get('type', 'bzip2').lower()
907 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
907 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
908 bundletype = btypes.get(bundletype)
908 bundletype = btypes.get(bundletype)
909 if bundletype not in changegroup.bundletypes:
909 if bundletype not in changegroup.bundletypes:
910 raise util.Abort(_('unknown bundle type specified with --type'))
910 raise util.Abort(_('unknown bundle type specified with --type'))
911
911
912 changegroup.writebundle(cg, fname, bundletype)
912 changegroup.writebundle(cg, fname, bundletype)
913
913
914 @command('cat',
914 @command('cat',
915 [('o', 'output', '',
915 [('o', 'output', '',
916 _('print output to file with formatted name'), _('FORMAT')),
916 _('print output to file with formatted name'), _('FORMAT')),
917 ('r', 'rev', '', _('print the given revision'), _('REV')),
917 ('r', 'rev', '', _('print the given revision'), _('REV')),
918 ('', 'decode', None, _('apply any matching decode filter')),
918 ('', 'decode', None, _('apply any matching decode filter')),
919 ] + walkopts,
919 ] + walkopts,
920 _('[OPTION]... FILE...'))
920 _('[OPTION]... FILE...'))
921 def cat(ui, repo, file1, *pats, **opts):
921 def cat(ui, repo, file1, *pats, **opts):
922 """output the current or given revision of files
922 """output the current or given revision of files
923
923
924 Print the specified files as they were at the given revision. If
924 Print the specified files as they were at the given revision. If
925 no revision is given, the parent of the working directory is used,
925 no revision is given, the parent of the working directory is used,
926 or tip if no revision is checked out.
926 or tip if no revision is checked out.
927
927
928 Output may be to a file, in which case the name of the file is
928 Output may be to a file, in which case the name of the file is
929 given using a format string. The formatting rules are the same as
929 given using a format string. The formatting rules are the same as
930 for the export command, with the following additions:
930 for the export command, with the following additions:
931
931
932 :``%s``: basename of file being printed
932 :``%s``: basename of file being printed
933 :``%d``: dirname of file being printed, or '.' if in repository root
933 :``%d``: dirname of file being printed, or '.' if in repository root
934 :``%p``: root-relative path name of file being printed
934 :``%p``: root-relative path name of file being printed
935
935
936 Returns 0 on success.
936 Returns 0 on success.
937 """
937 """
938 ctx = scmutil.revsingle(repo, opts.get('rev'))
938 ctx = scmutil.revsingle(repo, opts.get('rev'))
939 err = 1
939 err = 1
940 m = scmutil.match(repo, (file1,) + pats, opts)
940 m = scmutil.match(repo, (file1,) + pats, opts)
941 for abs in ctx.walk(m):
941 for abs in ctx.walk(m):
942 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
942 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
943 pathname=abs)
943 pathname=abs)
944 data = ctx[abs].data()
944 data = ctx[abs].data()
945 if opts.get('decode'):
945 if opts.get('decode'):
946 data = repo.wwritedata(abs, data)
946 data = repo.wwritedata(abs, data)
947 fp.write(data)
947 fp.write(data)
948 fp.close()
948 fp.close()
949 err = 0
949 err = 0
950 return err
950 return err
951
951
952 @command('^clone',
952 @command('^clone',
953 [('U', 'noupdate', None,
953 [('U', 'noupdate', None,
954 _('the clone will include an empty working copy (only a repository)')),
954 _('the clone will include an empty working copy (only a repository)')),
955 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
955 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
956 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
956 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
957 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
957 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
958 ('', 'pull', None, _('use pull protocol to copy metadata')),
958 ('', 'pull', None, _('use pull protocol to copy metadata')),
959 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
959 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
960 ] + remoteopts,
960 ] + remoteopts,
961 _('[OPTION]... SOURCE [DEST]'))
961 _('[OPTION]... SOURCE [DEST]'))
962 def clone(ui, source, dest=None, **opts):
962 def clone(ui, source, dest=None, **opts):
963 """make a copy of an existing repository
963 """make a copy of an existing repository
964
964
965 Create a copy of an existing repository in a new directory.
965 Create a copy of an existing repository in a new directory.
966
966
967 If no destination directory name is specified, it defaults to the
967 If no destination directory name is specified, it defaults to the
968 basename of the source.
968 basename of the source.
969
969
970 The location of the source is added to the new repository's
970 The location of the source is added to the new repository's
971 ``.hg/hgrc`` file, as the default to be used for future pulls.
971 ``.hg/hgrc`` file, as the default to be used for future pulls.
972
972
973 See :hg:`help urls` for valid source format details.
973 See :hg:`help urls` for valid source format details.
974
974
975 It is possible to specify an ``ssh://`` URL as the destination, but no
975 It is possible to specify an ``ssh://`` URL as the destination, but no
976 ``.hg/hgrc`` and working directory will be created on the remote side.
976 ``.hg/hgrc`` and working directory will be created on the remote side.
977 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
977 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
978
978
979 A set of changesets (tags, or branch names) to pull may be specified
979 A set of changesets (tags, or branch names) to pull may be specified
980 by listing each changeset (tag, or branch name) with -r/--rev.
980 by listing each changeset (tag, or branch name) with -r/--rev.
981 If -r/--rev is used, the cloned repository will contain only a subset
981 If -r/--rev is used, the cloned repository will contain only a subset
982 of the changesets of the source repository. Only the set of changesets
982 of the changesets of the source repository. Only the set of changesets
983 defined by all -r/--rev options (including all their ancestors)
983 defined by all -r/--rev options (including all their ancestors)
984 will be pulled into the destination repository.
984 will be pulled into the destination repository.
985 No subsequent changesets (including subsequent tags) will be present
985 No subsequent changesets (including subsequent tags) will be present
986 in the destination.
986 in the destination.
987
987
988 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
988 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
989 local source repositories.
989 local source repositories.
990
990
991 For efficiency, hardlinks are used for cloning whenever the source
991 For efficiency, hardlinks are used for cloning whenever the source
992 and destination are on the same filesystem (note this applies only
992 and destination are on the same filesystem (note this applies only
993 to the repository data, not to the working directory). Some
993 to the repository data, not to the working directory). Some
994 filesystems, such as AFS, implement hardlinking incorrectly, but
994 filesystems, such as AFS, implement hardlinking incorrectly, but
995 do not report errors. In these cases, use the --pull option to
995 do not report errors. In these cases, use the --pull option to
996 avoid hardlinking.
996 avoid hardlinking.
997
997
998 In some cases, you can clone repositories and the working directory
998 In some cases, you can clone repositories and the working directory
999 using full hardlinks with ::
999 using full hardlinks with ::
1000
1000
1001 $ cp -al REPO REPOCLONE
1001 $ cp -al REPO REPOCLONE
1002
1002
1003 This is the fastest way to clone, but it is not always safe. The
1003 This is the fastest way to clone, but it is not always safe. The
1004 operation is not atomic (making sure REPO is not modified during
1004 operation is not atomic (making sure REPO is not modified during
1005 the operation is up to you) and you have to make sure your editor
1005 the operation is up to you) and you have to make sure your editor
1006 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1006 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1007 this is not compatible with certain extensions that place their
1007 this is not compatible with certain extensions that place their
1008 metadata under the .hg directory, such as mq.
1008 metadata under the .hg directory, such as mq.
1009
1009
1010 Mercurial will update the working directory to the first applicable
1010 Mercurial will update the working directory to the first applicable
1011 revision from this list:
1011 revision from this list:
1012
1012
1013 a) null if -U or the source repository has no changesets
1013 a) null if -U or the source repository has no changesets
1014 b) if -u . and the source repository is local, the first parent of
1014 b) if -u . and the source repository is local, the first parent of
1015 the source repository's working directory
1015 the source repository's working directory
1016 c) the changeset specified with -u (if a branch name, this means the
1016 c) the changeset specified with -u (if a branch name, this means the
1017 latest head of that branch)
1017 latest head of that branch)
1018 d) the changeset specified with -r
1018 d) the changeset specified with -r
1019 e) the tipmost head specified with -b
1019 e) the tipmost head specified with -b
1020 f) the tipmost head specified with the url#branch source syntax
1020 f) the tipmost head specified with the url#branch source syntax
1021 g) the tipmost head of the default branch
1021 g) the tipmost head of the default branch
1022 h) tip
1022 h) tip
1023
1023
1024 Returns 0 on success.
1024 Returns 0 on success.
1025 """
1025 """
1026 if opts.get('noupdate') and opts.get('updaterev'):
1026 if opts.get('noupdate') and opts.get('updaterev'):
1027 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1027 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1028
1028
1029 r = hg.clone(ui, opts, source, dest,
1029 r = hg.clone(ui, opts, source, dest,
1030 pull=opts.get('pull'),
1030 pull=opts.get('pull'),
1031 stream=opts.get('uncompressed'),
1031 stream=opts.get('uncompressed'),
1032 rev=opts.get('rev'),
1032 rev=opts.get('rev'),
1033 update=opts.get('updaterev') or not opts.get('noupdate'),
1033 update=opts.get('updaterev') or not opts.get('noupdate'),
1034 branch=opts.get('branch'))
1034 branch=opts.get('branch'))
1035
1035
1036 return r is None
1036 return r is None
1037
1037
1038 @command('^commit|ci',
1038 @command('^commit|ci',
1039 [('A', 'addremove', None,
1039 [('A', 'addremove', None,
1040 _('mark new/missing files as added/removed before committing')),
1040 _('mark new/missing files as added/removed before committing')),
1041 ('', 'close-branch', None,
1041 ('', 'close-branch', None,
1042 _('mark a branch as closed, hiding it from the branch list')),
1042 _('mark a branch as closed, hiding it from the branch list')),
1043 ] + walkopts + commitopts + commitopts2,
1043 ] + walkopts + commitopts + commitopts2,
1044 _('[OPTION]... [FILE]...'))
1044 _('[OPTION]... [FILE]...'))
1045 def commit(ui, repo, *pats, **opts):
1045 def commit(ui, repo, *pats, **opts):
1046 """commit the specified files or all outstanding changes
1046 """commit the specified files or all outstanding changes
1047
1047
1048 Commit changes to the given files into the repository. Unlike a
1048 Commit changes to the given files into the repository. Unlike a
1049 centralized SCM, this operation is a local operation. See
1049 centralized SCM, this operation is a local operation. See
1050 :hg:`push` for a way to actively distribute your changes.
1050 :hg:`push` for a way to actively distribute your changes.
1051
1051
1052 If a list of files is omitted, all changes reported by :hg:`status`
1052 If a list of files is omitted, all changes reported by :hg:`status`
1053 will be committed.
1053 will be committed.
1054
1054
1055 If you are committing the result of a merge, do not provide any
1055 If you are committing the result of a merge, do not provide any
1056 filenames or -I/-X filters.
1056 filenames or -I/-X filters.
1057
1057
1058 If no commit message is specified, Mercurial starts your
1058 If no commit message is specified, Mercurial starts your
1059 configured editor where you can enter a message. In case your
1059 configured editor where you can enter a message. In case your
1060 commit fails, you will find a backup of your message in
1060 commit fails, you will find a backup of your message in
1061 ``.hg/last-message.txt``.
1061 ``.hg/last-message.txt``.
1062
1062
1063 See :hg:`help dates` for a list of formats valid for -d/--date.
1063 See :hg:`help dates` for a list of formats valid for -d/--date.
1064
1064
1065 Returns 0 on success, 1 if nothing changed.
1065 Returns 0 on success, 1 if nothing changed.
1066 """
1066 """
1067 extra = {}
1067 extra = {}
1068 if opts.get('close_branch'):
1068 if opts.get('close_branch'):
1069 if repo['.'].node() not in repo.branchheads():
1069 if repo['.'].node() not in repo.branchheads():
1070 # The topo heads set is included in the branch heads set of the
1070 # The topo heads set is included in the branch heads set of the
1071 # current branch, so it's sufficient to test branchheads
1071 # current branch, so it's sufficient to test branchheads
1072 raise util.Abort(_('can only close branch heads'))
1072 raise util.Abort(_('can only close branch heads'))
1073 extra['close'] = 1
1073 extra['close'] = 1
1074 e = cmdutil.commiteditor
1074 e = cmdutil.commiteditor
1075 if opts.get('force_editor'):
1075 if opts.get('force_editor'):
1076 e = cmdutil.commitforceeditor
1076 e = cmdutil.commitforceeditor
1077
1077
1078 def commitfunc(ui, repo, message, match, opts):
1078 def commitfunc(ui, repo, message, match, opts):
1079 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1079 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1080 editor=e, extra=extra)
1080 editor=e, extra=extra)
1081
1081
1082 branch = repo[None].branch()
1082 branch = repo[None].branch()
1083 bheads = repo.branchheads(branch)
1083 bheads = repo.branchheads(branch)
1084
1084
1085 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1085 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1086 if not node:
1086 if not node:
1087 stat = repo.status(match=scmutil.match(repo, pats, opts))
1087 stat = repo.status(match=scmutil.match(repo, pats, opts))
1088 if stat[3]:
1088 if stat[3]:
1089 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1089 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1090 % len(stat[3]))
1090 % len(stat[3]))
1091 else:
1091 else:
1092 ui.status(_("nothing changed\n"))
1092 ui.status(_("nothing changed\n"))
1093 return 1
1093 return 1
1094
1094
1095 ctx = repo[node]
1095 ctx = repo[node]
1096 parents = ctx.parents()
1096 parents = ctx.parents()
1097
1097
1098 if bheads and not [x for x in parents
1098 if bheads and not [x for x in parents
1099 if x.node() in bheads and x.branch() == branch]:
1099 if x.node() in bheads and x.branch() == branch]:
1100 ui.status(_('created new head\n'))
1100 ui.status(_('created new head\n'))
1101 # The message is not printed for initial roots. For the other
1101 # The message is not printed for initial roots. For the other
1102 # changesets, it is printed in the following situations:
1102 # changesets, it is printed in the following situations:
1103 #
1103 #
1104 # Par column: for the 2 parents with ...
1104 # Par column: for the 2 parents with ...
1105 # N: null or no parent
1105 # N: null or no parent
1106 # B: parent is on another named branch
1106 # B: parent is on another named branch
1107 # C: parent is a regular non head changeset
1107 # C: parent is a regular non head changeset
1108 # H: parent was a branch head of the current branch
1108 # H: parent was a branch head of the current branch
1109 # Msg column: whether we print "created new head" message
1109 # Msg column: whether we print "created new head" message
1110 # In the following, it is assumed that there already exists some
1110 # In the following, it is assumed that there already exists some
1111 # initial branch heads of the current branch, otherwise nothing is
1111 # initial branch heads of the current branch, otherwise nothing is
1112 # printed anyway.
1112 # printed anyway.
1113 #
1113 #
1114 # Par Msg Comment
1114 # Par Msg Comment
1115 # NN y additional topo root
1115 # NN y additional topo root
1116 #
1116 #
1117 # BN y additional branch root
1117 # BN y additional branch root
1118 # CN y additional topo head
1118 # CN y additional topo head
1119 # HN n usual case
1119 # HN n usual case
1120 #
1120 #
1121 # BB y weird additional branch root
1121 # BB y weird additional branch root
1122 # CB y branch merge
1122 # CB y branch merge
1123 # HB n merge with named branch
1123 # HB n merge with named branch
1124 #
1124 #
1125 # CC y additional head from merge
1125 # CC y additional head from merge
1126 # CH n merge with a head
1126 # CH n merge with a head
1127 #
1127 #
1128 # HH n head merge: head count decreases
1128 # HH n head merge: head count decreases
1129
1129
1130 if not opts.get('close_branch'):
1130 if not opts.get('close_branch'):
1131 for r in parents:
1131 for r in parents:
1132 if r.extra().get('close') and r.branch() == branch:
1132 if r.extra().get('close') and r.branch() == branch:
1133 ui.status(_('reopening closed branch head %d\n') % r)
1133 ui.status(_('reopening closed branch head %d\n') % r)
1134
1134
1135 if ui.debugflag:
1135 if ui.debugflag:
1136 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1136 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1137 elif ui.verbose:
1137 elif ui.verbose:
1138 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1138 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1139
1139
1140 @command('copy|cp',
1140 @command('copy|cp',
1141 [('A', 'after', None, _('record a copy that has already occurred')),
1141 [('A', 'after', None, _('record a copy that has already occurred')),
1142 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1142 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1143 ] + walkopts + dryrunopts,
1143 ] + walkopts + dryrunopts,
1144 _('[OPTION]... [SOURCE]... DEST'))
1144 _('[OPTION]... [SOURCE]... DEST'))
1145 def copy(ui, repo, *pats, **opts):
1145 def copy(ui, repo, *pats, **opts):
1146 """mark files as copied for the next commit
1146 """mark files as copied for the next commit
1147
1147
1148 Mark dest as having copies of source files. If dest is a
1148 Mark dest as having copies of source files. If dest is a
1149 directory, copies are put in that directory. If dest is a file,
1149 directory, copies are put in that directory. If dest is a file,
1150 the source must be a single file.
1150 the source must be a single file.
1151
1151
1152 By default, this command copies the contents of files as they
1152 By default, this command copies the contents of files as they
1153 exist in the working directory. If invoked with -A/--after, the
1153 exist in the working directory. If invoked with -A/--after, the
1154 operation is recorded, but no copying is performed.
1154 operation is recorded, but no copying is performed.
1155
1155
1156 This command takes effect with the next commit. To undo a copy
1156 This command takes effect with the next commit. To undo a copy
1157 before that, see :hg:`revert`.
1157 before that, see :hg:`revert`.
1158
1158
1159 Returns 0 on success, 1 if errors are encountered.
1159 Returns 0 on success, 1 if errors are encountered.
1160 """
1160 """
1161 wlock = repo.wlock(False)
1161 wlock = repo.wlock(False)
1162 try:
1162 try:
1163 return cmdutil.copy(ui, repo, pats, opts)
1163 return cmdutil.copy(ui, repo, pats, opts)
1164 finally:
1164 finally:
1165 wlock.release()
1165 wlock.release()
1166
1166
1167 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1167 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1168 def debugancestor(ui, repo, *args):
1168 def debugancestor(ui, repo, *args):
1169 """find the ancestor revision of two revisions in a given index"""
1169 """find the ancestor revision of two revisions in a given index"""
1170 if len(args) == 3:
1170 if len(args) == 3:
1171 index, rev1, rev2 = args
1171 index, rev1, rev2 = args
1172 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1172 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1173 lookup = r.lookup
1173 lookup = r.lookup
1174 elif len(args) == 2:
1174 elif len(args) == 2:
1175 if not repo:
1175 if not repo:
1176 raise util.Abort(_("there is no Mercurial repository here "
1176 raise util.Abort(_("there is no Mercurial repository here "
1177 "(.hg not found)"))
1177 "(.hg not found)"))
1178 rev1, rev2 = args
1178 rev1, rev2 = args
1179 r = repo.changelog
1179 r = repo.changelog
1180 lookup = repo.lookup
1180 lookup = repo.lookup
1181 else:
1181 else:
1182 raise util.Abort(_('either two or three arguments required'))
1182 raise util.Abort(_('either two or three arguments required'))
1183 a = r.ancestor(lookup(rev1), lookup(rev2))
1183 a = r.ancestor(lookup(rev1), lookup(rev2))
1184 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1184 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1185
1185
1186 @command('debugbuilddag',
1186 @command('debugbuilddag',
1187 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1187 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1188 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1188 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1189 ('n', 'new-file', None, _('add new file at each rev'))],
1189 ('n', 'new-file', None, _('add new file at each rev'))],
1190 _('[OPTION]... [TEXT]'))
1190 _('[OPTION]... [TEXT]'))
1191 def debugbuilddag(ui, repo, text=None,
1191 def debugbuilddag(ui, repo, text=None,
1192 mergeable_file=False,
1192 mergeable_file=False,
1193 overwritten_file=False,
1193 overwritten_file=False,
1194 new_file=False):
1194 new_file=False):
1195 """builds a repo with a given DAG from scratch in the current empty repo
1195 """builds a repo with a given DAG from scratch in the current empty repo
1196
1196
1197 The description of the DAG is read from stdin if not given on the
1197 The description of the DAG is read from stdin if not given on the
1198 command line.
1198 command line.
1199
1199
1200 Elements:
1200 Elements:
1201
1201
1202 - "+n" is a linear run of n nodes based on the current default parent
1202 - "+n" is a linear run of n nodes based on the current default parent
1203 - "." is a single node based on the current default parent
1203 - "." is a single node based on the current default parent
1204 - "$" resets the default parent to null (implied at the start);
1204 - "$" resets the default parent to null (implied at the start);
1205 otherwise the default parent is always the last node created
1205 otherwise the default parent is always the last node created
1206 - "<p" sets the default parent to the backref p
1206 - "<p" sets the default parent to the backref p
1207 - "*p" is a fork at parent p, which is a backref
1207 - "*p" is a fork at parent p, which is a backref
1208 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1208 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1209 - "/p2" is a merge of the preceding node and p2
1209 - "/p2" is a merge of the preceding node and p2
1210 - ":tag" defines a local tag for the preceding node
1210 - ":tag" defines a local tag for the preceding node
1211 - "@branch" sets the named branch for subsequent nodes
1211 - "@branch" sets the named branch for subsequent nodes
1212 - "#...\\n" is a comment up to the end of the line
1212 - "#...\\n" is a comment up to the end of the line
1213
1213
1214 Whitespace between the above elements is ignored.
1214 Whitespace between the above elements is ignored.
1215
1215
1216 A backref is either
1216 A backref is either
1217
1217
1218 - a number n, which references the node curr-n, where curr is the current
1218 - a number n, which references the node curr-n, where curr is the current
1219 node, or
1219 node, or
1220 - the name of a local tag you placed earlier using ":tag", or
1220 - the name of a local tag you placed earlier using ":tag", or
1221 - empty to denote the default parent.
1221 - empty to denote the default parent.
1222
1222
1223 All string valued-elements are either strictly alphanumeric, or must
1223 All string valued-elements are either strictly alphanumeric, or must
1224 be enclosed in double quotes ("..."), with "\\" as escape character.
1224 be enclosed in double quotes ("..."), with "\\" as escape character.
1225 """
1225 """
1226
1226
1227 if text is None:
1227 if text is None:
1228 ui.status(_("reading DAG from stdin\n"))
1228 ui.status(_("reading DAG from stdin\n"))
1229 text = sys.stdin.read()
1229 text = sys.stdin.read()
1230
1230
1231 cl = repo.changelog
1231 cl = repo.changelog
1232 if len(cl) > 0:
1232 if len(cl) > 0:
1233 raise util.Abort(_('repository is not empty'))
1233 raise util.Abort(_('repository is not empty'))
1234
1234
1235 # determine number of revs in DAG
1235 # determine number of revs in DAG
1236 total = 0
1236 total = 0
1237 for type, data in dagparser.parsedag(text):
1237 for type, data in dagparser.parsedag(text):
1238 if type == 'n':
1238 if type == 'n':
1239 total += 1
1239 total += 1
1240
1240
1241 if mergeable_file:
1241 if mergeable_file:
1242 linesperrev = 2
1242 linesperrev = 2
1243 # make a file with k lines per rev
1243 # make a file with k lines per rev
1244 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1244 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1245 initialmergedlines.append("")
1245 initialmergedlines.append("")
1246
1246
1247 tags = []
1247 tags = []
1248
1248
1249 tr = repo.transaction("builddag")
1249 tr = repo.transaction("builddag")
1250 try:
1250 try:
1251
1251
1252 at = -1
1252 at = -1
1253 atbranch = 'default'
1253 atbranch = 'default'
1254 nodeids = []
1254 nodeids = []
1255 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1255 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1256 for type, data in dagparser.parsedag(text):
1256 for type, data in dagparser.parsedag(text):
1257 if type == 'n':
1257 if type == 'n':
1258 ui.note('node %s\n' % str(data))
1258 ui.note('node %s\n' % str(data))
1259 id, ps = data
1259 id, ps = data
1260
1260
1261 files = []
1261 files = []
1262 fctxs = {}
1262 fctxs = {}
1263
1263
1264 p2 = None
1264 p2 = None
1265 if mergeable_file:
1265 if mergeable_file:
1266 fn = "mf"
1266 fn = "mf"
1267 p1 = repo[ps[0]]
1267 p1 = repo[ps[0]]
1268 if len(ps) > 1:
1268 if len(ps) > 1:
1269 p2 = repo[ps[1]]
1269 p2 = repo[ps[1]]
1270 pa = p1.ancestor(p2)
1270 pa = p1.ancestor(p2)
1271 base, local, other = [x[fn].data() for x in pa, p1, p2]
1271 base, local, other = [x[fn].data() for x in pa, p1, p2]
1272 m3 = simplemerge.Merge3Text(base, local, other)
1272 m3 = simplemerge.Merge3Text(base, local, other)
1273 ml = [l.strip() for l in m3.merge_lines()]
1273 ml = [l.strip() for l in m3.merge_lines()]
1274 ml.append("")
1274 ml.append("")
1275 elif at > 0:
1275 elif at > 0:
1276 ml = p1[fn].data().split("\n")
1276 ml = p1[fn].data().split("\n")
1277 else:
1277 else:
1278 ml = initialmergedlines
1278 ml = initialmergedlines
1279 ml[id * linesperrev] += " r%i" % id
1279 ml[id * linesperrev] += " r%i" % id
1280 mergedtext = "\n".join(ml)
1280 mergedtext = "\n".join(ml)
1281 files.append(fn)
1281 files.append(fn)
1282 fctxs[fn] = context.memfilectx(fn, mergedtext)
1282 fctxs[fn] = context.memfilectx(fn, mergedtext)
1283
1283
1284 if overwritten_file:
1284 if overwritten_file:
1285 fn = "of"
1285 fn = "of"
1286 files.append(fn)
1286 files.append(fn)
1287 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1287 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1288
1288
1289 if new_file:
1289 if new_file:
1290 fn = "nf%i" % id
1290 fn = "nf%i" % id
1291 files.append(fn)
1291 files.append(fn)
1292 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1292 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1293 if len(ps) > 1:
1293 if len(ps) > 1:
1294 if not p2:
1294 if not p2:
1295 p2 = repo[ps[1]]
1295 p2 = repo[ps[1]]
1296 for fn in p2:
1296 for fn in p2:
1297 if fn.startswith("nf"):
1297 if fn.startswith("nf"):
1298 files.append(fn)
1298 files.append(fn)
1299 fctxs[fn] = p2[fn]
1299 fctxs[fn] = p2[fn]
1300
1300
1301 def fctxfn(repo, cx, path):
1301 def fctxfn(repo, cx, path):
1302 return fctxs.get(path)
1302 return fctxs.get(path)
1303
1303
1304 if len(ps) == 0 or ps[0] < 0:
1304 if len(ps) == 0 or ps[0] < 0:
1305 pars = [None, None]
1305 pars = [None, None]
1306 elif len(ps) == 1:
1306 elif len(ps) == 1:
1307 pars = [nodeids[ps[0]], None]
1307 pars = [nodeids[ps[0]], None]
1308 else:
1308 else:
1309 pars = [nodeids[p] for p in ps]
1309 pars = [nodeids[p] for p in ps]
1310 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1310 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1311 date=(id, 0),
1311 date=(id, 0),
1312 user="debugbuilddag",
1312 user="debugbuilddag",
1313 extra={'branch': atbranch})
1313 extra={'branch': atbranch})
1314 nodeid = repo.commitctx(cx)
1314 nodeid = repo.commitctx(cx)
1315 nodeids.append(nodeid)
1315 nodeids.append(nodeid)
1316 at = id
1316 at = id
1317 elif type == 'l':
1317 elif type == 'l':
1318 id, name = data
1318 id, name = data
1319 ui.note('tag %s\n' % name)
1319 ui.note('tag %s\n' % name)
1320 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1320 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1321 elif type == 'a':
1321 elif type == 'a':
1322 ui.note('branch %s\n' % data)
1322 ui.note('branch %s\n' % data)
1323 atbranch = data
1323 atbranch = data
1324 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1324 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1325 tr.close()
1325 tr.close()
1326 finally:
1326 finally:
1327 ui.progress(_('building'), None)
1327 ui.progress(_('building'), None)
1328 tr.release()
1328 tr.release()
1329
1329
1330 if tags:
1330 if tags:
1331 repo.opener.write("localtags", "".join(tags))
1331 repo.opener.write("localtags", "".join(tags))
1332
1332
1333 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1333 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1334 def debugbundle(ui, bundlepath, all=None, **opts):
1334 def debugbundle(ui, bundlepath, all=None, **opts):
1335 """lists the contents of a bundle"""
1335 """lists the contents of a bundle"""
1336 f = url.open(ui, bundlepath)
1336 f = url.open(ui, bundlepath)
1337 try:
1337 try:
1338 gen = changegroup.readbundle(f, bundlepath)
1338 gen = changegroup.readbundle(f, bundlepath)
1339 if all:
1339 if all:
1340 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1340 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1341
1341
1342 def showchunks(named):
1342 def showchunks(named):
1343 ui.write("\n%s\n" % named)
1343 ui.write("\n%s\n" % named)
1344 chain = None
1344 chain = None
1345 while True:
1345 while True:
1346 chunkdata = gen.deltachunk(chain)
1346 chunkdata = gen.deltachunk(chain)
1347 if not chunkdata:
1347 if not chunkdata:
1348 break
1348 break
1349 node = chunkdata['node']
1349 node = chunkdata['node']
1350 p1 = chunkdata['p1']
1350 p1 = chunkdata['p1']
1351 p2 = chunkdata['p2']
1351 p2 = chunkdata['p2']
1352 cs = chunkdata['cs']
1352 cs = chunkdata['cs']
1353 deltabase = chunkdata['deltabase']
1353 deltabase = chunkdata['deltabase']
1354 delta = chunkdata['delta']
1354 delta = chunkdata['delta']
1355 ui.write("%s %s %s %s %s %s\n" %
1355 ui.write("%s %s %s %s %s %s\n" %
1356 (hex(node), hex(p1), hex(p2),
1356 (hex(node), hex(p1), hex(p2),
1357 hex(cs), hex(deltabase), len(delta)))
1357 hex(cs), hex(deltabase), len(delta)))
1358 chain = node
1358 chain = node
1359
1359
1360 chunkdata = gen.changelogheader()
1360 chunkdata = gen.changelogheader()
1361 showchunks("changelog")
1361 showchunks("changelog")
1362 chunkdata = gen.manifestheader()
1362 chunkdata = gen.manifestheader()
1363 showchunks("manifest")
1363 showchunks("manifest")
1364 while True:
1364 while True:
1365 chunkdata = gen.filelogheader()
1365 chunkdata = gen.filelogheader()
1366 if not chunkdata:
1366 if not chunkdata:
1367 break
1367 break
1368 fname = chunkdata['filename']
1368 fname = chunkdata['filename']
1369 showchunks(fname)
1369 showchunks(fname)
1370 else:
1370 else:
1371 chunkdata = gen.changelogheader()
1371 chunkdata = gen.changelogheader()
1372 chain = None
1372 chain = None
1373 while True:
1373 while True:
1374 chunkdata = gen.deltachunk(chain)
1374 chunkdata = gen.deltachunk(chain)
1375 if not chunkdata:
1375 if not chunkdata:
1376 break
1376 break
1377 node = chunkdata['node']
1377 node = chunkdata['node']
1378 ui.write("%s\n" % hex(node))
1378 ui.write("%s\n" % hex(node))
1379 chain = node
1379 chain = node
1380 finally:
1380 finally:
1381 f.close()
1381 f.close()
1382
1382
1383 @command('debugcheckstate', [], '')
1383 @command('debugcheckstate', [], '')
1384 def debugcheckstate(ui, repo):
1384 def debugcheckstate(ui, repo):
1385 """validate the correctness of the current dirstate"""
1385 """validate the correctness of the current dirstate"""
1386 parent1, parent2 = repo.dirstate.parents()
1386 parent1, parent2 = repo.dirstate.parents()
1387 m1 = repo[parent1].manifest()
1387 m1 = repo[parent1].manifest()
1388 m2 = repo[parent2].manifest()
1388 m2 = repo[parent2].manifest()
1389 errors = 0
1389 errors = 0
1390 for f in repo.dirstate:
1390 for f in repo.dirstate:
1391 state = repo.dirstate[f]
1391 state = repo.dirstate[f]
1392 if state in "nr" and f not in m1:
1392 if state in "nr" and f not in m1:
1393 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1393 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1394 errors += 1
1394 errors += 1
1395 if state in "a" and f in m1:
1395 if state in "a" and f in m1:
1396 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1396 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1397 errors += 1
1397 errors += 1
1398 if state in "m" and f not in m1 and f not in m2:
1398 if state in "m" and f not in m1 and f not in m2:
1399 ui.warn(_("%s in state %s, but not in either manifest\n") %
1399 ui.warn(_("%s in state %s, but not in either manifest\n") %
1400 (f, state))
1400 (f, state))
1401 errors += 1
1401 errors += 1
1402 for f in m1:
1402 for f in m1:
1403 state = repo.dirstate[f]
1403 state = repo.dirstate[f]
1404 if state not in "nrm":
1404 if state not in "nrm":
1405 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1405 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1406 errors += 1
1406 errors += 1
1407 if errors:
1407 if errors:
1408 error = _(".hg/dirstate inconsistent with current parent's manifest")
1408 error = _(".hg/dirstate inconsistent with current parent's manifest")
1409 raise util.Abort(error)
1409 raise util.Abort(error)
1410
1410
1411 @command('debugcommands', [], _('[COMMAND]'))
1411 @command('debugcommands', [], _('[COMMAND]'))
1412 def debugcommands(ui, cmd='', *args):
1412 def debugcommands(ui, cmd='', *args):
1413 """list all available commands and options"""
1413 """list all available commands and options"""
1414 for cmd, vals in sorted(table.iteritems()):
1414 for cmd, vals in sorted(table.iteritems()):
1415 cmd = cmd.split('|')[0].strip('^')
1415 cmd = cmd.split('|')[0].strip('^')
1416 opts = ', '.join([i[1] for i in vals[1]])
1416 opts = ', '.join([i[1] for i in vals[1]])
1417 ui.write('%s: %s\n' % (cmd, opts))
1417 ui.write('%s: %s\n' % (cmd, opts))
1418
1418
1419 @command('debugcomplete',
1419 @command('debugcomplete',
1420 [('o', 'options', None, _('show the command options'))],
1420 [('o', 'options', None, _('show the command options'))],
1421 _('[-o] CMD'))
1421 _('[-o] CMD'))
1422 def debugcomplete(ui, cmd='', **opts):
1422 def debugcomplete(ui, cmd='', **opts):
1423 """returns the completion list associated with the given command"""
1423 """returns the completion list associated with the given command"""
1424
1424
1425 if opts.get('options'):
1425 if opts.get('options'):
1426 options = []
1426 options = []
1427 otables = [globalopts]
1427 otables = [globalopts]
1428 if cmd:
1428 if cmd:
1429 aliases, entry = cmdutil.findcmd(cmd, table, False)
1429 aliases, entry = cmdutil.findcmd(cmd, table, False)
1430 otables.append(entry[1])
1430 otables.append(entry[1])
1431 for t in otables:
1431 for t in otables:
1432 for o in t:
1432 for o in t:
1433 if "(DEPRECATED)" in o[3]:
1433 if "(DEPRECATED)" in o[3]:
1434 continue
1434 continue
1435 if o[0]:
1435 if o[0]:
1436 options.append('-%s' % o[0])
1436 options.append('-%s' % o[0])
1437 options.append('--%s' % o[1])
1437 options.append('--%s' % o[1])
1438 ui.write("%s\n" % "\n".join(options))
1438 ui.write("%s\n" % "\n".join(options))
1439 return
1439 return
1440
1440
1441 cmdlist = cmdutil.findpossible(cmd, table)
1441 cmdlist = cmdutil.findpossible(cmd, table)
1442 if ui.verbose:
1442 if ui.verbose:
1443 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1443 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1444 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1444 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1445
1445
1446 @command('debugdag',
1446 @command('debugdag',
1447 [('t', 'tags', None, _('use tags as labels')),
1447 [('t', 'tags', None, _('use tags as labels')),
1448 ('b', 'branches', None, _('annotate with branch names')),
1448 ('b', 'branches', None, _('annotate with branch names')),
1449 ('', 'dots', None, _('use dots for runs')),
1449 ('', 'dots', None, _('use dots for runs')),
1450 ('s', 'spaces', None, _('separate elements by spaces'))],
1450 ('s', 'spaces', None, _('separate elements by spaces'))],
1451 _('[OPTION]... [FILE [REV]...]'))
1451 _('[OPTION]... [FILE [REV]...]'))
1452 def debugdag(ui, repo, file_=None, *revs, **opts):
1452 def debugdag(ui, repo, file_=None, *revs, **opts):
1453 """format the changelog or an index DAG as a concise textual description
1453 """format the changelog or an index DAG as a concise textual description
1454
1454
1455 If you pass a revlog index, the revlog's DAG is emitted. If you list
1455 If you pass a revlog index, the revlog's DAG is emitted. If you list
1456 revision numbers, they get labelled in the output as rN.
1456 revision numbers, they get labelled in the output as rN.
1457
1457
1458 Otherwise, the changelog DAG of the current repo is emitted.
1458 Otherwise, the changelog DAG of the current repo is emitted.
1459 """
1459 """
1460 spaces = opts.get('spaces')
1460 spaces = opts.get('spaces')
1461 dots = opts.get('dots')
1461 dots = opts.get('dots')
1462 if file_:
1462 if file_:
1463 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1463 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1464 revs = set((int(r) for r in revs))
1464 revs = set((int(r) for r in revs))
1465 def events():
1465 def events():
1466 for r in rlog:
1466 for r in rlog:
1467 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1467 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1468 if r in revs:
1468 if r in revs:
1469 yield 'l', (r, "r%i" % r)
1469 yield 'l', (r, "r%i" % r)
1470 elif repo:
1470 elif repo:
1471 cl = repo.changelog
1471 cl = repo.changelog
1472 tags = opts.get('tags')
1472 tags = opts.get('tags')
1473 branches = opts.get('branches')
1473 branches = opts.get('branches')
1474 if tags:
1474 if tags:
1475 labels = {}
1475 labels = {}
1476 for l, n in repo.tags().items():
1476 for l, n in repo.tags().items():
1477 labels.setdefault(cl.rev(n), []).append(l)
1477 labels.setdefault(cl.rev(n), []).append(l)
1478 def events():
1478 def events():
1479 b = "default"
1479 b = "default"
1480 for r in cl:
1480 for r in cl:
1481 if branches:
1481 if branches:
1482 newb = cl.read(cl.node(r))[5]['branch']
1482 newb = cl.read(cl.node(r))[5]['branch']
1483 if newb != b:
1483 if newb != b:
1484 yield 'a', newb
1484 yield 'a', newb
1485 b = newb
1485 b = newb
1486 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1486 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1487 if tags:
1487 if tags:
1488 ls = labels.get(r)
1488 ls = labels.get(r)
1489 if ls:
1489 if ls:
1490 for l in ls:
1490 for l in ls:
1491 yield 'l', (r, l)
1491 yield 'l', (r, l)
1492 else:
1492 else:
1493 raise util.Abort(_('need repo for changelog dag'))
1493 raise util.Abort(_('need repo for changelog dag'))
1494
1494
1495 for line in dagparser.dagtextlines(events(),
1495 for line in dagparser.dagtextlines(events(),
1496 addspaces=spaces,
1496 addspaces=spaces,
1497 wraplabels=True,
1497 wraplabels=True,
1498 wrapannotations=True,
1498 wrapannotations=True,
1499 wrapnonlinear=dots,
1499 wrapnonlinear=dots,
1500 usedots=dots,
1500 usedots=dots,
1501 maxlinewidth=70):
1501 maxlinewidth=70):
1502 ui.write(line)
1502 ui.write(line)
1503 ui.write("\n")
1503 ui.write("\n")
1504
1504
1505 @command('debugdata',
1505 @command('debugdata',
1506 [('c', 'changelog', False, _('open changelog')),
1506 [('c', 'changelog', False, _('open changelog')),
1507 ('m', 'manifest', False, _('open manifest'))],
1507 ('m', 'manifest', False, _('open manifest'))],
1508 _('-c|-m|FILE REV'))
1508 _('-c|-m|FILE REV'))
1509 def debugdata(ui, repo, file_, rev = None, **opts):
1509 def debugdata(ui, repo, file_, rev = None, **opts):
1510 """dump the contents of a data file revision"""
1510 """dump the contents of a data file revision"""
1511 if opts.get('changelog') or opts.get('manifest'):
1511 if opts.get('changelog') or opts.get('manifest'):
1512 file_, rev = None, file_
1512 file_, rev = None, file_
1513 elif rev is None:
1513 elif rev is None:
1514 raise error.CommandError('debugdata', _('invalid arguments'))
1514 raise error.CommandError('debugdata', _('invalid arguments'))
1515 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1515 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1516 try:
1516 try:
1517 ui.write(r.revision(r.lookup(rev)))
1517 ui.write(r.revision(r.lookup(rev)))
1518 except KeyError:
1518 except KeyError:
1519 raise util.Abort(_('invalid revision identifier %s') % rev)
1519 raise util.Abort(_('invalid revision identifier %s') % rev)
1520
1520
1521 @command('debugdate',
1521 @command('debugdate',
1522 [('e', 'extended', None, _('try extended date formats'))],
1522 [('e', 'extended', None, _('try extended date formats'))],
1523 _('[-e] DATE [RANGE]'))
1523 _('[-e] DATE [RANGE]'))
1524 def debugdate(ui, date, range=None, **opts):
1524 def debugdate(ui, date, range=None, **opts):
1525 """parse and display a date"""
1525 """parse and display a date"""
1526 if opts["extended"]:
1526 if opts["extended"]:
1527 d = util.parsedate(date, util.extendeddateformats)
1527 d = util.parsedate(date, util.extendeddateformats)
1528 else:
1528 else:
1529 d = util.parsedate(date)
1529 d = util.parsedate(date)
1530 ui.write("internal: %s %s\n" % d)
1530 ui.write("internal: %s %s\n" % d)
1531 ui.write("standard: %s\n" % util.datestr(d))
1531 ui.write("standard: %s\n" % util.datestr(d))
1532 if range:
1532 if range:
1533 m = util.matchdate(range)
1533 m = util.matchdate(range)
1534 ui.write("match: %s\n" % m(d[0]))
1534 ui.write("match: %s\n" % m(d[0]))
1535
1535
1536 @command('debugdiscovery',
1536 @command('debugdiscovery',
1537 [('', 'old', None, _('use old-style discovery')),
1537 [('', 'old', None, _('use old-style discovery')),
1538 ('', 'nonheads', None,
1538 ('', 'nonheads', None,
1539 _('use old-style discovery with non-heads included')),
1539 _('use old-style discovery with non-heads included')),
1540 ] + remoteopts,
1540 ] + remoteopts,
1541 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1541 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1542 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1542 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1543 """runs the changeset discovery protocol in isolation"""
1543 """runs the changeset discovery protocol in isolation"""
1544 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1544 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1545 remote = hg.peer(repo, opts, remoteurl)
1545 remote = hg.peer(repo, opts, remoteurl)
1546 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1546 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1547
1547
1548 # make sure tests are repeatable
1548 # make sure tests are repeatable
1549 random.seed(12323)
1549 random.seed(12323)
1550
1550
1551 def doit(localheads, remoteheads):
1551 def doit(localheads, remoteheads):
1552 if opts.get('old'):
1552 if opts.get('old'):
1553 if localheads:
1553 if localheads:
1554 raise util.Abort('cannot use localheads with old style discovery')
1554 raise util.Abort('cannot use localheads with old style discovery')
1555 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1555 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1556 force=True)
1556 force=True)
1557 common = set(common)
1557 common = set(common)
1558 if not opts.get('nonheads'):
1558 if not opts.get('nonheads'):
1559 ui.write("unpruned common: %s\n" % " ".join([short(n)
1559 ui.write("unpruned common: %s\n" % " ".join([short(n)
1560 for n in common]))
1560 for n in common]))
1561 dag = dagutil.revlogdag(repo.changelog)
1561 dag = dagutil.revlogdag(repo.changelog)
1562 all = dag.ancestorset(dag.internalizeall(common))
1562 all = dag.ancestorset(dag.internalizeall(common))
1563 common = dag.externalizeall(dag.headsetofconnecteds(all))
1563 common = dag.externalizeall(dag.headsetofconnecteds(all))
1564 else:
1564 else:
1565 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1565 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1566 common = set(common)
1566 common = set(common)
1567 rheads = set(hds)
1567 rheads = set(hds)
1568 lheads = set(repo.heads())
1568 lheads = set(repo.heads())
1569 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1569 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1570 if lheads <= common:
1570 if lheads <= common:
1571 ui.write("local is subset\n")
1571 ui.write("local is subset\n")
1572 elif rheads <= common:
1572 elif rheads <= common:
1573 ui.write("remote is subset\n")
1573 ui.write("remote is subset\n")
1574
1574
1575 serverlogs = opts.get('serverlog')
1575 serverlogs = opts.get('serverlog')
1576 if serverlogs:
1576 if serverlogs:
1577 for filename in serverlogs:
1577 for filename in serverlogs:
1578 logfile = open(filename, 'r')
1578 logfile = open(filename, 'r')
1579 try:
1579 try:
1580 line = logfile.readline()
1580 line = logfile.readline()
1581 while line:
1581 while line:
1582 parts = line.strip().split(';')
1582 parts = line.strip().split(';')
1583 op = parts[1]
1583 op = parts[1]
1584 if op == 'cg':
1584 if op == 'cg':
1585 pass
1585 pass
1586 elif op == 'cgss':
1586 elif op == 'cgss':
1587 doit(parts[2].split(' '), parts[3].split(' '))
1587 doit(parts[2].split(' '), parts[3].split(' '))
1588 elif op == 'unb':
1588 elif op == 'unb':
1589 doit(parts[3].split(' '), parts[2].split(' '))
1589 doit(parts[3].split(' '), parts[2].split(' '))
1590 line = logfile.readline()
1590 line = logfile.readline()
1591 finally:
1591 finally:
1592 logfile.close()
1592 logfile.close()
1593
1593
1594 else:
1594 else:
1595 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1595 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1596 opts.get('remote_head'))
1596 opts.get('remote_head'))
1597 localrevs = opts.get('local_head')
1597 localrevs = opts.get('local_head')
1598 doit(localrevs, remoterevs)
1598 doit(localrevs, remoterevs)
1599
1599
1600 @command('debugfileset', [], ('REVSPEC'))
1600 @command('debugfileset', [], ('REVSPEC'))
1601 def debugfileset(ui, repo, expr):
1601 def debugfileset(ui, repo, expr):
1602 '''parse and apply a fileset specification'''
1602 '''parse and apply a fileset specification'''
1603 if ui.verbose:
1603 if ui.verbose:
1604 tree = fileset.parse(expr)[0]
1604 tree = fileset.parse(expr)[0]
1605 ui.note(tree, "\n")
1605 ui.note(tree, "\n")
1606 matcher = lambda x: scmutil.match(repo, x, default='glob')
1606 matcher = lambda x: scmutil.match(repo, x, default='glob')
1607
1607
1608 for f in fileset.getfileset(repo[None], matcher, expr):
1608 for f in fileset.getfileset(repo[None], matcher, expr):
1609 ui.write("%s\n" % f)
1609 ui.write("%s\n" % f)
1610
1610
1611 @command('debugfsinfo', [], _('[PATH]'))
1611 @command('debugfsinfo', [], _('[PATH]'))
1612 def debugfsinfo(ui, path = "."):
1612 def debugfsinfo(ui, path = "."):
1613 """show information detected about current filesystem"""
1613 """show information detected about current filesystem"""
1614 util.writefile('.debugfsinfo', '')
1614 util.writefile('.debugfsinfo', '')
1615 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1615 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1616 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1616 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1617 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1617 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1618 and 'yes' or 'no'))
1618 and 'yes' or 'no'))
1619 os.unlink('.debugfsinfo')
1619 os.unlink('.debugfsinfo')
1620
1620
1621 @command('debuggetbundle',
1621 @command('debuggetbundle',
1622 [('H', 'head', [], _('id of head node'), _('ID')),
1622 [('H', 'head', [], _('id of head node'), _('ID')),
1623 ('C', 'common', [], _('id of common node'), _('ID')),
1623 ('C', 'common', [], _('id of common node'), _('ID')),
1624 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1624 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1625 _('REPO FILE [-H|-C ID]...'))
1625 _('REPO FILE [-H|-C ID]...'))
1626 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1626 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1627 """retrieves a bundle from a repo
1627 """retrieves a bundle from a repo
1628
1628
1629 Every ID must be a full-length hex node id string. Saves the bundle to the
1629 Every ID must be a full-length hex node id string. Saves the bundle to the
1630 given file.
1630 given file.
1631 """
1631 """
1632 repo = hg.peer(ui, opts, repopath)
1632 repo = hg.peer(ui, opts, repopath)
1633 if not repo.capable('getbundle'):
1633 if not repo.capable('getbundle'):
1634 raise util.Abort("getbundle() not supported by target repository")
1634 raise util.Abort("getbundle() not supported by target repository")
1635 args = {}
1635 args = {}
1636 if common:
1636 if common:
1637 args['common'] = [bin(s) for s in common]
1637 args['common'] = [bin(s) for s in common]
1638 if head:
1638 if head:
1639 args['heads'] = [bin(s) for s in head]
1639 args['heads'] = [bin(s) for s in head]
1640 bundle = repo.getbundle('debug', **args)
1640 bundle = repo.getbundle('debug', **args)
1641
1641
1642 bundletype = opts.get('type', 'bzip2').lower()
1642 bundletype = opts.get('type', 'bzip2').lower()
1643 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1643 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1644 bundletype = btypes.get(bundletype)
1644 bundletype = btypes.get(bundletype)
1645 if bundletype not in changegroup.bundletypes:
1645 if bundletype not in changegroup.bundletypes:
1646 raise util.Abort(_('unknown bundle type specified with --type'))
1646 raise util.Abort(_('unknown bundle type specified with --type'))
1647 changegroup.writebundle(bundle, bundlepath, bundletype)
1647 changegroup.writebundle(bundle, bundlepath, bundletype)
1648
1648
1649 @command('debugignore', [], '')
1649 @command('debugignore', [], '')
1650 def debugignore(ui, repo, *values, **opts):
1650 def debugignore(ui, repo, *values, **opts):
1651 """display the combined ignore pattern"""
1651 """display the combined ignore pattern"""
1652 ignore = repo.dirstate._ignore
1652 ignore = repo.dirstate._ignore
1653 if hasattr(ignore, 'includepat'):
1653 if hasattr(ignore, 'includepat'):
1654 ui.write("%s\n" % ignore.includepat)
1654 ui.write("%s\n" % ignore.includepat)
1655 else:
1655 else:
1656 raise util.Abort(_("no ignore patterns found"))
1656 raise util.Abort(_("no ignore patterns found"))
1657
1657
1658 @command('debugindex',
1658 @command('debugindex',
1659 [('c', 'changelog', False, _('open changelog')),
1659 [('c', 'changelog', False, _('open changelog')),
1660 ('m', 'manifest', False, _('open manifest')),
1660 ('m', 'manifest', False, _('open manifest')),
1661 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1661 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1662 _('[-f FORMAT] -c|-m|FILE'))
1662 _('[-f FORMAT] -c|-m|FILE'))
1663 def debugindex(ui, repo, file_ = None, **opts):
1663 def debugindex(ui, repo, file_ = None, **opts):
1664 """dump the contents of an index file"""
1664 """dump the contents of an index file"""
1665 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1665 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1666 format = opts.get('format', 0)
1666 format = opts.get('format', 0)
1667 if format not in (0, 1):
1667 if format not in (0, 1):
1668 raise util.Abort(_("unknown format %d") % format)
1668 raise util.Abort(_("unknown format %d") % format)
1669
1669
1670 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1670 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1671 if generaldelta:
1671 if generaldelta:
1672 basehdr = ' delta'
1672 basehdr = ' delta'
1673 else:
1673 else:
1674 basehdr = ' base'
1674 basehdr = ' base'
1675
1675
1676 if format == 0:
1676 if format == 0:
1677 ui.write(" rev offset length " + basehdr + " linkrev"
1677 ui.write(" rev offset length " + basehdr + " linkrev"
1678 " nodeid p1 p2\n")
1678 " nodeid p1 p2\n")
1679 elif format == 1:
1679 elif format == 1:
1680 ui.write(" rev flag offset length"
1680 ui.write(" rev flag offset length"
1681 " size " + basehdr + " link p1 p2 nodeid\n")
1681 " size " + basehdr + " link p1 p2 nodeid\n")
1682
1682
1683 for i in r:
1683 for i in r:
1684 node = r.node(i)
1684 node = r.node(i)
1685 if generaldelta:
1685 if generaldelta:
1686 base = r.deltaparent(i)
1686 base = r.deltaparent(i)
1687 else:
1687 else:
1688 base = r.chainbase(i)
1688 base = r.chainbase(i)
1689 if format == 0:
1689 if format == 0:
1690 try:
1690 try:
1691 pp = r.parents(node)
1691 pp = r.parents(node)
1692 except:
1692 except:
1693 pp = [nullid, nullid]
1693 pp = [nullid, nullid]
1694 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1694 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1695 i, r.start(i), r.length(i), base, r.linkrev(i),
1695 i, r.start(i), r.length(i), base, r.linkrev(i),
1696 short(node), short(pp[0]), short(pp[1])))
1696 short(node), short(pp[0]), short(pp[1])))
1697 elif format == 1:
1697 elif format == 1:
1698 pr = r.parentrevs(i)
1698 pr = r.parentrevs(i)
1699 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1699 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1700 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1700 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1701 base, r.linkrev(i), pr[0], pr[1], short(node)))
1701 base, r.linkrev(i), pr[0], pr[1], short(node)))
1702
1702
1703 @command('debugindexdot', [], _('FILE'))
1703 @command('debugindexdot', [], _('FILE'))
1704 def debugindexdot(ui, repo, file_):
1704 def debugindexdot(ui, repo, file_):
1705 """dump an index DAG as a graphviz dot file"""
1705 """dump an index DAG as a graphviz dot file"""
1706 r = None
1706 r = None
1707 if repo:
1707 if repo:
1708 filelog = repo.file(file_)
1708 filelog = repo.file(file_)
1709 if len(filelog):
1709 if len(filelog):
1710 r = filelog
1710 r = filelog
1711 if not r:
1711 if not r:
1712 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1712 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1713 ui.write("digraph G {\n")
1713 ui.write("digraph G {\n")
1714 for i in r:
1714 for i in r:
1715 node = r.node(i)
1715 node = r.node(i)
1716 pp = r.parents(node)
1716 pp = r.parents(node)
1717 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1717 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1718 if pp[1] != nullid:
1718 if pp[1] != nullid:
1719 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1719 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1720 ui.write("}\n")
1720 ui.write("}\n")
1721
1721
1722 @command('debuginstall', [], '')
1722 @command('debuginstall', [], '')
1723 def debuginstall(ui):
1723 def debuginstall(ui):
1724 '''test Mercurial installation
1724 '''test Mercurial installation
1725
1725
1726 Returns 0 on success.
1726 Returns 0 on success.
1727 '''
1727 '''
1728
1728
1729 def writetemp(contents):
1729 def writetemp(contents):
1730 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1730 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1731 f = os.fdopen(fd, "wb")
1731 f = os.fdopen(fd, "wb")
1732 f.write(contents)
1732 f.write(contents)
1733 f.close()
1733 f.close()
1734 return name
1734 return name
1735
1735
1736 problems = 0
1736 problems = 0
1737
1737
1738 # encoding
1738 # encoding
1739 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1739 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1740 try:
1740 try:
1741 encoding.fromlocal("test")
1741 encoding.fromlocal("test")
1742 except util.Abort, inst:
1742 except util.Abort, inst:
1743 ui.write(" %s\n" % inst)
1743 ui.write(" %s\n" % inst)
1744 ui.write(_(" (check that your locale is properly set)\n"))
1744 ui.write(_(" (check that your locale is properly set)\n"))
1745 problems += 1
1745 problems += 1
1746
1746
1747 # compiled modules
1747 # compiled modules
1748 ui.status(_("Checking installed modules (%s)...\n")
1748 ui.status(_("Checking installed modules (%s)...\n")
1749 % os.path.dirname(__file__))
1749 % os.path.dirname(__file__))
1750 try:
1750 try:
1751 import bdiff, mpatch, base85, osutil
1751 import bdiff, mpatch, base85, osutil
1752 except Exception, inst:
1752 except Exception, inst:
1753 ui.write(" %s\n" % inst)
1753 ui.write(" %s\n" % inst)
1754 ui.write(_(" One or more extensions could not be found"))
1754 ui.write(_(" One or more extensions could not be found"))
1755 ui.write(_(" (check that you compiled the extensions)\n"))
1755 ui.write(_(" (check that you compiled the extensions)\n"))
1756 problems += 1
1756 problems += 1
1757
1757
1758 # templates
1758 # templates
1759 ui.status(_("Checking templates...\n"))
1759 ui.status(_("Checking templates...\n"))
1760 try:
1760 try:
1761 import templater
1761 import templater
1762 templater.templater(templater.templatepath("map-cmdline.default"))
1762 templater.templater(templater.templatepath("map-cmdline.default"))
1763 except Exception, inst:
1763 except Exception, inst:
1764 ui.write(" %s\n" % inst)
1764 ui.write(" %s\n" % inst)
1765 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1765 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1766 problems += 1
1766 problems += 1
1767
1767
1768 # editor
1768 # editor
1769 ui.status(_("Checking commit editor...\n"))
1769 ui.status(_("Checking commit editor...\n"))
1770 editor = ui.geteditor()
1770 editor = ui.geteditor()
1771 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1771 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1772 if not cmdpath:
1772 if not cmdpath:
1773 if editor == 'vi':
1773 if editor == 'vi':
1774 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1774 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1775 ui.write(_(" (specify a commit editor in your configuration"
1775 ui.write(_(" (specify a commit editor in your configuration"
1776 " file)\n"))
1776 " file)\n"))
1777 else:
1777 else:
1778 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1778 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1779 ui.write(_(" (specify a commit editor in your configuration"
1779 ui.write(_(" (specify a commit editor in your configuration"
1780 " file)\n"))
1780 " file)\n"))
1781 problems += 1
1781 problems += 1
1782
1782
1783 # check username
1783 # check username
1784 ui.status(_("Checking username...\n"))
1784 ui.status(_("Checking username...\n"))
1785 try:
1785 try:
1786 ui.username()
1786 ui.username()
1787 except util.Abort, e:
1787 except util.Abort, e:
1788 ui.write(" %s\n" % e)
1788 ui.write(" %s\n" % e)
1789 ui.write(_(" (specify a username in your configuration file)\n"))
1789 ui.write(_(" (specify a username in your configuration file)\n"))
1790 problems += 1
1790 problems += 1
1791
1791
1792 if not problems:
1792 if not problems:
1793 ui.status(_("No problems detected\n"))
1793 ui.status(_("No problems detected\n"))
1794 else:
1794 else:
1795 ui.write(_("%s problems detected,"
1795 ui.write(_("%s problems detected,"
1796 " please check your install!\n") % problems)
1796 " please check your install!\n") % problems)
1797
1797
1798 return problems
1798 return problems
1799
1799
1800 @command('debugknown', [], _('REPO ID...'))
1800 @command('debugknown', [], _('REPO ID...'))
1801 def debugknown(ui, repopath, *ids, **opts):
1801 def debugknown(ui, repopath, *ids, **opts):
1802 """test whether node ids are known to a repo
1802 """test whether node ids are known to a repo
1803
1803
1804 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1804 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1805 indicating unknown/known.
1805 indicating unknown/known.
1806 """
1806 """
1807 repo = hg.peer(ui, opts, repopath)
1807 repo = hg.peer(ui, opts, repopath)
1808 if not repo.capable('known'):
1808 if not repo.capable('known'):
1809 raise util.Abort("known() not supported by target repository")
1809 raise util.Abort("known() not supported by target repository")
1810 flags = repo.known([bin(s) for s in ids])
1810 flags = repo.known([bin(s) for s in ids])
1811 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1811 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1812
1812
1813 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1813 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1814 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1814 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1815 '''access the pushkey key/value protocol
1815 '''access the pushkey key/value protocol
1816
1816
1817 With two args, list the keys in the given namespace.
1817 With two args, list the keys in the given namespace.
1818
1818
1819 With five args, set a key to new if it currently is set to old.
1819 With five args, set a key to new if it currently is set to old.
1820 Reports success or failure.
1820 Reports success or failure.
1821 '''
1821 '''
1822
1822
1823 target = hg.peer(ui, {}, repopath)
1823 target = hg.peer(ui, {}, repopath)
1824 if keyinfo:
1824 if keyinfo:
1825 key, old, new = keyinfo
1825 key, old, new = keyinfo
1826 r = target.pushkey(namespace, key, old, new)
1826 r = target.pushkey(namespace, key, old, new)
1827 ui.status(str(r) + '\n')
1827 ui.status(str(r) + '\n')
1828 return not r
1828 return not r
1829 else:
1829 else:
1830 for k, v in target.listkeys(namespace).iteritems():
1830 for k, v in target.listkeys(namespace).iteritems():
1831 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1831 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1832 v.encode('string-escape')))
1832 v.encode('string-escape')))
1833
1833
1834 @command('debugrebuildstate',
1834 @command('debugrebuildstate',
1835 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1835 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1836 _('[-r REV] [REV]'))
1836 _('[-r REV] [REV]'))
1837 def debugrebuildstate(ui, repo, rev="tip"):
1837 def debugrebuildstate(ui, repo, rev="tip"):
1838 """rebuild the dirstate as it would look like for the given revision"""
1838 """rebuild the dirstate as it would look like for the given revision"""
1839 ctx = scmutil.revsingle(repo, rev)
1839 ctx = scmutil.revsingle(repo, rev)
1840 wlock = repo.wlock()
1840 wlock = repo.wlock()
1841 try:
1841 try:
1842 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1842 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1843 finally:
1843 finally:
1844 wlock.release()
1844 wlock.release()
1845
1845
1846 @command('debugrename',
1846 @command('debugrename',
1847 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1847 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1848 _('[-r REV] FILE'))
1848 _('[-r REV] FILE'))
1849 def debugrename(ui, repo, file1, *pats, **opts):
1849 def debugrename(ui, repo, file1, *pats, **opts):
1850 """dump rename information"""
1850 """dump rename information"""
1851
1851
1852 ctx = scmutil.revsingle(repo, opts.get('rev'))
1852 ctx = scmutil.revsingle(repo, opts.get('rev'))
1853 m = scmutil.match(repo, (file1,) + pats, opts)
1853 m = scmutil.match(repo, (file1,) + pats, opts)
1854 for abs in ctx.walk(m):
1854 for abs in ctx.walk(m):
1855 fctx = ctx[abs]
1855 fctx = ctx[abs]
1856 o = fctx.filelog().renamed(fctx.filenode())
1856 o = fctx.filelog().renamed(fctx.filenode())
1857 rel = m.rel(abs)
1857 rel = m.rel(abs)
1858 if o:
1858 if o:
1859 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1859 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1860 else:
1860 else:
1861 ui.write(_("%s not renamed\n") % rel)
1861 ui.write(_("%s not renamed\n") % rel)
1862
1862
1863 @command('debugrevlog',
1863 @command('debugrevlog',
1864 [('c', 'changelog', False, _('open changelog')),
1864 [('c', 'changelog', False, _('open changelog')),
1865 ('m', 'manifest', False, _('open manifest')),
1865 ('m', 'manifest', False, _('open manifest')),
1866 ('d', 'dump', False, _('dump index data'))],
1866 ('d', 'dump', False, _('dump index data'))],
1867 _('-c|-m|FILE'))
1867 _('-c|-m|FILE'))
1868 def debugrevlog(ui, repo, file_ = None, **opts):
1868 def debugrevlog(ui, repo, file_ = None, **opts):
1869 """show data and statistics about a revlog"""
1869 """show data and statistics about a revlog"""
1870 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1870 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1871
1871
1872 if opts.get("dump"):
1872 if opts.get("dump"):
1873 numrevs = len(r)
1873 numrevs = len(r)
1874 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1874 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1875 " rawsize totalsize compression heads\n")
1875 " rawsize totalsize compression heads\n")
1876 ts = 0
1876 ts = 0
1877 heads = set()
1877 heads = set()
1878 for rev in xrange(numrevs):
1878 for rev in xrange(numrevs):
1879 dbase = r.deltaparent(rev)
1879 dbase = r.deltaparent(rev)
1880 if dbase == -1:
1880 if dbase == -1:
1881 dbase = rev
1881 dbase = rev
1882 cbase = r.chainbase(rev)
1882 cbase = r.chainbase(rev)
1883 p1, p2 = r.parentrevs(rev)
1883 p1, p2 = r.parentrevs(rev)
1884 rs = r.rawsize(rev)
1884 rs = r.rawsize(rev)
1885 ts = ts + rs
1885 ts = ts + rs
1886 heads -= set(r.parentrevs(rev))
1886 heads -= set(r.parentrevs(rev))
1887 heads.add(rev)
1887 heads.add(rev)
1888 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1888 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1889 (rev, p1, p2, r.start(rev), r.end(rev),
1889 (rev, p1, p2, r.start(rev), r.end(rev),
1890 r.start(dbase), r.start(cbase),
1890 r.start(dbase), r.start(cbase),
1891 r.start(p1), r.start(p2),
1891 r.start(p1), r.start(p2),
1892 rs, ts, ts / r.end(rev), len(heads)))
1892 rs, ts, ts / r.end(rev), len(heads)))
1893 return 0
1893 return 0
1894
1894
1895 v = r.version
1895 v = r.version
1896 format = v & 0xFFFF
1896 format = v & 0xFFFF
1897 flags = []
1897 flags = []
1898 gdelta = False
1898 gdelta = False
1899 if v & revlog.REVLOGNGINLINEDATA:
1899 if v & revlog.REVLOGNGINLINEDATA:
1900 flags.append('inline')
1900 flags.append('inline')
1901 if v & revlog.REVLOGGENERALDELTA:
1901 if v & revlog.REVLOGGENERALDELTA:
1902 gdelta = True
1902 gdelta = True
1903 flags.append('generaldelta')
1903 flags.append('generaldelta')
1904 if not flags:
1904 if not flags:
1905 flags = ['(none)']
1905 flags = ['(none)']
1906
1906
1907 nummerges = 0
1907 nummerges = 0
1908 numfull = 0
1908 numfull = 0
1909 numprev = 0
1909 numprev = 0
1910 nump1 = 0
1910 nump1 = 0
1911 nump2 = 0
1911 nump2 = 0
1912 numother = 0
1912 numother = 0
1913 nump1prev = 0
1913 nump1prev = 0
1914 nump2prev = 0
1914 nump2prev = 0
1915 chainlengths = []
1915 chainlengths = []
1916
1916
1917 datasize = [None, 0, 0L]
1917 datasize = [None, 0, 0L]
1918 fullsize = [None, 0, 0L]
1918 fullsize = [None, 0, 0L]
1919 deltasize = [None, 0, 0L]
1919 deltasize = [None, 0, 0L]
1920
1920
1921 def addsize(size, l):
1921 def addsize(size, l):
1922 if l[0] is None or size < l[0]:
1922 if l[0] is None or size < l[0]:
1923 l[0] = size
1923 l[0] = size
1924 if size > l[1]:
1924 if size > l[1]:
1925 l[1] = size
1925 l[1] = size
1926 l[2] += size
1926 l[2] += size
1927
1927
1928 numrevs = len(r)
1928 numrevs = len(r)
1929 for rev in xrange(numrevs):
1929 for rev in xrange(numrevs):
1930 p1, p2 = r.parentrevs(rev)
1930 p1, p2 = r.parentrevs(rev)
1931 delta = r.deltaparent(rev)
1931 delta = r.deltaparent(rev)
1932 if format > 0:
1932 if format > 0:
1933 addsize(r.rawsize(rev), datasize)
1933 addsize(r.rawsize(rev), datasize)
1934 if p2 != nullrev:
1934 if p2 != nullrev:
1935 nummerges += 1
1935 nummerges += 1
1936 size = r.length(rev)
1936 size = r.length(rev)
1937 if delta == nullrev:
1937 if delta == nullrev:
1938 chainlengths.append(0)
1938 chainlengths.append(0)
1939 numfull += 1
1939 numfull += 1
1940 addsize(size, fullsize)
1940 addsize(size, fullsize)
1941 else:
1941 else:
1942 chainlengths.append(chainlengths[delta] + 1)
1942 chainlengths.append(chainlengths[delta] + 1)
1943 addsize(size, deltasize)
1943 addsize(size, deltasize)
1944 if delta == rev - 1:
1944 if delta == rev - 1:
1945 numprev += 1
1945 numprev += 1
1946 if delta == p1:
1946 if delta == p1:
1947 nump1prev += 1
1947 nump1prev += 1
1948 elif delta == p2:
1948 elif delta == p2:
1949 nump2prev += 1
1949 nump2prev += 1
1950 elif delta == p1:
1950 elif delta == p1:
1951 nump1 += 1
1951 nump1 += 1
1952 elif delta == p2:
1952 elif delta == p2:
1953 nump2 += 1
1953 nump2 += 1
1954 elif delta != nullrev:
1954 elif delta != nullrev:
1955 numother += 1
1955 numother += 1
1956
1956
1957 numdeltas = numrevs - numfull
1957 numdeltas = numrevs - numfull
1958 numoprev = numprev - nump1prev - nump2prev
1958 numoprev = numprev - nump1prev - nump2prev
1959 totalrawsize = datasize[2]
1959 totalrawsize = datasize[2]
1960 datasize[2] /= numrevs
1960 datasize[2] /= numrevs
1961 fulltotal = fullsize[2]
1961 fulltotal = fullsize[2]
1962 fullsize[2] /= numfull
1962 fullsize[2] /= numfull
1963 deltatotal = deltasize[2]
1963 deltatotal = deltasize[2]
1964 deltasize[2] /= numrevs - numfull
1964 deltasize[2] /= numrevs - numfull
1965 totalsize = fulltotal + deltatotal
1965 totalsize = fulltotal + deltatotal
1966 avgchainlen = sum(chainlengths) / numrevs
1966 avgchainlen = sum(chainlengths) / numrevs
1967 compratio = totalrawsize / totalsize
1967 compratio = totalrawsize / totalsize
1968
1968
1969 basedfmtstr = '%%%dd\n'
1969 basedfmtstr = '%%%dd\n'
1970 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1970 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1971
1971
1972 def dfmtstr(max):
1972 def dfmtstr(max):
1973 return basedfmtstr % len(str(max))
1973 return basedfmtstr % len(str(max))
1974 def pcfmtstr(max, padding=0):
1974 def pcfmtstr(max, padding=0):
1975 return basepcfmtstr % (len(str(max)), ' ' * padding)
1975 return basepcfmtstr % (len(str(max)), ' ' * padding)
1976
1976
1977 def pcfmt(value, total):
1977 def pcfmt(value, total):
1978 return (value, 100 * float(value) / total)
1978 return (value, 100 * float(value) / total)
1979
1979
1980 ui.write('format : %d\n' % format)
1980 ui.write('format : %d\n' % format)
1981 ui.write('flags : %s\n' % ', '.join(flags))
1981 ui.write('flags : %s\n' % ', '.join(flags))
1982
1982
1983 ui.write('\n')
1983 ui.write('\n')
1984 fmt = pcfmtstr(totalsize)
1984 fmt = pcfmtstr(totalsize)
1985 fmt2 = dfmtstr(totalsize)
1985 fmt2 = dfmtstr(totalsize)
1986 ui.write('revisions : ' + fmt2 % numrevs)
1986 ui.write('revisions : ' + fmt2 % numrevs)
1987 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1987 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1988 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1988 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1989 ui.write('revisions : ' + fmt2 % numrevs)
1989 ui.write('revisions : ' + fmt2 % numrevs)
1990 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1990 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1991 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1991 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1992 ui.write('revision size : ' + fmt2 % totalsize)
1992 ui.write('revision size : ' + fmt2 % totalsize)
1993 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1993 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1994 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
1994 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
1995
1995
1996 ui.write('\n')
1996 ui.write('\n')
1997 fmt = dfmtstr(max(avgchainlen, compratio))
1997 fmt = dfmtstr(max(avgchainlen, compratio))
1998 ui.write('avg chain length : ' + fmt % avgchainlen)
1998 ui.write('avg chain length : ' + fmt % avgchainlen)
1999 ui.write('compression ratio : ' + fmt % compratio)
1999 ui.write('compression ratio : ' + fmt % compratio)
2000
2000
2001 if format > 0:
2001 if format > 0:
2002 ui.write('\n')
2002 ui.write('\n')
2003 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2003 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2004 % tuple(datasize))
2004 % tuple(datasize))
2005 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2005 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2006 % tuple(fullsize))
2006 % tuple(fullsize))
2007 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2007 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2008 % tuple(deltasize))
2008 % tuple(deltasize))
2009
2009
2010 if numdeltas > 0:
2010 if numdeltas > 0:
2011 ui.write('\n')
2011 ui.write('\n')
2012 fmt = pcfmtstr(numdeltas)
2012 fmt = pcfmtstr(numdeltas)
2013 fmt2 = pcfmtstr(numdeltas, 4)
2013 fmt2 = pcfmtstr(numdeltas, 4)
2014 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2014 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2015 if numprev > 0:
2015 if numprev > 0:
2016 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2016 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2017 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2017 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2018 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2018 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2019 if gdelta:
2019 if gdelta:
2020 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2020 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2021 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2021 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2022 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2022 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2023
2023
2024 @command('debugrevspec', [], ('REVSPEC'))
2024 @command('debugrevspec', [], ('REVSPEC'))
2025 def debugrevspec(ui, repo, expr):
2025 def debugrevspec(ui, repo, expr):
2026 '''parse and apply a revision specification'''
2026 '''parse and apply a revision specification'''
2027 if ui.verbose:
2027 if ui.verbose:
2028 tree = revset.parse(expr)[0]
2028 tree = revset.parse(expr)[0]
2029 ui.note(tree, "\n")
2029 ui.note(tree, "\n")
2030 newtree = revset.findaliases(ui, tree)
2030 newtree = revset.findaliases(ui, tree)
2031 if newtree != tree:
2031 if newtree != tree:
2032 ui.note(newtree, "\n")
2032 ui.note(newtree, "\n")
2033 func = revset.match(ui, expr)
2033 func = revset.match(ui, expr)
2034 for c in func(repo, range(len(repo))):
2034 for c in func(repo, range(len(repo))):
2035 ui.write("%s\n" % c)
2035 ui.write("%s\n" % c)
2036
2036
2037 @command('debugsetparents', [], _('REV1 [REV2]'))
2037 @command('debugsetparents', [], _('REV1 [REV2]'))
2038 def debugsetparents(ui, repo, rev1, rev2=None):
2038 def debugsetparents(ui, repo, rev1, rev2=None):
2039 """manually set the parents of the current working directory
2039 """manually set the parents of the current working directory
2040
2040
2041 This is useful for writing repository conversion tools, but should
2041 This is useful for writing repository conversion tools, but should
2042 be used with care.
2042 be used with care.
2043
2043
2044 Returns 0 on success.
2044 Returns 0 on success.
2045 """
2045 """
2046
2046
2047 r1 = scmutil.revsingle(repo, rev1).node()
2047 r1 = scmutil.revsingle(repo, rev1).node()
2048 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2048 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2049
2049
2050 wlock = repo.wlock()
2050 wlock = repo.wlock()
2051 try:
2051 try:
2052 repo.dirstate.setparents(r1, r2)
2052 repo.dirstate.setparents(r1, r2)
2053 finally:
2053 finally:
2054 wlock.release()
2054 wlock.release()
2055
2055
2056 @command('debugstate',
2056 @command('debugstate',
2057 [('', 'nodates', None, _('do not display the saved mtime')),
2057 [('', 'nodates', None, _('do not display the saved mtime')),
2058 ('', 'datesort', None, _('sort by saved mtime'))],
2058 ('', 'datesort', None, _('sort by saved mtime'))],
2059 _('[OPTION]...'))
2059 _('[OPTION]...'))
2060 def debugstate(ui, repo, nodates=None, datesort=None):
2060 def debugstate(ui, repo, nodates=None, datesort=None):
2061 """show the contents of the current dirstate"""
2061 """show the contents of the current dirstate"""
2062 timestr = ""
2062 timestr = ""
2063 showdate = not nodates
2063 showdate = not nodates
2064 if datesort:
2064 if datesort:
2065 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2065 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2066 else:
2066 else:
2067 keyfunc = None # sort by filename
2067 keyfunc = None # sort by filename
2068 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2068 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2069 if showdate:
2069 if showdate:
2070 if ent[3] == -1:
2070 if ent[3] == -1:
2071 # Pad or slice to locale representation
2071 # Pad or slice to locale representation
2072 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2072 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2073 time.localtime(0)))
2073 time.localtime(0)))
2074 timestr = 'unset'
2074 timestr = 'unset'
2075 timestr = (timestr[:locale_len] +
2075 timestr = (timestr[:locale_len] +
2076 ' ' * (locale_len - len(timestr)))
2076 ' ' * (locale_len - len(timestr)))
2077 else:
2077 else:
2078 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2078 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2079 time.localtime(ent[3]))
2079 time.localtime(ent[3]))
2080 if ent[1] & 020000:
2080 if ent[1] & 020000:
2081 mode = 'lnk'
2081 mode = 'lnk'
2082 else:
2082 else:
2083 mode = '%3o' % (ent[1] & 0777)
2083 mode = '%3o' % (ent[1] & 0777)
2084 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2084 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2085 for f in repo.dirstate.copies():
2085 for f in repo.dirstate.copies():
2086 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2086 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2087
2087
2088 @command('debugsub',
2088 @command('debugsub',
2089 [('r', 'rev', '',
2089 [('r', 'rev', '',
2090 _('revision to check'), _('REV'))],
2090 _('revision to check'), _('REV'))],
2091 _('[-r REV] [REV]'))
2091 _('[-r REV] [REV]'))
2092 def debugsub(ui, repo, rev=None):
2092 def debugsub(ui, repo, rev=None):
2093 ctx = scmutil.revsingle(repo, rev, None)
2093 ctx = scmutil.revsingle(repo, rev, None)
2094 for k, v in sorted(ctx.substate.items()):
2094 for k, v in sorted(ctx.substate.items()):
2095 ui.write('path %s\n' % k)
2095 ui.write('path %s\n' % k)
2096 ui.write(' source %s\n' % v[0])
2096 ui.write(' source %s\n' % v[0])
2097 ui.write(' revision %s\n' % v[1])
2097 ui.write(' revision %s\n' % v[1])
2098
2098
2099 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2099 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2100 def debugwalk(ui, repo, *pats, **opts):
2100 def debugwalk(ui, repo, *pats, **opts):
2101 """show how files match on given patterns"""
2101 """show how files match on given patterns"""
2102 m = scmutil.match(repo, pats, opts)
2102 m = scmutil.match(repo, pats, opts)
2103 items = list(repo.walk(m))
2103 items = list(repo.walk(m))
2104 if not items:
2104 if not items:
2105 return
2105 return
2106 fmt = 'f %%-%ds %%-%ds %%s' % (
2106 fmt = 'f %%-%ds %%-%ds %%s' % (
2107 max([len(abs) for abs in items]),
2107 max([len(abs) for abs in items]),
2108 max([len(m.rel(abs)) for abs in items]))
2108 max([len(m.rel(abs)) for abs in items]))
2109 for abs in items:
2109 for abs in items:
2110 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2110 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2111 ui.write("%s\n" % line.rstrip())
2111 ui.write("%s\n" % line.rstrip())
2112
2112
2113 @command('debugwireargs',
2113 @command('debugwireargs',
2114 [('', 'three', '', 'three'),
2114 [('', 'three', '', 'three'),
2115 ('', 'four', '', 'four'),
2115 ('', 'four', '', 'four'),
2116 ('', 'five', '', 'five'),
2116 ('', 'five', '', 'five'),
2117 ] + remoteopts,
2117 ] + remoteopts,
2118 _('REPO [OPTIONS]... [ONE [TWO]]'))
2118 _('REPO [OPTIONS]... [ONE [TWO]]'))
2119 def debugwireargs(ui, repopath, *vals, **opts):
2119 def debugwireargs(ui, repopath, *vals, **opts):
2120 repo = hg.peer(ui, opts, repopath)
2120 repo = hg.peer(ui, opts, repopath)
2121 for opt in remoteopts:
2121 for opt in remoteopts:
2122 del opts[opt[1]]
2122 del opts[opt[1]]
2123 args = {}
2123 args = {}
2124 for k, v in opts.iteritems():
2124 for k, v in opts.iteritems():
2125 if v:
2125 if v:
2126 args[k] = v
2126 args[k] = v
2127 # run twice to check that we don't mess up the stream for the next command
2127 # run twice to check that we don't mess up the stream for the next command
2128 res1 = repo.debugwireargs(*vals, **args)
2128 res1 = repo.debugwireargs(*vals, **args)
2129 res2 = repo.debugwireargs(*vals, **args)
2129 res2 = repo.debugwireargs(*vals, **args)
2130 ui.write("%s\n" % res1)
2130 ui.write("%s\n" % res1)
2131 if res1 != res2:
2131 if res1 != res2:
2132 ui.warn("%s\n" % res2)
2132 ui.warn("%s\n" % res2)
2133
2133
2134 @command('^diff',
2134 @command('^diff',
2135 [('r', 'rev', [], _('revision'), _('REV')),
2135 [('r', 'rev', [], _('revision'), _('REV')),
2136 ('c', 'change', '', _('change made by revision'), _('REV'))
2136 ('c', 'change', '', _('change made by revision'), _('REV'))
2137 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2137 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2138 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2138 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2139 def diff(ui, repo, *pats, **opts):
2139 def diff(ui, repo, *pats, **opts):
2140 """diff repository (or selected files)
2140 """diff repository (or selected files)
2141
2141
2142 Show differences between revisions for the specified files.
2142 Show differences between revisions for the specified files.
2143
2143
2144 Differences between files are shown using the unified diff format.
2144 Differences between files are shown using the unified diff format.
2145
2145
2146 .. note::
2146 .. note::
2147 diff may generate unexpected results for merges, as it will
2147 diff may generate unexpected results for merges, as it will
2148 default to comparing against the working directory's first
2148 default to comparing against the working directory's first
2149 parent changeset if no revisions are specified.
2149 parent changeset if no revisions are specified.
2150
2150
2151 When two revision arguments are given, then changes are shown
2151 When two revision arguments are given, then changes are shown
2152 between those revisions. If only one revision is specified then
2152 between those revisions. If only one revision is specified then
2153 that revision is compared to the working directory, and, when no
2153 that revision is compared to the working directory, and, when no
2154 revisions are specified, the working directory files are compared
2154 revisions are specified, the working directory files are compared
2155 to its parent.
2155 to its parent.
2156
2156
2157 Alternatively you can specify -c/--change with a revision to see
2157 Alternatively you can specify -c/--change with a revision to see
2158 the changes in that changeset relative to its first parent.
2158 the changes in that changeset relative to its first parent.
2159
2159
2160 Without the -a/--text option, diff will avoid generating diffs of
2160 Without the -a/--text option, diff will avoid generating diffs of
2161 files it detects as binary. With -a, diff will generate a diff
2161 files it detects as binary. With -a, diff will generate a diff
2162 anyway, probably with undesirable results.
2162 anyway, probably with undesirable results.
2163
2163
2164 Use the -g/--git option to generate diffs in the git extended diff
2164 Use the -g/--git option to generate diffs in the git extended diff
2165 format. For more information, read :hg:`help diffs`.
2165 format. For more information, read :hg:`help diffs`.
2166
2166
2167 Returns 0 on success.
2167 Returns 0 on success.
2168 """
2168 """
2169
2169
2170 revs = opts.get('rev')
2170 revs = opts.get('rev')
2171 change = opts.get('change')
2171 change = opts.get('change')
2172 stat = opts.get('stat')
2172 stat = opts.get('stat')
2173 reverse = opts.get('reverse')
2173 reverse = opts.get('reverse')
2174
2174
2175 if revs and change:
2175 if revs and change:
2176 msg = _('cannot specify --rev and --change at the same time')
2176 msg = _('cannot specify --rev and --change at the same time')
2177 raise util.Abort(msg)
2177 raise util.Abort(msg)
2178 elif change:
2178 elif change:
2179 node2 = scmutil.revsingle(repo, change, None).node()
2179 node2 = scmutil.revsingle(repo, change, None).node()
2180 node1 = repo[node2].p1().node()
2180 node1 = repo[node2].p1().node()
2181 else:
2181 else:
2182 node1, node2 = scmutil.revpair(repo, revs)
2182 node1, node2 = scmutil.revpair(repo, revs)
2183
2183
2184 if reverse:
2184 if reverse:
2185 node1, node2 = node2, node1
2185 node1, node2 = node2, node1
2186
2186
2187 diffopts = patch.diffopts(ui, opts)
2187 diffopts = patch.diffopts(ui, opts)
2188 m = scmutil.match(repo, pats, opts)
2188 m = scmutil.match(repo, pats, opts)
2189 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2189 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2190 listsubrepos=opts.get('subrepos'))
2190 listsubrepos=opts.get('subrepos'))
2191
2191
2192 @command('^export',
2192 @command('^export',
2193 [('o', 'output', '',
2193 [('o', 'output', '',
2194 _('print output to file with formatted name'), _('FORMAT')),
2194 _('print output to file with formatted name'), _('FORMAT')),
2195 ('', 'switch-parent', None, _('diff against the second parent')),
2195 ('', 'switch-parent', None, _('diff against the second parent')),
2196 ('r', 'rev', [], _('revisions to export'), _('REV')),
2196 ('r', 'rev', [], _('revisions to export'), _('REV')),
2197 ] + diffopts,
2197 ] + diffopts,
2198 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2198 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2199 def export(ui, repo, *changesets, **opts):
2199 def export(ui, repo, *changesets, **opts):
2200 """dump the header and diffs for one or more changesets
2200 """dump the header and diffs for one or more changesets
2201
2201
2202 Print the changeset header and diffs for one or more revisions.
2202 Print the changeset header and diffs for one or more revisions.
2203
2203
2204 The information shown in the changeset header is: author, date,
2204 The information shown in the changeset header is: author, date,
2205 branch name (if non-default), changeset hash, parent(s) and commit
2205 branch name (if non-default), changeset hash, parent(s) and commit
2206 comment.
2206 comment.
2207
2207
2208 .. note::
2208 .. note::
2209 export may generate unexpected diff output for merge
2209 export may generate unexpected diff output for merge
2210 changesets, as it will compare the merge changeset against its
2210 changesets, as it will compare the merge changeset against its
2211 first parent only.
2211 first parent only.
2212
2212
2213 Output may be to a file, in which case the name of the file is
2213 Output may be to a file, in which case the name of the file is
2214 given using a format string. The formatting rules are as follows:
2214 given using a format string. The formatting rules are as follows:
2215
2215
2216 :``%%``: literal "%" character
2216 :``%%``: literal "%" character
2217 :``%H``: changeset hash (40 hexadecimal digits)
2217 :``%H``: changeset hash (40 hexadecimal digits)
2218 :``%N``: number of patches being generated
2218 :``%N``: number of patches being generated
2219 :``%R``: changeset revision number
2219 :``%R``: changeset revision number
2220 :``%b``: basename of the exporting repository
2220 :``%b``: basename of the exporting repository
2221 :``%h``: short-form changeset hash (12 hexadecimal digits)
2221 :``%h``: short-form changeset hash (12 hexadecimal digits)
2222 :``%n``: zero-padded sequence number, starting at 1
2222 :``%n``: zero-padded sequence number, starting at 1
2223 :``%r``: zero-padded changeset revision number
2223 :``%r``: zero-padded changeset revision number
2224
2224
2225 Without the -a/--text option, export will avoid generating diffs
2225 Without the -a/--text option, export will avoid generating diffs
2226 of files it detects as binary. With -a, export will generate a
2226 of files it detects as binary. With -a, export will generate a
2227 diff anyway, probably with undesirable results.
2227 diff anyway, probably with undesirable results.
2228
2228
2229 Use the -g/--git option to generate diffs in the git extended diff
2229 Use the -g/--git option to generate diffs in the git extended diff
2230 format. See :hg:`help diffs` for more information.
2230 format. See :hg:`help diffs` for more information.
2231
2231
2232 With the --switch-parent option, the diff will be against the
2232 With the --switch-parent option, the diff will be against the
2233 second parent. It can be useful to review a merge.
2233 second parent. It can be useful to review a merge.
2234
2234
2235 Returns 0 on success.
2235 Returns 0 on success.
2236 """
2236 """
2237 changesets += tuple(opts.get('rev', []))
2237 changesets += tuple(opts.get('rev', []))
2238 if not changesets:
2238 if not changesets:
2239 raise util.Abort(_("export requires at least one changeset"))
2239 raise util.Abort(_("export requires at least one changeset"))
2240 revs = scmutil.revrange(repo, changesets)
2240 revs = scmutil.revrange(repo, changesets)
2241 if len(revs) > 1:
2241 if len(revs) > 1:
2242 ui.note(_('exporting patches:\n'))
2242 ui.note(_('exporting patches:\n'))
2243 else:
2243 else:
2244 ui.note(_('exporting patch:\n'))
2244 ui.note(_('exporting patch:\n'))
2245 cmdutil.export(repo, revs, template=opts.get('output'),
2245 cmdutil.export(repo, revs, template=opts.get('output'),
2246 switch_parent=opts.get('switch_parent'),
2246 switch_parent=opts.get('switch_parent'),
2247 opts=patch.diffopts(ui, opts))
2247 opts=patch.diffopts(ui, opts))
2248
2248
2249 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2249 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2250 def forget(ui, repo, *pats, **opts):
2250 def forget(ui, repo, *pats, **opts):
2251 """forget the specified files on the next commit
2251 """forget the specified files on the next commit
2252
2252
2253 Mark the specified files so they will no longer be tracked
2253 Mark the specified files so they will no longer be tracked
2254 after the next commit.
2254 after the next commit.
2255
2255
2256 This only removes files from the current branch, not from the
2256 This only removes files from the current branch, not from the
2257 entire project history, and it does not delete them from the
2257 entire project history, and it does not delete them from the
2258 working directory.
2258 working directory.
2259
2259
2260 To undo a forget before the next commit, see :hg:`add`.
2260 To undo a forget before the next commit, see :hg:`add`.
2261
2261
2262 Returns 0 on success.
2262 Returns 0 on success.
2263 """
2263 """
2264
2264
2265 if not pats:
2265 if not pats:
2266 raise util.Abort(_('no files specified'))
2266 raise util.Abort(_('no files specified'))
2267
2267
2268 m = scmutil.match(repo, pats, opts)
2268 m = scmutil.match(repo, pats, opts)
2269 s = repo.status(match=m, clean=True)
2269 s = repo.status(match=m, clean=True)
2270 forget = sorted(s[0] + s[1] + s[3] + s[6])
2270 forget = sorted(s[0] + s[1] + s[3] + s[6])
2271 errs = 0
2271 errs = 0
2272
2272
2273 for f in m.files():
2273 for f in m.files():
2274 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2274 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2275 if os.path.exists(m.rel(f)):
2275 if os.path.exists(m.rel(f)):
2276 ui.warn(_('not removing %s: file is already untracked\n')
2276 ui.warn(_('not removing %s: file is already untracked\n')
2277 % m.rel(f))
2277 % m.rel(f))
2278 errs = 1
2278 errs = 1
2279
2279
2280 for f in forget:
2280 for f in forget:
2281 if ui.verbose or not m.exact(f):
2281 if ui.verbose or not m.exact(f):
2282 ui.status(_('removing %s\n') % m.rel(f))
2282 ui.status(_('removing %s\n') % m.rel(f))
2283
2283
2284 repo[None].forget(forget)
2284 repo[None].forget(forget)
2285 return errs
2285 return errs
2286
2286
2287 @command('grep',
2287 @command('grep',
2288 [('0', 'print0', None, _('end fields with NUL')),
2288 [('0', 'print0', None, _('end fields with NUL')),
2289 ('', 'all', None, _('print all revisions that match')),
2289 ('', 'all', None, _('print all revisions that match')),
2290 ('a', 'text', None, _('treat all files as text')),
2290 ('a', 'text', None, _('treat all files as text')),
2291 ('f', 'follow', None,
2291 ('f', 'follow', None,
2292 _('follow changeset history,'
2292 _('follow changeset history,'
2293 ' or file history across copies and renames')),
2293 ' or file history across copies and renames')),
2294 ('i', 'ignore-case', None, _('ignore case when matching')),
2294 ('i', 'ignore-case', None, _('ignore case when matching')),
2295 ('l', 'files-with-matches', None,
2295 ('l', 'files-with-matches', None,
2296 _('print only filenames and revisions that match')),
2296 _('print only filenames and revisions that match')),
2297 ('n', 'line-number', None, _('print matching line numbers')),
2297 ('n', 'line-number', None, _('print matching line numbers')),
2298 ('r', 'rev', [],
2298 ('r', 'rev', [],
2299 _('only search files changed within revision range'), _('REV')),
2299 _('only search files changed within revision range'), _('REV')),
2300 ('u', 'user', None, _('list the author (long with -v)')),
2300 ('u', 'user', None, _('list the author (long with -v)')),
2301 ('d', 'date', None, _('list the date (short with -q)')),
2301 ('d', 'date', None, _('list the date (short with -q)')),
2302 ] + walkopts,
2302 ] + walkopts,
2303 _('[OPTION]... PATTERN [FILE]...'))
2303 _('[OPTION]... PATTERN [FILE]...'))
2304 def grep(ui, repo, pattern, *pats, **opts):
2304 def grep(ui, repo, pattern, *pats, **opts):
2305 """search for a pattern in specified files and revisions
2305 """search for a pattern in specified files and revisions
2306
2306
2307 Search revisions of files for a regular expression.
2307 Search revisions of files for a regular expression.
2308
2308
2309 This command behaves differently than Unix grep. It only accepts
2309 This command behaves differently than Unix grep. It only accepts
2310 Python/Perl regexps. It searches repository history, not the
2310 Python/Perl regexps. It searches repository history, not the
2311 working directory. It always prints the revision number in which a
2311 working directory. It always prints the revision number in which a
2312 match appears.
2312 match appears.
2313
2313
2314 By default, grep only prints output for the first revision of a
2314 By default, grep only prints output for the first revision of a
2315 file in which it finds a match. To get it to print every revision
2315 file in which it finds a match. To get it to print every revision
2316 that contains a change in match status ("-" for a match that
2316 that contains a change in match status ("-" for a match that
2317 becomes a non-match, or "+" for a non-match that becomes a match),
2317 becomes a non-match, or "+" for a non-match that becomes a match),
2318 use the --all flag.
2318 use the --all flag.
2319
2319
2320 Returns 0 if a match is found, 1 otherwise.
2320 Returns 0 if a match is found, 1 otherwise.
2321 """
2321 """
2322 reflags = 0
2322 reflags = 0
2323 if opts.get('ignore_case'):
2323 if opts.get('ignore_case'):
2324 reflags |= re.I
2324 reflags |= re.I
2325 try:
2325 try:
2326 regexp = re.compile(pattern, reflags)
2326 regexp = re.compile(pattern, reflags)
2327 except re.error, inst:
2327 except re.error, inst:
2328 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2328 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2329 return 1
2329 return 1
2330 sep, eol = ':', '\n'
2330 sep, eol = ':', '\n'
2331 if opts.get('print0'):
2331 if opts.get('print0'):
2332 sep = eol = '\0'
2332 sep = eol = '\0'
2333
2333
2334 getfile = util.lrucachefunc(repo.file)
2334 getfile = util.lrucachefunc(repo.file)
2335
2335
2336 def matchlines(body):
2336 def matchlines(body):
2337 begin = 0
2337 begin = 0
2338 linenum = 0
2338 linenum = 0
2339 while True:
2339 while True:
2340 match = regexp.search(body, begin)
2340 match = regexp.search(body, begin)
2341 if not match:
2341 if not match:
2342 break
2342 break
2343 mstart, mend = match.span()
2343 mstart, mend = match.span()
2344 linenum += body.count('\n', begin, mstart) + 1
2344 linenum += body.count('\n', begin, mstart) + 1
2345 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2345 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2346 begin = body.find('\n', mend) + 1 or len(body)
2346 begin = body.find('\n', mend) + 1 or len(body)
2347 lend = begin - 1
2347 lend = begin - 1
2348 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2348 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2349
2349
2350 class linestate(object):
2350 class linestate(object):
2351 def __init__(self, line, linenum, colstart, colend):
2351 def __init__(self, line, linenum, colstart, colend):
2352 self.line = line
2352 self.line = line
2353 self.linenum = linenum
2353 self.linenum = linenum
2354 self.colstart = colstart
2354 self.colstart = colstart
2355 self.colend = colend
2355 self.colend = colend
2356
2356
2357 def __hash__(self):
2357 def __hash__(self):
2358 return hash((self.linenum, self.line))
2358 return hash((self.linenum, self.line))
2359
2359
2360 def __eq__(self, other):
2360 def __eq__(self, other):
2361 return self.line == other.line
2361 return self.line == other.line
2362
2362
2363 matches = {}
2363 matches = {}
2364 copies = {}
2364 copies = {}
2365 def grepbody(fn, rev, body):
2365 def grepbody(fn, rev, body):
2366 matches[rev].setdefault(fn, [])
2366 matches[rev].setdefault(fn, [])
2367 m = matches[rev][fn]
2367 m = matches[rev][fn]
2368 for lnum, cstart, cend, line in matchlines(body):
2368 for lnum, cstart, cend, line in matchlines(body):
2369 s = linestate(line, lnum, cstart, cend)
2369 s = linestate(line, lnum, cstart, cend)
2370 m.append(s)
2370 m.append(s)
2371
2371
2372 def difflinestates(a, b):
2372 def difflinestates(a, b):
2373 sm = difflib.SequenceMatcher(None, a, b)
2373 sm = difflib.SequenceMatcher(None, a, b)
2374 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2374 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2375 if tag == 'insert':
2375 if tag == 'insert':
2376 for i in xrange(blo, bhi):
2376 for i in xrange(blo, bhi):
2377 yield ('+', b[i])
2377 yield ('+', b[i])
2378 elif tag == 'delete':
2378 elif tag == 'delete':
2379 for i in xrange(alo, ahi):
2379 for i in xrange(alo, ahi):
2380 yield ('-', a[i])
2380 yield ('-', a[i])
2381 elif tag == 'replace':
2381 elif tag == 'replace':
2382 for i in xrange(alo, ahi):
2382 for i in xrange(alo, ahi):
2383 yield ('-', a[i])
2383 yield ('-', a[i])
2384 for i in xrange(blo, bhi):
2384 for i in xrange(blo, bhi):
2385 yield ('+', b[i])
2385 yield ('+', b[i])
2386
2386
2387 def display(fn, ctx, pstates, states):
2387 def display(fn, ctx, pstates, states):
2388 rev = ctx.rev()
2388 rev = ctx.rev()
2389 datefunc = ui.quiet and util.shortdate or util.datestr
2389 datefunc = ui.quiet and util.shortdate or util.datestr
2390 found = False
2390 found = False
2391 filerevmatches = {}
2391 filerevmatches = {}
2392 def binary():
2392 def binary():
2393 flog = getfile(fn)
2393 flog = getfile(fn)
2394 return util.binary(flog.read(ctx.filenode(fn)))
2394 return util.binary(flog.read(ctx.filenode(fn)))
2395
2395
2396 if opts.get('all'):
2396 if opts.get('all'):
2397 iter = difflinestates(pstates, states)
2397 iter = difflinestates(pstates, states)
2398 else:
2398 else:
2399 iter = [('', l) for l in states]
2399 iter = [('', l) for l in states]
2400 for change, l in iter:
2400 for change, l in iter:
2401 cols = [fn, str(rev)]
2401 cols = [fn, str(rev)]
2402 before, match, after = None, None, None
2402 before, match, after = None, None, None
2403 if opts.get('line_number'):
2403 if opts.get('line_number'):
2404 cols.append(str(l.linenum))
2404 cols.append(str(l.linenum))
2405 if opts.get('all'):
2405 if opts.get('all'):
2406 cols.append(change)
2406 cols.append(change)
2407 if opts.get('user'):
2407 if opts.get('user'):
2408 cols.append(ui.shortuser(ctx.user()))
2408 cols.append(ui.shortuser(ctx.user()))
2409 if opts.get('date'):
2409 if opts.get('date'):
2410 cols.append(datefunc(ctx.date()))
2410 cols.append(datefunc(ctx.date()))
2411 if opts.get('files_with_matches'):
2411 if opts.get('files_with_matches'):
2412 c = (fn, rev)
2412 c = (fn, rev)
2413 if c in filerevmatches:
2413 if c in filerevmatches:
2414 continue
2414 continue
2415 filerevmatches[c] = 1
2415 filerevmatches[c] = 1
2416 else:
2416 else:
2417 before = l.line[:l.colstart]
2417 before = l.line[:l.colstart]
2418 match = l.line[l.colstart:l.colend]
2418 match = l.line[l.colstart:l.colend]
2419 after = l.line[l.colend:]
2419 after = l.line[l.colend:]
2420 ui.write(sep.join(cols))
2420 ui.write(sep.join(cols))
2421 if before is not None:
2421 if before is not None:
2422 if not opts.get('text') and binary():
2422 if not opts.get('text') and binary():
2423 ui.write(sep + " Binary file matches")
2423 ui.write(sep + " Binary file matches")
2424 else:
2424 else:
2425 ui.write(sep + before)
2425 ui.write(sep + before)
2426 ui.write(match, label='grep.match')
2426 ui.write(match, label='grep.match')
2427 ui.write(after)
2427 ui.write(after)
2428 ui.write(eol)
2428 ui.write(eol)
2429 found = True
2429 found = True
2430 return found
2430 return found
2431
2431
2432 skip = {}
2432 skip = {}
2433 revfiles = {}
2433 revfiles = {}
2434 matchfn = scmutil.match(repo, pats, opts)
2434 matchfn = scmutil.match(repo, pats, opts)
2435 found = False
2435 found = False
2436 follow = opts.get('follow')
2436 follow = opts.get('follow')
2437
2437
2438 def prep(ctx, fns):
2438 def prep(ctx, fns):
2439 rev = ctx.rev()
2439 rev = ctx.rev()
2440 pctx = ctx.p1()
2440 pctx = ctx.p1()
2441 parent = pctx.rev()
2441 parent = pctx.rev()
2442 matches.setdefault(rev, {})
2442 matches.setdefault(rev, {})
2443 matches.setdefault(parent, {})
2443 matches.setdefault(parent, {})
2444 files = revfiles.setdefault(rev, [])
2444 files = revfiles.setdefault(rev, [])
2445 for fn in fns:
2445 for fn in fns:
2446 flog = getfile(fn)
2446 flog = getfile(fn)
2447 try:
2447 try:
2448 fnode = ctx.filenode(fn)
2448 fnode = ctx.filenode(fn)
2449 except error.LookupError:
2449 except error.LookupError:
2450 continue
2450 continue
2451
2451
2452 copied = flog.renamed(fnode)
2452 copied = flog.renamed(fnode)
2453 copy = follow and copied and copied[0]
2453 copy = follow and copied and copied[0]
2454 if copy:
2454 if copy:
2455 copies.setdefault(rev, {})[fn] = copy
2455 copies.setdefault(rev, {})[fn] = copy
2456 if fn in skip:
2456 if fn in skip:
2457 if copy:
2457 if copy:
2458 skip[copy] = True
2458 skip[copy] = True
2459 continue
2459 continue
2460 files.append(fn)
2460 files.append(fn)
2461
2461
2462 if fn not in matches[rev]:
2462 if fn not in matches[rev]:
2463 grepbody(fn, rev, flog.read(fnode))
2463 grepbody(fn, rev, flog.read(fnode))
2464
2464
2465 pfn = copy or fn
2465 pfn = copy or fn
2466 if pfn not in matches[parent]:
2466 if pfn not in matches[parent]:
2467 try:
2467 try:
2468 fnode = pctx.filenode(pfn)
2468 fnode = pctx.filenode(pfn)
2469 grepbody(pfn, parent, flog.read(fnode))
2469 grepbody(pfn, parent, flog.read(fnode))
2470 except error.LookupError:
2470 except error.LookupError:
2471 pass
2471 pass
2472
2472
2473 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2473 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2474 rev = ctx.rev()
2474 rev = ctx.rev()
2475 parent = ctx.p1().rev()
2475 parent = ctx.p1().rev()
2476 for fn in sorted(revfiles.get(rev, [])):
2476 for fn in sorted(revfiles.get(rev, [])):
2477 states = matches[rev][fn]
2477 states = matches[rev][fn]
2478 copy = copies.get(rev, {}).get(fn)
2478 copy = copies.get(rev, {}).get(fn)
2479 if fn in skip:
2479 if fn in skip:
2480 if copy:
2480 if copy:
2481 skip[copy] = True
2481 skip[copy] = True
2482 continue
2482 continue
2483 pstates = matches.get(parent, {}).get(copy or fn, [])
2483 pstates = matches.get(parent, {}).get(copy or fn, [])
2484 if pstates or states:
2484 if pstates or states:
2485 r = display(fn, ctx, pstates, states)
2485 r = display(fn, ctx, pstates, states)
2486 found = found or r
2486 found = found or r
2487 if r and not opts.get('all'):
2487 if r and not opts.get('all'):
2488 skip[fn] = True
2488 skip[fn] = True
2489 if copy:
2489 if copy:
2490 skip[copy] = True
2490 skip[copy] = True
2491 del matches[rev]
2491 del matches[rev]
2492 del revfiles[rev]
2492 del revfiles[rev]
2493
2493
2494 return not found
2494 return not found
2495
2495
2496 @command('heads',
2496 @command('heads',
2497 [('r', 'rev', '',
2497 [('r', 'rev', '',
2498 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2498 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2499 ('t', 'topo', False, _('show topological heads only')),
2499 ('t', 'topo', False, _('show topological heads only')),
2500 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2500 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2501 ('c', 'closed', False, _('show normal and closed branch heads')),
2501 ('c', 'closed', False, _('show normal and closed branch heads')),
2502 ] + templateopts,
2502 ] + templateopts,
2503 _('[-ac] [-r STARTREV] [REV]...'))
2503 _('[-ac] [-r STARTREV] [REV]...'))
2504 def heads(ui, repo, *branchrevs, **opts):
2504 def heads(ui, repo, *branchrevs, **opts):
2505 """show current repository heads or show branch heads
2505 """show current repository heads or show branch heads
2506
2506
2507 With no arguments, show all repository branch heads.
2507 With no arguments, show all repository branch heads.
2508
2508
2509 Repository "heads" are changesets with no child changesets. They are
2509 Repository "heads" are changesets with no child changesets. They are
2510 where development generally takes place and are the usual targets
2510 where development generally takes place and are the usual targets
2511 for update and merge operations. Branch heads are changesets that have
2511 for update and merge operations. Branch heads are changesets that have
2512 no child changeset on the same branch.
2512 no child changeset on the same branch.
2513
2513
2514 If one or more REVs are given, only branch heads on the branches
2514 If one or more REVs are given, only branch heads on the branches
2515 associated with the specified changesets are shown.
2515 associated with the specified changesets are shown.
2516
2516
2517 If -c/--closed is specified, also show branch heads marked closed
2517 If -c/--closed is specified, also show branch heads marked closed
2518 (see :hg:`commit --close-branch`).
2518 (see :hg:`commit --close-branch`).
2519
2519
2520 If STARTREV is specified, only those heads that are descendants of
2520 If STARTREV is specified, only those heads that are descendants of
2521 STARTREV will be displayed.
2521 STARTREV will be displayed.
2522
2522
2523 If -t/--topo is specified, named branch mechanics will be ignored and only
2523 If -t/--topo is specified, named branch mechanics will be ignored and only
2524 changesets without children will be shown.
2524 changesets without children will be shown.
2525
2525
2526 Returns 0 if matching heads are found, 1 if not.
2526 Returns 0 if matching heads are found, 1 if not.
2527 """
2527 """
2528
2528
2529 start = None
2529 start = None
2530 if 'rev' in opts:
2530 if 'rev' in opts:
2531 start = scmutil.revsingle(repo, opts['rev'], None).node()
2531 start = scmutil.revsingle(repo, opts['rev'], None).node()
2532
2532
2533 if opts.get('topo'):
2533 if opts.get('topo'):
2534 heads = [repo[h] for h in repo.heads(start)]
2534 heads = [repo[h] for h in repo.heads(start)]
2535 else:
2535 else:
2536 heads = []
2536 heads = []
2537 for branch in repo.branchmap():
2537 for branch in repo.branchmap():
2538 heads += repo.branchheads(branch, start, opts.get('closed'))
2538 heads += repo.branchheads(branch, start, opts.get('closed'))
2539 heads = [repo[h] for h in heads]
2539 heads = [repo[h] for h in heads]
2540
2540
2541 if branchrevs:
2541 if branchrevs:
2542 branches = set(repo[br].branch() for br in branchrevs)
2542 branches = set(repo[br].branch() for br in branchrevs)
2543 heads = [h for h in heads if h.branch() in branches]
2543 heads = [h for h in heads if h.branch() in branches]
2544
2544
2545 if opts.get('active') and branchrevs:
2545 if opts.get('active') and branchrevs:
2546 dagheads = repo.heads(start)
2546 dagheads = repo.heads(start)
2547 heads = [h for h in heads if h.node() in dagheads]
2547 heads = [h for h in heads if h.node() in dagheads]
2548
2548
2549 if branchrevs:
2549 if branchrevs:
2550 haveheads = set(h.branch() for h in heads)
2550 haveheads = set(h.branch() for h in heads)
2551 if branches - haveheads:
2551 if branches - haveheads:
2552 headless = ', '.join(b for b in branches - haveheads)
2552 headless = ', '.join(b for b in branches - haveheads)
2553 msg = _('no open branch heads found on branches %s')
2553 msg = _('no open branch heads found on branches %s')
2554 if opts.get('rev'):
2554 if opts.get('rev'):
2555 msg += _(' (started at %s)' % opts['rev'])
2555 msg += _(' (started at %s)' % opts['rev'])
2556 ui.warn((msg + '\n') % headless)
2556 ui.warn((msg + '\n') % headless)
2557
2557
2558 if not heads:
2558 if not heads:
2559 return 1
2559 return 1
2560
2560
2561 heads = sorted(heads, key=lambda x: -x.rev())
2561 heads = sorted(heads, key=lambda x: -x.rev())
2562 displayer = cmdutil.show_changeset(ui, repo, opts)
2562 displayer = cmdutil.show_changeset(ui, repo, opts)
2563 for ctx in heads:
2563 for ctx in heads:
2564 displayer.show(ctx)
2564 displayer.show(ctx)
2565 displayer.close()
2565 displayer.close()
2566
2566
2567 @command('help',
2567 @command('help',
2568 [('e', 'extension', None, _('show only help for extensions')),
2568 [('e', 'extension', None, _('show only help for extensions')),
2569 ('c', 'command', None, _('show only help for commands'))],
2569 ('c', 'command', None, _('show only help for commands'))],
2570 _('[-ec] [TOPIC]'))
2570 _('[-ec] [TOPIC]'))
2571 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2571 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2572 """show help for a given topic or a help overview
2572 """show help for a given topic or a help overview
2573
2573
2574 With no arguments, print a list of commands with short help messages.
2574 With no arguments, print a list of commands with short help messages.
2575
2575
2576 Given a topic, extension, or command name, print help for that
2576 Given a topic, extension, or command name, print help for that
2577 topic.
2577 topic.
2578
2578
2579 Returns 0 if successful.
2579 Returns 0 if successful.
2580 """
2580 """
2581 option_lists = []
2581 option_lists = []
2582 textwidth = min(ui.termwidth(), 80) - 2
2582 textwidth = min(ui.termwidth(), 80) - 2
2583
2583
2584 def addglobalopts(aliases):
2584 def addglobalopts(aliases):
2585 if ui.verbose:
2585 if ui.verbose:
2586 option_lists.append((_("global options:"), globalopts))
2586 option_lists.append((_("global options:"), globalopts))
2587 if name == 'shortlist':
2587 if name == 'shortlist':
2588 option_lists.append((_('use "hg help" for the full list '
2588 option_lists.append((_('use "hg help" for the full list '
2589 'of commands'), ()))
2589 'of commands'), ()))
2590 else:
2590 else:
2591 if name == 'shortlist':
2591 if name == 'shortlist':
2592 msg = _('use "hg help" for the full list of commands '
2592 msg = _('use "hg help" for the full list of commands '
2593 'or "hg -v" for details')
2593 'or "hg -v" for details')
2594 elif name and not full:
2594 elif name and not full:
2595 msg = _('use "hg help %s" to show the full help text' % name)
2595 msg = _('use "hg help %s" to show the full help text' % name)
2596 elif aliases:
2596 elif aliases:
2597 msg = _('use "hg -v help%s" to show builtin aliases and '
2597 msg = _('use "hg -v help%s" to show builtin aliases and '
2598 'global options') % (name and " " + name or "")
2598 'global options') % (name and " " + name or "")
2599 else:
2599 else:
2600 msg = _('use "hg -v help %s" to show global options') % name
2600 msg = _('use "hg -v help %s" to show global options') % name
2601 option_lists.append((msg, ()))
2601 option_lists.append((msg, ()))
2602
2602
2603 def helpcmd(name):
2603 def helpcmd(name):
2604 if with_version:
2604 if with_version:
2605 version_(ui)
2605 version_(ui)
2606 ui.write('\n')
2606 ui.write('\n')
2607
2607
2608 try:
2608 try:
2609 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2609 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2610 except error.AmbiguousCommand, inst:
2610 except error.AmbiguousCommand, inst:
2611 # py3k fix: except vars can't be used outside the scope of the
2611 # py3k fix: except vars can't be used outside the scope of the
2612 # except block, nor can be used inside a lambda. python issue4617
2612 # except block, nor can be used inside a lambda. python issue4617
2613 prefix = inst.args[0]
2613 prefix = inst.args[0]
2614 select = lambda c: c.lstrip('^').startswith(prefix)
2614 select = lambda c: c.lstrip('^').startswith(prefix)
2615 helplist(_('list of commands:\n\n'), select)
2615 helplist(_('list of commands:\n\n'), select)
2616 return
2616 return
2617
2617
2618 # check if it's an invalid alias and display its error if it is
2618 # check if it's an invalid alias and display its error if it is
2619 if getattr(entry[0], 'badalias', False):
2619 if getattr(entry[0], 'badalias', False):
2620 if not unknowncmd:
2620 if not unknowncmd:
2621 entry[0](ui)
2621 entry[0](ui)
2622 return
2622 return
2623
2623
2624 # synopsis
2624 # synopsis
2625 if len(entry) > 2:
2625 if len(entry) > 2:
2626 if entry[2].startswith('hg'):
2626 if entry[2].startswith('hg'):
2627 ui.write("%s\n" % entry[2])
2627 ui.write("%s\n" % entry[2])
2628 else:
2628 else:
2629 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2629 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2630 else:
2630 else:
2631 ui.write('hg %s\n' % aliases[0])
2631 ui.write('hg %s\n' % aliases[0])
2632
2632
2633 # aliases
2633 # aliases
2634 if full and not ui.quiet and len(aliases) > 1:
2634 if full and not ui.quiet and len(aliases) > 1:
2635 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2635 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2636
2636
2637 # description
2637 # description
2638 doc = gettext(entry[0].__doc__)
2638 doc = gettext(entry[0].__doc__)
2639 if not doc:
2639 if not doc:
2640 doc = _("(no help text available)")
2640 doc = _("(no help text available)")
2641 if hasattr(entry[0], 'definition'): # aliased command
2641 if hasattr(entry[0], 'definition'): # aliased command
2642 if entry[0].definition.startswith('!'): # shell alias
2642 if entry[0].definition.startswith('!'): # shell alias
2643 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2643 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2644 else:
2644 else:
2645 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2645 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2646 if ui.quiet or not full:
2646 if ui.quiet or not full:
2647 doc = doc.splitlines()[0]
2647 doc = doc.splitlines()[0]
2648 keep = ui.verbose and ['verbose'] or []
2648 keep = ui.verbose and ['verbose'] or []
2649 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2649 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2650 ui.write("\n%s\n" % formatted)
2650 ui.write("\n%s\n" % formatted)
2651 if pruned:
2651 if pruned:
2652 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2652 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2653
2653
2654 if not ui.quiet:
2654 if not ui.quiet:
2655 # options
2655 # options
2656 if entry[1]:
2656 if entry[1]:
2657 option_lists.append((_("options:\n"), entry[1]))
2657 option_lists.append((_("options:\n"), entry[1]))
2658
2658
2659 addglobalopts(False)
2659 addglobalopts(False)
2660
2660
2661 # check if this command shadows a non-trivial (multi-line)
2661 # check if this command shadows a non-trivial (multi-line)
2662 # extension help text
2662 # extension help text
2663 try:
2663 try:
2664 mod = extensions.find(name)
2664 mod = extensions.find(name)
2665 doc = gettext(mod.__doc__) or ''
2665 doc = gettext(mod.__doc__) or ''
2666 if '\n' in doc.strip():
2666 if '\n' in doc.strip():
2667 msg = _('use "hg help -e %s" to show help for '
2667 msg = _('use "hg help -e %s" to show help for '
2668 'the %s extension') % (name, name)
2668 'the %s extension') % (name, name)
2669 ui.write('\n%s\n' % msg)
2669 ui.write('\n%s\n' % msg)
2670 except KeyError:
2670 except KeyError:
2671 pass
2671 pass
2672
2672
2673 def helplist(header, select=None):
2673 def helplist(header, select=None):
2674 h = {}
2674 h = {}
2675 cmds = {}
2675 cmds = {}
2676 for c, e in table.iteritems():
2676 for c, e in table.iteritems():
2677 f = c.split("|", 1)[0]
2677 f = c.split("|", 1)[0]
2678 if select and not select(f):
2678 if select and not select(f):
2679 continue
2679 continue
2680 if (not select and name != 'shortlist' and
2680 if (not select and name != 'shortlist' and
2681 e[0].__module__ != __name__):
2681 e[0].__module__ != __name__):
2682 continue
2682 continue
2683 if name == "shortlist" and not f.startswith("^"):
2683 if name == "shortlist" and not f.startswith("^"):
2684 continue
2684 continue
2685 f = f.lstrip("^")
2685 f = f.lstrip("^")
2686 if not ui.debugflag and f.startswith("debug"):
2686 if not ui.debugflag and f.startswith("debug"):
2687 continue
2687 continue
2688 doc = e[0].__doc__
2688 doc = e[0].__doc__
2689 if doc and 'DEPRECATED' in doc and not ui.verbose:
2689 if doc and 'DEPRECATED' in doc and not ui.verbose:
2690 continue
2690 continue
2691 doc = gettext(doc)
2691 doc = gettext(doc)
2692 if not doc:
2692 if not doc:
2693 doc = _("(no help text available)")
2693 doc = _("(no help text available)")
2694 h[f] = doc.splitlines()[0].rstrip()
2694 h[f] = doc.splitlines()[0].rstrip()
2695 cmds[f] = c.lstrip("^")
2695 cmds[f] = c.lstrip("^")
2696
2696
2697 if not h:
2697 if not h:
2698 ui.status(_('no commands defined\n'))
2698 ui.status(_('no commands defined\n'))
2699 return
2699 return
2700
2700
2701 ui.status(header)
2701 ui.status(header)
2702 fns = sorted(h)
2702 fns = sorted(h)
2703 m = max(map(len, fns))
2703 m = max(map(len, fns))
2704 for f in fns:
2704 for f in fns:
2705 if ui.verbose:
2705 if ui.verbose:
2706 commands = cmds[f].replace("|",", ")
2706 commands = cmds[f].replace("|",", ")
2707 ui.write(" %s:\n %s\n"%(commands, h[f]))
2707 ui.write(" %s:\n %s\n"%(commands, h[f]))
2708 else:
2708 else:
2709 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2709 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2710 initindent=' %-*s ' % (m, f),
2710 initindent=' %-*s ' % (m, f),
2711 hangindent=' ' * (m + 4))))
2711 hangindent=' ' * (m + 4))))
2712
2712
2713 if not ui.quiet:
2713 if not ui.quiet:
2714 addglobalopts(True)
2714 addglobalopts(True)
2715
2715
2716 def helptopic(name):
2716 def helptopic(name):
2717 for names, header, doc in help.helptable:
2717 for names, header, doc in help.helptable:
2718 if name in names:
2718 if name in names:
2719 break
2719 break
2720 else:
2720 else:
2721 raise error.UnknownCommand(name)
2721 raise error.UnknownCommand(name)
2722
2722
2723 # description
2723 # description
2724 if not doc:
2724 if not doc:
2725 doc = _("(no help text available)")
2725 doc = _("(no help text available)")
2726 if hasattr(doc, '__call__'):
2726 if hasattr(doc, '__call__'):
2727 doc = doc()
2727 doc = doc()
2728
2728
2729 ui.write("%s\n\n" % header)
2729 ui.write("%s\n\n" % header)
2730 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2730 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2731 try:
2731 try:
2732 cmdutil.findcmd(name, table)
2732 cmdutil.findcmd(name, table)
2733 ui.write(_('\nuse "hg help -c %s" to see help for '
2733 ui.write(_('\nuse "hg help -c %s" to see help for '
2734 'the %s command\n') % (name, name))
2734 'the %s command\n') % (name, name))
2735 except error.UnknownCommand:
2735 except error.UnknownCommand:
2736 pass
2736 pass
2737
2737
2738 def helpext(name):
2738 def helpext(name):
2739 try:
2739 try:
2740 mod = extensions.find(name)
2740 mod = extensions.find(name)
2741 doc = gettext(mod.__doc__) or _('no help text available')
2741 doc = gettext(mod.__doc__) or _('no help text available')
2742 except KeyError:
2742 except KeyError:
2743 mod = None
2743 mod = None
2744 doc = extensions.disabledext(name)
2744 doc = extensions.disabledext(name)
2745 if not doc:
2745 if not doc:
2746 raise error.UnknownCommand(name)
2746 raise error.UnknownCommand(name)
2747
2747
2748 if '\n' not in doc:
2748 if '\n' not in doc:
2749 head, tail = doc, ""
2749 head, tail = doc, ""
2750 else:
2750 else:
2751 head, tail = doc.split('\n', 1)
2751 head, tail = doc.split('\n', 1)
2752 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2752 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2753 if tail:
2753 if tail:
2754 ui.write(minirst.format(tail, textwidth))
2754 ui.write(minirst.format(tail, textwidth))
2755 ui.status('\n\n')
2755 ui.status('\n\n')
2756
2756
2757 if mod:
2757 if mod:
2758 try:
2758 try:
2759 ct = mod.cmdtable
2759 ct = mod.cmdtable
2760 except AttributeError:
2760 except AttributeError:
2761 ct = {}
2761 ct = {}
2762 modcmds = set([c.split('|', 1)[0] for c in ct])
2762 modcmds = set([c.split('|', 1)[0] for c in ct])
2763 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2763 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2764 else:
2764 else:
2765 ui.write(_('use "hg help extensions" for information on enabling '
2765 ui.write(_('use "hg help extensions" for information on enabling '
2766 'extensions\n'))
2766 'extensions\n'))
2767
2767
2768 def helpextcmd(name):
2768 def helpextcmd(name):
2769 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2769 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2770 doc = gettext(mod.__doc__).splitlines()[0]
2770 doc = gettext(mod.__doc__).splitlines()[0]
2771
2771
2772 msg = help.listexts(_("'%s' is provided by the following "
2772 msg = help.listexts(_("'%s' is provided by the following "
2773 "extension:") % cmd, {ext: doc}, indent=4)
2773 "extension:") % cmd, {ext: doc}, indent=4)
2774 ui.write(minirst.format(msg, textwidth))
2774 ui.write(minirst.format(msg, textwidth))
2775 ui.write('\n\n')
2775 ui.write('\n\n')
2776 ui.write(_('use "hg help extensions" for information on enabling '
2776 ui.write(_('use "hg help extensions" for information on enabling '
2777 'extensions\n'))
2777 'extensions\n'))
2778
2778
2779 if name and name != 'shortlist':
2779 if name and name != 'shortlist':
2780 i = None
2780 i = None
2781 if unknowncmd:
2781 if unknowncmd:
2782 queries = (helpextcmd,)
2782 queries = (helpextcmd,)
2783 elif opts.get('extension'):
2783 elif opts.get('extension'):
2784 queries = (helpext,)
2784 queries = (helpext,)
2785 elif opts.get('command'):
2785 elif opts.get('command'):
2786 queries = (helpcmd,)
2786 queries = (helpcmd,)
2787 else:
2787 else:
2788 queries = (helptopic, helpcmd, helpext, helpextcmd)
2788 queries = (helptopic, helpcmd, helpext, helpextcmd)
2789 for f in queries:
2789 for f in queries:
2790 try:
2790 try:
2791 f(name)
2791 f(name)
2792 i = None
2792 i = None
2793 break
2793 break
2794 except error.UnknownCommand, inst:
2794 except error.UnknownCommand, inst:
2795 i = inst
2795 i = inst
2796 if i:
2796 if i:
2797 raise i
2797 raise i
2798
2798
2799 else:
2799 else:
2800 # program name
2800 # program name
2801 if ui.verbose or with_version:
2801 if ui.verbose or with_version:
2802 version_(ui)
2802 version_(ui)
2803 else:
2803 else:
2804 ui.status(_("Mercurial Distributed SCM\n"))
2804 ui.status(_("Mercurial Distributed SCM\n"))
2805 ui.status('\n')
2805 ui.status('\n')
2806
2806
2807 # list of commands
2807 # list of commands
2808 if name == "shortlist":
2808 if name == "shortlist":
2809 header = _('basic commands:\n\n')
2809 header = _('basic commands:\n\n')
2810 else:
2810 else:
2811 header = _('list of commands:\n\n')
2811 header = _('list of commands:\n\n')
2812
2812
2813 helplist(header)
2813 helplist(header)
2814 if name != 'shortlist':
2814 if name != 'shortlist':
2815 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2815 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2816 if text:
2816 if text:
2817 ui.write("\n%s\n" % minirst.format(text, textwidth))
2817 ui.write("\n%s\n" % minirst.format(text, textwidth))
2818
2818
2819 # list all option lists
2819 # list all option lists
2820 opt_output = []
2820 opt_output = []
2821 multioccur = False
2821 multioccur = False
2822 for title, options in option_lists:
2822 for title, options in option_lists:
2823 opt_output.append(("\n%s" % title, None))
2823 opt_output.append(("\n%s" % title, None))
2824 for option in options:
2824 for option in options:
2825 if len(option) == 5:
2825 if len(option) == 5:
2826 shortopt, longopt, default, desc, optlabel = option
2826 shortopt, longopt, default, desc, optlabel = option
2827 else:
2827 else:
2828 shortopt, longopt, default, desc = option
2828 shortopt, longopt, default, desc = option
2829 optlabel = _("VALUE") # default label
2829 optlabel = _("VALUE") # default label
2830
2830
2831 if _("DEPRECATED") in desc and not ui.verbose:
2831 if _("DEPRECATED") in desc and not ui.verbose:
2832 continue
2832 continue
2833 if isinstance(default, list):
2833 if isinstance(default, list):
2834 numqualifier = " %s [+]" % optlabel
2834 numqualifier = " %s [+]" % optlabel
2835 multioccur = True
2835 multioccur = True
2836 elif (default is not None) and not isinstance(default, bool):
2836 elif (default is not None) and not isinstance(default, bool):
2837 numqualifier = " %s" % optlabel
2837 numqualifier = " %s" % optlabel
2838 else:
2838 else:
2839 numqualifier = ""
2839 numqualifier = ""
2840 opt_output.append(("%2s%s" %
2840 opt_output.append(("%2s%s" %
2841 (shortopt and "-%s" % shortopt,
2841 (shortopt and "-%s" % shortopt,
2842 longopt and " --%s%s" %
2842 longopt and " --%s%s" %
2843 (longopt, numqualifier)),
2843 (longopt, numqualifier)),
2844 "%s%s" % (desc,
2844 "%s%s" % (desc,
2845 default
2845 default
2846 and _(" (default: %s)") % default
2846 and _(" (default: %s)") % default
2847 or "")))
2847 or "")))
2848 if multioccur:
2848 if multioccur:
2849 msg = _("\n[+] marked option can be specified multiple times")
2849 msg = _("\n[+] marked option can be specified multiple times")
2850 if ui.verbose and name != 'shortlist':
2850 if ui.verbose and name != 'shortlist':
2851 opt_output.append((msg, None))
2851 opt_output.append((msg, None))
2852 else:
2852 else:
2853 opt_output.insert(-1, (msg, None))
2853 opt_output.insert(-1, (msg, None))
2854
2854
2855 if not name:
2855 if not name:
2856 ui.write(_("\nadditional help topics:\n\n"))
2856 ui.write(_("\nadditional help topics:\n\n"))
2857 topics = []
2857 topics = []
2858 for names, header, doc in help.helptable:
2858 for names, header, doc in help.helptable:
2859 topics.append((sorted(names, key=len, reverse=True)[0], header))
2859 topics.append((sorted(names, key=len, reverse=True)[0], header))
2860 topics_len = max([len(s[0]) for s in topics])
2860 topics_len = max([len(s[0]) for s in topics])
2861 for t, desc in topics:
2861 for t, desc in topics:
2862 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2862 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2863
2863
2864 if opt_output:
2864 if opt_output:
2865 colwidth = encoding.colwidth
2865 colwidth = encoding.colwidth
2866 # normalize: (opt or message, desc or None, width of opt)
2866 # normalize: (opt or message, desc or None, width of opt)
2867 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2867 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2868 for opt, desc in opt_output]
2868 for opt, desc in opt_output]
2869 hanging = max([e[2] for e in entries])
2869 hanging = max([e[2] for e in entries])
2870 for opt, desc, width in entries:
2870 for opt, desc, width in entries:
2871 if desc:
2871 if desc:
2872 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2872 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2873 hangindent = ' ' * (hanging + 3)
2873 hangindent = ' ' * (hanging + 3)
2874 ui.write('%s\n' % (util.wrap(desc, textwidth,
2874 ui.write('%s\n' % (util.wrap(desc, textwidth,
2875 initindent=initindent,
2875 initindent=initindent,
2876 hangindent=hangindent)))
2876 hangindent=hangindent)))
2877 else:
2877 else:
2878 ui.write("%s\n" % opt)
2878 ui.write("%s\n" % opt)
2879
2879
2880 @command('identify|id',
2880 @command('identify|id',
2881 [('r', 'rev', '',
2881 [('r', 'rev', '',
2882 _('identify the specified revision'), _('REV')),
2882 _('identify the specified revision'), _('REV')),
2883 ('n', 'num', None, _('show local revision number')),
2883 ('n', 'num', None, _('show local revision number')),
2884 ('i', 'id', None, _('show global revision id')),
2884 ('i', 'id', None, _('show global revision id')),
2885 ('b', 'branch', None, _('show branch')),
2885 ('b', 'branch', None, _('show branch')),
2886 ('t', 'tags', None, _('show tags')),
2886 ('t', 'tags', None, _('show tags')),
2887 ('B', 'bookmarks', None, _('show bookmarks'))],
2887 ('B', 'bookmarks', None, _('show bookmarks'))],
2888 _('[-nibtB] [-r REV] [SOURCE]'))
2888 _('[-nibtB] [-r REV] [SOURCE]'))
2889 def identify(ui, repo, source=None, rev=None,
2889 def identify(ui, repo, source=None, rev=None,
2890 num=None, id=None, branch=None, tags=None, bookmarks=None):
2890 num=None, id=None, branch=None, tags=None, bookmarks=None):
2891 """identify the working copy or specified revision
2891 """identify the working copy or specified revision
2892
2892
2893 Print a summary identifying the repository state at REV using one or
2893 Print a summary identifying the repository state at REV using one or
2894 two parent hash identifiers, followed by a "+" if the working
2894 two parent hash identifiers, followed by a "+" if the working
2895 directory has uncommitted changes, the branch name (if not default),
2895 directory has uncommitted changes, the branch name (if not default),
2896 a list of tags, and a list of bookmarks.
2896 a list of tags, and a list of bookmarks.
2897
2897
2898 When REV is not given, print a summary of the current state of the
2898 When REV is not given, print a summary of the current state of the
2899 repository.
2899 repository.
2900
2900
2901 Specifying a path to a repository root or Mercurial bundle will
2901 Specifying a path to a repository root or Mercurial bundle will
2902 cause lookup to operate on that repository/bundle.
2902 cause lookup to operate on that repository/bundle.
2903
2903
2904 Returns 0 if successful.
2904 Returns 0 if successful.
2905 """
2905 """
2906
2906
2907 if not repo and not source:
2907 if not repo and not source:
2908 raise util.Abort(_("there is no Mercurial repository here "
2908 raise util.Abort(_("there is no Mercurial repository here "
2909 "(.hg not found)"))
2909 "(.hg not found)"))
2910
2910
2911 hexfunc = ui.debugflag and hex or short
2911 hexfunc = ui.debugflag and hex or short
2912 default = not (num or id or branch or tags or bookmarks)
2912 default = not (num or id or branch or tags or bookmarks)
2913 output = []
2913 output = []
2914 revs = []
2914 revs = []
2915
2915
2916 if source:
2916 if source:
2917 source, branches = hg.parseurl(ui.expandpath(source))
2917 source, branches = hg.parseurl(ui.expandpath(source))
2918 repo = hg.peer(ui, {}, source)
2918 repo = hg.peer(ui, {}, source)
2919 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2919 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2920
2920
2921 if not repo.local():
2921 if not repo.local():
2922 if num or branch or tags:
2922 if num or branch or tags:
2923 raise util.Abort(
2923 raise util.Abort(
2924 _("can't query remote revision number, branch, or tags"))
2924 _("can't query remote revision number, branch, or tags"))
2925 if not rev and revs:
2925 if not rev and revs:
2926 rev = revs[0]
2926 rev = revs[0]
2927 if not rev:
2927 if not rev:
2928 rev = "tip"
2928 rev = "tip"
2929
2929
2930 remoterev = repo.lookup(rev)
2930 remoterev = repo.lookup(rev)
2931 if default or id:
2931 if default or id:
2932 output = [hexfunc(remoterev)]
2932 output = [hexfunc(remoterev)]
2933
2933
2934 def getbms():
2934 def getbms():
2935 bms = []
2935 bms = []
2936
2936
2937 if 'bookmarks' in repo.listkeys('namespaces'):
2937 if 'bookmarks' in repo.listkeys('namespaces'):
2938 hexremoterev = hex(remoterev)
2938 hexremoterev = hex(remoterev)
2939 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2939 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2940 if bmr == hexremoterev]
2940 if bmr == hexremoterev]
2941
2941
2942 return bms
2942 return bms
2943
2943
2944 if bookmarks:
2944 if bookmarks:
2945 output.extend(getbms())
2945 output.extend(getbms())
2946 elif default and not ui.quiet:
2946 elif default and not ui.quiet:
2947 # multiple bookmarks for a single parent separated by '/'
2947 # multiple bookmarks for a single parent separated by '/'
2948 bm = '/'.join(getbms())
2948 bm = '/'.join(getbms())
2949 if bm:
2949 if bm:
2950 output.append(bm)
2950 output.append(bm)
2951 else:
2951 else:
2952 if not rev:
2952 if not rev:
2953 ctx = repo[None]
2953 ctx = repo[None]
2954 parents = ctx.parents()
2954 parents = ctx.parents()
2955 changed = ""
2955 changed = ""
2956 if default or id or num:
2956 if default or id or num:
2957 changed = util.any(repo.status()) and "+" or ""
2957 changed = util.any(repo.status()) and "+" or ""
2958 if default or id:
2958 if default or id:
2959 output = ["%s%s" %
2959 output = ["%s%s" %
2960 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2960 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2961 if num:
2961 if num:
2962 output.append("%s%s" %
2962 output.append("%s%s" %
2963 ('+'.join([str(p.rev()) for p in parents]), changed))
2963 ('+'.join([str(p.rev()) for p in parents]), changed))
2964 else:
2964 else:
2965 ctx = scmutil.revsingle(repo, rev)
2965 ctx = scmutil.revsingle(repo, rev)
2966 if default or id:
2966 if default or id:
2967 output = [hexfunc(ctx.node())]
2967 output = [hexfunc(ctx.node())]
2968 if num:
2968 if num:
2969 output.append(str(ctx.rev()))
2969 output.append(str(ctx.rev()))
2970
2970
2971 if default and not ui.quiet:
2971 if default and not ui.quiet:
2972 b = ctx.branch()
2972 b = ctx.branch()
2973 if b != 'default':
2973 if b != 'default':
2974 output.append("(%s)" % b)
2974 output.append("(%s)" % b)
2975
2975
2976 # multiple tags for a single parent separated by '/'
2976 # multiple tags for a single parent separated by '/'
2977 t = '/'.join(ctx.tags())
2977 t = '/'.join(ctx.tags())
2978 if t:
2978 if t:
2979 output.append(t)
2979 output.append(t)
2980
2980
2981 # multiple bookmarks for a single parent separated by '/'
2981 # multiple bookmarks for a single parent separated by '/'
2982 bm = '/'.join(ctx.bookmarks())
2982 bm = '/'.join(ctx.bookmarks())
2983 if bm:
2983 if bm:
2984 output.append(bm)
2984 output.append(bm)
2985 else:
2985 else:
2986 if branch:
2986 if branch:
2987 output.append(ctx.branch())
2987 output.append(ctx.branch())
2988
2988
2989 if tags:
2989 if tags:
2990 output.extend(ctx.tags())
2990 output.extend(ctx.tags())
2991
2991
2992 if bookmarks:
2992 if bookmarks:
2993 output.extend(ctx.bookmarks())
2993 output.extend(ctx.bookmarks())
2994
2994
2995 ui.write("%s\n" % ' '.join(output))
2995 ui.write("%s\n" % ' '.join(output))
2996
2996
2997 @command('import|patch',
2997 @command('import|patch',
2998 [('p', 'strip', 1,
2998 [('p', 'strip', 1,
2999 _('directory strip option for patch. This has the same '
2999 _('directory strip option for patch. This has the same '
3000 'meaning as the corresponding patch option'), _('NUM')),
3000 'meaning as the corresponding patch option'), _('NUM')),
3001 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3001 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3002 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3002 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3003 ('', 'no-commit', None,
3003 ('', 'no-commit', None,
3004 _("don't commit, just update the working directory")),
3004 _("don't commit, just update the working directory")),
3005 ('', 'bypass', None,
3006 _("apply patch without touching the working directory")),
3005 ('', 'exact', None,
3007 ('', 'exact', None,
3006 _('apply patch to the nodes from which it was generated')),
3008 _('apply patch to the nodes from which it was generated')),
3007 ('', 'import-branch', None,
3009 ('', 'import-branch', None,
3008 _('use any branch information in patch (implied by --exact)'))] +
3010 _('use any branch information in patch (implied by --exact)'))] +
3009 commitopts + commitopts2 + similarityopts,
3011 commitopts + commitopts2 + similarityopts,
3010 _('[OPTION]... PATCH...'))
3012 _('[OPTION]... PATCH...'))
3011 def import_(ui, repo, patch1, *patches, **opts):
3013 def import_(ui, repo, patch1, *patches, **opts):
3012 """import an ordered set of patches
3014 """import an ordered set of patches
3013
3015
3014 Import a list of patches and commit them individually (unless
3016 Import a list of patches and commit them individually (unless
3015 --no-commit is specified).
3017 --no-commit is specified).
3016
3018
3017 If there are outstanding changes in the working directory, import
3019 If there are outstanding changes in the working directory, import
3018 will abort unless given the -f/--force flag.
3020 will abort unless given the -f/--force flag.
3019
3021
3020 You can import a patch straight from a mail message. Even patches
3022 You can import a patch straight from a mail message. Even patches
3021 as attachments work (to use the body part, it must have type
3023 as attachments work (to use the body part, it must have type
3022 text/plain or text/x-patch). From and Subject headers of email
3024 text/plain or text/x-patch). From and Subject headers of email
3023 message are used as default committer and commit message. All
3025 message are used as default committer and commit message. All
3024 text/plain body parts before first diff are added to commit
3026 text/plain body parts before first diff are added to commit
3025 message.
3027 message.
3026
3028
3027 If the imported patch was generated by :hg:`export`, user and
3029 If the imported patch was generated by :hg:`export`, user and
3028 description from patch override values from message headers and
3030 description from patch override values from message headers and
3029 body. Values given on command line with -m/--message and -u/--user
3031 body. Values given on command line with -m/--message and -u/--user
3030 override these.
3032 override these.
3031
3033
3032 If --exact is specified, import will set the working directory to
3034 If --exact is specified, import will set the working directory to
3033 the parent of each patch before applying it, and will abort if the
3035 the parent of each patch before applying it, and will abort if the
3034 resulting changeset has a different ID than the one recorded in
3036 resulting changeset has a different ID than the one recorded in
3035 the patch. This may happen due to character set problems or other
3037 the patch. This may happen due to character set problems or other
3036 deficiencies in the text patch format.
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 With -s/--similarity, hg will attempt to discover renames and
3045 With -s/--similarity, hg will attempt to discover renames and
3039 copies in the patch in the same way as 'addremove'.
3046 copies in the patch in the same way as 'addremove'.
3040
3047
3041 To read a patch from standard input, use "-" as the patch name. If
3048 To read a patch from standard input, use "-" as the patch name. If
3042 a URL is specified, the patch will be downloaded from it.
3049 a URL is specified, the patch will be downloaded from it.
3043 See :hg:`help dates` for a list of formats valid for -d/--date.
3050 See :hg:`help dates` for a list of formats valid for -d/--date.
3044
3051
3045 Returns 0 on success.
3052 Returns 0 on success.
3046 """
3053 """
3047 patches = (patch1,) + patches
3054 patches = (patch1,) + patches
3048
3055
3049 date = opts.get('date')
3056 date = opts.get('date')
3050 if date:
3057 if date:
3051 opts['date'] = util.parsedate(date)
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 try:
3063 try:
3054 sim = float(opts.get('similarity') or 0)
3064 sim = float(opts.get('similarity') or 0)
3055 except ValueError:
3065 except ValueError:
3056 raise util.Abort(_('similarity must be a number'))
3066 raise util.Abort(_('similarity must be a number'))
3057 if sim < 0 or sim > 100:
3067 if sim < 0 or sim > 100:
3058 raise util.Abort(_('similarity must be between 0 and 100'))
3068 raise util.Abort(_('similarity must be between 0 and 100'))
3059
3069 if sim and not update:
3060 if opts.get('exact') or not opts.get('force'):
3070 raise util.Abort(_('cannot use --similarity with --bypass'))
3071
3072 if (opts.get('exact') or not opts.get('force')) and update:
3061 cmdutil.bailifchanged(repo)
3073 cmdutil.bailifchanged(repo)
3062
3074
3063 d = opts["base"]
3075 d = opts["base"]
3064 strip = opts["strip"]
3076 strip = opts["strip"]
3065 wlock = lock = None
3077 wlock = lock = None
3066 msgs = []
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 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3086 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3070 patch.extract(ui, hunk)
3087 patch.extract(ui, hunk)
3071
3088
3072 if not tmpname:
3089 if not tmpname:
3073 return None
3090 return None
3074 commitid = _('to working directory')
3091 commitid = _('to working directory')
3075
3092
3076 try:
3093 try:
3077 cmdline_message = cmdutil.logmessage(opts)
3094 cmdline_message = cmdutil.logmessage(opts)
3078 if cmdline_message:
3095 if cmdline_message:
3079 # pickup the cmdline msg
3096 # pickup the cmdline msg
3080 message = cmdline_message
3097 message = cmdline_message
3081 elif message:
3098 elif message:
3082 # pickup the patch msg
3099 # pickup the patch msg
3083 message = message.strip()
3100 message = message.strip()
3084 else:
3101 else:
3085 # launch the editor
3102 # launch the editor
3086 message = None
3103 message = None
3087 ui.debug('message:\n%s\n' % message)
3104 ui.debug('message:\n%s\n' % message)
3088
3105
3089 wp = repo.parents()
3106 if len(parents) == 1:
3090 if len(wp) == 1:
3107 parents.append(repo[nullid])
3091 wp.append(repo[nullid])
3092 if opts.get('exact'):
3108 if opts.get('exact'):
3093 if not nodeid or not p1:
3109 if not nodeid or not p1:
3094 raise util.Abort(_('not a Mercurial patch'))
3110 raise util.Abort(_('not a Mercurial patch'))
3095 p1 = repo[p1]
3111 p1 = repo[p1]
3096 p2 = repo[p2 or nullid]
3112 p2 = repo[p2 or nullid]
3097 elif p2:
3113 elif p2:
3098 try:
3114 try:
3099 p1 = repo[p1]
3115 p1 = repo[p1]
3100 p2 = repo[p2]
3116 p2 = repo[p2]
3101 except error.RepoError:
3117 except error.RepoError:
3102 p1, p2 = wp
3118 p1, p2 = parents
3103 else:
3119 else:
3104 p1, p2 = wp
3120 p1, p2 = parents
3105
3121
3106 if opts.get('exact') and p1 != wp[0]:
3122 n = None
3123 if update:
3124 if opts.get('exact') and p1 != parents[0]:
3107 hg.clean(repo, p1.node())
3125 hg.clean(repo, p1.node())
3108 if p1 != wp[0] and p2 != wp[1]:
3126 if p1 != parents[0] and p2 != parents[1]:
3109 repo.dirstate.setparents(p1.node(), p2.node())
3127 repo.dirstate.setparents(p1.node(), p2.node())
3110
3128
3111 if opts.get('exact') or opts.get('import_branch'):
3129 if opts.get('exact') or opts.get('import_branch'):
3112 repo.dirstate.setbranch(branch or 'default')
3130 repo.dirstate.setbranch(branch or 'default')
3113
3131
3114 files = set()
3132 files = set()
3115 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3133 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3116 eolmode=None, similarity=sim / 100.0)
3134 eolmode=None, similarity=sim / 100.0)
3117 files = list(files)
3135 files = list(files)
3118 if opts.get('no_commit'):
3136 if opts.get('no_commit'):
3119 if message:
3137 if message:
3120 msgs.append(message)
3138 msgs.append(message)
3121 else:
3139 else:
3122 if opts.get('exact'):
3140 if opts.get('exact'):
3123 m = None
3141 m = None
3124 else:
3142 else:
3125 m = scmutil.matchfiles(repo, files or [])
3143 m = scmutil.matchfiles(repo, files or [])
3126 n = repo.commit(message, opts.get('user') or user,
3144 n = repo.commit(message, opts.get('user') or user,
3127 opts.get('date') or date, match=m,
3145 opts.get('date') or date, match=m,
3128 editor=cmdutil.commiteditor)
3146 editor=cmdutil.commiteditor)
3129 if opts.get('exact'):
3147 checkexact(repo, n, nodeid)
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
3148 # Force a dirstate write so that the next transaction
3135 # backups an up-do-date file.
3149 # backups an up-to-date file.
3136 repo.dirstate.write()
3150 repo.dirstate.write()
3151 else:
3152 if opts.get('exact') or opts.get('import_branch'):
3153 branch = branch or 'default'
3154 else:
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()
3137 if n:
3175 if n:
3138 commitid = short(n)
3176 commitid = short(n)
3139
3140 return commitid
3177 return commitid
3141 finally:
3178 finally:
3142 os.unlink(tmpname)
3179 os.unlink(tmpname)
3143
3180
3144 try:
3181 try:
3145 wlock = repo.wlock()
3182 wlock = repo.wlock()
3146 lock = repo.lock()
3183 lock = repo.lock()
3184 parents = repo.parents()
3147 lastcommit = None
3185 lastcommit = None
3148 for p in patches:
3186 for p in patches:
3149 pf = os.path.join(d, p)
3187 pf = os.path.join(d, p)
3150
3188
3151 if pf == '-':
3189 if pf == '-':
3152 ui.status(_("applying patch from stdin\n"))
3190 ui.status(_("applying patch from stdin\n"))
3153 pf = sys.stdin
3191 pf = sys.stdin
3154 else:
3192 else:
3155 ui.status(_("applying %s\n") % p)
3193 ui.status(_("applying %s\n") % p)
3156 pf = url.open(ui, pf)
3194 pf = url.open(ui, pf)
3157
3195
3158 haspatch = False
3196 haspatch = False
3159 for hunk in patch.split(pf):
3197 for hunk in patch.split(pf):
3160 commitid = tryone(ui, hunk)
3198 commitid = tryone(ui, hunk, parents)
3161 if commitid:
3199 if commitid:
3162 haspatch = True
3200 haspatch = True
3163 if lastcommit:
3201 if lastcommit:
3164 ui.status(_('applied %s\n') % lastcommit)
3202 ui.status(_('applied %s\n') % lastcommit)
3165 lastcommit = commitid
3203 lastcommit = commitid
3204 if update or opts.get('exact'):
3205 parents = repo.parents()
3206 else:
3207 parents = [repo[commitid]]
3166
3208
3167 if not haspatch:
3209 if not haspatch:
3168 raise util.Abort(_('no diffs found'))
3210 raise util.Abort(_('no diffs found'))
3169
3211
3170 if msgs:
3212 if msgs:
3171 repo.savecommitmessage('\n* * *\n'.join(msgs))
3213 repo.savecommitmessage('\n* * *\n'.join(msgs))
3172 finally:
3214 finally:
3173 release(lock, wlock)
3215 release(lock, wlock)
3174
3216
3175 @command('incoming|in',
3217 @command('incoming|in',
3176 [('f', 'force', None,
3218 [('f', 'force', None,
3177 _('run even if remote repository is unrelated')),
3219 _('run even if remote repository is unrelated')),
3178 ('n', 'newest-first', None, _('show newest record first')),
3220 ('n', 'newest-first', None, _('show newest record first')),
3179 ('', 'bundle', '',
3221 ('', 'bundle', '',
3180 _('file to store the bundles into'), _('FILE')),
3222 _('file to store the bundles into'), _('FILE')),
3181 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3223 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3182 ('B', 'bookmarks', False, _("compare bookmarks")),
3224 ('B', 'bookmarks', False, _("compare bookmarks")),
3183 ('b', 'branch', [],
3225 ('b', 'branch', [],
3184 _('a specific branch you would like to pull'), _('BRANCH')),
3226 _('a specific branch you would like to pull'), _('BRANCH')),
3185 ] + logopts + remoteopts + subrepoopts,
3227 ] + logopts + remoteopts + subrepoopts,
3186 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3228 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3187 def incoming(ui, repo, source="default", **opts):
3229 def incoming(ui, repo, source="default", **opts):
3188 """show new changesets found in source
3230 """show new changesets found in source
3189
3231
3190 Show new changesets found in the specified path/URL or the default
3232 Show new changesets found in the specified path/URL or the default
3191 pull location. These are the changesets that would have been pulled
3233 pull location. These are the changesets that would have been pulled
3192 if a pull at the time you issued this command.
3234 if a pull at the time you issued this command.
3193
3235
3194 For remote repository, using --bundle avoids downloading the
3236 For remote repository, using --bundle avoids downloading the
3195 changesets twice if the incoming is followed by a pull.
3237 changesets twice if the incoming is followed by a pull.
3196
3238
3197 See pull for valid source format details.
3239 See pull for valid source format details.
3198
3240
3199 Returns 0 if there are incoming changes, 1 otherwise.
3241 Returns 0 if there are incoming changes, 1 otherwise.
3200 """
3242 """
3201 if opts.get('bundle') and opts.get('subrepos'):
3243 if opts.get('bundle') and opts.get('subrepos'):
3202 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3244 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3203
3245
3204 if opts.get('bookmarks'):
3246 if opts.get('bookmarks'):
3205 source, branches = hg.parseurl(ui.expandpath(source),
3247 source, branches = hg.parseurl(ui.expandpath(source),
3206 opts.get('branch'))
3248 opts.get('branch'))
3207 other = hg.peer(repo, opts, source)
3249 other = hg.peer(repo, opts, source)
3208 if 'bookmarks' not in other.listkeys('namespaces'):
3250 if 'bookmarks' not in other.listkeys('namespaces'):
3209 ui.warn(_("remote doesn't support bookmarks\n"))
3251 ui.warn(_("remote doesn't support bookmarks\n"))
3210 return 0
3252 return 0
3211 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3253 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3212 return bookmarks.diff(ui, repo, other)
3254 return bookmarks.diff(ui, repo, other)
3213
3255
3214 repo._subtoppath = ui.expandpath(source)
3256 repo._subtoppath = ui.expandpath(source)
3215 try:
3257 try:
3216 return hg.incoming(ui, repo, source, opts)
3258 return hg.incoming(ui, repo, source, opts)
3217 finally:
3259 finally:
3218 del repo._subtoppath
3260 del repo._subtoppath
3219
3261
3220
3262
3221 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3263 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3222 def init(ui, dest=".", **opts):
3264 def init(ui, dest=".", **opts):
3223 """create a new repository in the given directory
3265 """create a new repository in the given directory
3224
3266
3225 Initialize a new repository in the given directory. If the given
3267 Initialize a new repository in the given directory. If the given
3226 directory does not exist, it will be created.
3268 directory does not exist, it will be created.
3227
3269
3228 If no directory is given, the current directory is used.
3270 If no directory is given, the current directory is used.
3229
3271
3230 It is possible to specify an ``ssh://`` URL as the destination.
3272 It is possible to specify an ``ssh://`` URL as the destination.
3231 See :hg:`help urls` for more information.
3273 See :hg:`help urls` for more information.
3232
3274
3233 Returns 0 on success.
3275 Returns 0 on success.
3234 """
3276 """
3235 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3277 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3236
3278
3237 @command('locate',
3279 @command('locate',
3238 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3280 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3239 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3281 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3240 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3282 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3241 ] + walkopts,
3283 ] + walkopts,
3242 _('[OPTION]... [PATTERN]...'))
3284 _('[OPTION]... [PATTERN]...'))
3243 def locate(ui, repo, *pats, **opts):
3285 def locate(ui, repo, *pats, **opts):
3244 """locate files matching specific patterns
3286 """locate files matching specific patterns
3245
3287
3246 Print files under Mercurial control in the working directory whose
3288 Print files under Mercurial control in the working directory whose
3247 names match the given patterns.
3289 names match the given patterns.
3248
3290
3249 By default, this command searches all directories in the working
3291 By default, this command searches all directories in the working
3250 directory. To search just the current directory and its
3292 directory. To search just the current directory and its
3251 subdirectories, use "--include .".
3293 subdirectories, use "--include .".
3252
3294
3253 If no patterns are given to match, this command prints the names
3295 If no patterns are given to match, this command prints the names
3254 of all files under Mercurial control in the working directory.
3296 of all files under Mercurial control in the working directory.
3255
3297
3256 If you want to feed the output of this command into the "xargs"
3298 If you want to feed the output of this command into the "xargs"
3257 command, use the -0 option to both this command and "xargs". This
3299 command, use the -0 option to both this command and "xargs". This
3258 will avoid the problem of "xargs" treating single filenames that
3300 will avoid the problem of "xargs" treating single filenames that
3259 contain whitespace as multiple filenames.
3301 contain whitespace as multiple filenames.
3260
3302
3261 Returns 0 if a match is found, 1 otherwise.
3303 Returns 0 if a match is found, 1 otherwise.
3262 """
3304 """
3263 end = opts.get('print0') and '\0' or '\n'
3305 end = opts.get('print0') and '\0' or '\n'
3264 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3306 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3265
3307
3266 ret = 1
3308 ret = 1
3267 m = scmutil.match(repo, pats, opts, default='relglob')
3309 m = scmutil.match(repo, pats, opts, default='relglob')
3268 m.bad = lambda x, y: False
3310 m.bad = lambda x, y: False
3269 for abs in repo[rev].walk(m):
3311 for abs in repo[rev].walk(m):
3270 if not rev and abs not in repo.dirstate:
3312 if not rev and abs not in repo.dirstate:
3271 continue
3313 continue
3272 if opts.get('fullpath'):
3314 if opts.get('fullpath'):
3273 ui.write(repo.wjoin(abs), end)
3315 ui.write(repo.wjoin(abs), end)
3274 else:
3316 else:
3275 ui.write(((pats and m.rel(abs)) or abs), end)
3317 ui.write(((pats and m.rel(abs)) or abs), end)
3276 ret = 0
3318 ret = 0
3277
3319
3278 return ret
3320 return ret
3279
3321
3280 @command('^log|history',
3322 @command('^log|history',
3281 [('f', 'follow', None,
3323 [('f', 'follow', None,
3282 _('follow changeset history, or file history across copies and renames')),
3324 _('follow changeset history, or file history across copies and renames')),
3283 ('', 'follow-first', None,
3325 ('', 'follow-first', None,
3284 _('only follow the first parent of merge changesets')),
3326 _('only follow the first parent of merge changesets')),
3285 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3327 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3286 ('C', 'copies', None, _('show copied files')),
3328 ('C', 'copies', None, _('show copied files')),
3287 ('k', 'keyword', [],
3329 ('k', 'keyword', [],
3288 _('do case-insensitive search for a given text'), _('TEXT')),
3330 _('do case-insensitive search for a given text'), _('TEXT')),
3289 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3331 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3290 ('', 'removed', None, _('include revisions where files were removed')),
3332 ('', 'removed', None, _('include revisions where files were removed')),
3291 ('m', 'only-merges', None, _('show only merges')),
3333 ('m', 'only-merges', None, _('show only merges')),
3292 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3334 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3293 ('', 'only-branch', [],
3335 ('', 'only-branch', [],
3294 _('show only changesets within the given named branch (DEPRECATED)'),
3336 _('show only changesets within the given named branch (DEPRECATED)'),
3295 _('BRANCH')),
3337 _('BRANCH')),
3296 ('b', 'branch', [],
3338 ('b', 'branch', [],
3297 _('show changesets within the given named branch'), _('BRANCH')),
3339 _('show changesets within the given named branch'), _('BRANCH')),
3298 ('P', 'prune', [],
3340 ('P', 'prune', [],
3299 _('do not display revision or any of its ancestors'), _('REV')),
3341 _('do not display revision or any of its ancestors'), _('REV')),
3300 ] + logopts + walkopts,
3342 ] + logopts + walkopts,
3301 _('[OPTION]... [FILE]'))
3343 _('[OPTION]... [FILE]'))
3302 def log(ui, repo, *pats, **opts):
3344 def log(ui, repo, *pats, **opts):
3303 """show revision history of entire repository or files
3345 """show revision history of entire repository or files
3304
3346
3305 Print the revision history of the specified files or the entire
3347 Print the revision history of the specified files or the entire
3306 project.
3348 project.
3307
3349
3308 File history is shown without following rename or copy history of
3350 File history is shown without following rename or copy history of
3309 files. Use -f/--follow with a filename to follow history across
3351 files. Use -f/--follow with a filename to follow history across
3310 renames and copies. --follow without a filename will only show
3352 renames and copies. --follow without a filename will only show
3311 ancestors or descendants of the starting revision. --follow-first
3353 ancestors or descendants of the starting revision. --follow-first
3312 only follows the first parent of merge revisions.
3354 only follows the first parent of merge revisions.
3313
3355
3314 If no revision range is specified, the default is ``tip:0`` unless
3356 If no revision range is specified, the default is ``tip:0`` unless
3315 --follow is set, in which case the working directory parent is
3357 --follow is set, in which case the working directory parent is
3316 used as the starting revision. You can specify a revision set for
3358 used as the starting revision. You can specify a revision set for
3317 log, see :hg:`help revsets` for more information.
3359 log, see :hg:`help revsets` for more information.
3318
3360
3319 See :hg:`help dates` for a list of formats valid for -d/--date.
3361 See :hg:`help dates` for a list of formats valid for -d/--date.
3320
3362
3321 By default this command prints revision number and changeset id,
3363 By default this command prints revision number and changeset id,
3322 tags, non-trivial parents, user, date and time, and a summary for
3364 tags, non-trivial parents, user, date and time, and a summary for
3323 each commit. When the -v/--verbose switch is used, the list of
3365 each commit. When the -v/--verbose switch is used, the list of
3324 changed files and full commit message are shown.
3366 changed files and full commit message are shown.
3325
3367
3326 .. note::
3368 .. note::
3327 log -p/--patch may generate unexpected diff output for merge
3369 log -p/--patch may generate unexpected diff output for merge
3328 changesets, as it will only compare the merge changeset against
3370 changesets, as it will only compare the merge changeset against
3329 its first parent. Also, only files different from BOTH parents
3371 its first parent. Also, only files different from BOTH parents
3330 will appear in files:.
3372 will appear in files:.
3331
3373
3332 Returns 0 on success.
3374 Returns 0 on success.
3333 """
3375 """
3334
3376
3335 matchfn = scmutil.match(repo, pats, opts)
3377 matchfn = scmutil.match(repo, pats, opts)
3336 limit = cmdutil.loglimit(opts)
3378 limit = cmdutil.loglimit(opts)
3337 count = 0
3379 count = 0
3338
3380
3339 endrev = None
3381 endrev = None
3340 if opts.get('copies') and opts.get('rev'):
3382 if opts.get('copies') and opts.get('rev'):
3341 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3383 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3342
3384
3343 df = False
3385 df = False
3344 if opts["date"]:
3386 if opts["date"]:
3345 df = util.matchdate(opts["date"])
3387 df = util.matchdate(opts["date"])
3346
3388
3347 branches = opts.get('branch', []) + opts.get('only_branch', [])
3389 branches = opts.get('branch', []) + opts.get('only_branch', [])
3348 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3390 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3349
3391
3350 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3392 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3351 def prep(ctx, fns):
3393 def prep(ctx, fns):
3352 rev = ctx.rev()
3394 rev = ctx.rev()
3353 parents = [p for p in repo.changelog.parentrevs(rev)
3395 parents = [p for p in repo.changelog.parentrevs(rev)
3354 if p != nullrev]
3396 if p != nullrev]
3355 if opts.get('no_merges') and len(parents) == 2:
3397 if opts.get('no_merges') and len(parents) == 2:
3356 return
3398 return
3357 if opts.get('only_merges') and len(parents) != 2:
3399 if opts.get('only_merges') and len(parents) != 2:
3358 return
3400 return
3359 if opts.get('branch') and ctx.branch() not in opts['branch']:
3401 if opts.get('branch') and ctx.branch() not in opts['branch']:
3360 return
3402 return
3361 if df and not df(ctx.date()[0]):
3403 if df and not df(ctx.date()[0]):
3362 return
3404 return
3363 if opts['user'] and not [k for k in opts['user']
3405 if opts['user'] and not [k for k in opts['user']
3364 if k.lower() in ctx.user().lower()]:
3406 if k.lower() in ctx.user().lower()]:
3365 return
3407 return
3366 if opts.get('keyword'):
3408 if opts.get('keyword'):
3367 for k in [kw.lower() for kw in opts['keyword']]:
3409 for k in [kw.lower() for kw in opts['keyword']]:
3368 if (k in ctx.user().lower() or
3410 if (k in ctx.user().lower() or
3369 k in ctx.description().lower() or
3411 k in ctx.description().lower() or
3370 k in " ".join(ctx.files()).lower()):
3412 k in " ".join(ctx.files()).lower()):
3371 break
3413 break
3372 else:
3414 else:
3373 return
3415 return
3374
3416
3375 copies = None
3417 copies = None
3376 if opts.get('copies') and rev:
3418 if opts.get('copies') and rev:
3377 copies = []
3419 copies = []
3378 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3420 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3379 for fn in ctx.files():
3421 for fn in ctx.files():
3380 rename = getrenamed(fn, rev)
3422 rename = getrenamed(fn, rev)
3381 if rename:
3423 if rename:
3382 copies.append((fn, rename[0]))
3424 copies.append((fn, rename[0]))
3383
3425
3384 revmatchfn = None
3426 revmatchfn = None
3385 if opts.get('patch') or opts.get('stat'):
3427 if opts.get('patch') or opts.get('stat'):
3386 if opts.get('follow') or opts.get('follow_first'):
3428 if opts.get('follow') or opts.get('follow_first'):
3387 # note: this might be wrong when following through merges
3429 # note: this might be wrong when following through merges
3388 revmatchfn = scmutil.match(repo, fns, default='path')
3430 revmatchfn = scmutil.match(repo, fns, default='path')
3389 else:
3431 else:
3390 revmatchfn = matchfn
3432 revmatchfn = matchfn
3391
3433
3392 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3434 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3393
3435
3394 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3436 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3395 if count == limit:
3437 if count == limit:
3396 break
3438 break
3397 if displayer.flush(ctx.rev()):
3439 if displayer.flush(ctx.rev()):
3398 count += 1
3440 count += 1
3399 displayer.close()
3441 displayer.close()
3400
3442
3401 @command('manifest',
3443 @command('manifest',
3402 [('r', 'rev', '', _('revision to display'), _('REV')),
3444 [('r', 'rev', '', _('revision to display'), _('REV')),
3403 ('', 'all', False, _("list files from all revisions"))],
3445 ('', 'all', False, _("list files from all revisions"))],
3404 _('[-r REV]'))
3446 _('[-r REV]'))
3405 def manifest(ui, repo, node=None, rev=None, **opts):
3447 def manifest(ui, repo, node=None, rev=None, **opts):
3406 """output the current or given revision of the project manifest
3448 """output the current or given revision of the project manifest
3407
3449
3408 Print a list of version controlled files for the given revision.
3450 Print a list of version controlled files for the given revision.
3409 If no revision is given, the first parent of the working directory
3451 If no revision is given, the first parent of the working directory
3410 is used, or the null revision if no revision is checked out.
3452 is used, or the null revision if no revision is checked out.
3411
3453
3412 With -v, print file permissions, symlink and executable bits.
3454 With -v, print file permissions, symlink and executable bits.
3413 With --debug, print file revision hashes.
3455 With --debug, print file revision hashes.
3414
3456
3415 If option --all is specified, the list of all files from all revisions
3457 If option --all is specified, the list of all files from all revisions
3416 is printed. This includes deleted and renamed files.
3458 is printed. This includes deleted and renamed files.
3417
3459
3418 Returns 0 on success.
3460 Returns 0 on success.
3419 """
3461 """
3420 if opts.get('all'):
3462 if opts.get('all'):
3421 if rev or node:
3463 if rev or node:
3422 raise util.Abort(_("can't specify a revision with --all"))
3464 raise util.Abort(_("can't specify a revision with --all"))
3423
3465
3424 res = []
3466 res = []
3425 prefix = "data/"
3467 prefix = "data/"
3426 suffix = ".i"
3468 suffix = ".i"
3427 plen = len(prefix)
3469 plen = len(prefix)
3428 slen = len(suffix)
3470 slen = len(suffix)
3429 lock = repo.lock()
3471 lock = repo.lock()
3430 try:
3472 try:
3431 for fn, b, size in repo.store.datafiles():
3473 for fn, b, size in repo.store.datafiles():
3432 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3474 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3433 res.append(fn[plen:-slen])
3475 res.append(fn[plen:-slen])
3434 finally:
3476 finally:
3435 lock.release()
3477 lock.release()
3436 for f in sorted(res):
3478 for f in sorted(res):
3437 ui.write("%s\n" % f)
3479 ui.write("%s\n" % f)
3438 return
3480 return
3439
3481
3440 if rev and node:
3482 if rev and node:
3441 raise util.Abort(_("please specify just one revision"))
3483 raise util.Abort(_("please specify just one revision"))
3442
3484
3443 if not node:
3485 if not node:
3444 node = rev
3486 node = rev
3445
3487
3446 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3488 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3447 ctx = scmutil.revsingle(repo, node)
3489 ctx = scmutil.revsingle(repo, node)
3448 for f in ctx:
3490 for f in ctx:
3449 if ui.debugflag:
3491 if ui.debugflag:
3450 ui.write("%40s " % hex(ctx.manifest()[f]))
3492 ui.write("%40s " % hex(ctx.manifest()[f]))
3451 if ui.verbose:
3493 if ui.verbose:
3452 ui.write(decor[ctx.flags(f)])
3494 ui.write(decor[ctx.flags(f)])
3453 ui.write("%s\n" % f)
3495 ui.write("%s\n" % f)
3454
3496
3455 @command('^merge',
3497 @command('^merge',
3456 [('f', 'force', None, _('force a merge with outstanding changes')),
3498 [('f', 'force', None, _('force a merge with outstanding changes')),
3457 ('t', 'tool', '', _('specify merge tool')),
3499 ('t', 'tool', '', _('specify merge tool')),
3458 ('r', 'rev', '', _('revision to merge'), _('REV')),
3500 ('r', 'rev', '', _('revision to merge'), _('REV')),
3459 ('P', 'preview', None,
3501 ('P', 'preview', None,
3460 _('review revisions to merge (no merge is performed)'))],
3502 _('review revisions to merge (no merge is performed)'))],
3461 _('[-P] [-f] [[-r] REV]'))
3503 _('[-P] [-f] [[-r] REV]'))
3462 def merge(ui, repo, node=None, **opts):
3504 def merge(ui, repo, node=None, **opts):
3463 """merge working directory with another revision
3505 """merge working directory with another revision
3464
3506
3465 The current working directory is updated with all changes made in
3507 The current working directory is updated with all changes made in
3466 the requested revision since the last common predecessor revision.
3508 the requested revision since the last common predecessor revision.
3467
3509
3468 Files that changed between either parent are marked as changed for
3510 Files that changed between either parent are marked as changed for
3469 the next commit and a commit must be performed before any further
3511 the next commit and a commit must be performed before any further
3470 updates to the repository are allowed. The next commit will have
3512 updates to the repository are allowed. The next commit will have
3471 two parents.
3513 two parents.
3472
3514
3473 ``--tool`` can be used to specify the merge tool used for file
3515 ``--tool`` can be used to specify the merge tool used for file
3474 merges. It overrides the HGMERGE environment variable and your
3516 merges. It overrides the HGMERGE environment variable and your
3475 configuration files. See :hg:`help merge-tools` for options.
3517 configuration files. See :hg:`help merge-tools` for options.
3476
3518
3477 If no revision is specified, the working directory's parent is a
3519 If no revision is specified, the working directory's parent is a
3478 head revision, and the current branch contains exactly one other
3520 head revision, and the current branch contains exactly one other
3479 head, the other head is merged with by default. Otherwise, an
3521 head, the other head is merged with by default. Otherwise, an
3480 explicit revision with which to merge with must be provided.
3522 explicit revision with which to merge with must be provided.
3481
3523
3482 :hg:`resolve` must be used to resolve unresolved files.
3524 :hg:`resolve` must be used to resolve unresolved files.
3483
3525
3484 To undo an uncommitted merge, use :hg:`update --clean .` which
3526 To undo an uncommitted merge, use :hg:`update --clean .` which
3485 will check out a clean copy of the original merge parent, losing
3527 will check out a clean copy of the original merge parent, losing
3486 all changes.
3528 all changes.
3487
3529
3488 Returns 0 on success, 1 if there are unresolved files.
3530 Returns 0 on success, 1 if there are unresolved files.
3489 """
3531 """
3490
3532
3491 if opts.get('rev') and node:
3533 if opts.get('rev') and node:
3492 raise util.Abort(_("please specify just one revision"))
3534 raise util.Abort(_("please specify just one revision"))
3493 if not node:
3535 if not node:
3494 node = opts.get('rev')
3536 node = opts.get('rev')
3495
3537
3496 if not node:
3538 if not node:
3497 branch = repo[None].branch()
3539 branch = repo[None].branch()
3498 bheads = repo.branchheads(branch)
3540 bheads = repo.branchheads(branch)
3499 if len(bheads) > 2:
3541 if len(bheads) > 2:
3500 raise util.Abort(_("branch '%s' has %d heads - "
3542 raise util.Abort(_("branch '%s' has %d heads - "
3501 "please merge with an explicit rev")
3543 "please merge with an explicit rev")
3502 % (branch, len(bheads)),
3544 % (branch, len(bheads)),
3503 hint=_("run 'hg heads .' to see heads"))
3545 hint=_("run 'hg heads .' to see heads"))
3504
3546
3505 parent = repo.dirstate.p1()
3547 parent = repo.dirstate.p1()
3506 if len(bheads) == 1:
3548 if len(bheads) == 1:
3507 if len(repo.heads()) > 1:
3549 if len(repo.heads()) > 1:
3508 raise util.Abort(_("branch '%s' has one head - "
3550 raise util.Abort(_("branch '%s' has one head - "
3509 "please merge with an explicit rev")
3551 "please merge with an explicit rev")
3510 % branch,
3552 % branch,
3511 hint=_("run 'hg heads' to see all heads"))
3553 hint=_("run 'hg heads' to see all heads"))
3512 msg = _('there is nothing to merge')
3554 msg = _('there is nothing to merge')
3513 if parent != repo.lookup(repo[None].branch()):
3555 if parent != repo.lookup(repo[None].branch()):
3514 msg = _('%s - use "hg update" instead') % msg
3556 msg = _('%s - use "hg update" instead') % msg
3515 raise util.Abort(msg)
3557 raise util.Abort(msg)
3516
3558
3517 if parent not in bheads:
3559 if parent not in bheads:
3518 raise util.Abort(_('working directory not at a head revision'),
3560 raise util.Abort(_('working directory not at a head revision'),
3519 hint=_("use 'hg update' or merge with an "
3561 hint=_("use 'hg update' or merge with an "
3520 "explicit revision"))
3562 "explicit revision"))
3521 node = parent == bheads[0] and bheads[-1] or bheads[0]
3563 node = parent == bheads[0] and bheads[-1] or bheads[0]
3522 else:
3564 else:
3523 node = scmutil.revsingle(repo, node).node()
3565 node = scmutil.revsingle(repo, node).node()
3524
3566
3525 if opts.get('preview'):
3567 if opts.get('preview'):
3526 # find nodes that are ancestors of p2 but not of p1
3568 # find nodes that are ancestors of p2 but not of p1
3527 p1 = repo.lookup('.')
3569 p1 = repo.lookup('.')
3528 p2 = repo.lookup(node)
3570 p2 = repo.lookup(node)
3529 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3571 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3530
3572
3531 displayer = cmdutil.show_changeset(ui, repo, opts)
3573 displayer = cmdutil.show_changeset(ui, repo, opts)
3532 for node in nodes:
3574 for node in nodes:
3533 displayer.show(repo[node])
3575 displayer.show(repo[node])
3534 displayer.close()
3576 displayer.close()
3535 return 0
3577 return 0
3536
3578
3537 try:
3579 try:
3538 # ui.forcemerge is an internal variable, do not document
3580 # ui.forcemerge is an internal variable, do not document
3539 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3581 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3540 return hg.merge(repo, node, force=opts.get('force'))
3582 return hg.merge(repo, node, force=opts.get('force'))
3541 finally:
3583 finally:
3542 ui.setconfig('ui', 'forcemerge', '')
3584 ui.setconfig('ui', 'forcemerge', '')
3543
3585
3544 @command('outgoing|out',
3586 @command('outgoing|out',
3545 [('f', 'force', None, _('run even when the destination is unrelated')),
3587 [('f', 'force', None, _('run even when the destination is unrelated')),
3546 ('r', 'rev', [],
3588 ('r', 'rev', [],
3547 _('a changeset intended to be included in the destination'), _('REV')),
3589 _('a changeset intended to be included in the destination'), _('REV')),
3548 ('n', 'newest-first', None, _('show newest record first')),
3590 ('n', 'newest-first', None, _('show newest record first')),
3549 ('B', 'bookmarks', False, _('compare bookmarks')),
3591 ('B', 'bookmarks', False, _('compare bookmarks')),
3550 ('b', 'branch', [], _('a specific branch you would like to push'),
3592 ('b', 'branch', [], _('a specific branch you would like to push'),
3551 _('BRANCH')),
3593 _('BRANCH')),
3552 ] + logopts + remoteopts + subrepoopts,
3594 ] + logopts + remoteopts + subrepoopts,
3553 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3595 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3554 def outgoing(ui, repo, dest=None, **opts):
3596 def outgoing(ui, repo, dest=None, **opts):
3555 """show changesets not found in the destination
3597 """show changesets not found in the destination
3556
3598
3557 Show changesets not found in the specified destination repository
3599 Show changesets not found in the specified destination repository
3558 or the default push location. These are the changesets that would
3600 or the default push location. These are the changesets that would
3559 be pushed if a push was requested.
3601 be pushed if a push was requested.
3560
3602
3561 See pull for details of valid destination formats.
3603 See pull for details of valid destination formats.
3562
3604
3563 Returns 0 if there are outgoing changes, 1 otherwise.
3605 Returns 0 if there are outgoing changes, 1 otherwise.
3564 """
3606 """
3565
3607
3566 if opts.get('bookmarks'):
3608 if opts.get('bookmarks'):
3567 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3609 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3568 dest, branches = hg.parseurl(dest, opts.get('branch'))
3610 dest, branches = hg.parseurl(dest, opts.get('branch'))
3569 other = hg.peer(repo, opts, dest)
3611 other = hg.peer(repo, opts, dest)
3570 if 'bookmarks' not in other.listkeys('namespaces'):
3612 if 'bookmarks' not in other.listkeys('namespaces'):
3571 ui.warn(_("remote doesn't support bookmarks\n"))
3613 ui.warn(_("remote doesn't support bookmarks\n"))
3572 return 0
3614 return 0
3573 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3615 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3574 return bookmarks.diff(ui, other, repo)
3616 return bookmarks.diff(ui, other, repo)
3575
3617
3576 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3618 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3577 try:
3619 try:
3578 return hg.outgoing(ui, repo, dest, opts)
3620 return hg.outgoing(ui, repo, dest, opts)
3579 finally:
3621 finally:
3580 del repo._subtoppath
3622 del repo._subtoppath
3581
3623
3582 @command('parents',
3624 @command('parents',
3583 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3625 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3584 ] + templateopts,
3626 ] + templateopts,
3585 _('[-r REV] [FILE]'))
3627 _('[-r REV] [FILE]'))
3586 def parents(ui, repo, file_=None, **opts):
3628 def parents(ui, repo, file_=None, **opts):
3587 """show the parents of the working directory or revision
3629 """show the parents of the working directory or revision
3588
3630
3589 Print the working directory's parent revisions. If a revision is
3631 Print the working directory's parent revisions. If a revision is
3590 given via -r/--rev, the parent of that revision will be printed.
3632 given via -r/--rev, the parent of that revision will be printed.
3591 If a file argument is given, the revision in which the file was
3633 If a file argument is given, the revision in which the file was
3592 last changed (before the working directory revision or the
3634 last changed (before the working directory revision or the
3593 argument to --rev if given) is printed.
3635 argument to --rev if given) is printed.
3594
3636
3595 Returns 0 on success.
3637 Returns 0 on success.
3596 """
3638 """
3597
3639
3598 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3640 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3599
3641
3600 if file_:
3642 if file_:
3601 m = scmutil.match(repo, (file_,), opts)
3643 m = scmutil.match(repo, (file_,), opts)
3602 if m.anypats() or len(m.files()) != 1:
3644 if m.anypats() or len(m.files()) != 1:
3603 raise util.Abort(_('can only specify an explicit filename'))
3645 raise util.Abort(_('can only specify an explicit filename'))
3604 file_ = m.files()[0]
3646 file_ = m.files()[0]
3605 filenodes = []
3647 filenodes = []
3606 for cp in ctx.parents():
3648 for cp in ctx.parents():
3607 if not cp:
3649 if not cp:
3608 continue
3650 continue
3609 try:
3651 try:
3610 filenodes.append(cp.filenode(file_))
3652 filenodes.append(cp.filenode(file_))
3611 except error.LookupError:
3653 except error.LookupError:
3612 pass
3654 pass
3613 if not filenodes:
3655 if not filenodes:
3614 raise util.Abort(_("'%s' not found in manifest!") % file_)
3656 raise util.Abort(_("'%s' not found in manifest!") % file_)
3615 fl = repo.file(file_)
3657 fl = repo.file(file_)
3616 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3658 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3617 else:
3659 else:
3618 p = [cp.node() for cp in ctx.parents()]
3660 p = [cp.node() for cp in ctx.parents()]
3619
3661
3620 displayer = cmdutil.show_changeset(ui, repo, opts)
3662 displayer = cmdutil.show_changeset(ui, repo, opts)
3621 for n in p:
3663 for n in p:
3622 if n != nullid:
3664 if n != nullid:
3623 displayer.show(repo[n])
3665 displayer.show(repo[n])
3624 displayer.close()
3666 displayer.close()
3625
3667
3626 @command('paths', [], _('[NAME]'))
3668 @command('paths', [], _('[NAME]'))
3627 def paths(ui, repo, search=None):
3669 def paths(ui, repo, search=None):
3628 """show aliases for remote repositories
3670 """show aliases for remote repositories
3629
3671
3630 Show definition of symbolic path name NAME. If no name is given,
3672 Show definition of symbolic path name NAME. If no name is given,
3631 show definition of all available names.
3673 show definition of all available names.
3632
3674
3633 Option -q/--quiet suppresses all output when searching for NAME
3675 Option -q/--quiet suppresses all output when searching for NAME
3634 and shows only the path names when listing all definitions.
3676 and shows only the path names when listing all definitions.
3635
3677
3636 Path names are defined in the [paths] section of your
3678 Path names are defined in the [paths] section of your
3637 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3679 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3638 repository, ``.hg/hgrc`` is used, too.
3680 repository, ``.hg/hgrc`` is used, too.
3639
3681
3640 The path names ``default`` and ``default-push`` have a special
3682 The path names ``default`` and ``default-push`` have a special
3641 meaning. When performing a push or pull operation, they are used
3683 meaning. When performing a push or pull operation, they are used
3642 as fallbacks if no location is specified on the command-line.
3684 as fallbacks if no location is specified on the command-line.
3643 When ``default-push`` is set, it will be used for push and
3685 When ``default-push`` is set, it will be used for push and
3644 ``default`` will be used for pull; otherwise ``default`` is used
3686 ``default`` will be used for pull; otherwise ``default`` is used
3645 as the fallback for both. When cloning a repository, the clone
3687 as the fallback for both. When cloning a repository, the clone
3646 source is written as ``default`` in ``.hg/hgrc``. Note that
3688 source is written as ``default`` in ``.hg/hgrc``. Note that
3647 ``default`` and ``default-push`` apply to all inbound (e.g.
3689 ``default`` and ``default-push`` apply to all inbound (e.g.
3648 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3690 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3649 :hg:`bundle`) operations.
3691 :hg:`bundle`) operations.
3650
3692
3651 See :hg:`help urls` for more information.
3693 See :hg:`help urls` for more information.
3652
3694
3653 Returns 0 on success.
3695 Returns 0 on success.
3654 """
3696 """
3655 if search:
3697 if search:
3656 for name, path in ui.configitems("paths"):
3698 for name, path in ui.configitems("paths"):
3657 if name == search:
3699 if name == search:
3658 ui.status("%s\n" % util.hidepassword(path))
3700 ui.status("%s\n" % util.hidepassword(path))
3659 return
3701 return
3660 if not ui.quiet:
3702 if not ui.quiet:
3661 ui.warn(_("not found!\n"))
3703 ui.warn(_("not found!\n"))
3662 return 1
3704 return 1
3663 else:
3705 else:
3664 for name, path in ui.configitems("paths"):
3706 for name, path in ui.configitems("paths"):
3665 if ui.quiet:
3707 if ui.quiet:
3666 ui.write("%s\n" % name)
3708 ui.write("%s\n" % name)
3667 else:
3709 else:
3668 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3710 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3669
3711
3670 def postincoming(ui, repo, modheads, optupdate, checkout):
3712 def postincoming(ui, repo, modheads, optupdate, checkout):
3671 if modheads == 0:
3713 if modheads == 0:
3672 return
3714 return
3673 if optupdate:
3715 if optupdate:
3674 try:
3716 try:
3675 return hg.update(repo, checkout)
3717 return hg.update(repo, checkout)
3676 except util.Abort, inst:
3718 except util.Abort, inst:
3677 ui.warn(_("not updating: %s\n" % str(inst)))
3719 ui.warn(_("not updating: %s\n" % str(inst)))
3678 return 0
3720 return 0
3679 if modheads > 1:
3721 if modheads > 1:
3680 currentbranchheads = len(repo.branchheads())
3722 currentbranchheads = len(repo.branchheads())
3681 if currentbranchheads == modheads:
3723 if currentbranchheads == modheads:
3682 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3724 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3683 elif currentbranchheads > 1:
3725 elif currentbranchheads > 1:
3684 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3726 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3685 else:
3727 else:
3686 ui.status(_("(run 'hg heads' to see heads)\n"))
3728 ui.status(_("(run 'hg heads' to see heads)\n"))
3687 else:
3729 else:
3688 ui.status(_("(run 'hg update' to get a working copy)\n"))
3730 ui.status(_("(run 'hg update' to get a working copy)\n"))
3689
3731
3690 @command('^pull',
3732 @command('^pull',
3691 [('u', 'update', None,
3733 [('u', 'update', None,
3692 _('update to new branch head if changesets were pulled')),
3734 _('update to new branch head if changesets were pulled')),
3693 ('f', 'force', None, _('run even when remote repository is unrelated')),
3735 ('f', 'force', None, _('run even when remote repository is unrelated')),
3694 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3736 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3695 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3737 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3696 ('b', 'branch', [], _('a specific branch you would like to pull'),
3738 ('b', 'branch', [], _('a specific branch you would like to pull'),
3697 _('BRANCH')),
3739 _('BRANCH')),
3698 ] + remoteopts,
3740 ] + remoteopts,
3699 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3741 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3700 def pull(ui, repo, source="default", **opts):
3742 def pull(ui, repo, source="default", **opts):
3701 """pull changes from the specified source
3743 """pull changes from the specified source
3702
3744
3703 Pull changes from a remote repository to a local one.
3745 Pull changes from a remote repository to a local one.
3704
3746
3705 This finds all changes from the repository at the specified path
3747 This finds all changes from the repository at the specified path
3706 or URL and adds them to a local repository (the current one unless
3748 or URL and adds them to a local repository (the current one unless
3707 -R is specified). By default, this does not update the copy of the
3749 -R is specified). By default, this does not update the copy of the
3708 project in the working directory.
3750 project in the working directory.
3709
3751
3710 Use :hg:`incoming` if you want to see what would have been added
3752 Use :hg:`incoming` if you want to see what would have been added
3711 by a pull at the time you issued this command. If you then decide
3753 by a pull at the time you issued this command. If you then decide
3712 to add those changes to the repository, you should use :hg:`pull
3754 to add those changes to the repository, you should use :hg:`pull
3713 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3755 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3714
3756
3715 If SOURCE is omitted, the 'default' path will be used.
3757 If SOURCE is omitted, the 'default' path will be used.
3716 See :hg:`help urls` for more information.
3758 See :hg:`help urls` for more information.
3717
3759
3718 Returns 0 on success, 1 if an update had unresolved files.
3760 Returns 0 on success, 1 if an update had unresolved files.
3719 """
3761 """
3720 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3762 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3721 other = hg.peer(repo, opts, source)
3763 other = hg.peer(repo, opts, source)
3722 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3764 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3723 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3765 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3724
3766
3725 if opts.get('bookmark'):
3767 if opts.get('bookmark'):
3726 if not revs:
3768 if not revs:
3727 revs = []
3769 revs = []
3728 rb = other.listkeys('bookmarks')
3770 rb = other.listkeys('bookmarks')
3729 for b in opts['bookmark']:
3771 for b in opts['bookmark']:
3730 if b not in rb:
3772 if b not in rb:
3731 raise util.Abort(_('remote bookmark %s not found!') % b)
3773 raise util.Abort(_('remote bookmark %s not found!') % b)
3732 revs.append(rb[b])
3774 revs.append(rb[b])
3733
3775
3734 if revs:
3776 if revs:
3735 try:
3777 try:
3736 revs = [other.lookup(rev) for rev in revs]
3778 revs = [other.lookup(rev) for rev in revs]
3737 except error.CapabilityError:
3779 except error.CapabilityError:
3738 err = _("other repository doesn't support revision lookup, "
3780 err = _("other repository doesn't support revision lookup, "
3739 "so a rev cannot be specified.")
3781 "so a rev cannot be specified.")
3740 raise util.Abort(err)
3782 raise util.Abort(err)
3741
3783
3742 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3784 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3743 bookmarks.updatefromremote(ui, repo, other)
3785 bookmarks.updatefromremote(ui, repo, other)
3744 if checkout:
3786 if checkout:
3745 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3787 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3746 repo._subtoppath = source
3788 repo._subtoppath = source
3747 try:
3789 try:
3748 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3790 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3749
3791
3750 finally:
3792 finally:
3751 del repo._subtoppath
3793 del repo._subtoppath
3752
3794
3753 # update specified bookmarks
3795 # update specified bookmarks
3754 if opts.get('bookmark'):
3796 if opts.get('bookmark'):
3755 for b in opts['bookmark']:
3797 for b in opts['bookmark']:
3756 # explicit pull overrides local bookmark if any
3798 # explicit pull overrides local bookmark if any
3757 ui.status(_("importing bookmark %s\n") % b)
3799 ui.status(_("importing bookmark %s\n") % b)
3758 repo._bookmarks[b] = repo[rb[b]].node()
3800 repo._bookmarks[b] = repo[rb[b]].node()
3759 bookmarks.write(repo)
3801 bookmarks.write(repo)
3760
3802
3761 return ret
3803 return ret
3762
3804
3763 @command('^push',
3805 @command('^push',
3764 [('f', 'force', None, _('force push')),
3806 [('f', 'force', None, _('force push')),
3765 ('r', 'rev', [],
3807 ('r', 'rev', [],
3766 _('a changeset intended to be included in the destination'),
3808 _('a changeset intended to be included in the destination'),
3767 _('REV')),
3809 _('REV')),
3768 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3810 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3769 ('b', 'branch', [],
3811 ('b', 'branch', [],
3770 _('a specific branch you would like to push'), _('BRANCH')),
3812 _('a specific branch you would like to push'), _('BRANCH')),
3771 ('', 'new-branch', False, _('allow pushing a new branch')),
3813 ('', 'new-branch', False, _('allow pushing a new branch')),
3772 ] + remoteopts,
3814 ] + remoteopts,
3773 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3815 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3774 def push(ui, repo, dest=None, **opts):
3816 def push(ui, repo, dest=None, **opts):
3775 """push changes to the specified destination
3817 """push changes to the specified destination
3776
3818
3777 Push changesets from the local repository to the specified
3819 Push changesets from the local repository to the specified
3778 destination.
3820 destination.
3779
3821
3780 This operation is symmetrical to pull: it is identical to a pull
3822 This operation is symmetrical to pull: it is identical to a pull
3781 in the destination repository from the current one.
3823 in the destination repository from the current one.
3782
3824
3783 By default, push will not allow creation of new heads at the
3825 By default, push will not allow creation of new heads at the
3784 destination, since multiple heads would make it unclear which head
3826 destination, since multiple heads would make it unclear which head
3785 to use. In this situation, it is recommended to pull and merge
3827 to use. In this situation, it is recommended to pull and merge
3786 before pushing.
3828 before pushing.
3787
3829
3788 Use --new-branch if you want to allow push to create a new named
3830 Use --new-branch if you want to allow push to create a new named
3789 branch that is not present at the destination. This allows you to
3831 branch that is not present at the destination. This allows you to
3790 only create a new branch without forcing other changes.
3832 only create a new branch without forcing other changes.
3791
3833
3792 Use -f/--force to override the default behavior and push all
3834 Use -f/--force to override the default behavior and push all
3793 changesets on all branches.
3835 changesets on all branches.
3794
3836
3795 If -r/--rev is used, the specified revision and all its ancestors
3837 If -r/--rev is used, the specified revision and all its ancestors
3796 will be pushed to the remote repository.
3838 will be pushed to the remote repository.
3797
3839
3798 Please see :hg:`help urls` for important details about ``ssh://``
3840 Please see :hg:`help urls` for important details about ``ssh://``
3799 URLs. If DESTINATION is omitted, a default path will be used.
3841 URLs. If DESTINATION is omitted, a default path will be used.
3800
3842
3801 Returns 0 if push was successful, 1 if nothing to push.
3843 Returns 0 if push was successful, 1 if nothing to push.
3802 """
3844 """
3803
3845
3804 if opts.get('bookmark'):
3846 if opts.get('bookmark'):
3805 for b in opts['bookmark']:
3847 for b in opts['bookmark']:
3806 # translate -B options to -r so changesets get pushed
3848 # translate -B options to -r so changesets get pushed
3807 if b in repo._bookmarks:
3849 if b in repo._bookmarks:
3808 opts.setdefault('rev', []).append(b)
3850 opts.setdefault('rev', []).append(b)
3809 else:
3851 else:
3810 # if we try to push a deleted bookmark, translate it to null
3852 # if we try to push a deleted bookmark, translate it to null
3811 # this lets simultaneous -r, -b options continue working
3853 # this lets simultaneous -r, -b options continue working
3812 opts.setdefault('rev', []).append("null")
3854 opts.setdefault('rev', []).append("null")
3813
3855
3814 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3856 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3815 dest, branches = hg.parseurl(dest, opts.get('branch'))
3857 dest, branches = hg.parseurl(dest, opts.get('branch'))
3816 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3858 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3817 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3859 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3818 other = hg.peer(repo, opts, dest)
3860 other = hg.peer(repo, opts, dest)
3819 if revs:
3861 if revs:
3820 revs = [repo.lookup(rev) for rev in revs]
3862 revs = [repo.lookup(rev) for rev in revs]
3821
3863
3822 repo._subtoppath = dest
3864 repo._subtoppath = dest
3823 try:
3865 try:
3824 # push subrepos depth-first for coherent ordering
3866 # push subrepos depth-first for coherent ordering
3825 c = repo['']
3867 c = repo['']
3826 subs = c.substate # only repos that are committed
3868 subs = c.substate # only repos that are committed
3827 for s in sorted(subs):
3869 for s in sorted(subs):
3828 if not c.sub(s).push(opts.get('force')):
3870 if not c.sub(s).push(opts.get('force')):
3829 return False
3871 return False
3830 finally:
3872 finally:
3831 del repo._subtoppath
3873 del repo._subtoppath
3832 result = repo.push(other, opts.get('force'), revs=revs,
3874 result = repo.push(other, opts.get('force'), revs=revs,
3833 newbranch=opts.get('new_branch'))
3875 newbranch=opts.get('new_branch'))
3834
3876
3835 result = (result == 0)
3877 result = (result == 0)
3836
3878
3837 if opts.get('bookmark'):
3879 if opts.get('bookmark'):
3838 rb = other.listkeys('bookmarks')
3880 rb = other.listkeys('bookmarks')
3839 for b in opts['bookmark']:
3881 for b in opts['bookmark']:
3840 # explicit push overrides remote bookmark if any
3882 # explicit push overrides remote bookmark if any
3841 if b in repo._bookmarks:
3883 if b in repo._bookmarks:
3842 ui.status(_("exporting bookmark %s\n") % b)
3884 ui.status(_("exporting bookmark %s\n") % b)
3843 new = repo[b].hex()
3885 new = repo[b].hex()
3844 elif b in rb:
3886 elif b in rb:
3845 ui.status(_("deleting remote bookmark %s\n") % b)
3887 ui.status(_("deleting remote bookmark %s\n") % b)
3846 new = '' # delete
3888 new = '' # delete
3847 else:
3889 else:
3848 ui.warn(_('bookmark %s does not exist on the local '
3890 ui.warn(_('bookmark %s does not exist on the local '
3849 'or remote repository!\n') % b)
3891 'or remote repository!\n') % b)
3850 return 2
3892 return 2
3851 old = rb.get(b, '')
3893 old = rb.get(b, '')
3852 r = other.pushkey('bookmarks', b, old, new)
3894 r = other.pushkey('bookmarks', b, old, new)
3853 if not r:
3895 if not r:
3854 ui.warn(_('updating bookmark %s failed!\n') % b)
3896 ui.warn(_('updating bookmark %s failed!\n') % b)
3855 if not result:
3897 if not result:
3856 result = 2
3898 result = 2
3857
3899
3858 return result
3900 return result
3859
3901
3860 @command('recover', [])
3902 @command('recover', [])
3861 def recover(ui, repo):
3903 def recover(ui, repo):
3862 """roll back an interrupted transaction
3904 """roll back an interrupted transaction
3863
3905
3864 Recover from an interrupted commit or pull.
3906 Recover from an interrupted commit or pull.
3865
3907
3866 This command tries to fix the repository status after an
3908 This command tries to fix the repository status after an
3867 interrupted operation. It should only be necessary when Mercurial
3909 interrupted operation. It should only be necessary when Mercurial
3868 suggests it.
3910 suggests it.
3869
3911
3870 Returns 0 if successful, 1 if nothing to recover or verify fails.
3912 Returns 0 if successful, 1 if nothing to recover or verify fails.
3871 """
3913 """
3872 if repo.recover():
3914 if repo.recover():
3873 return hg.verify(repo)
3915 return hg.verify(repo)
3874 return 1
3916 return 1
3875
3917
3876 @command('^remove|rm',
3918 @command('^remove|rm',
3877 [('A', 'after', None, _('record delete for missing files')),
3919 [('A', 'after', None, _('record delete for missing files')),
3878 ('f', 'force', None,
3920 ('f', 'force', None,
3879 _('remove (and delete) file even if added or modified')),
3921 _('remove (and delete) file even if added or modified')),
3880 ] + walkopts,
3922 ] + walkopts,
3881 _('[OPTION]... FILE...'))
3923 _('[OPTION]... FILE...'))
3882 def remove(ui, repo, *pats, **opts):
3924 def remove(ui, repo, *pats, **opts):
3883 """remove the specified files on the next commit
3925 """remove the specified files on the next commit
3884
3926
3885 Schedule the indicated files for removal from the repository.
3927 Schedule the indicated files for removal from the repository.
3886
3928
3887 This only removes files from the current branch, not from the
3929 This only removes files from the current branch, not from the
3888 entire project history. -A/--after can be used to remove only
3930 entire project history. -A/--after can be used to remove only
3889 files that have already been deleted, -f/--force can be used to
3931 files that have already been deleted, -f/--force can be used to
3890 force deletion, and -Af can be used to remove files from the next
3932 force deletion, and -Af can be used to remove files from the next
3891 revision without deleting them from the working directory.
3933 revision without deleting them from the working directory.
3892
3934
3893 The following table details the behavior of remove for different
3935 The following table details the behavior of remove for different
3894 file states (columns) and option combinations (rows). The file
3936 file states (columns) and option combinations (rows). The file
3895 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3937 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3896 reported by :hg:`status`). The actions are Warn, Remove (from
3938 reported by :hg:`status`). The actions are Warn, Remove (from
3897 branch) and Delete (from disk)::
3939 branch) and Delete (from disk)::
3898
3940
3899 A C M !
3941 A C M !
3900 none W RD W R
3942 none W RD W R
3901 -f R RD RD R
3943 -f R RD RD R
3902 -A W W W R
3944 -A W W W R
3903 -Af R R R R
3945 -Af R R R R
3904
3946
3905 Note that remove never deletes files in Added [A] state from the
3947 Note that remove never deletes files in Added [A] state from the
3906 working directory, not even if option --force is specified.
3948 working directory, not even if option --force is specified.
3907
3949
3908 This command schedules the files to be removed at the next commit.
3950 This command schedules the files to be removed at the next commit.
3909 To undo a remove before that, see :hg:`revert`.
3951 To undo a remove before that, see :hg:`revert`.
3910
3952
3911 Returns 0 on success, 1 if any warnings encountered.
3953 Returns 0 on success, 1 if any warnings encountered.
3912 """
3954 """
3913
3955
3914 ret = 0
3956 ret = 0
3915 after, force = opts.get('after'), opts.get('force')
3957 after, force = opts.get('after'), opts.get('force')
3916 if not pats and not after:
3958 if not pats and not after:
3917 raise util.Abort(_('no files specified'))
3959 raise util.Abort(_('no files specified'))
3918
3960
3919 m = scmutil.match(repo, pats, opts)
3961 m = scmutil.match(repo, pats, opts)
3920 s = repo.status(match=m, clean=True)
3962 s = repo.status(match=m, clean=True)
3921 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3963 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3922
3964
3923 for f in m.files():
3965 for f in m.files():
3924 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3966 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3925 if os.path.exists(m.rel(f)):
3967 if os.path.exists(m.rel(f)):
3926 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3968 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3927 ret = 1
3969 ret = 1
3928
3970
3929 if force:
3971 if force:
3930 list = modified + deleted + clean + added
3972 list = modified + deleted + clean + added
3931 elif after:
3973 elif after:
3932 list = deleted
3974 list = deleted
3933 for f in modified + added + clean:
3975 for f in modified + added + clean:
3934 ui.warn(_('not removing %s: file still exists (use -f'
3976 ui.warn(_('not removing %s: file still exists (use -f'
3935 ' to force removal)\n') % m.rel(f))
3977 ' to force removal)\n') % m.rel(f))
3936 ret = 1
3978 ret = 1
3937 else:
3979 else:
3938 list = deleted + clean
3980 list = deleted + clean
3939 for f in modified:
3981 for f in modified:
3940 ui.warn(_('not removing %s: file is modified (use -f'
3982 ui.warn(_('not removing %s: file is modified (use -f'
3941 ' to force removal)\n') % m.rel(f))
3983 ' to force removal)\n') % m.rel(f))
3942 ret = 1
3984 ret = 1
3943 for f in added:
3985 for f in added:
3944 ui.warn(_('not removing %s: file has been marked for add (use -f'
3986 ui.warn(_('not removing %s: file has been marked for add (use -f'
3945 ' to force removal)\n') % m.rel(f))
3987 ' to force removal)\n') % m.rel(f))
3946 ret = 1
3988 ret = 1
3947
3989
3948 for f in sorted(list):
3990 for f in sorted(list):
3949 if ui.verbose or not m.exact(f):
3991 if ui.verbose or not m.exact(f):
3950 ui.status(_('removing %s\n') % m.rel(f))
3992 ui.status(_('removing %s\n') % m.rel(f))
3951
3993
3952 wlock = repo.wlock()
3994 wlock = repo.wlock()
3953 try:
3995 try:
3954 if not after:
3996 if not after:
3955 for f in list:
3997 for f in list:
3956 if f in added:
3998 if f in added:
3957 continue # we never unlink added files on remove
3999 continue # we never unlink added files on remove
3958 try:
4000 try:
3959 util.unlinkpath(repo.wjoin(f))
4001 util.unlinkpath(repo.wjoin(f))
3960 except OSError, inst:
4002 except OSError, inst:
3961 if inst.errno != errno.ENOENT:
4003 if inst.errno != errno.ENOENT:
3962 raise
4004 raise
3963 repo[None].forget(list)
4005 repo[None].forget(list)
3964 finally:
4006 finally:
3965 wlock.release()
4007 wlock.release()
3966
4008
3967 return ret
4009 return ret
3968
4010
3969 @command('rename|move|mv',
4011 @command('rename|move|mv',
3970 [('A', 'after', None, _('record a rename that has already occurred')),
4012 [('A', 'after', None, _('record a rename that has already occurred')),
3971 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4013 ('f', 'force', None, _('forcibly copy over an existing managed file')),
3972 ] + walkopts + dryrunopts,
4014 ] + walkopts + dryrunopts,
3973 _('[OPTION]... SOURCE... DEST'))
4015 _('[OPTION]... SOURCE... DEST'))
3974 def rename(ui, repo, *pats, **opts):
4016 def rename(ui, repo, *pats, **opts):
3975 """rename files; equivalent of copy + remove
4017 """rename files; equivalent of copy + remove
3976
4018
3977 Mark dest as copies of sources; mark sources for deletion. If dest
4019 Mark dest as copies of sources; mark sources for deletion. If dest
3978 is a directory, copies are put in that directory. If dest is a
4020 is a directory, copies are put in that directory. If dest is a
3979 file, there can only be one source.
4021 file, there can only be one source.
3980
4022
3981 By default, this command copies the contents of files as they
4023 By default, this command copies the contents of files as they
3982 exist in the working directory. If invoked with -A/--after, the
4024 exist in the working directory. If invoked with -A/--after, the
3983 operation is recorded, but no copying is performed.
4025 operation is recorded, but no copying is performed.
3984
4026
3985 This command takes effect at the next commit. To undo a rename
4027 This command takes effect at the next commit. To undo a rename
3986 before that, see :hg:`revert`.
4028 before that, see :hg:`revert`.
3987
4029
3988 Returns 0 on success, 1 if errors are encountered.
4030 Returns 0 on success, 1 if errors are encountered.
3989 """
4031 """
3990 wlock = repo.wlock(False)
4032 wlock = repo.wlock(False)
3991 try:
4033 try:
3992 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4034 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3993 finally:
4035 finally:
3994 wlock.release()
4036 wlock.release()
3995
4037
3996 @command('resolve',
4038 @command('resolve',
3997 [('a', 'all', None, _('select all unresolved files')),
4039 [('a', 'all', None, _('select all unresolved files')),
3998 ('l', 'list', None, _('list state of files needing merge')),
4040 ('l', 'list', None, _('list state of files needing merge')),
3999 ('m', 'mark', None, _('mark files as resolved')),
4041 ('m', 'mark', None, _('mark files as resolved')),
4000 ('u', 'unmark', None, _('mark files as unresolved')),
4042 ('u', 'unmark', None, _('mark files as unresolved')),
4001 ('t', 'tool', '', _('specify merge tool')),
4043 ('t', 'tool', '', _('specify merge tool')),
4002 ('n', 'no-status', None, _('hide status prefix'))]
4044 ('n', 'no-status', None, _('hide status prefix'))]
4003 + walkopts,
4045 + walkopts,
4004 _('[OPTION]... [FILE]...'))
4046 _('[OPTION]... [FILE]...'))
4005 def resolve(ui, repo, *pats, **opts):
4047 def resolve(ui, repo, *pats, **opts):
4006 """redo merges or set/view the merge status of files
4048 """redo merges or set/view the merge status of files
4007
4049
4008 Merges with unresolved conflicts are often the result of
4050 Merges with unresolved conflicts are often the result of
4009 non-interactive merging using the ``internal:merge`` configuration
4051 non-interactive merging using the ``internal:merge`` configuration
4010 setting, or a command-line merge tool like ``diff3``. The resolve
4052 setting, or a command-line merge tool like ``diff3``. The resolve
4011 command is used to manage the files involved in a merge, after
4053 command is used to manage the files involved in a merge, after
4012 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4054 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4013 working directory must have two parents).
4055 working directory must have two parents).
4014
4056
4015 The resolve command can be used in the following ways:
4057 The resolve command can be used in the following ways:
4016
4058
4017 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4059 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4018 files, discarding any previous merge attempts. Re-merging is not
4060 files, discarding any previous merge attempts. Re-merging is not
4019 performed for files already marked as resolved. Use ``--all/-a``
4061 performed for files already marked as resolved. Use ``--all/-a``
4020 to selects all unresolved files. ``--tool`` can be used to specify
4062 to selects all unresolved files. ``--tool`` can be used to specify
4021 the merge tool used for the given files. It overrides the HGMERGE
4063 the merge tool used for the given files. It overrides the HGMERGE
4022 environment variable and your configuration files.
4064 environment variable and your configuration files.
4023
4065
4024 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4066 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4025 (e.g. after having manually fixed-up the files). The default is
4067 (e.g. after having manually fixed-up the files). The default is
4026 to mark all unresolved files.
4068 to mark all unresolved files.
4027
4069
4028 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4070 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4029 default is to mark all resolved files.
4071 default is to mark all resolved files.
4030
4072
4031 - :hg:`resolve -l`: list files which had or still have conflicts.
4073 - :hg:`resolve -l`: list files which had or still have conflicts.
4032 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4074 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4033
4075
4034 Note that Mercurial will not let you commit files with unresolved
4076 Note that Mercurial will not let you commit files with unresolved
4035 merge conflicts. You must use :hg:`resolve -m ...` before you can
4077 merge conflicts. You must use :hg:`resolve -m ...` before you can
4036 commit after a conflicting merge.
4078 commit after a conflicting merge.
4037
4079
4038 Returns 0 on success, 1 if any files fail a resolve attempt.
4080 Returns 0 on success, 1 if any files fail a resolve attempt.
4039 """
4081 """
4040
4082
4041 all, mark, unmark, show, nostatus = \
4083 all, mark, unmark, show, nostatus = \
4042 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4084 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4043
4085
4044 if (show and (mark or unmark)) or (mark and unmark):
4086 if (show and (mark or unmark)) or (mark and unmark):
4045 raise util.Abort(_("too many options specified"))
4087 raise util.Abort(_("too many options specified"))
4046 if pats and all:
4088 if pats and all:
4047 raise util.Abort(_("can't specify --all and patterns"))
4089 raise util.Abort(_("can't specify --all and patterns"))
4048 if not (all or pats or show or mark or unmark):
4090 if not (all or pats or show or mark or unmark):
4049 raise util.Abort(_('no files or directories specified; '
4091 raise util.Abort(_('no files or directories specified; '
4050 'use --all to remerge all files'))
4092 'use --all to remerge all files'))
4051
4093
4052 ms = mergemod.mergestate(repo)
4094 ms = mergemod.mergestate(repo)
4053 m = scmutil.match(repo, pats, opts)
4095 m = scmutil.match(repo, pats, opts)
4054 ret = 0
4096 ret = 0
4055
4097
4056 for f in ms:
4098 for f in ms:
4057 if m(f):
4099 if m(f):
4058 if show:
4100 if show:
4059 if nostatus:
4101 if nostatus:
4060 ui.write("%s\n" % f)
4102 ui.write("%s\n" % f)
4061 else:
4103 else:
4062 ui.write("%s %s\n" % (ms[f].upper(), f),
4104 ui.write("%s %s\n" % (ms[f].upper(), f),
4063 label='resolve.' +
4105 label='resolve.' +
4064 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4106 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4065 elif mark:
4107 elif mark:
4066 ms.mark(f, "r")
4108 ms.mark(f, "r")
4067 elif unmark:
4109 elif unmark:
4068 ms.mark(f, "u")
4110 ms.mark(f, "u")
4069 else:
4111 else:
4070 wctx = repo[None]
4112 wctx = repo[None]
4071 mctx = wctx.parents()[-1]
4113 mctx = wctx.parents()[-1]
4072
4114
4073 # backup pre-resolve (merge uses .orig for its own purposes)
4115 # backup pre-resolve (merge uses .orig for its own purposes)
4074 a = repo.wjoin(f)
4116 a = repo.wjoin(f)
4075 util.copyfile(a, a + ".resolve")
4117 util.copyfile(a, a + ".resolve")
4076
4118
4077 try:
4119 try:
4078 # resolve file
4120 # resolve file
4079 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4121 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4080 if ms.resolve(f, wctx, mctx):
4122 if ms.resolve(f, wctx, mctx):
4081 ret = 1
4123 ret = 1
4082 finally:
4124 finally:
4083 ui.setconfig('ui', 'forcemerge', '')
4125 ui.setconfig('ui', 'forcemerge', '')
4084
4126
4085 # replace filemerge's .orig file with our resolve file
4127 # replace filemerge's .orig file with our resolve file
4086 util.rename(a + ".resolve", a + ".orig")
4128 util.rename(a + ".resolve", a + ".orig")
4087
4129
4088 ms.commit()
4130 ms.commit()
4089 return ret
4131 return ret
4090
4132
4091 @command('revert',
4133 @command('revert',
4092 [('a', 'all', None, _('revert all changes when no arguments given')),
4134 [('a', 'all', None, _('revert all changes when no arguments given')),
4093 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4135 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4094 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4136 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4095 ('', 'no-backup', None, _('do not save backup copies of files')),
4137 ('', 'no-backup', None, _('do not save backup copies of files')),
4096 ] + walkopts + dryrunopts,
4138 ] + walkopts + dryrunopts,
4097 _('[OPTION]... [-r REV] [NAME]...'))
4139 _('[OPTION]... [-r REV] [NAME]...'))
4098 def revert(ui, repo, *pats, **opts):
4140 def revert(ui, repo, *pats, **opts):
4099 """restore files to their checkout state
4141 """restore files to their checkout state
4100
4142
4101 .. note::
4143 .. note::
4102 To check out earlier revisions, you should use :hg:`update REV`.
4144 To check out earlier revisions, you should use :hg:`update REV`.
4103 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4145 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4104
4146
4105 With no revision specified, revert the specified files or directories
4147 With no revision specified, revert the specified files or directories
4106 to the state they had in the first parent of the working directory.
4148 to the state they had in the first parent of the working directory.
4107 This restores the contents of files to an unmodified
4149 This restores the contents of files to an unmodified
4108 state and unschedules adds, removes, copies, and renames.
4150 state and unschedules adds, removes, copies, and renames.
4109
4151
4110 Using the -r/--rev or -d/--date options, revert the given files or
4152 Using the -r/--rev or -d/--date options, revert the given files or
4111 directories to their states as of a specific revision. Because
4153 directories to their states as of a specific revision. Because
4112 revert does not change the working directory parents, this will
4154 revert does not change the working directory parents, this will
4113 cause these files to appear modified. This can be helpful to "back
4155 cause these files to appear modified. This can be helpful to "back
4114 out" some or all of an earlier change. See :hg:`backout` for a
4156 out" some or all of an earlier change. See :hg:`backout` for a
4115 related method.
4157 related method.
4116
4158
4117 Modified files are saved with a .orig suffix before reverting.
4159 Modified files are saved with a .orig suffix before reverting.
4118 To disable these backups, use --no-backup.
4160 To disable these backups, use --no-backup.
4119
4161
4120 See :hg:`help dates` for a list of formats valid for -d/--date.
4162 See :hg:`help dates` for a list of formats valid for -d/--date.
4121
4163
4122 Returns 0 on success.
4164 Returns 0 on success.
4123 """
4165 """
4124
4166
4125 if opts.get("date"):
4167 if opts.get("date"):
4126 if opts.get("rev"):
4168 if opts.get("rev"):
4127 raise util.Abort(_("you can't specify a revision and a date"))
4169 raise util.Abort(_("you can't specify a revision and a date"))
4128 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4170 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4129
4171
4130 parent, p2 = repo.dirstate.parents()
4172 parent, p2 = repo.dirstate.parents()
4131
4173
4132 if not pats and not opts.get('all'):
4174 if not pats and not opts.get('all'):
4133 raise util.Abort(_('no files or directories specified'),
4175 raise util.Abort(_('no files or directories specified'),
4134 hint=_('use --all to revert all files'))
4176 hint=_('use --all to revert all files'))
4135
4177
4136 ctx = scmutil.revsingle(repo, opts.get('rev'))
4178 ctx = scmutil.revsingle(repo, opts.get('rev'))
4137 node = ctx.node()
4179 node = ctx.node()
4138 mf = ctx.manifest()
4180 mf = ctx.manifest()
4139 if node == parent:
4181 if node == parent:
4140 pmf = mf
4182 pmf = mf
4141 else:
4183 else:
4142 pmf = None
4184 pmf = None
4143
4185
4144 # need all matching names in dirstate and manifest of target rev,
4186 # need all matching names in dirstate and manifest of target rev,
4145 # so have to walk both. do not print errors if files exist in one
4187 # so have to walk both. do not print errors if files exist in one
4146 # but not other.
4188 # but not other.
4147
4189
4148 names = {}
4190 names = {}
4149
4191
4150 wlock = repo.wlock()
4192 wlock = repo.wlock()
4151 try:
4193 try:
4152 # walk dirstate.
4194 # walk dirstate.
4153
4195
4154 m = scmutil.match(repo, pats, opts)
4196 m = scmutil.match(repo, pats, opts)
4155 m.bad = lambda x, y: False
4197 m.bad = lambda x, y: False
4156 for abs in repo.walk(m):
4198 for abs in repo.walk(m):
4157 names[abs] = m.rel(abs), m.exact(abs)
4199 names[abs] = m.rel(abs), m.exact(abs)
4158
4200
4159 # walk target manifest.
4201 # walk target manifest.
4160
4202
4161 def badfn(path, msg):
4203 def badfn(path, msg):
4162 if path in names:
4204 if path in names:
4163 return
4205 return
4164 path_ = path + '/'
4206 path_ = path + '/'
4165 for f in names:
4207 for f in names:
4166 if f.startswith(path_):
4208 if f.startswith(path_):
4167 return
4209 return
4168 ui.warn("%s: %s\n" % (m.rel(path), msg))
4210 ui.warn("%s: %s\n" % (m.rel(path), msg))
4169
4211
4170 m = scmutil.match(repo, pats, opts)
4212 m = scmutil.match(repo, pats, opts)
4171 m.bad = badfn
4213 m.bad = badfn
4172 for abs in repo[node].walk(m):
4214 for abs in repo[node].walk(m):
4173 if abs not in names:
4215 if abs not in names:
4174 names[abs] = m.rel(abs), m.exact(abs)
4216 names[abs] = m.rel(abs), m.exact(abs)
4175
4217
4176 m = scmutil.matchfiles(repo, names)
4218 m = scmutil.matchfiles(repo, names)
4177 changes = repo.status(match=m)[:4]
4219 changes = repo.status(match=m)[:4]
4178 modified, added, removed, deleted = map(set, changes)
4220 modified, added, removed, deleted = map(set, changes)
4179
4221
4180 # if f is a rename, also revert the source
4222 # if f is a rename, also revert the source
4181 cwd = repo.getcwd()
4223 cwd = repo.getcwd()
4182 for f in added:
4224 for f in added:
4183 src = repo.dirstate.copied(f)
4225 src = repo.dirstate.copied(f)
4184 if src and src not in names and repo.dirstate[src] == 'r':
4226 if src and src not in names and repo.dirstate[src] == 'r':
4185 removed.add(src)
4227 removed.add(src)
4186 names[src] = (repo.pathto(src, cwd), True)
4228 names[src] = (repo.pathto(src, cwd), True)
4187
4229
4188 def removeforget(abs):
4230 def removeforget(abs):
4189 if repo.dirstate[abs] == 'a':
4231 if repo.dirstate[abs] == 'a':
4190 return _('forgetting %s\n')
4232 return _('forgetting %s\n')
4191 return _('removing %s\n')
4233 return _('removing %s\n')
4192
4234
4193 revert = ([], _('reverting %s\n'))
4235 revert = ([], _('reverting %s\n'))
4194 add = ([], _('adding %s\n'))
4236 add = ([], _('adding %s\n'))
4195 remove = ([], removeforget)
4237 remove = ([], removeforget)
4196 undelete = ([], _('undeleting %s\n'))
4238 undelete = ([], _('undeleting %s\n'))
4197
4239
4198 disptable = (
4240 disptable = (
4199 # dispatch table:
4241 # dispatch table:
4200 # file state
4242 # file state
4201 # action if in target manifest
4243 # action if in target manifest
4202 # action if not in target manifest
4244 # action if not in target manifest
4203 # make backup if in target manifest
4245 # make backup if in target manifest
4204 # make backup if not in target manifest
4246 # make backup if not in target manifest
4205 (modified, revert, remove, True, True),
4247 (modified, revert, remove, True, True),
4206 (added, revert, remove, True, False),
4248 (added, revert, remove, True, False),
4207 (removed, undelete, None, False, False),
4249 (removed, undelete, None, False, False),
4208 (deleted, revert, remove, False, False),
4250 (deleted, revert, remove, False, False),
4209 )
4251 )
4210
4252
4211 for abs, (rel, exact) in sorted(names.items()):
4253 for abs, (rel, exact) in sorted(names.items()):
4212 mfentry = mf.get(abs)
4254 mfentry = mf.get(abs)
4213 target = repo.wjoin(abs)
4255 target = repo.wjoin(abs)
4214 def handle(xlist, dobackup):
4256 def handle(xlist, dobackup):
4215 xlist[0].append(abs)
4257 xlist[0].append(abs)
4216 if (dobackup and not opts.get('no_backup') and
4258 if (dobackup and not opts.get('no_backup') and
4217 os.path.lexists(target)):
4259 os.path.lexists(target)):
4218 bakname = "%s.orig" % rel
4260 bakname = "%s.orig" % rel
4219 ui.note(_('saving current version of %s as %s\n') %
4261 ui.note(_('saving current version of %s as %s\n') %
4220 (rel, bakname))
4262 (rel, bakname))
4221 if not opts.get('dry_run'):
4263 if not opts.get('dry_run'):
4222 util.rename(target, bakname)
4264 util.rename(target, bakname)
4223 if ui.verbose or not exact:
4265 if ui.verbose or not exact:
4224 msg = xlist[1]
4266 msg = xlist[1]
4225 if not isinstance(msg, basestring):
4267 if not isinstance(msg, basestring):
4226 msg = msg(abs)
4268 msg = msg(abs)
4227 ui.status(msg % rel)
4269 ui.status(msg % rel)
4228 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4270 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4229 if abs not in table:
4271 if abs not in table:
4230 continue
4272 continue
4231 # file has changed in dirstate
4273 # file has changed in dirstate
4232 if mfentry:
4274 if mfentry:
4233 handle(hitlist, backuphit)
4275 handle(hitlist, backuphit)
4234 elif misslist is not None:
4276 elif misslist is not None:
4235 handle(misslist, backupmiss)
4277 handle(misslist, backupmiss)
4236 break
4278 break
4237 else:
4279 else:
4238 if abs not in repo.dirstate:
4280 if abs not in repo.dirstate:
4239 if mfentry:
4281 if mfentry:
4240 handle(add, True)
4282 handle(add, True)
4241 elif exact:
4283 elif exact:
4242 ui.warn(_('file not managed: %s\n') % rel)
4284 ui.warn(_('file not managed: %s\n') % rel)
4243 continue
4285 continue
4244 # file has not changed in dirstate
4286 # file has not changed in dirstate
4245 if node == parent:
4287 if node == parent:
4246 if exact:
4288 if exact:
4247 ui.warn(_('no changes needed to %s\n') % rel)
4289 ui.warn(_('no changes needed to %s\n') % rel)
4248 continue
4290 continue
4249 if pmf is None:
4291 if pmf is None:
4250 # only need parent manifest in this unlikely case,
4292 # only need parent manifest in this unlikely case,
4251 # so do not read by default
4293 # so do not read by default
4252 pmf = repo[parent].manifest()
4294 pmf = repo[parent].manifest()
4253 if abs in pmf:
4295 if abs in pmf:
4254 if mfentry:
4296 if mfentry:
4255 # if version of file is same in parent and target
4297 # if version of file is same in parent and target
4256 # manifests, do nothing
4298 # manifests, do nothing
4257 if (pmf[abs] != mfentry or
4299 if (pmf[abs] != mfentry or
4258 pmf.flags(abs) != mf.flags(abs)):
4300 pmf.flags(abs) != mf.flags(abs)):
4259 handle(revert, False)
4301 handle(revert, False)
4260 else:
4302 else:
4261 handle(remove, False)
4303 handle(remove, False)
4262
4304
4263 if not opts.get('dry_run'):
4305 if not opts.get('dry_run'):
4264 def checkout(f):
4306 def checkout(f):
4265 fc = ctx[f]
4307 fc = ctx[f]
4266 repo.wwrite(f, fc.data(), fc.flags())
4308 repo.wwrite(f, fc.data(), fc.flags())
4267
4309
4268 audit_path = scmutil.pathauditor(repo.root)
4310 audit_path = scmutil.pathauditor(repo.root)
4269 for f in remove[0]:
4311 for f in remove[0]:
4270 if repo.dirstate[f] == 'a':
4312 if repo.dirstate[f] == 'a':
4271 repo.dirstate.drop(f)
4313 repo.dirstate.drop(f)
4272 continue
4314 continue
4273 audit_path(f)
4315 audit_path(f)
4274 try:
4316 try:
4275 util.unlinkpath(repo.wjoin(f))
4317 util.unlinkpath(repo.wjoin(f))
4276 except OSError:
4318 except OSError:
4277 pass
4319 pass
4278 repo.dirstate.remove(f)
4320 repo.dirstate.remove(f)
4279
4321
4280 normal = None
4322 normal = None
4281 if node == parent:
4323 if node == parent:
4282 # We're reverting to our parent. If possible, we'd like status
4324 # We're reverting to our parent. If possible, we'd like status
4283 # to report the file as clean. We have to use normallookup for
4325 # to report the file as clean. We have to use normallookup for
4284 # merges to avoid losing information about merged/dirty files.
4326 # merges to avoid losing information about merged/dirty files.
4285 if p2 != nullid:
4327 if p2 != nullid:
4286 normal = repo.dirstate.normallookup
4328 normal = repo.dirstate.normallookup
4287 else:
4329 else:
4288 normal = repo.dirstate.normal
4330 normal = repo.dirstate.normal
4289 for f in revert[0]:
4331 for f in revert[0]:
4290 checkout(f)
4332 checkout(f)
4291 if normal:
4333 if normal:
4292 normal(f)
4334 normal(f)
4293
4335
4294 for f in add[0]:
4336 for f in add[0]:
4295 checkout(f)
4337 checkout(f)
4296 repo.dirstate.add(f)
4338 repo.dirstate.add(f)
4297
4339
4298 normal = repo.dirstate.normallookup
4340 normal = repo.dirstate.normallookup
4299 if node == parent and p2 == nullid:
4341 if node == parent and p2 == nullid:
4300 normal = repo.dirstate.normal
4342 normal = repo.dirstate.normal
4301 for f in undelete[0]:
4343 for f in undelete[0]:
4302 checkout(f)
4344 checkout(f)
4303 normal(f)
4345 normal(f)
4304
4346
4305 finally:
4347 finally:
4306 wlock.release()
4348 wlock.release()
4307
4349
4308 @command('rollback', dryrunopts)
4350 @command('rollback', dryrunopts)
4309 def rollback(ui, repo, **opts):
4351 def rollback(ui, repo, **opts):
4310 """roll back the last transaction (dangerous)
4352 """roll back the last transaction (dangerous)
4311
4353
4312 This command should be used with care. There is only one level of
4354 This command should be used with care. There is only one level of
4313 rollback, and there is no way to undo a rollback. It will also
4355 rollback, and there is no way to undo a rollback. It will also
4314 restore the dirstate at the time of the last transaction, losing
4356 restore the dirstate at the time of the last transaction, losing
4315 any dirstate changes since that time. This command does not alter
4357 any dirstate changes since that time. This command does not alter
4316 the working directory.
4358 the working directory.
4317
4359
4318 Transactions are used to encapsulate the effects of all commands
4360 Transactions are used to encapsulate the effects of all commands
4319 that create new changesets or propagate existing changesets into a
4361 that create new changesets or propagate existing changesets into a
4320 repository. For example, the following commands are transactional,
4362 repository. For example, the following commands are transactional,
4321 and their effects can be rolled back:
4363 and their effects can be rolled back:
4322
4364
4323 - commit
4365 - commit
4324 - import
4366 - import
4325 - pull
4367 - pull
4326 - push (with this repository as the destination)
4368 - push (with this repository as the destination)
4327 - unbundle
4369 - unbundle
4328
4370
4329 This command is not intended for use on public repositories. Once
4371 This command is not intended for use on public repositories. Once
4330 changes are visible for pull by other users, rolling a transaction
4372 changes are visible for pull by other users, rolling a transaction
4331 back locally is ineffective (someone else may already have pulled
4373 back locally is ineffective (someone else may already have pulled
4332 the changes). Furthermore, a race is possible with readers of the
4374 the changes). Furthermore, a race is possible with readers of the
4333 repository; for example an in-progress pull from the repository
4375 repository; for example an in-progress pull from the repository
4334 may fail if a rollback is performed.
4376 may fail if a rollback is performed.
4335
4377
4336 Returns 0 on success, 1 if no rollback data is available.
4378 Returns 0 on success, 1 if no rollback data is available.
4337 """
4379 """
4338 return repo.rollback(opts.get('dry_run'))
4380 return repo.rollback(opts.get('dry_run'))
4339
4381
4340 @command('root', [])
4382 @command('root', [])
4341 def root(ui, repo):
4383 def root(ui, repo):
4342 """print the root (top) of the current working directory
4384 """print the root (top) of the current working directory
4343
4385
4344 Print the root directory of the current repository.
4386 Print the root directory of the current repository.
4345
4387
4346 Returns 0 on success.
4388 Returns 0 on success.
4347 """
4389 """
4348 ui.write(repo.root + "\n")
4390 ui.write(repo.root + "\n")
4349
4391
4350 @command('^serve',
4392 @command('^serve',
4351 [('A', 'accesslog', '', _('name of access log file to write to'),
4393 [('A', 'accesslog', '', _('name of access log file to write to'),
4352 _('FILE')),
4394 _('FILE')),
4353 ('d', 'daemon', None, _('run server in background')),
4395 ('d', 'daemon', None, _('run server in background')),
4354 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4396 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4355 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4397 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4356 # use string type, then we can check if something was passed
4398 # use string type, then we can check if something was passed
4357 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4399 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4358 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4400 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4359 _('ADDR')),
4401 _('ADDR')),
4360 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4402 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4361 _('PREFIX')),
4403 _('PREFIX')),
4362 ('n', 'name', '',
4404 ('n', 'name', '',
4363 _('name to show in web pages (default: working directory)'), _('NAME')),
4405 _('name to show in web pages (default: working directory)'), _('NAME')),
4364 ('', 'web-conf', '',
4406 ('', 'web-conf', '',
4365 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4407 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4366 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4408 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4367 _('FILE')),
4409 _('FILE')),
4368 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4410 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4369 ('', 'stdio', None, _('for remote clients')),
4411 ('', 'stdio', None, _('for remote clients')),
4370 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4412 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4371 ('', 'style', '', _('template style to use'), _('STYLE')),
4413 ('', 'style', '', _('template style to use'), _('STYLE')),
4372 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4414 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4373 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4415 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4374 _('[OPTION]...'))
4416 _('[OPTION]...'))
4375 def serve(ui, repo, **opts):
4417 def serve(ui, repo, **opts):
4376 """start stand-alone webserver
4418 """start stand-alone webserver
4377
4419
4378 Start a local HTTP repository browser and pull server. You can use
4420 Start a local HTTP repository browser and pull server. You can use
4379 this for ad-hoc sharing and browsing of repositories. It is
4421 this for ad-hoc sharing and browsing of repositories. It is
4380 recommended to use a real web server to serve a repository for
4422 recommended to use a real web server to serve a repository for
4381 longer periods of time.
4423 longer periods of time.
4382
4424
4383 Please note that the server does not implement access control.
4425 Please note that the server does not implement access control.
4384 This means that, by default, anybody can read from the server and
4426 This means that, by default, anybody can read from the server and
4385 nobody can write to it by default. Set the ``web.allow_push``
4427 nobody can write to it by default. Set the ``web.allow_push``
4386 option to ``*`` to allow everybody to push to the server. You
4428 option to ``*`` to allow everybody to push to the server. You
4387 should use a real web server if you need to authenticate users.
4429 should use a real web server if you need to authenticate users.
4388
4430
4389 By default, the server logs accesses to stdout and errors to
4431 By default, the server logs accesses to stdout and errors to
4390 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4432 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4391 files.
4433 files.
4392
4434
4393 To have the server choose a free port number to listen on, specify
4435 To have the server choose a free port number to listen on, specify
4394 a port number of 0; in this case, the server will print the port
4436 a port number of 0; in this case, the server will print the port
4395 number it uses.
4437 number it uses.
4396
4438
4397 Returns 0 on success.
4439 Returns 0 on success.
4398 """
4440 """
4399
4441
4400 if opts["stdio"]:
4442 if opts["stdio"]:
4401 if repo is None:
4443 if repo is None:
4402 raise error.RepoError(_("There is no Mercurial repository here"
4444 raise error.RepoError(_("There is no Mercurial repository here"
4403 " (.hg not found)"))
4445 " (.hg not found)"))
4404 s = sshserver.sshserver(ui, repo)
4446 s = sshserver.sshserver(ui, repo)
4405 s.serve_forever()
4447 s.serve_forever()
4406
4448
4407 # this way we can check if something was given in the command-line
4449 # this way we can check if something was given in the command-line
4408 if opts.get('port'):
4450 if opts.get('port'):
4409 opts['port'] = util.getport(opts.get('port'))
4451 opts['port'] = util.getport(opts.get('port'))
4410
4452
4411 baseui = repo and repo.baseui or ui
4453 baseui = repo and repo.baseui or ui
4412 optlist = ("name templates style address port prefix ipv6"
4454 optlist = ("name templates style address port prefix ipv6"
4413 " accesslog errorlog certificate encoding")
4455 " accesslog errorlog certificate encoding")
4414 for o in optlist.split():
4456 for o in optlist.split():
4415 val = opts.get(o, '')
4457 val = opts.get(o, '')
4416 if val in (None, ''): # should check against default options instead
4458 if val in (None, ''): # should check against default options instead
4417 continue
4459 continue
4418 baseui.setconfig("web", o, val)
4460 baseui.setconfig("web", o, val)
4419 if repo and repo.ui != baseui:
4461 if repo and repo.ui != baseui:
4420 repo.ui.setconfig("web", o, val)
4462 repo.ui.setconfig("web", o, val)
4421
4463
4422 o = opts.get('web_conf') or opts.get('webdir_conf')
4464 o = opts.get('web_conf') or opts.get('webdir_conf')
4423 if not o:
4465 if not o:
4424 if not repo:
4466 if not repo:
4425 raise error.RepoError(_("There is no Mercurial repository"
4467 raise error.RepoError(_("There is no Mercurial repository"
4426 " here (.hg not found)"))
4468 " here (.hg not found)"))
4427 o = repo.root
4469 o = repo.root
4428
4470
4429 app = hgweb.hgweb(o, baseui=ui)
4471 app = hgweb.hgweb(o, baseui=ui)
4430
4472
4431 class service(object):
4473 class service(object):
4432 def init(self):
4474 def init(self):
4433 util.setsignalhandler()
4475 util.setsignalhandler()
4434 self.httpd = hgweb.server.create_server(ui, app)
4476 self.httpd = hgweb.server.create_server(ui, app)
4435
4477
4436 if opts['port'] and not ui.verbose:
4478 if opts['port'] and not ui.verbose:
4437 return
4479 return
4438
4480
4439 if self.httpd.prefix:
4481 if self.httpd.prefix:
4440 prefix = self.httpd.prefix.strip('/') + '/'
4482 prefix = self.httpd.prefix.strip('/') + '/'
4441 else:
4483 else:
4442 prefix = ''
4484 prefix = ''
4443
4485
4444 port = ':%d' % self.httpd.port
4486 port = ':%d' % self.httpd.port
4445 if port == ':80':
4487 if port == ':80':
4446 port = ''
4488 port = ''
4447
4489
4448 bindaddr = self.httpd.addr
4490 bindaddr = self.httpd.addr
4449 if bindaddr == '0.0.0.0':
4491 if bindaddr == '0.0.0.0':
4450 bindaddr = '*'
4492 bindaddr = '*'
4451 elif ':' in bindaddr: # IPv6
4493 elif ':' in bindaddr: # IPv6
4452 bindaddr = '[%s]' % bindaddr
4494 bindaddr = '[%s]' % bindaddr
4453
4495
4454 fqaddr = self.httpd.fqaddr
4496 fqaddr = self.httpd.fqaddr
4455 if ':' in fqaddr:
4497 if ':' in fqaddr:
4456 fqaddr = '[%s]' % fqaddr
4498 fqaddr = '[%s]' % fqaddr
4457 if opts['port']:
4499 if opts['port']:
4458 write = ui.status
4500 write = ui.status
4459 else:
4501 else:
4460 write = ui.write
4502 write = ui.write
4461 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4503 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4462 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4504 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4463
4505
4464 def run(self):
4506 def run(self):
4465 self.httpd.serve_forever()
4507 self.httpd.serve_forever()
4466
4508
4467 service = service()
4509 service = service()
4468
4510
4469 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4511 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4470
4512
4471 @command('showconfig|debugconfig',
4513 @command('showconfig|debugconfig',
4472 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4514 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4473 _('[-u] [NAME]...'))
4515 _('[-u] [NAME]...'))
4474 def showconfig(ui, repo, *values, **opts):
4516 def showconfig(ui, repo, *values, **opts):
4475 """show combined config settings from all hgrc files
4517 """show combined config settings from all hgrc files
4476
4518
4477 With no arguments, print names and values of all config items.
4519 With no arguments, print names and values of all config items.
4478
4520
4479 With one argument of the form section.name, print just the value
4521 With one argument of the form section.name, print just the value
4480 of that config item.
4522 of that config item.
4481
4523
4482 With multiple arguments, print names and values of all config
4524 With multiple arguments, print names and values of all config
4483 items with matching section names.
4525 items with matching section names.
4484
4526
4485 With --debug, the source (filename and line number) is printed
4527 With --debug, the source (filename and line number) is printed
4486 for each config item.
4528 for each config item.
4487
4529
4488 Returns 0 on success.
4530 Returns 0 on success.
4489 """
4531 """
4490
4532
4491 for f in scmutil.rcpath():
4533 for f in scmutil.rcpath():
4492 ui.debug(_('read config from: %s\n') % f)
4534 ui.debug(_('read config from: %s\n') % f)
4493 untrusted = bool(opts.get('untrusted'))
4535 untrusted = bool(opts.get('untrusted'))
4494 if values:
4536 if values:
4495 sections = [v for v in values if '.' not in v]
4537 sections = [v for v in values if '.' not in v]
4496 items = [v for v in values if '.' in v]
4538 items = [v for v in values if '.' in v]
4497 if len(items) > 1 or items and sections:
4539 if len(items) > 1 or items and sections:
4498 raise util.Abort(_('only one config item permitted'))
4540 raise util.Abort(_('only one config item permitted'))
4499 for section, name, value in ui.walkconfig(untrusted=untrusted):
4541 for section, name, value in ui.walkconfig(untrusted=untrusted):
4500 value = str(value).replace('\n', '\\n')
4542 value = str(value).replace('\n', '\\n')
4501 sectname = section + '.' + name
4543 sectname = section + '.' + name
4502 if values:
4544 if values:
4503 for v in values:
4545 for v in values:
4504 if v == section:
4546 if v == section:
4505 ui.debug('%s: ' %
4547 ui.debug('%s: ' %
4506 ui.configsource(section, name, untrusted))
4548 ui.configsource(section, name, untrusted))
4507 ui.write('%s=%s\n' % (sectname, value))
4549 ui.write('%s=%s\n' % (sectname, value))
4508 elif v == sectname:
4550 elif v == sectname:
4509 ui.debug('%s: ' %
4551 ui.debug('%s: ' %
4510 ui.configsource(section, name, untrusted))
4552 ui.configsource(section, name, untrusted))
4511 ui.write(value, '\n')
4553 ui.write(value, '\n')
4512 else:
4554 else:
4513 ui.debug('%s: ' %
4555 ui.debug('%s: ' %
4514 ui.configsource(section, name, untrusted))
4556 ui.configsource(section, name, untrusted))
4515 ui.write('%s=%s\n' % (sectname, value))
4557 ui.write('%s=%s\n' % (sectname, value))
4516
4558
4517 @command('^status|st',
4559 @command('^status|st',
4518 [('A', 'all', None, _('show status of all files')),
4560 [('A', 'all', None, _('show status of all files')),
4519 ('m', 'modified', None, _('show only modified files')),
4561 ('m', 'modified', None, _('show only modified files')),
4520 ('a', 'added', None, _('show only added files')),
4562 ('a', 'added', None, _('show only added files')),
4521 ('r', 'removed', None, _('show only removed files')),
4563 ('r', 'removed', None, _('show only removed files')),
4522 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4564 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4523 ('c', 'clean', None, _('show only files without changes')),
4565 ('c', 'clean', None, _('show only files without changes')),
4524 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4566 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4525 ('i', 'ignored', None, _('show only ignored files')),
4567 ('i', 'ignored', None, _('show only ignored files')),
4526 ('n', 'no-status', None, _('hide status prefix')),
4568 ('n', 'no-status', None, _('hide status prefix')),
4527 ('C', 'copies', None, _('show source of copied files')),
4569 ('C', 'copies', None, _('show source of copied files')),
4528 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4570 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4529 ('', 'rev', [], _('show difference from revision'), _('REV')),
4571 ('', 'rev', [], _('show difference from revision'), _('REV')),
4530 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4572 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4531 ] + walkopts + subrepoopts,
4573 ] + walkopts + subrepoopts,
4532 _('[OPTION]... [FILE]...'))
4574 _('[OPTION]... [FILE]...'))
4533 def status(ui, repo, *pats, **opts):
4575 def status(ui, repo, *pats, **opts):
4534 """show changed files in the working directory
4576 """show changed files in the working directory
4535
4577
4536 Show status of files in the repository. If names are given, only
4578 Show status of files in the repository. If names are given, only
4537 files that match are shown. Files that are clean or ignored or
4579 files that match are shown. Files that are clean or ignored or
4538 the source of a copy/move operation, are not listed unless
4580 the source of a copy/move operation, are not listed unless
4539 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4581 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4540 Unless options described with "show only ..." are given, the
4582 Unless options described with "show only ..." are given, the
4541 options -mardu are used.
4583 options -mardu are used.
4542
4584
4543 Option -q/--quiet hides untracked (unknown and ignored) files
4585 Option -q/--quiet hides untracked (unknown and ignored) files
4544 unless explicitly requested with -u/--unknown or -i/--ignored.
4586 unless explicitly requested with -u/--unknown or -i/--ignored.
4545
4587
4546 .. note::
4588 .. note::
4547 status may appear to disagree with diff if permissions have
4589 status may appear to disagree with diff if permissions have
4548 changed or a merge has occurred. The standard diff format does
4590 changed or a merge has occurred. The standard diff format does
4549 not report permission changes and diff only reports changes
4591 not report permission changes and diff only reports changes
4550 relative to one merge parent.
4592 relative to one merge parent.
4551
4593
4552 If one revision is given, it is used as the base revision.
4594 If one revision is given, it is used as the base revision.
4553 If two revisions are given, the differences between them are
4595 If two revisions are given, the differences between them are
4554 shown. The --change option can also be used as a shortcut to list
4596 shown. The --change option can also be used as a shortcut to list
4555 the changed files of a revision from its first parent.
4597 the changed files of a revision from its first parent.
4556
4598
4557 The codes used to show the status of files are::
4599 The codes used to show the status of files are::
4558
4600
4559 M = modified
4601 M = modified
4560 A = added
4602 A = added
4561 R = removed
4603 R = removed
4562 C = clean
4604 C = clean
4563 ! = missing (deleted by non-hg command, but still tracked)
4605 ! = missing (deleted by non-hg command, but still tracked)
4564 ? = not tracked
4606 ? = not tracked
4565 I = ignored
4607 I = ignored
4566 = origin of the previous file listed as A (added)
4608 = origin of the previous file listed as A (added)
4567
4609
4568 Returns 0 on success.
4610 Returns 0 on success.
4569 """
4611 """
4570
4612
4571 revs = opts.get('rev')
4613 revs = opts.get('rev')
4572 change = opts.get('change')
4614 change = opts.get('change')
4573
4615
4574 if revs and change:
4616 if revs and change:
4575 msg = _('cannot specify --rev and --change at the same time')
4617 msg = _('cannot specify --rev and --change at the same time')
4576 raise util.Abort(msg)
4618 raise util.Abort(msg)
4577 elif change:
4619 elif change:
4578 node2 = repo.lookup(change)
4620 node2 = repo.lookup(change)
4579 node1 = repo[node2].p1().node()
4621 node1 = repo[node2].p1().node()
4580 else:
4622 else:
4581 node1, node2 = scmutil.revpair(repo, revs)
4623 node1, node2 = scmutil.revpair(repo, revs)
4582
4624
4583 cwd = (pats and repo.getcwd()) or ''
4625 cwd = (pats and repo.getcwd()) or ''
4584 end = opts.get('print0') and '\0' or '\n'
4626 end = opts.get('print0') and '\0' or '\n'
4585 copy = {}
4627 copy = {}
4586 states = 'modified added removed deleted unknown ignored clean'.split()
4628 states = 'modified added removed deleted unknown ignored clean'.split()
4587 show = [k for k in states if opts.get(k)]
4629 show = [k for k in states if opts.get(k)]
4588 if opts.get('all'):
4630 if opts.get('all'):
4589 show += ui.quiet and (states[:4] + ['clean']) or states
4631 show += ui.quiet and (states[:4] + ['clean']) or states
4590 if not show:
4632 if not show:
4591 show = ui.quiet and states[:4] or states[:5]
4633 show = ui.quiet and states[:4] or states[:5]
4592
4634
4593 stat = repo.status(node1, node2, scmutil.match(repo, pats, opts),
4635 stat = repo.status(node1, node2, scmutil.match(repo, pats, opts),
4594 'ignored' in show, 'clean' in show, 'unknown' in show,
4636 'ignored' in show, 'clean' in show, 'unknown' in show,
4595 opts.get('subrepos'))
4637 opts.get('subrepos'))
4596 changestates = zip(states, 'MAR!?IC', stat)
4638 changestates = zip(states, 'MAR!?IC', stat)
4597
4639
4598 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4640 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4599 ctxn = repo[nullid]
4641 ctxn = repo[nullid]
4600 ctx1 = repo[node1]
4642 ctx1 = repo[node1]
4601 ctx2 = repo[node2]
4643 ctx2 = repo[node2]
4602 added = stat[1]
4644 added = stat[1]
4603 if node2 is None:
4645 if node2 is None:
4604 added = stat[0] + stat[1] # merged?
4646 added = stat[0] + stat[1] # merged?
4605
4647
4606 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4648 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4607 if k in added:
4649 if k in added:
4608 copy[k] = v
4650 copy[k] = v
4609 elif v in added:
4651 elif v in added:
4610 copy[v] = k
4652 copy[v] = k
4611
4653
4612 for state, char, files in changestates:
4654 for state, char, files in changestates:
4613 if state in show:
4655 if state in show:
4614 format = "%s %%s%s" % (char, end)
4656 format = "%s %%s%s" % (char, end)
4615 if opts.get('no_status'):
4657 if opts.get('no_status'):
4616 format = "%%s%s" % end
4658 format = "%%s%s" % end
4617
4659
4618 for f in files:
4660 for f in files:
4619 ui.write(format % repo.pathto(f, cwd),
4661 ui.write(format % repo.pathto(f, cwd),
4620 label='status.' + state)
4662 label='status.' + state)
4621 if f in copy:
4663 if f in copy:
4622 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4664 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4623 label='status.copied')
4665 label='status.copied')
4624
4666
4625 @command('^summary|sum',
4667 @command('^summary|sum',
4626 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4668 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4627 def summary(ui, repo, **opts):
4669 def summary(ui, repo, **opts):
4628 """summarize working directory state
4670 """summarize working directory state
4629
4671
4630 This generates a brief summary of the working directory state,
4672 This generates a brief summary of the working directory state,
4631 including parents, branch, commit status, and available updates.
4673 including parents, branch, commit status, and available updates.
4632
4674
4633 With the --remote option, this will check the default paths for
4675 With the --remote option, this will check the default paths for
4634 incoming and outgoing changes. This can be time-consuming.
4676 incoming and outgoing changes. This can be time-consuming.
4635
4677
4636 Returns 0 on success.
4678 Returns 0 on success.
4637 """
4679 """
4638
4680
4639 ctx = repo[None]
4681 ctx = repo[None]
4640 parents = ctx.parents()
4682 parents = ctx.parents()
4641 pnode = parents[0].node()
4683 pnode = parents[0].node()
4642
4684
4643 for p in parents:
4685 for p in parents:
4644 # label with log.changeset (instead of log.parent) since this
4686 # label with log.changeset (instead of log.parent) since this
4645 # shows a working directory parent *changeset*:
4687 # shows a working directory parent *changeset*:
4646 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4688 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4647 label='log.changeset')
4689 label='log.changeset')
4648 ui.write(' '.join(p.tags()), label='log.tag')
4690 ui.write(' '.join(p.tags()), label='log.tag')
4649 if p.bookmarks():
4691 if p.bookmarks():
4650 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4692 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4651 if p.rev() == -1:
4693 if p.rev() == -1:
4652 if not len(repo):
4694 if not len(repo):
4653 ui.write(_(' (empty repository)'))
4695 ui.write(_(' (empty repository)'))
4654 else:
4696 else:
4655 ui.write(_(' (no revision checked out)'))
4697 ui.write(_(' (no revision checked out)'))
4656 ui.write('\n')
4698 ui.write('\n')
4657 if p.description():
4699 if p.description():
4658 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4700 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4659 label='log.summary')
4701 label='log.summary')
4660
4702
4661 branch = ctx.branch()
4703 branch = ctx.branch()
4662 bheads = repo.branchheads(branch)
4704 bheads = repo.branchheads(branch)
4663 m = _('branch: %s\n') % branch
4705 m = _('branch: %s\n') % branch
4664 if branch != 'default':
4706 if branch != 'default':
4665 ui.write(m, label='log.branch')
4707 ui.write(m, label='log.branch')
4666 else:
4708 else:
4667 ui.status(m, label='log.branch')
4709 ui.status(m, label='log.branch')
4668
4710
4669 st = list(repo.status(unknown=True))[:6]
4711 st = list(repo.status(unknown=True))[:6]
4670
4712
4671 c = repo.dirstate.copies()
4713 c = repo.dirstate.copies()
4672 copied, renamed = [], []
4714 copied, renamed = [], []
4673 for d, s in c.iteritems():
4715 for d, s in c.iteritems():
4674 if s in st[2]:
4716 if s in st[2]:
4675 st[2].remove(s)
4717 st[2].remove(s)
4676 renamed.append(d)
4718 renamed.append(d)
4677 else:
4719 else:
4678 copied.append(d)
4720 copied.append(d)
4679 if d in st[1]:
4721 if d in st[1]:
4680 st[1].remove(d)
4722 st[1].remove(d)
4681 st.insert(3, renamed)
4723 st.insert(3, renamed)
4682 st.insert(4, copied)
4724 st.insert(4, copied)
4683
4725
4684 ms = mergemod.mergestate(repo)
4726 ms = mergemod.mergestate(repo)
4685 st.append([f for f in ms if ms[f] == 'u'])
4727 st.append([f for f in ms if ms[f] == 'u'])
4686
4728
4687 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4729 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4688 st.append(subs)
4730 st.append(subs)
4689
4731
4690 labels = [ui.label(_('%d modified'), 'status.modified'),
4732 labels = [ui.label(_('%d modified'), 'status.modified'),
4691 ui.label(_('%d added'), 'status.added'),
4733 ui.label(_('%d added'), 'status.added'),
4692 ui.label(_('%d removed'), 'status.removed'),
4734 ui.label(_('%d removed'), 'status.removed'),
4693 ui.label(_('%d renamed'), 'status.copied'),
4735 ui.label(_('%d renamed'), 'status.copied'),
4694 ui.label(_('%d copied'), 'status.copied'),
4736 ui.label(_('%d copied'), 'status.copied'),
4695 ui.label(_('%d deleted'), 'status.deleted'),
4737 ui.label(_('%d deleted'), 'status.deleted'),
4696 ui.label(_('%d unknown'), 'status.unknown'),
4738 ui.label(_('%d unknown'), 'status.unknown'),
4697 ui.label(_('%d ignored'), 'status.ignored'),
4739 ui.label(_('%d ignored'), 'status.ignored'),
4698 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4740 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4699 ui.label(_('%d subrepos'), 'status.modified')]
4741 ui.label(_('%d subrepos'), 'status.modified')]
4700 t = []
4742 t = []
4701 for s, l in zip(st, labels):
4743 for s, l in zip(st, labels):
4702 if s:
4744 if s:
4703 t.append(l % len(s))
4745 t.append(l % len(s))
4704
4746
4705 t = ', '.join(t)
4747 t = ', '.join(t)
4706 cleanworkdir = False
4748 cleanworkdir = False
4707
4749
4708 if len(parents) > 1:
4750 if len(parents) > 1:
4709 t += _(' (merge)')
4751 t += _(' (merge)')
4710 elif branch != parents[0].branch():
4752 elif branch != parents[0].branch():
4711 t += _(' (new branch)')
4753 t += _(' (new branch)')
4712 elif (parents[0].extra().get('close') and
4754 elif (parents[0].extra().get('close') and
4713 pnode in repo.branchheads(branch, closed=True)):
4755 pnode in repo.branchheads(branch, closed=True)):
4714 t += _(' (head closed)')
4756 t += _(' (head closed)')
4715 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4757 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4716 t += _(' (clean)')
4758 t += _(' (clean)')
4717 cleanworkdir = True
4759 cleanworkdir = True
4718 elif pnode not in bheads:
4760 elif pnode not in bheads:
4719 t += _(' (new branch head)')
4761 t += _(' (new branch head)')
4720
4762
4721 if cleanworkdir:
4763 if cleanworkdir:
4722 ui.status(_('commit: %s\n') % t.strip())
4764 ui.status(_('commit: %s\n') % t.strip())
4723 else:
4765 else:
4724 ui.write(_('commit: %s\n') % t.strip())
4766 ui.write(_('commit: %s\n') % t.strip())
4725
4767
4726 # all ancestors of branch heads - all ancestors of parent = new csets
4768 # all ancestors of branch heads - all ancestors of parent = new csets
4727 new = [0] * len(repo)
4769 new = [0] * len(repo)
4728 cl = repo.changelog
4770 cl = repo.changelog
4729 for a in [cl.rev(n) for n in bheads]:
4771 for a in [cl.rev(n) for n in bheads]:
4730 new[a] = 1
4772 new[a] = 1
4731 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4773 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4732 new[a] = 1
4774 new[a] = 1
4733 for a in [p.rev() for p in parents]:
4775 for a in [p.rev() for p in parents]:
4734 if a >= 0:
4776 if a >= 0:
4735 new[a] = 0
4777 new[a] = 0
4736 for a in cl.ancestors(*[p.rev() for p in parents]):
4778 for a in cl.ancestors(*[p.rev() for p in parents]):
4737 new[a] = 0
4779 new[a] = 0
4738 new = sum(new)
4780 new = sum(new)
4739
4781
4740 if new == 0:
4782 if new == 0:
4741 ui.status(_('update: (current)\n'))
4783 ui.status(_('update: (current)\n'))
4742 elif pnode not in bheads:
4784 elif pnode not in bheads:
4743 ui.write(_('update: %d new changesets (update)\n') % new)
4785 ui.write(_('update: %d new changesets (update)\n') % new)
4744 else:
4786 else:
4745 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4787 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4746 (new, len(bheads)))
4788 (new, len(bheads)))
4747
4789
4748 if opts.get('remote'):
4790 if opts.get('remote'):
4749 t = []
4791 t = []
4750 source, branches = hg.parseurl(ui.expandpath('default'))
4792 source, branches = hg.parseurl(ui.expandpath('default'))
4751 other = hg.peer(repo, {}, source)
4793 other = hg.peer(repo, {}, source)
4752 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4794 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4753 ui.debug('comparing with %s\n' % util.hidepassword(source))
4795 ui.debug('comparing with %s\n' % util.hidepassword(source))
4754 repo.ui.pushbuffer()
4796 repo.ui.pushbuffer()
4755 commoninc = discovery.findcommonincoming(repo, other)
4797 commoninc = discovery.findcommonincoming(repo, other)
4756 _common, incoming, _rheads = commoninc
4798 _common, incoming, _rheads = commoninc
4757 repo.ui.popbuffer()
4799 repo.ui.popbuffer()
4758 if incoming:
4800 if incoming:
4759 t.append(_('1 or more incoming'))
4801 t.append(_('1 or more incoming'))
4760
4802
4761 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4803 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4762 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4804 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4763 if source != dest:
4805 if source != dest:
4764 other = hg.peer(repo, {}, dest)
4806 other = hg.peer(repo, {}, dest)
4765 commoninc = None
4807 commoninc = None
4766 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4808 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4767 repo.ui.pushbuffer()
4809 repo.ui.pushbuffer()
4768 common, outheads = discovery.findcommonoutgoing(repo, other,
4810 common, outheads = discovery.findcommonoutgoing(repo, other,
4769 commoninc=commoninc)
4811 commoninc=commoninc)
4770 repo.ui.popbuffer()
4812 repo.ui.popbuffer()
4771 o = repo.changelog.findmissing(common=common, heads=outheads)
4813 o = repo.changelog.findmissing(common=common, heads=outheads)
4772 if o:
4814 if o:
4773 t.append(_('%d outgoing') % len(o))
4815 t.append(_('%d outgoing') % len(o))
4774 if 'bookmarks' in other.listkeys('namespaces'):
4816 if 'bookmarks' in other.listkeys('namespaces'):
4775 lmarks = repo.listkeys('bookmarks')
4817 lmarks = repo.listkeys('bookmarks')
4776 rmarks = other.listkeys('bookmarks')
4818 rmarks = other.listkeys('bookmarks')
4777 diff = set(rmarks) - set(lmarks)
4819 diff = set(rmarks) - set(lmarks)
4778 if len(diff) > 0:
4820 if len(diff) > 0:
4779 t.append(_('%d incoming bookmarks') % len(diff))
4821 t.append(_('%d incoming bookmarks') % len(diff))
4780 diff = set(lmarks) - set(rmarks)
4822 diff = set(lmarks) - set(rmarks)
4781 if len(diff) > 0:
4823 if len(diff) > 0:
4782 t.append(_('%d outgoing bookmarks') % len(diff))
4824 t.append(_('%d outgoing bookmarks') % len(diff))
4783
4825
4784 if t:
4826 if t:
4785 ui.write(_('remote: %s\n') % (', '.join(t)))
4827 ui.write(_('remote: %s\n') % (', '.join(t)))
4786 else:
4828 else:
4787 ui.status(_('remote: (synced)\n'))
4829 ui.status(_('remote: (synced)\n'))
4788
4830
4789 @command('tag',
4831 @command('tag',
4790 [('f', 'force', None, _('force tag')),
4832 [('f', 'force', None, _('force tag')),
4791 ('l', 'local', None, _('make the tag local')),
4833 ('l', 'local', None, _('make the tag local')),
4792 ('r', 'rev', '', _('revision to tag'), _('REV')),
4834 ('r', 'rev', '', _('revision to tag'), _('REV')),
4793 ('', 'remove', None, _('remove a tag')),
4835 ('', 'remove', None, _('remove a tag')),
4794 # -l/--local is already there, commitopts cannot be used
4836 # -l/--local is already there, commitopts cannot be used
4795 ('e', 'edit', None, _('edit commit message')),
4837 ('e', 'edit', None, _('edit commit message')),
4796 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4838 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4797 ] + commitopts2,
4839 ] + commitopts2,
4798 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4840 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4799 def tag(ui, repo, name1, *names, **opts):
4841 def tag(ui, repo, name1, *names, **opts):
4800 """add one or more tags for the current or given revision
4842 """add one or more tags for the current or given revision
4801
4843
4802 Name a particular revision using <name>.
4844 Name a particular revision using <name>.
4803
4845
4804 Tags are used to name particular revisions of the repository and are
4846 Tags are used to name particular revisions of the repository and are
4805 very useful to compare different revisions, to go back to significant
4847 very useful to compare different revisions, to go back to significant
4806 earlier versions or to mark branch points as releases, etc. Changing
4848 earlier versions or to mark branch points as releases, etc. Changing
4807 an existing tag is normally disallowed; use -f/--force to override.
4849 an existing tag is normally disallowed; use -f/--force to override.
4808
4850
4809 If no revision is given, the parent of the working directory is
4851 If no revision is given, the parent of the working directory is
4810 used, or tip if no revision is checked out.
4852 used, or tip if no revision is checked out.
4811
4853
4812 To facilitate version control, distribution, and merging of tags,
4854 To facilitate version control, distribution, and merging of tags,
4813 they are stored as a file named ".hgtags" which is managed similarly
4855 they are stored as a file named ".hgtags" which is managed similarly
4814 to other project files and can be hand-edited if necessary. This
4856 to other project files and can be hand-edited if necessary. This
4815 also means that tagging creates a new commit. The file
4857 also means that tagging creates a new commit. The file
4816 ".hg/localtags" is used for local tags (not shared among
4858 ".hg/localtags" is used for local tags (not shared among
4817 repositories).
4859 repositories).
4818
4860
4819 Tag commits are usually made at the head of a branch. If the parent
4861 Tag commits are usually made at the head of a branch. If the parent
4820 of the working directory is not a branch head, :hg:`tag` aborts; use
4862 of the working directory is not a branch head, :hg:`tag` aborts; use
4821 -f/--force to force the tag commit to be based on a non-head
4863 -f/--force to force the tag commit to be based on a non-head
4822 changeset.
4864 changeset.
4823
4865
4824 See :hg:`help dates` for a list of formats valid for -d/--date.
4866 See :hg:`help dates` for a list of formats valid for -d/--date.
4825
4867
4826 Since tag names have priority over branch names during revision
4868 Since tag names have priority over branch names during revision
4827 lookup, using an existing branch name as a tag name is discouraged.
4869 lookup, using an existing branch name as a tag name is discouraged.
4828
4870
4829 Returns 0 on success.
4871 Returns 0 on success.
4830 """
4872 """
4831
4873
4832 rev_ = "."
4874 rev_ = "."
4833 names = [t.strip() for t in (name1,) + names]
4875 names = [t.strip() for t in (name1,) + names]
4834 if len(names) != len(set(names)):
4876 if len(names) != len(set(names)):
4835 raise util.Abort(_('tag names must be unique'))
4877 raise util.Abort(_('tag names must be unique'))
4836 for n in names:
4878 for n in names:
4837 if n in ['tip', '.', 'null']:
4879 if n in ['tip', '.', 'null']:
4838 raise util.Abort(_("the name '%s' is reserved") % n)
4880 raise util.Abort(_("the name '%s' is reserved") % n)
4839 if not n:
4881 if not n:
4840 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4882 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4841 if opts.get('rev') and opts.get('remove'):
4883 if opts.get('rev') and opts.get('remove'):
4842 raise util.Abort(_("--rev and --remove are incompatible"))
4884 raise util.Abort(_("--rev and --remove are incompatible"))
4843 if opts.get('rev'):
4885 if opts.get('rev'):
4844 rev_ = opts['rev']
4886 rev_ = opts['rev']
4845 message = opts.get('message')
4887 message = opts.get('message')
4846 if opts.get('remove'):
4888 if opts.get('remove'):
4847 expectedtype = opts.get('local') and 'local' or 'global'
4889 expectedtype = opts.get('local') and 'local' or 'global'
4848 for n in names:
4890 for n in names:
4849 if not repo.tagtype(n):
4891 if not repo.tagtype(n):
4850 raise util.Abort(_("tag '%s' does not exist") % n)
4892 raise util.Abort(_("tag '%s' does not exist") % n)
4851 if repo.tagtype(n) != expectedtype:
4893 if repo.tagtype(n) != expectedtype:
4852 if expectedtype == 'global':
4894 if expectedtype == 'global':
4853 raise util.Abort(_("tag '%s' is not a global tag") % n)
4895 raise util.Abort(_("tag '%s' is not a global tag") % n)
4854 else:
4896 else:
4855 raise util.Abort(_("tag '%s' is not a local tag") % n)
4897 raise util.Abort(_("tag '%s' is not a local tag") % n)
4856 rev_ = nullid
4898 rev_ = nullid
4857 if not message:
4899 if not message:
4858 # we don't translate commit messages
4900 # we don't translate commit messages
4859 message = 'Removed tag %s' % ', '.join(names)
4901 message = 'Removed tag %s' % ', '.join(names)
4860 elif not opts.get('force'):
4902 elif not opts.get('force'):
4861 for n in names:
4903 for n in names:
4862 if n in repo.tags():
4904 if n in repo.tags():
4863 raise util.Abort(_("tag '%s' already exists "
4905 raise util.Abort(_("tag '%s' already exists "
4864 "(use -f to force)") % n)
4906 "(use -f to force)") % n)
4865 if not opts.get('local'):
4907 if not opts.get('local'):
4866 p1, p2 = repo.dirstate.parents()
4908 p1, p2 = repo.dirstate.parents()
4867 if p2 != nullid:
4909 if p2 != nullid:
4868 raise util.Abort(_('uncommitted merge'))
4910 raise util.Abort(_('uncommitted merge'))
4869 bheads = repo.branchheads()
4911 bheads = repo.branchheads()
4870 if not opts.get('force') and bheads and p1 not in bheads:
4912 if not opts.get('force') and bheads and p1 not in bheads:
4871 raise util.Abort(_('not at a branch head (use -f to force)'))
4913 raise util.Abort(_('not at a branch head (use -f to force)'))
4872 r = scmutil.revsingle(repo, rev_).node()
4914 r = scmutil.revsingle(repo, rev_).node()
4873
4915
4874 if not message:
4916 if not message:
4875 # we don't translate commit messages
4917 # we don't translate commit messages
4876 message = ('Added tag %s for changeset %s' %
4918 message = ('Added tag %s for changeset %s' %
4877 (', '.join(names), short(r)))
4919 (', '.join(names), short(r)))
4878
4920
4879 date = opts.get('date')
4921 date = opts.get('date')
4880 if date:
4922 if date:
4881 date = util.parsedate(date)
4923 date = util.parsedate(date)
4882
4924
4883 if opts.get('edit'):
4925 if opts.get('edit'):
4884 message = ui.edit(message, ui.username())
4926 message = ui.edit(message, ui.username())
4885
4927
4886 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4928 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4887
4929
4888 @command('tags', [], '')
4930 @command('tags', [], '')
4889 def tags(ui, repo):
4931 def tags(ui, repo):
4890 """list repository tags
4932 """list repository tags
4891
4933
4892 This lists both regular and local tags. When the -v/--verbose
4934 This lists both regular and local tags. When the -v/--verbose
4893 switch is used, a third column "local" is printed for local tags.
4935 switch is used, a third column "local" is printed for local tags.
4894
4936
4895 Returns 0 on success.
4937 Returns 0 on success.
4896 """
4938 """
4897
4939
4898 hexfunc = ui.debugflag and hex or short
4940 hexfunc = ui.debugflag and hex or short
4899 tagtype = ""
4941 tagtype = ""
4900
4942
4901 for t, n in reversed(repo.tagslist()):
4943 for t, n in reversed(repo.tagslist()):
4902 if ui.quiet:
4944 if ui.quiet:
4903 ui.write("%s\n" % t)
4945 ui.write("%s\n" % t)
4904 continue
4946 continue
4905
4947
4906 hn = hexfunc(n)
4948 hn = hexfunc(n)
4907 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4949 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4908 spaces = " " * (30 - encoding.colwidth(t))
4950 spaces = " " * (30 - encoding.colwidth(t))
4909
4951
4910 if ui.verbose:
4952 if ui.verbose:
4911 if repo.tagtype(t) == 'local':
4953 if repo.tagtype(t) == 'local':
4912 tagtype = " local"
4954 tagtype = " local"
4913 else:
4955 else:
4914 tagtype = ""
4956 tagtype = ""
4915 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4957 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4916
4958
4917 @command('tip',
4959 @command('tip',
4918 [('p', 'patch', None, _('show patch')),
4960 [('p', 'patch', None, _('show patch')),
4919 ('g', 'git', None, _('use git extended diff format')),
4961 ('g', 'git', None, _('use git extended diff format')),
4920 ] + templateopts,
4962 ] + templateopts,
4921 _('[-p] [-g]'))
4963 _('[-p] [-g]'))
4922 def tip(ui, repo, **opts):
4964 def tip(ui, repo, **opts):
4923 """show the tip revision
4965 """show the tip revision
4924
4966
4925 The tip revision (usually just called the tip) is the changeset
4967 The tip revision (usually just called the tip) is the changeset
4926 most recently added to the repository (and therefore the most
4968 most recently added to the repository (and therefore the most
4927 recently changed head).
4969 recently changed head).
4928
4970
4929 If you have just made a commit, that commit will be the tip. If
4971 If you have just made a commit, that commit will be the tip. If
4930 you have just pulled changes from another repository, the tip of
4972 you have just pulled changes from another repository, the tip of
4931 that repository becomes the current tip. The "tip" tag is special
4973 that repository becomes the current tip. The "tip" tag is special
4932 and cannot be renamed or assigned to a different changeset.
4974 and cannot be renamed or assigned to a different changeset.
4933
4975
4934 Returns 0 on success.
4976 Returns 0 on success.
4935 """
4977 """
4936 displayer = cmdutil.show_changeset(ui, repo, opts)
4978 displayer = cmdutil.show_changeset(ui, repo, opts)
4937 displayer.show(repo[len(repo) - 1])
4979 displayer.show(repo[len(repo) - 1])
4938 displayer.close()
4980 displayer.close()
4939
4981
4940 @command('unbundle',
4982 @command('unbundle',
4941 [('u', 'update', None,
4983 [('u', 'update', None,
4942 _('update to new branch head if changesets were unbundled'))],
4984 _('update to new branch head if changesets were unbundled'))],
4943 _('[-u] FILE...'))
4985 _('[-u] FILE...'))
4944 def unbundle(ui, repo, fname1, *fnames, **opts):
4986 def unbundle(ui, repo, fname1, *fnames, **opts):
4945 """apply one or more changegroup files
4987 """apply one or more changegroup files
4946
4988
4947 Apply one or more compressed changegroup files generated by the
4989 Apply one or more compressed changegroup files generated by the
4948 bundle command.
4990 bundle command.
4949
4991
4950 Returns 0 on success, 1 if an update has unresolved files.
4992 Returns 0 on success, 1 if an update has unresolved files.
4951 """
4993 """
4952 fnames = (fname1,) + fnames
4994 fnames = (fname1,) + fnames
4953
4995
4954 lock = repo.lock()
4996 lock = repo.lock()
4955 wc = repo['.']
4997 wc = repo['.']
4956 try:
4998 try:
4957 for fname in fnames:
4999 for fname in fnames:
4958 f = url.open(ui, fname)
5000 f = url.open(ui, fname)
4959 gen = changegroup.readbundle(f, fname)
5001 gen = changegroup.readbundle(f, fname)
4960 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
5002 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4961 lock=lock)
5003 lock=lock)
4962 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5004 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4963 finally:
5005 finally:
4964 lock.release()
5006 lock.release()
4965 return postincoming(ui, repo, modheads, opts.get('update'), None)
5007 return postincoming(ui, repo, modheads, opts.get('update'), None)
4966
5008
4967 @command('^update|up|checkout|co',
5009 @command('^update|up|checkout|co',
4968 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5010 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4969 ('c', 'check', None,
5011 ('c', 'check', None,
4970 _('update across branches if no uncommitted changes')),
5012 _('update across branches if no uncommitted changes')),
4971 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5013 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4972 ('r', 'rev', '', _('revision'), _('REV'))],
5014 ('r', 'rev', '', _('revision'), _('REV'))],
4973 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5015 _('[-c] [-C] [-d DATE] [[-r] REV]'))
4974 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5016 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4975 """update working directory (or switch revisions)
5017 """update working directory (or switch revisions)
4976
5018
4977 Update the repository's working directory to the specified
5019 Update the repository's working directory to the specified
4978 changeset. If no changeset is specified, update to the tip of the
5020 changeset. If no changeset is specified, update to the tip of the
4979 current named branch.
5021 current named branch.
4980
5022
4981 If the changeset is not a descendant of the working directory's
5023 If the changeset is not a descendant of the working directory's
4982 parent, the update is aborted. With the -c/--check option, the
5024 parent, the update is aborted. With the -c/--check option, the
4983 working directory is checked for uncommitted changes; if none are
5025 working directory is checked for uncommitted changes; if none are
4984 found, the working directory is updated to the specified
5026 found, the working directory is updated to the specified
4985 changeset.
5027 changeset.
4986
5028
4987 The following rules apply when the working directory contains
5029 The following rules apply when the working directory contains
4988 uncommitted changes:
5030 uncommitted changes:
4989
5031
4990 1. If neither -c/--check nor -C/--clean is specified, and if
5032 1. If neither -c/--check nor -C/--clean is specified, and if
4991 the requested changeset is an ancestor or descendant of
5033 the requested changeset is an ancestor or descendant of
4992 the working directory's parent, the uncommitted changes
5034 the working directory's parent, the uncommitted changes
4993 are merged into the requested changeset and the merged
5035 are merged into the requested changeset and the merged
4994 result is left uncommitted. If the requested changeset is
5036 result is left uncommitted. If the requested changeset is
4995 not an ancestor or descendant (that is, it is on another
5037 not an ancestor or descendant (that is, it is on another
4996 branch), the update is aborted and the uncommitted changes
5038 branch), the update is aborted and the uncommitted changes
4997 are preserved.
5039 are preserved.
4998
5040
4999 2. With the -c/--check option, the update is aborted and the
5041 2. With the -c/--check option, the update is aborted and the
5000 uncommitted changes are preserved.
5042 uncommitted changes are preserved.
5001
5043
5002 3. With the -C/--clean option, uncommitted changes are discarded and
5044 3. With the -C/--clean option, uncommitted changes are discarded and
5003 the working directory is updated to the requested changeset.
5045 the working directory is updated to the requested changeset.
5004
5046
5005 Use null as the changeset to remove the working directory (like
5047 Use null as the changeset to remove the working directory (like
5006 :hg:`clone -U`).
5048 :hg:`clone -U`).
5007
5049
5008 If you want to update just one file to an older changeset, use
5050 If you want to update just one file to an older changeset, use
5009 :hg:`revert`.
5051 :hg:`revert`.
5010
5052
5011 See :hg:`help dates` for a list of formats valid for -d/--date.
5053 See :hg:`help dates` for a list of formats valid for -d/--date.
5012
5054
5013 Returns 0 on success, 1 if there are unresolved files.
5055 Returns 0 on success, 1 if there are unresolved files.
5014 """
5056 """
5015 if rev and node:
5057 if rev and node:
5016 raise util.Abort(_("please specify just one revision"))
5058 raise util.Abort(_("please specify just one revision"))
5017
5059
5018 if rev is None or rev == '':
5060 if rev is None or rev == '':
5019 rev = node
5061 rev = node
5020
5062
5021 # if we defined a bookmark, we have to remember the original bookmark name
5063 # if we defined a bookmark, we have to remember the original bookmark name
5022 brev = rev
5064 brev = rev
5023 rev = scmutil.revsingle(repo, rev, rev).rev()
5065 rev = scmutil.revsingle(repo, rev, rev).rev()
5024
5066
5025 if check and clean:
5067 if check and clean:
5026 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5068 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5027
5069
5028 if check:
5070 if check:
5029 # we could use dirty() but we can ignore merge and branch trivia
5071 # we could use dirty() but we can ignore merge and branch trivia
5030 c = repo[None]
5072 c = repo[None]
5031 if c.modified() or c.added() or c.removed():
5073 if c.modified() or c.added() or c.removed():
5032 raise util.Abort(_("uncommitted local changes"))
5074 raise util.Abort(_("uncommitted local changes"))
5033
5075
5034 if date:
5076 if date:
5035 if rev is not None:
5077 if rev is not None:
5036 raise util.Abort(_("you can't specify a revision and a date"))
5078 raise util.Abort(_("you can't specify a revision and a date"))
5037 rev = cmdutil.finddate(ui, repo, date)
5079 rev = cmdutil.finddate(ui, repo, date)
5038
5080
5039 if clean or check:
5081 if clean or check:
5040 ret = hg.clean(repo, rev)
5082 ret = hg.clean(repo, rev)
5041 else:
5083 else:
5042 ret = hg.update(repo, rev)
5084 ret = hg.update(repo, rev)
5043
5085
5044 if brev in repo._bookmarks:
5086 if brev in repo._bookmarks:
5045 bookmarks.setcurrent(repo, brev)
5087 bookmarks.setcurrent(repo, brev)
5046
5088
5047 return ret
5089 return ret
5048
5090
5049 @command('verify', [])
5091 @command('verify', [])
5050 def verify(ui, repo):
5092 def verify(ui, repo):
5051 """verify the integrity of the repository
5093 """verify the integrity of the repository
5052
5094
5053 Verify the integrity of the current repository.
5095 Verify the integrity of the current repository.
5054
5096
5055 This will perform an extensive check of the repository's
5097 This will perform an extensive check of the repository's
5056 integrity, validating the hashes and checksums of each entry in
5098 integrity, validating the hashes and checksums of each entry in
5057 the changelog, manifest, and tracked files, as well as the
5099 the changelog, manifest, and tracked files, as well as the
5058 integrity of their crosslinks and indices.
5100 integrity of their crosslinks and indices.
5059
5101
5060 Returns 0 on success, 1 if errors are encountered.
5102 Returns 0 on success, 1 if errors are encountered.
5061 """
5103 """
5062 return hg.verify(repo)
5104 return hg.verify(repo)
5063
5105
5064 @command('version', [])
5106 @command('version', [])
5065 def version_(ui):
5107 def version_(ui):
5066 """output version and copyright information"""
5108 """output version and copyright information"""
5067 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5109 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5068 % util.version())
5110 % util.version())
5069 ui.status(_(
5111 ui.status(_(
5070 "(see http://mercurial.selenic.com for more information)\n"
5112 "(see http://mercurial.selenic.com for more information)\n"
5071 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5113 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5072 "This is free software; see the source for copying conditions. "
5114 "This is free software; see the source for copying conditions. "
5073 "There is NO\nwarranty; "
5115 "There is NO\nwarranty; "
5074 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5116 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5075 ))
5117 ))
5076
5118
5077 norepo = ("clone init version help debugcommands debugcomplete"
5119 norepo = ("clone init version help debugcommands debugcomplete"
5078 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5120 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5079 " debugknown debuggetbundle debugbundle")
5121 " debugknown debuggetbundle debugbundle")
5080 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5122 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5081 " debugdata debugindex debugindexdot debugrevlog")
5123 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,1783 +1,1848 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import cStringIO, email.Parser, os, errno, re
9 import cStringIO, email.Parser, os, errno, re
10 import tempfile, zlib, shutil
10 import tempfile, zlib, shutil
11
11
12 from i18n import _
12 from i18n import _
13 from node import hex, nullid, short
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 gitre = re.compile('diff --git a/(.*) b/(.*)')
17 gitre = re.compile('diff --git a/(.*) b/(.*)')
17
18
18 class PatchError(Exception):
19 class PatchError(Exception):
19 pass
20 pass
20
21
21
22
22 # public functions
23 # public functions
23
24
24 def split(stream):
25 def split(stream):
25 '''return an iterator of individual patches from a stream'''
26 '''return an iterator of individual patches from a stream'''
26 def isheader(line, inheader):
27 def isheader(line, inheader):
27 if inheader and line[0] in (' ', '\t'):
28 if inheader and line[0] in (' ', '\t'):
28 # continuation
29 # continuation
29 return True
30 return True
30 if line[0] in (' ', '-', '+'):
31 if line[0] in (' ', '-', '+'):
31 # diff line - don't check for header pattern in there
32 # diff line - don't check for header pattern in there
32 return False
33 return False
33 l = line.split(': ', 1)
34 l = line.split(': ', 1)
34 return len(l) == 2 and ' ' not in l[0]
35 return len(l) == 2 and ' ' not in l[0]
35
36
36 def chunk(lines):
37 def chunk(lines):
37 return cStringIO.StringIO(''.join(lines))
38 return cStringIO.StringIO(''.join(lines))
38
39
39 def hgsplit(stream, cur):
40 def hgsplit(stream, cur):
40 inheader = True
41 inheader = True
41
42
42 for line in stream:
43 for line in stream:
43 if not line.strip():
44 if not line.strip():
44 inheader = False
45 inheader = False
45 if not inheader and line.startswith('# HG changeset patch'):
46 if not inheader and line.startswith('# HG changeset patch'):
46 yield chunk(cur)
47 yield chunk(cur)
47 cur = []
48 cur = []
48 inheader = True
49 inheader = True
49
50
50 cur.append(line)
51 cur.append(line)
51
52
52 if cur:
53 if cur:
53 yield chunk(cur)
54 yield chunk(cur)
54
55
55 def mboxsplit(stream, cur):
56 def mboxsplit(stream, cur):
56 for line in stream:
57 for line in stream:
57 if line.startswith('From '):
58 if line.startswith('From '):
58 for c in split(chunk(cur[1:])):
59 for c in split(chunk(cur[1:])):
59 yield c
60 yield c
60 cur = []
61 cur = []
61
62
62 cur.append(line)
63 cur.append(line)
63
64
64 if cur:
65 if cur:
65 for c in split(chunk(cur[1:])):
66 for c in split(chunk(cur[1:])):
66 yield c
67 yield c
67
68
68 def mimesplit(stream, cur):
69 def mimesplit(stream, cur):
69 def msgfp(m):
70 def msgfp(m):
70 fp = cStringIO.StringIO()
71 fp = cStringIO.StringIO()
71 g = email.Generator.Generator(fp, mangle_from_=False)
72 g = email.Generator.Generator(fp, mangle_from_=False)
72 g.flatten(m)
73 g.flatten(m)
73 fp.seek(0)
74 fp.seek(0)
74 return fp
75 return fp
75
76
76 for line in stream:
77 for line in stream:
77 cur.append(line)
78 cur.append(line)
78 c = chunk(cur)
79 c = chunk(cur)
79
80
80 m = email.Parser.Parser().parse(c)
81 m = email.Parser.Parser().parse(c)
81 if not m.is_multipart():
82 if not m.is_multipart():
82 yield msgfp(m)
83 yield msgfp(m)
83 else:
84 else:
84 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
85 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
85 for part in m.walk():
86 for part in m.walk():
86 ct = part.get_content_type()
87 ct = part.get_content_type()
87 if ct not in ok_types:
88 if ct not in ok_types:
88 continue
89 continue
89 yield msgfp(part)
90 yield msgfp(part)
90
91
91 def headersplit(stream, cur):
92 def headersplit(stream, cur):
92 inheader = False
93 inheader = False
93
94
94 for line in stream:
95 for line in stream:
95 if not inheader and isheader(line, inheader):
96 if not inheader and isheader(line, inheader):
96 yield chunk(cur)
97 yield chunk(cur)
97 cur = []
98 cur = []
98 inheader = True
99 inheader = True
99 if inheader and not isheader(line, inheader):
100 if inheader and not isheader(line, inheader):
100 inheader = False
101 inheader = False
101
102
102 cur.append(line)
103 cur.append(line)
103
104
104 if cur:
105 if cur:
105 yield chunk(cur)
106 yield chunk(cur)
106
107
107 def remainder(cur):
108 def remainder(cur):
108 yield chunk(cur)
109 yield chunk(cur)
109
110
110 class fiter(object):
111 class fiter(object):
111 def __init__(self, fp):
112 def __init__(self, fp):
112 self.fp = fp
113 self.fp = fp
113
114
114 def __iter__(self):
115 def __iter__(self):
115 return self
116 return self
116
117
117 def next(self):
118 def next(self):
118 l = self.fp.readline()
119 l = self.fp.readline()
119 if not l:
120 if not l:
120 raise StopIteration
121 raise StopIteration
121 return l
122 return l
122
123
123 inheader = False
124 inheader = False
124 cur = []
125 cur = []
125
126
126 mimeheaders = ['content-type']
127 mimeheaders = ['content-type']
127
128
128 if not hasattr(stream, 'next'):
129 if not hasattr(stream, 'next'):
129 # http responses, for example, have readline but not next
130 # http responses, for example, have readline but not next
130 stream = fiter(stream)
131 stream = fiter(stream)
131
132
132 for line in stream:
133 for line in stream:
133 cur.append(line)
134 cur.append(line)
134 if line.startswith('# HG changeset patch'):
135 if line.startswith('# HG changeset patch'):
135 return hgsplit(stream, cur)
136 return hgsplit(stream, cur)
136 elif line.startswith('From '):
137 elif line.startswith('From '):
137 return mboxsplit(stream, cur)
138 return mboxsplit(stream, cur)
138 elif isheader(line, inheader):
139 elif isheader(line, inheader):
139 inheader = True
140 inheader = True
140 if line.split(':', 1)[0].lower() in mimeheaders:
141 if line.split(':', 1)[0].lower() in mimeheaders:
141 # let email parser handle this
142 # let email parser handle this
142 return mimesplit(stream, cur)
143 return mimesplit(stream, cur)
143 elif line.startswith('--- ') and inheader:
144 elif line.startswith('--- ') and inheader:
144 # No evil headers seen by diff start, split by hand
145 # No evil headers seen by diff start, split by hand
145 return headersplit(stream, cur)
146 return headersplit(stream, cur)
146 # Not enough info, keep reading
147 # Not enough info, keep reading
147
148
148 # if we are here, we have a very plain patch
149 # if we are here, we have a very plain patch
149 return remainder(cur)
150 return remainder(cur)
150
151
151 def extract(ui, fileobj):
152 def extract(ui, fileobj):
152 '''extract patch from data read from fileobj.
153 '''extract patch from data read from fileobj.
153
154
154 patch can be a normal patch or contained in an email message.
155 patch can be a normal patch or contained in an email message.
155
156
156 return tuple (filename, message, user, date, branch, node, p1, p2).
157 return tuple (filename, message, user, date, branch, node, p1, p2).
157 Any item in the returned tuple can be None. If filename is None,
158 Any item in the returned tuple can be None. If filename is None,
158 fileobj did not contain a patch. Caller must unlink filename when done.'''
159 fileobj did not contain a patch. Caller must unlink filename when done.'''
159
160
160 # attempt to detect the start of a patch
161 # attempt to detect the start of a patch
161 # (this heuristic is borrowed from quilt)
162 # (this heuristic is borrowed from quilt)
162 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
163 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
163 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
164 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
164 r'---[ \t].*?^\+\+\+[ \t]|'
165 r'---[ \t].*?^\+\+\+[ \t]|'
165 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
166 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
166
167
167 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
168 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
168 tmpfp = os.fdopen(fd, 'w')
169 tmpfp = os.fdopen(fd, 'w')
169 try:
170 try:
170 msg = email.Parser.Parser().parse(fileobj)
171 msg = email.Parser.Parser().parse(fileobj)
171
172
172 subject = msg['Subject']
173 subject = msg['Subject']
173 user = msg['From']
174 user = msg['From']
174 if not subject and not user:
175 if not subject and not user:
175 # Not an email, restore parsed headers if any
176 # Not an email, restore parsed headers if any
176 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
177 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
177
178
178 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
179 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
179 # should try to parse msg['Date']
180 # should try to parse msg['Date']
180 date = None
181 date = None
181 nodeid = None
182 nodeid = None
182 branch = None
183 branch = None
183 parents = []
184 parents = []
184
185
185 if subject:
186 if subject:
186 if subject.startswith('[PATCH'):
187 if subject.startswith('[PATCH'):
187 pend = subject.find(']')
188 pend = subject.find(']')
188 if pend >= 0:
189 if pend >= 0:
189 subject = subject[pend + 1:].lstrip()
190 subject = subject[pend + 1:].lstrip()
190 subject = subject.replace('\n\t', ' ')
191 subject = subject.replace('\n\t', ' ')
191 ui.debug('Subject: %s\n' % subject)
192 ui.debug('Subject: %s\n' % subject)
192 if user:
193 if user:
193 ui.debug('From: %s\n' % user)
194 ui.debug('From: %s\n' % user)
194 diffs_seen = 0
195 diffs_seen = 0
195 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
196 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
196 message = ''
197 message = ''
197 for part in msg.walk():
198 for part in msg.walk():
198 content_type = part.get_content_type()
199 content_type = part.get_content_type()
199 ui.debug('Content-Type: %s\n' % content_type)
200 ui.debug('Content-Type: %s\n' % content_type)
200 if content_type not in ok_types:
201 if content_type not in ok_types:
201 continue
202 continue
202 payload = part.get_payload(decode=True)
203 payload = part.get_payload(decode=True)
203 m = diffre.search(payload)
204 m = diffre.search(payload)
204 if m:
205 if m:
205 hgpatch = False
206 hgpatch = False
206 hgpatchheader = False
207 hgpatchheader = False
207 ignoretext = False
208 ignoretext = False
208
209
209 ui.debug('found patch at byte %d\n' % m.start(0))
210 ui.debug('found patch at byte %d\n' % m.start(0))
210 diffs_seen += 1
211 diffs_seen += 1
211 cfp = cStringIO.StringIO()
212 cfp = cStringIO.StringIO()
212 for line in payload[:m.start(0)].splitlines():
213 for line in payload[:m.start(0)].splitlines():
213 if line.startswith('# HG changeset patch') and not hgpatch:
214 if line.startswith('# HG changeset patch') and not hgpatch:
214 ui.debug('patch generated by hg export\n')
215 ui.debug('patch generated by hg export\n')
215 hgpatch = True
216 hgpatch = True
216 hgpatchheader = True
217 hgpatchheader = True
217 # drop earlier commit message content
218 # drop earlier commit message content
218 cfp.seek(0)
219 cfp.seek(0)
219 cfp.truncate()
220 cfp.truncate()
220 subject = None
221 subject = None
221 elif hgpatchheader:
222 elif hgpatchheader:
222 if line.startswith('# User '):
223 if line.startswith('# User '):
223 user = line[7:]
224 user = line[7:]
224 ui.debug('From: %s\n' % user)
225 ui.debug('From: %s\n' % user)
225 elif line.startswith("# Date "):
226 elif line.startswith("# Date "):
226 date = line[7:]
227 date = line[7:]
227 elif line.startswith("# Branch "):
228 elif line.startswith("# Branch "):
228 branch = line[9:]
229 branch = line[9:]
229 elif line.startswith("# Node ID "):
230 elif line.startswith("# Node ID "):
230 nodeid = line[10:]
231 nodeid = line[10:]
231 elif line.startswith("# Parent "):
232 elif line.startswith("# Parent "):
232 parents.append(line[10:])
233 parents.append(line[10:])
233 elif not line.startswith("# "):
234 elif not line.startswith("# "):
234 hgpatchheader = False
235 hgpatchheader = False
235 elif line == '---' and gitsendmail:
236 elif line == '---' and gitsendmail:
236 ignoretext = True
237 ignoretext = True
237 if not hgpatchheader and not ignoretext:
238 if not hgpatchheader and not ignoretext:
238 cfp.write(line)
239 cfp.write(line)
239 cfp.write('\n')
240 cfp.write('\n')
240 message = cfp.getvalue()
241 message = cfp.getvalue()
241 if tmpfp:
242 if tmpfp:
242 tmpfp.write(payload)
243 tmpfp.write(payload)
243 if not payload.endswith('\n'):
244 if not payload.endswith('\n'):
244 tmpfp.write('\n')
245 tmpfp.write('\n')
245 elif not diffs_seen and message and content_type == 'text/plain':
246 elif not diffs_seen and message and content_type == 'text/plain':
246 message += '\n' + payload
247 message += '\n' + payload
247 except:
248 except:
248 tmpfp.close()
249 tmpfp.close()
249 os.unlink(tmpname)
250 os.unlink(tmpname)
250 raise
251 raise
251
252
252 if subject and not message.startswith(subject):
253 if subject and not message.startswith(subject):
253 message = '%s\n%s' % (subject, message)
254 message = '%s\n%s' % (subject, message)
254 tmpfp.close()
255 tmpfp.close()
255 if not diffs_seen:
256 if not diffs_seen:
256 os.unlink(tmpname)
257 os.unlink(tmpname)
257 return None, message, user, date, branch, None, None, None
258 return None, message, user, date, branch, None, None, None
258 p1 = parents and parents.pop(0) or None
259 p1 = parents and parents.pop(0) or None
259 p2 = parents and parents.pop(0) or None
260 p2 = parents and parents.pop(0) or None
260 return tmpname, message, user, date, branch, nodeid, p1, p2
261 return tmpname, message, user, date, branch, nodeid, p1, p2
261
262
262 class patchmeta(object):
263 class patchmeta(object):
263 """Patched file metadata
264 """Patched file metadata
264
265
265 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
266 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
266 or COPY. 'path' is patched file path. 'oldpath' is set to the
267 or COPY. 'path' is patched file path. 'oldpath' is set to the
267 origin file when 'op' is either COPY or RENAME, None otherwise. If
268 origin file when 'op' is either COPY or RENAME, None otherwise. If
268 file mode is changed, 'mode' is a tuple (islink, isexec) where
269 file mode is changed, 'mode' is a tuple (islink, isexec) where
269 'islink' is True if the file is a symlink and 'isexec' is True if
270 'islink' is True if the file is a symlink and 'isexec' is True if
270 the file is executable. Otherwise, 'mode' is None.
271 the file is executable. Otherwise, 'mode' is None.
271 """
272 """
272 def __init__(self, path):
273 def __init__(self, path):
273 self.path = path
274 self.path = path
274 self.oldpath = None
275 self.oldpath = None
275 self.mode = None
276 self.mode = None
276 self.op = 'MODIFY'
277 self.op = 'MODIFY'
277 self.binary = False
278 self.binary = False
278
279
279 def setmode(self, mode):
280 def setmode(self, mode):
280 islink = mode & 020000
281 islink = mode & 020000
281 isexec = mode & 0100
282 isexec = mode & 0100
282 self.mode = (islink, isexec)
283 self.mode = (islink, isexec)
283
284
284 def copy(self):
285 def copy(self):
285 other = patchmeta(self.path)
286 other = patchmeta(self.path)
286 other.oldpath = self.oldpath
287 other.oldpath = self.oldpath
287 other.mode = self.mode
288 other.mode = self.mode
288 other.op = self.op
289 other.op = self.op
289 other.binary = self.binary
290 other.binary = self.binary
290 return other
291 return other
291
292
292 def __repr__(self):
293 def __repr__(self):
293 return "<patchmeta %s %r>" % (self.op, self.path)
294 return "<patchmeta %s %r>" % (self.op, self.path)
294
295
295 def readgitpatch(lr):
296 def readgitpatch(lr):
296 """extract git-style metadata about patches from <patchname>"""
297 """extract git-style metadata about patches from <patchname>"""
297
298
298 # Filter patch for git information
299 # Filter patch for git information
299 gp = None
300 gp = None
300 gitpatches = []
301 gitpatches = []
301 for line in lr:
302 for line in lr:
302 line = line.rstrip(' \r\n')
303 line = line.rstrip(' \r\n')
303 if line.startswith('diff --git'):
304 if line.startswith('diff --git'):
304 m = gitre.match(line)
305 m = gitre.match(line)
305 if m:
306 if m:
306 if gp:
307 if gp:
307 gitpatches.append(gp)
308 gitpatches.append(gp)
308 dst = m.group(2)
309 dst = m.group(2)
309 gp = patchmeta(dst)
310 gp = patchmeta(dst)
310 elif gp:
311 elif gp:
311 if line.startswith('--- '):
312 if line.startswith('--- '):
312 gitpatches.append(gp)
313 gitpatches.append(gp)
313 gp = None
314 gp = None
314 continue
315 continue
315 if line.startswith('rename from '):
316 if line.startswith('rename from '):
316 gp.op = 'RENAME'
317 gp.op = 'RENAME'
317 gp.oldpath = line[12:]
318 gp.oldpath = line[12:]
318 elif line.startswith('rename to '):
319 elif line.startswith('rename to '):
319 gp.path = line[10:]
320 gp.path = line[10:]
320 elif line.startswith('copy from '):
321 elif line.startswith('copy from '):
321 gp.op = 'COPY'
322 gp.op = 'COPY'
322 gp.oldpath = line[10:]
323 gp.oldpath = line[10:]
323 elif line.startswith('copy to '):
324 elif line.startswith('copy to '):
324 gp.path = line[8:]
325 gp.path = line[8:]
325 elif line.startswith('deleted file'):
326 elif line.startswith('deleted file'):
326 gp.op = 'DELETE'
327 gp.op = 'DELETE'
327 elif line.startswith('new file mode '):
328 elif line.startswith('new file mode '):
328 gp.op = 'ADD'
329 gp.op = 'ADD'
329 gp.setmode(int(line[-6:], 8))
330 gp.setmode(int(line[-6:], 8))
330 elif line.startswith('new mode '):
331 elif line.startswith('new mode '):
331 gp.setmode(int(line[-6:], 8))
332 gp.setmode(int(line[-6:], 8))
332 elif line.startswith('GIT binary patch'):
333 elif line.startswith('GIT binary patch'):
333 gp.binary = True
334 gp.binary = True
334 if gp:
335 if gp:
335 gitpatches.append(gp)
336 gitpatches.append(gp)
336
337
337 return gitpatches
338 return gitpatches
338
339
339 class linereader(object):
340 class linereader(object):
340 # simple class to allow pushing lines back into the input stream
341 # simple class to allow pushing lines back into the input stream
341 def __init__(self, fp):
342 def __init__(self, fp):
342 self.fp = fp
343 self.fp = fp
343 self.buf = []
344 self.buf = []
344
345
345 def push(self, line):
346 def push(self, line):
346 if line is not None:
347 if line is not None:
347 self.buf.append(line)
348 self.buf.append(line)
348
349
349 def readline(self):
350 def readline(self):
350 if self.buf:
351 if self.buf:
351 l = self.buf[0]
352 l = self.buf[0]
352 del self.buf[0]
353 del self.buf[0]
353 return l
354 return l
354 return self.fp.readline()
355 return self.fp.readline()
355
356
356 def __iter__(self):
357 def __iter__(self):
357 while True:
358 while True:
358 l = self.readline()
359 l = self.readline()
359 if not l:
360 if not l:
360 break
361 break
361 yield l
362 yield l
362
363
363 class abstractbackend(object):
364 class abstractbackend(object):
364 def __init__(self, ui):
365 def __init__(self, ui):
365 self.ui = ui
366 self.ui = ui
366
367
367 def getfile(self, fname):
368 def getfile(self, fname):
368 """Return target file data and flags as a (data, (islink,
369 """Return target file data and flags as a (data, (islink,
369 isexec)) tuple.
370 isexec)) tuple.
370 """
371 """
371 raise NotImplementedError
372 raise NotImplementedError
372
373
373 def setfile(self, fname, data, mode, copysource):
374 def setfile(self, fname, data, mode, copysource):
374 """Write data to target file fname and set its mode. mode is a
375 """Write data to target file fname and set its mode. mode is a
375 (islink, isexec) tuple. If data is None, the file content should
376 (islink, isexec) tuple. If data is None, the file content should
376 be left unchanged. If the file is modified after being copied,
377 be left unchanged. If the file is modified after being copied,
377 copysource is set to the original file name.
378 copysource is set to the original file name.
378 """
379 """
379 raise NotImplementedError
380 raise NotImplementedError
380
381
381 def unlink(self, fname):
382 def unlink(self, fname):
382 """Unlink target file."""
383 """Unlink target file."""
383 raise NotImplementedError
384 raise NotImplementedError
384
385
385 def writerej(self, fname, failed, total, lines):
386 def writerej(self, fname, failed, total, lines):
386 """Write rejected lines for fname. total is the number of hunks
387 """Write rejected lines for fname. total is the number of hunks
387 which failed to apply and total the total number of hunks for this
388 which failed to apply and total the total number of hunks for this
388 files.
389 files.
389 """
390 """
390 pass
391 pass
391
392
392 def exists(self, fname):
393 def exists(self, fname):
393 raise NotImplementedError
394 raise NotImplementedError
394
395
395 class fsbackend(abstractbackend):
396 class fsbackend(abstractbackend):
396 def __init__(self, ui, basedir):
397 def __init__(self, ui, basedir):
397 super(fsbackend, self).__init__(ui)
398 super(fsbackend, self).__init__(ui)
398 self.opener = scmutil.opener(basedir)
399 self.opener = scmutil.opener(basedir)
399
400
400 def _join(self, f):
401 def _join(self, f):
401 return os.path.join(self.opener.base, f)
402 return os.path.join(self.opener.base, f)
402
403
403 def getfile(self, fname):
404 def getfile(self, fname):
404 path = self._join(fname)
405 path = self._join(fname)
405 if os.path.islink(path):
406 if os.path.islink(path):
406 return (os.readlink(path), (True, False))
407 return (os.readlink(path), (True, False))
407 isexec = False
408 isexec = False
408 try:
409 try:
409 isexec = os.lstat(path).st_mode & 0100 != 0
410 isexec = os.lstat(path).st_mode & 0100 != 0
410 except OSError, e:
411 except OSError, e:
411 if e.errno != errno.ENOENT:
412 if e.errno != errno.ENOENT:
412 raise
413 raise
413 return (self.opener.read(fname), (False, isexec))
414 return (self.opener.read(fname), (False, isexec))
414
415
415 def setfile(self, fname, data, mode, copysource):
416 def setfile(self, fname, data, mode, copysource):
416 islink, isexec = mode
417 islink, isexec = mode
417 if data is None:
418 if data is None:
418 util.setflags(self._join(fname), islink, isexec)
419 util.setflags(self._join(fname), islink, isexec)
419 return
420 return
420 if islink:
421 if islink:
421 self.opener.symlink(data, fname)
422 self.opener.symlink(data, fname)
422 else:
423 else:
423 self.opener.write(fname, data)
424 self.opener.write(fname, data)
424 if isexec:
425 if isexec:
425 util.setflags(self._join(fname), False, True)
426 util.setflags(self._join(fname), False, True)
426
427
427 def unlink(self, fname):
428 def unlink(self, fname):
428 try:
429 try:
429 util.unlinkpath(self._join(fname))
430 util.unlinkpath(self._join(fname))
430 except OSError, inst:
431 except OSError, inst:
431 if inst.errno != errno.ENOENT:
432 if inst.errno != errno.ENOENT:
432 raise
433 raise
433
434
434 def writerej(self, fname, failed, total, lines):
435 def writerej(self, fname, failed, total, lines):
435 fname = fname + ".rej"
436 fname = fname + ".rej"
436 self.ui.warn(
437 self.ui.warn(
437 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
438 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
438 (failed, total, fname))
439 (failed, total, fname))
439 fp = self.opener(fname, 'w')
440 fp = self.opener(fname, 'w')
440 fp.writelines(lines)
441 fp.writelines(lines)
441 fp.close()
442 fp.close()
442
443
443 def exists(self, fname):
444 def exists(self, fname):
444 return os.path.lexists(self._join(fname))
445 return os.path.lexists(self._join(fname))
445
446
446 class workingbackend(fsbackend):
447 class workingbackend(fsbackend):
447 def __init__(self, ui, repo, similarity):
448 def __init__(self, ui, repo, similarity):
448 super(workingbackend, self).__init__(ui, repo.root)
449 super(workingbackend, self).__init__(ui, repo.root)
449 self.repo = repo
450 self.repo = repo
450 self.similarity = similarity
451 self.similarity = similarity
451 self.removed = set()
452 self.removed = set()
452 self.changed = set()
453 self.changed = set()
453 self.copied = []
454 self.copied = []
454
455
455 def _checkknown(self, fname):
456 def _checkknown(self, fname):
456 if self.repo.dirstate[fname] == '?' and self.exists(fname):
457 if self.repo.dirstate[fname] == '?' and self.exists(fname):
457 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
458 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
458
459
459 def setfile(self, fname, data, mode, copysource):
460 def setfile(self, fname, data, mode, copysource):
460 self._checkknown(fname)
461 self._checkknown(fname)
461 super(workingbackend, self).setfile(fname, data, mode, copysource)
462 super(workingbackend, self).setfile(fname, data, mode, copysource)
462 if copysource is not None:
463 if copysource is not None:
463 self.copied.append((copysource, fname))
464 self.copied.append((copysource, fname))
464 self.changed.add(fname)
465 self.changed.add(fname)
465
466
466 def unlink(self, fname):
467 def unlink(self, fname):
467 self._checkknown(fname)
468 self._checkknown(fname)
468 super(workingbackend, self).unlink(fname)
469 super(workingbackend, self).unlink(fname)
469 self.removed.add(fname)
470 self.removed.add(fname)
470 self.changed.add(fname)
471 self.changed.add(fname)
471
472
472 def close(self):
473 def close(self):
473 wctx = self.repo[None]
474 wctx = self.repo[None]
474 addremoved = set(self.changed)
475 addremoved = set(self.changed)
475 for src, dst in self.copied:
476 for src, dst in self.copied:
476 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
477 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
477 addremoved.discard(src)
478 addremoved.discard(src)
478 if (not self.similarity) and self.removed:
479 if (not self.similarity) and self.removed:
479 wctx.forget(sorted(self.removed))
480 wctx.forget(sorted(self.removed))
480 if addremoved:
481 if addremoved:
481 cwd = self.repo.getcwd()
482 cwd = self.repo.getcwd()
482 if cwd:
483 if cwd:
483 addremoved = [util.pathto(self.repo.root, cwd, f)
484 addremoved = [util.pathto(self.repo.root, cwd, f)
484 for f in addremoved]
485 for f in addremoved]
485 scmutil.addremove(self.repo, addremoved, similarity=self.similarity)
486 scmutil.addremove(self.repo, addremoved, similarity=self.similarity)
486 return sorted(self.changed)
487 return sorted(self.changed)
487
488
488 class filestore(object):
489 class filestore(object):
489 def __init__(self):
490 def __init__(self):
490 self.opener = None
491 self.opener = None
491 self.files = {}
492 self.files = {}
492 self.created = 0
493 self.created = 0
493
494
494 def setfile(self, fname, data, mode, copied=None):
495 def setfile(self, fname, data, mode, copied=None):
495 if self.opener is None:
496 if self.opener is None:
496 root = tempfile.mkdtemp(prefix='hg-patch-')
497 root = tempfile.mkdtemp(prefix='hg-patch-')
497 self.opener = scmutil.opener(root)
498 self.opener = scmutil.opener(root)
498 # Avoid filename issues with these simple names
499 # Avoid filename issues with these simple names
499 fn = str(self.created)
500 fn = str(self.created)
500 self.opener.write(fn, data)
501 self.opener.write(fn, data)
501 self.created += 1
502 self.created += 1
502 self.files[fname] = (fn, mode, copied)
503 self.files[fname] = (fn, mode, copied)
503
504
504 def getfile(self, fname):
505 def getfile(self, fname):
505 if fname not in self.files:
506 if fname not in self.files:
506 raise IOError()
507 raise IOError()
507 fn, mode, copied = self.files[fname]
508 fn, mode, copied = self.files[fname]
508 return self.opener.read(fn), mode, copied
509 return self.opener.read(fn), mode, copied
509
510
510 def close(self):
511 def close(self):
511 if self.opener:
512 if self.opener:
512 shutil.rmtree(self.opener.base)
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 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
557 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
515 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
558 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
516 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
559 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
517 eolmodes = ['strict', 'crlf', 'lf', 'auto']
560 eolmodes = ['strict', 'crlf', 'lf', 'auto']
518
561
519 class patchfile(object):
562 class patchfile(object):
520 def __init__(self, ui, gp, backend, store, eolmode='strict'):
563 def __init__(self, ui, gp, backend, store, eolmode='strict'):
521 self.fname = gp.path
564 self.fname = gp.path
522 self.eolmode = eolmode
565 self.eolmode = eolmode
523 self.eol = None
566 self.eol = None
524 self.backend = backend
567 self.backend = backend
525 self.ui = ui
568 self.ui = ui
526 self.lines = []
569 self.lines = []
527 self.exists = False
570 self.exists = False
528 self.missing = True
571 self.missing = True
529 self.mode = gp.mode
572 self.mode = gp.mode
530 self.copysource = gp.oldpath
573 self.copysource = gp.oldpath
531 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
574 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
532 self.remove = gp.op == 'DELETE'
575 self.remove = gp.op == 'DELETE'
533 try:
576 try:
534 if self.copysource is None:
577 if self.copysource is None:
535 data, mode = backend.getfile(self.fname)
578 data, mode = backend.getfile(self.fname)
536 self.exists = True
579 self.exists = True
537 else:
580 else:
538 data, mode = store.getfile(self.copysource)[:2]
581 data, mode = store.getfile(self.copysource)[:2]
539 self.exists = backend.exists(self.fname)
582 self.exists = backend.exists(self.fname)
540 self.missing = False
583 self.missing = False
541 if data:
584 if data:
542 self.lines = data.splitlines(True)
585 self.lines = data.splitlines(True)
543 if self.mode is None:
586 if self.mode is None:
544 self.mode = mode
587 self.mode = mode
545 if self.lines:
588 if self.lines:
546 # Normalize line endings
589 # Normalize line endings
547 if self.lines[0].endswith('\r\n'):
590 if self.lines[0].endswith('\r\n'):
548 self.eol = '\r\n'
591 self.eol = '\r\n'
549 elif self.lines[0].endswith('\n'):
592 elif self.lines[0].endswith('\n'):
550 self.eol = '\n'
593 self.eol = '\n'
551 if eolmode != 'strict':
594 if eolmode != 'strict':
552 nlines = []
595 nlines = []
553 for l in self.lines:
596 for l in self.lines:
554 if l.endswith('\r\n'):
597 if l.endswith('\r\n'):
555 l = l[:-2] + '\n'
598 l = l[:-2] + '\n'
556 nlines.append(l)
599 nlines.append(l)
557 self.lines = nlines
600 self.lines = nlines
558 except IOError:
601 except IOError:
559 if self.create:
602 if self.create:
560 self.missing = False
603 self.missing = False
561 if self.mode is None:
604 if self.mode is None:
562 self.mode = (False, False)
605 self.mode = (False, False)
563 if self.missing:
606 if self.missing:
564 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
607 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
565
608
566 self.hash = {}
609 self.hash = {}
567 self.dirty = 0
610 self.dirty = 0
568 self.offset = 0
611 self.offset = 0
569 self.skew = 0
612 self.skew = 0
570 self.rej = []
613 self.rej = []
571 self.fileprinted = False
614 self.fileprinted = False
572 self.printfile(False)
615 self.printfile(False)
573 self.hunks = 0
616 self.hunks = 0
574
617
575 def writelines(self, fname, lines, mode):
618 def writelines(self, fname, lines, mode):
576 if self.eolmode == 'auto':
619 if self.eolmode == 'auto':
577 eol = self.eol
620 eol = self.eol
578 elif self.eolmode == 'crlf':
621 elif self.eolmode == 'crlf':
579 eol = '\r\n'
622 eol = '\r\n'
580 else:
623 else:
581 eol = '\n'
624 eol = '\n'
582
625
583 if self.eolmode != 'strict' and eol and eol != '\n':
626 if self.eolmode != 'strict' and eol and eol != '\n':
584 rawlines = []
627 rawlines = []
585 for l in lines:
628 for l in lines:
586 if l and l[-1] == '\n':
629 if l and l[-1] == '\n':
587 l = l[:-1] + eol
630 l = l[:-1] + eol
588 rawlines.append(l)
631 rawlines.append(l)
589 lines = rawlines
632 lines = rawlines
590
633
591 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
634 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
592
635
593 def printfile(self, warn):
636 def printfile(self, warn):
594 if self.fileprinted:
637 if self.fileprinted:
595 return
638 return
596 if warn or self.ui.verbose:
639 if warn or self.ui.verbose:
597 self.fileprinted = True
640 self.fileprinted = True
598 s = _("patching file %s\n") % self.fname
641 s = _("patching file %s\n") % self.fname
599 if warn:
642 if warn:
600 self.ui.warn(s)
643 self.ui.warn(s)
601 else:
644 else:
602 self.ui.note(s)
645 self.ui.note(s)
603
646
604
647
605 def findlines(self, l, linenum):
648 def findlines(self, l, linenum):
606 # looks through the hash and finds candidate lines. The
649 # looks through the hash and finds candidate lines. The
607 # result is a list of line numbers sorted based on distance
650 # result is a list of line numbers sorted based on distance
608 # from linenum
651 # from linenum
609
652
610 cand = self.hash.get(l, [])
653 cand = self.hash.get(l, [])
611 if len(cand) > 1:
654 if len(cand) > 1:
612 # resort our list of potentials forward then back.
655 # resort our list of potentials forward then back.
613 cand.sort(key=lambda x: abs(x - linenum))
656 cand.sort(key=lambda x: abs(x - linenum))
614 return cand
657 return cand
615
658
616 def write_rej(self):
659 def write_rej(self):
617 # our rejects are a little different from patch(1). This always
660 # our rejects are a little different from patch(1). This always
618 # creates rejects in the same form as the original patch. A file
661 # creates rejects in the same form as the original patch. A file
619 # header is inserted so that you can run the reject through patch again
662 # header is inserted so that you can run the reject through patch again
620 # without having to type the filename.
663 # without having to type the filename.
621 if not self.rej:
664 if not self.rej:
622 return
665 return
623 base = os.path.basename(self.fname)
666 base = os.path.basename(self.fname)
624 lines = ["--- %s\n+++ %s\n" % (base, base)]
667 lines = ["--- %s\n+++ %s\n" % (base, base)]
625 for x in self.rej:
668 for x in self.rej:
626 for l in x.hunk:
669 for l in x.hunk:
627 lines.append(l)
670 lines.append(l)
628 if l[-1] != '\n':
671 if l[-1] != '\n':
629 lines.append("\n\ No newline at end of file\n")
672 lines.append("\n\ No newline at end of file\n")
630 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
673 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
631
674
632 def apply(self, h):
675 def apply(self, h):
633 if not h.complete():
676 if not h.complete():
634 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
677 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
635 (h.number, h.desc, len(h.a), h.lena, len(h.b),
678 (h.number, h.desc, len(h.a), h.lena, len(h.b),
636 h.lenb))
679 h.lenb))
637
680
638 self.hunks += 1
681 self.hunks += 1
639
682
640 if self.missing:
683 if self.missing:
641 self.rej.append(h)
684 self.rej.append(h)
642 return -1
685 return -1
643
686
644 if self.exists and self.create:
687 if self.exists and self.create:
645 if self.copysource:
688 if self.copysource:
646 self.ui.warn(_("cannot create %s: destination already "
689 self.ui.warn(_("cannot create %s: destination already "
647 "exists\n" % self.fname))
690 "exists\n" % self.fname))
648 else:
691 else:
649 self.ui.warn(_("file %s already exists\n") % self.fname)
692 self.ui.warn(_("file %s already exists\n") % self.fname)
650 self.rej.append(h)
693 self.rej.append(h)
651 return -1
694 return -1
652
695
653 if isinstance(h, binhunk):
696 if isinstance(h, binhunk):
654 if self.remove:
697 if self.remove:
655 self.backend.unlink(self.fname)
698 self.backend.unlink(self.fname)
656 else:
699 else:
657 self.lines[:] = h.new()
700 self.lines[:] = h.new()
658 self.offset += len(h.new())
701 self.offset += len(h.new())
659 self.dirty = True
702 self.dirty = True
660 return 0
703 return 0
661
704
662 horig = h
705 horig = h
663 if (self.eolmode in ('crlf', 'lf')
706 if (self.eolmode in ('crlf', 'lf')
664 or self.eolmode == 'auto' and self.eol):
707 or self.eolmode == 'auto' and self.eol):
665 # If new eols are going to be normalized, then normalize
708 # If new eols are going to be normalized, then normalize
666 # hunk data before patching. Otherwise, preserve input
709 # hunk data before patching. Otherwise, preserve input
667 # line-endings.
710 # line-endings.
668 h = h.getnormalized()
711 h = h.getnormalized()
669
712
670 # fast case first, no offsets, no fuzz
713 # fast case first, no offsets, no fuzz
671 old = h.old()
714 old = h.old()
672 # patch starts counting at 1 unless we are adding the file
715 # patch starts counting at 1 unless we are adding the file
673 if h.starta == 0:
716 if h.starta == 0:
674 start = 0
717 start = 0
675 else:
718 else:
676 start = h.starta + self.offset - 1
719 start = h.starta + self.offset - 1
677 orig_start = start
720 orig_start = start
678 # if there's skew we want to emit the "(offset %d lines)" even
721 # if there's skew we want to emit the "(offset %d lines)" even
679 # when the hunk cleanly applies at start + skew, so skip the
722 # when the hunk cleanly applies at start + skew, so skip the
680 # fast case code
723 # fast case code
681 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
724 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
682 if self.remove:
725 if self.remove:
683 self.backend.unlink(self.fname)
726 self.backend.unlink(self.fname)
684 else:
727 else:
685 self.lines[start : start + h.lena] = h.new()
728 self.lines[start : start + h.lena] = h.new()
686 self.offset += h.lenb - h.lena
729 self.offset += h.lenb - h.lena
687 self.dirty = True
730 self.dirty = True
688 return 0
731 return 0
689
732
690 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
733 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
691 self.hash = {}
734 self.hash = {}
692 for x, s in enumerate(self.lines):
735 for x, s in enumerate(self.lines):
693 self.hash.setdefault(s, []).append(x)
736 self.hash.setdefault(s, []).append(x)
694 if h.hunk[-1][0] != ' ':
737 if h.hunk[-1][0] != ' ':
695 # if the hunk tried to put something at the bottom of the file
738 # if the hunk tried to put something at the bottom of the file
696 # override the start line and use eof here
739 # override the start line and use eof here
697 search_start = len(self.lines)
740 search_start = len(self.lines)
698 else:
741 else:
699 search_start = orig_start + self.skew
742 search_start = orig_start + self.skew
700
743
701 for fuzzlen in xrange(3):
744 for fuzzlen in xrange(3):
702 for toponly in [True, False]:
745 for toponly in [True, False]:
703 old = h.old(fuzzlen, toponly)
746 old = h.old(fuzzlen, toponly)
704
747
705 cand = self.findlines(old[0][1:], search_start)
748 cand = self.findlines(old[0][1:], search_start)
706 for l in cand:
749 for l in cand:
707 if diffhelpers.testhunk(old, self.lines, l) == 0:
750 if diffhelpers.testhunk(old, self.lines, l) == 0:
708 newlines = h.new(fuzzlen, toponly)
751 newlines = h.new(fuzzlen, toponly)
709 self.lines[l : l + len(old)] = newlines
752 self.lines[l : l + len(old)] = newlines
710 self.offset += len(newlines) - len(old)
753 self.offset += len(newlines) - len(old)
711 self.skew = l - orig_start
754 self.skew = l - orig_start
712 self.dirty = True
755 self.dirty = True
713 offset = l - orig_start - fuzzlen
756 offset = l - orig_start - fuzzlen
714 if fuzzlen:
757 if fuzzlen:
715 msg = _("Hunk #%d succeeded at %d "
758 msg = _("Hunk #%d succeeded at %d "
716 "with fuzz %d "
759 "with fuzz %d "
717 "(offset %d lines).\n")
760 "(offset %d lines).\n")
718 self.printfile(True)
761 self.printfile(True)
719 self.ui.warn(msg %
762 self.ui.warn(msg %
720 (h.number, l + 1, fuzzlen, offset))
763 (h.number, l + 1, fuzzlen, offset))
721 else:
764 else:
722 msg = _("Hunk #%d succeeded at %d "
765 msg = _("Hunk #%d succeeded at %d "
723 "(offset %d lines).\n")
766 "(offset %d lines).\n")
724 self.ui.note(msg % (h.number, l + 1, offset))
767 self.ui.note(msg % (h.number, l + 1, offset))
725 return fuzzlen
768 return fuzzlen
726 self.printfile(True)
769 self.printfile(True)
727 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
770 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
728 self.rej.append(horig)
771 self.rej.append(horig)
729 return -1
772 return -1
730
773
731 def close(self):
774 def close(self):
732 if self.dirty:
775 if self.dirty:
733 self.writelines(self.fname, self.lines, self.mode)
776 self.writelines(self.fname, self.lines, self.mode)
734 self.write_rej()
777 self.write_rej()
735 return len(self.rej)
778 return len(self.rej)
736
779
737 class hunk(object):
780 class hunk(object):
738 def __init__(self, desc, num, lr, context):
781 def __init__(self, desc, num, lr, context):
739 self.number = num
782 self.number = num
740 self.desc = desc
783 self.desc = desc
741 self.hunk = [desc]
784 self.hunk = [desc]
742 self.a = []
785 self.a = []
743 self.b = []
786 self.b = []
744 self.starta = self.lena = None
787 self.starta = self.lena = None
745 self.startb = self.lenb = None
788 self.startb = self.lenb = None
746 if lr is not None:
789 if lr is not None:
747 if context:
790 if context:
748 self.read_context_hunk(lr)
791 self.read_context_hunk(lr)
749 else:
792 else:
750 self.read_unified_hunk(lr)
793 self.read_unified_hunk(lr)
751
794
752 def getnormalized(self):
795 def getnormalized(self):
753 """Return a copy with line endings normalized to LF."""
796 """Return a copy with line endings normalized to LF."""
754
797
755 def normalize(lines):
798 def normalize(lines):
756 nlines = []
799 nlines = []
757 for line in lines:
800 for line in lines:
758 if line.endswith('\r\n'):
801 if line.endswith('\r\n'):
759 line = line[:-2] + '\n'
802 line = line[:-2] + '\n'
760 nlines.append(line)
803 nlines.append(line)
761 return nlines
804 return nlines
762
805
763 # Dummy object, it is rebuilt manually
806 # Dummy object, it is rebuilt manually
764 nh = hunk(self.desc, self.number, None, None)
807 nh = hunk(self.desc, self.number, None, None)
765 nh.number = self.number
808 nh.number = self.number
766 nh.desc = self.desc
809 nh.desc = self.desc
767 nh.hunk = self.hunk
810 nh.hunk = self.hunk
768 nh.a = normalize(self.a)
811 nh.a = normalize(self.a)
769 nh.b = normalize(self.b)
812 nh.b = normalize(self.b)
770 nh.starta = self.starta
813 nh.starta = self.starta
771 nh.startb = self.startb
814 nh.startb = self.startb
772 nh.lena = self.lena
815 nh.lena = self.lena
773 nh.lenb = self.lenb
816 nh.lenb = self.lenb
774 return nh
817 return nh
775
818
776 def read_unified_hunk(self, lr):
819 def read_unified_hunk(self, lr):
777 m = unidesc.match(self.desc)
820 m = unidesc.match(self.desc)
778 if not m:
821 if not m:
779 raise PatchError(_("bad hunk #%d") % self.number)
822 raise PatchError(_("bad hunk #%d") % self.number)
780 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
823 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
781 if self.lena is None:
824 if self.lena is None:
782 self.lena = 1
825 self.lena = 1
783 else:
826 else:
784 self.lena = int(self.lena)
827 self.lena = int(self.lena)
785 if self.lenb is None:
828 if self.lenb is None:
786 self.lenb = 1
829 self.lenb = 1
787 else:
830 else:
788 self.lenb = int(self.lenb)
831 self.lenb = int(self.lenb)
789 self.starta = int(self.starta)
832 self.starta = int(self.starta)
790 self.startb = int(self.startb)
833 self.startb = int(self.startb)
791 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
834 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
792 # if we hit eof before finishing out the hunk, the last line will
835 # if we hit eof before finishing out the hunk, the last line will
793 # be zero length. Lets try to fix it up.
836 # be zero length. Lets try to fix it up.
794 while len(self.hunk[-1]) == 0:
837 while len(self.hunk[-1]) == 0:
795 del self.hunk[-1]
838 del self.hunk[-1]
796 del self.a[-1]
839 del self.a[-1]
797 del self.b[-1]
840 del self.b[-1]
798 self.lena -= 1
841 self.lena -= 1
799 self.lenb -= 1
842 self.lenb -= 1
800 self._fixnewline(lr)
843 self._fixnewline(lr)
801
844
802 def read_context_hunk(self, lr):
845 def read_context_hunk(self, lr):
803 self.desc = lr.readline()
846 self.desc = lr.readline()
804 m = contextdesc.match(self.desc)
847 m = contextdesc.match(self.desc)
805 if not m:
848 if not m:
806 raise PatchError(_("bad hunk #%d") % self.number)
849 raise PatchError(_("bad hunk #%d") % self.number)
807 foo, self.starta, foo2, aend, foo3 = m.groups()
850 foo, self.starta, foo2, aend, foo3 = m.groups()
808 self.starta = int(self.starta)
851 self.starta = int(self.starta)
809 if aend is None:
852 if aend is None:
810 aend = self.starta
853 aend = self.starta
811 self.lena = int(aend) - self.starta
854 self.lena = int(aend) - self.starta
812 if self.starta:
855 if self.starta:
813 self.lena += 1
856 self.lena += 1
814 for x in xrange(self.lena):
857 for x in xrange(self.lena):
815 l = lr.readline()
858 l = lr.readline()
816 if l.startswith('---'):
859 if l.startswith('---'):
817 # lines addition, old block is empty
860 # lines addition, old block is empty
818 lr.push(l)
861 lr.push(l)
819 break
862 break
820 s = l[2:]
863 s = l[2:]
821 if l.startswith('- ') or l.startswith('! '):
864 if l.startswith('- ') or l.startswith('! '):
822 u = '-' + s
865 u = '-' + s
823 elif l.startswith(' '):
866 elif l.startswith(' '):
824 u = ' ' + s
867 u = ' ' + s
825 else:
868 else:
826 raise PatchError(_("bad hunk #%d old text line %d") %
869 raise PatchError(_("bad hunk #%d old text line %d") %
827 (self.number, x))
870 (self.number, x))
828 self.a.append(u)
871 self.a.append(u)
829 self.hunk.append(u)
872 self.hunk.append(u)
830
873
831 l = lr.readline()
874 l = lr.readline()
832 if l.startswith('\ '):
875 if l.startswith('\ '):
833 s = self.a[-1][:-1]
876 s = self.a[-1][:-1]
834 self.a[-1] = s
877 self.a[-1] = s
835 self.hunk[-1] = s
878 self.hunk[-1] = s
836 l = lr.readline()
879 l = lr.readline()
837 m = contextdesc.match(l)
880 m = contextdesc.match(l)
838 if not m:
881 if not m:
839 raise PatchError(_("bad hunk #%d") % self.number)
882 raise PatchError(_("bad hunk #%d") % self.number)
840 foo, self.startb, foo2, bend, foo3 = m.groups()
883 foo, self.startb, foo2, bend, foo3 = m.groups()
841 self.startb = int(self.startb)
884 self.startb = int(self.startb)
842 if bend is None:
885 if bend is None:
843 bend = self.startb
886 bend = self.startb
844 self.lenb = int(bend) - self.startb
887 self.lenb = int(bend) - self.startb
845 if self.startb:
888 if self.startb:
846 self.lenb += 1
889 self.lenb += 1
847 hunki = 1
890 hunki = 1
848 for x in xrange(self.lenb):
891 for x in xrange(self.lenb):
849 l = lr.readline()
892 l = lr.readline()
850 if l.startswith('\ '):
893 if l.startswith('\ '):
851 # XXX: the only way to hit this is with an invalid line range.
894 # XXX: the only way to hit this is with an invalid line range.
852 # The no-eol marker is not counted in the line range, but I
895 # The no-eol marker is not counted in the line range, but I
853 # guess there are diff(1) out there which behave differently.
896 # guess there are diff(1) out there which behave differently.
854 s = self.b[-1][:-1]
897 s = self.b[-1][:-1]
855 self.b[-1] = s
898 self.b[-1] = s
856 self.hunk[hunki - 1] = s
899 self.hunk[hunki - 1] = s
857 continue
900 continue
858 if not l:
901 if not l:
859 # line deletions, new block is empty and we hit EOF
902 # line deletions, new block is empty and we hit EOF
860 lr.push(l)
903 lr.push(l)
861 break
904 break
862 s = l[2:]
905 s = l[2:]
863 if l.startswith('+ ') or l.startswith('! '):
906 if l.startswith('+ ') or l.startswith('! '):
864 u = '+' + s
907 u = '+' + s
865 elif l.startswith(' '):
908 elif l.startswith(' '):
866 u = ' ' + s
909 u = ' ' + s
867 elif len(self.b) == 0:
910 elif len(self.b) == 0:
868 # line deletions, new block is empty
911 # line deletions, new block is empty
869 lr.push(l)
912 lr.push(l)
870 break
913 break
871 else:
914 else:
872 raise PatchError(_("bad hunk #%d old text line %d") %
915 raise PatchError(_("bad hunk #%d old text line %d") %
873 (self.number, x))
916 (self.number, x))
874 self.b.append(s)
917 self.b.append(s)
875 while True:
918 while True:
876 if hunki >= len(self.hunk):
919 if hunki >= len(self.hunk):
877 h = ""
920 h = ""
878 else:
921 else:
879 h = self.hunk[hunki]
922 h = self.hunk[hunki]
880 hunki += 1
923 hunki += 1
881 if h == u:
924 if h == u:
882 break
925 break
883 elif h.startswith('-'):
926 elif h.startswith('-'):
884 continue
927 continue
885 else:
928 else:
886 self.hunk.insert(hunki - 1, u)
929 self.hunk.insert(hunki - 1, u)
887 break
930 break
888
931
889 if not self.a:
932 if not self.a:
890 # this happens when lines were only added to the hunk
933 # this happens when lines were only added to the hunk
891 for x in self.hunk:
934 for x in self.hunk:
892 if x.startswith('-') or x.startswith(' '):
935 if x.startswith('-') or x.startswith(' '):
893 self.a.append(x)
936 self.a.append(x)
894 if not self.b:
937 if not self.b:
895 # this happens when lines were only deleted from the hunk
938 # this happens when lines were only deleted from the hunk
896 for x in self.hunk:
939 for x in self.hunk:
897 if x.startswith('+') or x.startswith(' '):
940 if x.startswith('+') or x.startswith(' '):
898 self.b.append(x[1:])
941 self.b.append(x[1:])
899 # @@ -start,len +start,len @@
942 # @@ -start,len +start,len @@
900 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
943 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
901 self.startb, self.lenb)
944 self.startb, self.lenb)
902 self.hunk[0] = self.desc
945 self.hunk[0] = self.desc
903 self._fixnewline(lr)
946 self._fixnewline(lr)
904
947
905 def _fixnewline(self, lr):
948 def _fixnewline(self, lr):
906 l = lr.readline()
949 l = lr.readline()
907 if l.startswith('\ '):
950 if l.startswith('\ '):
908 diffhelpers.fix_newline(self.hunk, self.a, self.b)
951 diffhelpers.fix_newline(self.hunk, self.a, self.b)
909 else:
952 else:
910 lr.push(l)
953 lr.push(l)
911
954
912 def complete(self):
955 def complete(self):
913 return len(self.a) == self.lena and len(self.b) == self.lenb
956 return len(self.a) == self.lena and len(self.b) == self.lenb
914
957
915 def fuzzit(self, l, fuzz, toponly):
958 def fuzzit(self, l, fuzz, toponly):
916 # this removes context lines from the top and bottom of list 'l'. It
959 # this removes context lines from the top and bottom of list 'l'. It
917 # checks the hunk to make sure only context lines are removed, and then
960 # checks the hunk to make sure only context lines are removed, and then
918 # returns a new shortened list of lines.
961 # returns a new shortened list of lines.
919 fuzz = min(fuzz, len(l)-1)
962 fuzz = min(fuzz, len(l)-1)
920 if fuzz:
963 if fuzz:
921 top = 0
964 top = 0
922 bot = 0
965 bot = 0
923 hlen = len(self.hunk)
966 hlen = len(self.hunk)
924 for x in xrange(hlen - 1):
967 for x in xrange(hlen - 1):
925 # the hunk starts with the @@ line, so use x+1
968 # the hunk starts with the @@ line, so use x+1
926 if self.hunk[x + 1][0] == ' ':
969 if self.hunk[x + 1][0] == ' ':
927 top += 1
970 top += 1
928 else:
971 else:
929 break
972 break
930 if not toponly:
973 if not toponly:
931 for x in xrange(hlen - 1):
974 for x in xrange(hlen - 1):
932 if self.hunk[hlen - bot - 1][0] == ' ':
975 if self.hunk[hlen - bot - 1][0] == ' ':
933 bot += 1
976 bot += 1
934 else:
977 else:
935 break
978 break
936
979
937 # top and bot now count context in the hunk
980 # top and bot now count context in the hunk
938 # adjust them if either one is short
981 # adjust them if either one is short
939 context = max(top, bot, 3)
982 context = max(top, bot, 3)
940 if bot < context:
983 if bot < context:
941 bot = max(0, fuzz - (context - bot))
984 bot = max(0, fuzz - (context - bot))
942 else:
985 else:
943 bot = min(fuzz, bot)
986 bot = min(fuzz, bot)
944 if top < context:
987 if top < context:
945 top = max(0, fuzz - (context - top))
988 top = max(0, fuzz - (context - top))
946 else:
989 else:
947 top = min(fuzz, top)
990 top = min(fuzz, top)
948
991
949 return l[top:len(l)-bot]
992 return l[top:len(l)-bot]
950 return l
993 return l
951
994
952 def old(self, fuzz=0, toponly=False):
995 def old(self, fuzz=0, toponly=False):
953 return self.fuzzit(self.a, fuzz, toponly)
996 return self.fuzzit(self.a, fuzz, toponly)
954
997
955 def new(self, fuzz=0, toponly=False):
998 def new(self, fuzz=0, toponly=False):
956 return self.fuzzit(self.b, fuzz, toponly)
999 return self.fuzzit(self.b, fuzz, toponly)
957
1000
958 class binhunk:
1001 class binhunk:
959 'A binary patch file. Only understands literals so far.'
1002 'A binary patch file. Only understands literals so far.'
960 def __init__(self, lr):
1003 def __init__(self, lr):
961 self.text = None
1004 self.text = None
962 self.hunk = ['GIT binary patch\n']
1005 self.hunk = ['GIT binary patch\n']
963 self._read(lr)
1006 self._read(lr)
964
1007
965 def complete(self):
1008 def complete(self):
966 return self.text is not None
1009 return self.text is not None
967
1010
968 def new(self):
1011 def new(self):
969 return [self.text]
1012 return [self.text]
970
1013
971 def _read(self, lr):
1014 def _read(self, lr):
972 line = lr.readline()
1015 line = lr.readline()
973 self.hunk.append(line)
1016 self.hunk.append(line)
974 while line and not line.startswith('literal '):
1017 while line and not line.startswith('literal '):
975 line = lr.readline()
1018 line = lr.readline()
976 self.hunk.append(line)
1019 self.hunk.append(line)
977 if not line:
1020 if not line:
978 raise PatchError(_('could not extract binary patch'))
1021 raise PatchError(_('could not extract binary patch'))
979 size = int(line[8:].rstrip())
1022 size = int(line[8:].rstrip())
980 dec = []
1023 dec = []
981 line = lr.readline()
1024 line = lr.readline()
982 self.hunk.append(line)
1025 self.hunk.append(line)
983 while len(line) > 1:
1026 while len(line) > 1:
984 l = line[0]
1027 l = line[0]
985 if l <= 'Z' and l >= 'A':
1028 if l <= 'Z' and l >= 'A':
986 l = ord(l) - ord('A') + 1
1029 l = ord(l) - ord('A') + 1
987 else:
1030 else:
988 l = ord(l) - ord('a') + 27
1031 l = ord(l) - ord('a') + 27
989 dec.append(base85.b85decode(line[1:-1])[:l])
1032 dec.append(base85.b85decode(line[1:-1])[:l])
990 line = lr.readline()
1033 line = lr.readline()
991 self.hunk.append(line)
1034 self.hunk.append(line)
992 text = zlib.decompress(''.join(dec))
1035 text = zlib.decompress(''.join(dec))
993 if len(text) != size:
1036 if len(text) != size:
994 raise PatchError(_('binary patch is %d bytes, not %d') %
1037 raise PatchError(_('binary patch is %d bytes, not %d') %
995 len(text), size)
1038 len(text), size)
996 self.text = text
1039 self.text = text
997
1040
998 def parsefilename(str):
1041 def parsefilename(str):
999 # --- filename \t|space stuff
1042 # --- filename \t|space stuff
1000 s = str[4:].rstrip('\r\n')
1043 s = str[4:].rstrip('\r\n')
1001 i = s.find('\t')
1044 i = s.find('\t')
1002 if i < 0:
1045 if i < 0:
1003 i = s.find(' ')
1046 i = s.find(' ')
1004 if i < 0:
1047 if i < 0:
1005 return s
1048 return s
1006 return s[:i]
1049 return s[:i]
1007
1050
1008 def pathstrip(path, strip):
1051 def pathstrip(path, strip):
1009 pathlen = len(path)
1052 pathlen = len(path)
1010 i = 0
1053 i = 0
1011 if strip == 0:
1054 if strip == 0:
1012 return '', path.rstrip()
1055 return '', path.rstrip()
1013 count = strip
1056 count = strip
1014 while count > 0:
1057 while count > 0:
1015 i = path.find('/', i)
1058 i = path.find('/', i)
1016 if i == -1:
1059 if i == -1:
1017 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1060 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1018 (count, strip, path))
1061 (count, strip, path))
1019 i += 1
1062 i += 1
1020 # consume '//' in the path
1063 # consume '//' in the path
1021 while i < pathlen - 1 and path[i] == '/':
1064 while i < pathlen - 1 and path[i] == '/':
1022 i += 1
1065 i += 1
1023 count -= 1
1066 count -= 1
1024 return path[:i].lstrip(), path[i:].rstrip()
1067 return path[:i].lstrip(), path[i:].rstrip()
1025
1068
1026 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
1069 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
1027 nulla = afile_orig == "/dev/null"
1070 nulla = afile_orig == "/dev/null"
1028 nullb = bfile_orig == "/dev/null"
1071 nullb = bfile_orig == "/dev/null"
1029 create = nulla and hunk.starta == 0 and hunk.lena == 0
1072 create = nulla and hunk.starta == 0 and hunk.lena == 0
1030 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1073 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1031 abase, afile = pathstrip(afile_orig, strip)
1074 abase, afile = pathstrip(afile_orig, strip)
1032 gooda = not nulla and backend.exists(afile)
1075 gooda = not nulla and backend.exists(afile)
1033 bbase, bfile = pathstrip(bfile_orig, strip)
1076 bbase, bfile = pathstrip(bfile_orig, strip)
1034 if afile == bfile:
1077 if afile == bfile:
1035 goodb = gooda
1078 goodb = gooda
1036 else:
1079 else:
1037 goodb = not nullb and backend.exists(bfile)
1080 goodb = not nullb and backend.exists(bfile)
1038 missing = not goodb and not gooda and not create
1081 missing = not goodb and not gooda and not create
1039
1082
1040 # some diff programs apparently produce patches where the afile is
1083 # some diff programs apparently produce patches where the afile is
1041 # not /dev/null, but afile starts with bfile
1084 # not /dev/null, but afile starts with bfile
1042 abasedir = afile[:afile.rfind('/') + 1]
1085 abasedir = afile[:afile.rfind('/') + 1]
1043 bbasedir = bfile[:bfile.rfind('/') + 1]
1086 bbasedir = bfile[:bfile.rfind('/') + 1]
1044 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1087 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1045 and hunk.starta == 0 and hunk.lena == 0):
1088 and hunk.starta == 0 and hunk.lena == 0):
1046 create = True
1089 create = True
1047 missing = False
1090 missing = False
1048
1091
1049 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1092 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1050 # diff is between a file and its backup. In this case, the original
1093 # diff is between a file and its backup. In this case, the original
1051 # file should be patched (see original mpatch code).
1094 # file should be patched (see original mpatch code).
1052 isbackup = (abase == bbase and bfile.startswith(afile))
1095 isbackup = (abase == bbase and bfile.startswith(afile))
1053 fname = None
1096 fname = None
1054 if not missing:
1097 if not missing:
1055 if gooda and goodb:
1098 if gooda and goodb:
1056 fname = isbackup and afile or bfile
1099 fname = isbackup and afile or bfile
1057 elif gooda:
1100 elif gooda:
1058 fname = afile
1101 fname = afile
1059
1102
1060 if not fname:
1103 if not fname:
1061 if not nullb:
1104 if not nullb:
1062 fname = isbackup and afile or bfile
1105 fname = isbackup and afile or bfile
1063 elif not nulla:
1106 elif not nulla:
1064 fname = afile
1107 fname = afile
1065 else:
1108 else:
1066 raise PatchError(_("undefined source and destination files"))
1109 raise PatchError(_("undefined source and destination files"))
1067
1110
1068 gp = patchmeta(fname)
1111 gp = patchmeta(fname)
1069 if create:
1112 if create:
1070 gp.op = 'ADD'
1113 gp.op = 'ADD'
1071 elif remove:
1114 elif remove:
1072 gp.op = 'DELETE'
1115 gp.op = 'DELETE'
1073 return gp
1116 return gp
1074
1117
1075 def scangitpatch(lr, firstline):
1118 def scangitpatch(lr, firstline):
1076 """
1119 """
1077 Git patches can emit:
1120 Git patches can emit:
1078 - rename a to b
1121 - rename a to b
1079 - change b
1122 - change b
1080 - copy a to c
1123 - copy a to c
1081 - change c
1124 - change c
1082
1125
1083 We cannot apply this sequence as-is, the renamed 'a' could not be
1126 We cannot apply this sequence as-is, the renamed 'a' could not be
1084 found for it would have been renamed already. And we cannot copy
1127 found for it would have been renamed already. And we cannot copy
1085 from 'b' instead because 'b' would have been changed already. So
1128 from 'b' instead because 'b' would have been changed already. So
1086 we scan the git patch for copy and rename commands so we can
1129 we scan the git patch for copy and rename commands so we can
1087 perform the copies ahead of time.
1130 perform the copies ahead of time.
1088 """
1131 """
1089 pos = 0
1132 pos = 0
1090 try:
1133 try:
1091 pos = lr.fp.tell()
1134 pos = lr.fp.tell()
1092 fp = lr.fp
1135 fp = lr.fp
1093 except IOError:
1136 except IOError:
1094 fp = cStringIO.StringIO(lr.fp.read())
1137 fp = cStringIO.StringIO(lr.fp.read())
1095 gitlr = linereader(fp)
1138 gitlr = linereader(fp)
1096 gitlr.push(firstline)
1139 gitlr.push(firstline)
1097 gitpatches = readgitpatch(gitlr)
1140 gitpatches = readgitpatch(gitlr)
1098 fp.seek(pos)
1141 fp.seek(pos)
1099 return gitpatches
1142 return gitpatches
1100
1143
1101 def iterhunks(fp):
1144 def iterhunks(fp):
1102 """Read a patch and yield the following events:
1145 """Read a patch and yield the following events:
1103 - ("file", afile, bfile, firsthunk): select a new target file.
1146 - ("file", afile, bfile, firsthunk): select a new target file.
1104 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1147 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1105 "file" event.
1148 "file" event.
1106 - ("git", gitchanges): current diff is in git format, gitchanges
1149 - ("git", gitchanges): current diff is in git format, gitchanges
1107 maps filenames to gitpatch records. Unique event.
1150 maps filenames to gitpatch records. Unique event.
1108 """
1151 """
1109 afile = ""
1152 afile = ""
1110 bfile = ""
1153 bfile = ""
1111 state = None
1154 state = None
1112 hunknum = 0
1155 hunknum = 0
1113 emitfile = newfile = False
1156 emitfile = newfile = False
1114 gitpatches = None
1157 gitpatches = None
1115
1158
1116 # our states
1159 # our states
1117 BFILE = 1
1160 BFILE = 1
1118 context = None
1161 context = None
1119 lr = linereader(fp)
1162 lr = linereader(fp)
1120
1163
1121 while True:
1164 while True:
1122 x = lr.readline()
1165 x = lr.readline()
1123 if not x:
1166 if not x:
1124 break
1167 break
1125 if state == BFILE and (
1168 if state == BFILE and (
1126 (not context and x[0] == '@')
1169 (not context and x[0] == '@')
1127 or (context is not False and x.startswith('***************'))
1170 or (context is not False and x.startswith('***************'))
1128 or x.startswith('GIT binary patch')):
1171 or x.startswith('GIT binary patch')):
1129 gp = None
1172 gp = None
1130 if (gitpatches and
1173 if (gitpatches and
1131 (gitpatches[-1][0] == afile or gitpatches[-1][1] == bfile)):
1174 (gitpatches[-1][0] == afile or gitpatches[-1][1] == bfile)):
1132 gp = gitpatches.pop()[2]
1175 gp = gitpatches.pop()[2]
1133 if x.startswith('GIT binary patch'):
1176 if x.startswith('GIT binary patch'):
1134 h = binhunk(lr)
1177 h = binhunk(lr)
1135 else:
1178 else:
1136 if context is None and x.startswith('***************'):
1179 if context is None and x.startswith('***************'):
1137 context = True
1180 context = True
1138 h = hunk(x, hunknum + 1, lr, context)
1181 h = hunk(x, hunknum + 1, lr, context)
1139 hunknum += 1
1182 hunknum += 1
1140 if emitfile:
1183 if emitfile:
1141 emitfile = False
1184 emitfile = False
1142 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1185 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1143 yield 'hunk', h
1186 yield 'hunk', h
1144 elif x.startswith('diff --git'):
1187 elif x.startswith('diff --git'):
1145 m = gitre.match(x)
1188 m = gitre.match(x)
1146 if not m:
1189 if not m:
1147 continue
1190 continue
1148 if gitpatches is None:
1191 if gitpatches is None:
1149 # scan whole input for git metadata
1192 # scan whole input for git metadata
1150 gitpatches = [('a/' + gp.path, 'b/' + gp.path, gp) for gp
1193 gitpatches = [('a/' + gp.path, 'b/' + gp.path, gp) for gp
1151 in scangitpatch(lr, x)]
1194 in scangitpatch(lr, x)]
1152 yield 'git', [g[2].copy() for g in gitpatches
1195 yield 'git', [g[2].copy() for g in gitpatches
1153 if g[2].op in ('COPY', 'RENAME')]
1196 if g[2].op in ('COPY', 'RENAME')]
1154 gitpatches.reverse()
1197 gitpatches.reverse()
1155 afile = 'a/' + m.group(1)
1198 afile = 'a/' + m.group(1)
1156 bfile = 'b/' + m.group(2)
1199 bfile = 'b/' + m.group(2)
1157 while afile != gitpatches[-1][0] and bfile != gitpatches[-1][1]:
1200 while afile != gitpatches[-1][0] and bfile != gitpatches[-1][1]:
1158 gp = gitpatches.pop()[2]
1201 gp = gitpatches.pop()[2]
1159 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1202 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1160 gp = gitpatches[-1][2]
1203 gp = gitpatches[-1][2]
1161 # copy/rename + modify should modify target, not source
1204 # copy/rename + modify should modify target, not source
1162 if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode:
1205 if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode:
1163 afile = bfile
1206 afile = bfile
1164 newfile = True
1207 newfile = True
1165 elif x.startswith('---'):
1208 elif x.startswith('---'):
1166 # check for a unified diff
1209 # check for a unified diff
1167 l2 = lr.readline()
1210 l2 = lr.readline()
1168 if not l2.startswith('+++'):
1211 if not l2.startswith('+++'):
1169 lr.push(l2)
1212 lr.push(l2)
1170 continue
1213 continue
1171 newfile = True
1214 newfile = True
1172 context = False
1215 context = False
1173 afile = parsefilename(x)
1216 afile = parsefilename(x)
1174 bfile = parsefilename(l2)
1217 bfile = parsefilename(l2)
1175 elif x.startswith('***'):
1218 elif x.startswith('***'):
1176 # check for a context diff
1219 # check for a context diff
1177 l2 = lr.readline()
1220 l2 = lr.readline()
1178 if not l2.startswith('---'):
1221 if not l2.startswith('---'):
1179 lr.push(l2)
1222 lr.push(l2)
1180 continue
1223 continue
1181 l3 = lr.readline()
1224 l3 = lr.readline()
1182 lr.push(l3)
1225 lr.push(l3)
1183 if not l3.startswith("***************"):
1226 if not l3.startswith("***************"):
1184 lr.push(l2)
1227 lr.push(l2)
1185 continue
1228 continue
1186 newfile = True
1229 newfile = True
1187 context = True
1230 context = True
1188 afile = parsefilename(x)
1231 afile = parsefilename(x)
1189 bfile = parsefilename(l2)
1232 bfile = parsefilename(l2)
1190
1233
1191 if newfile:
1234 if newfile:
1192 newfile = False
1235 newfile = False
1193 emitfile = True
1236 emitfile = True
1194 state = BFILE
1237 state = BFILE
1195 hunknum = 0
1238 hunknum = 0
1196
1239
1197 while gitpatches:
1240 while gitpatches:
1198 gp = gitpatches.pop()[2]
1241 gp = gitpatches.pop()[2]
1199 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1242 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1200
1243
1201 def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
1244 def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
1202 """Reads a patch from fp and tries to apply it.
1245 """Reads a patch from fp and tries to apply it.
1203
1246
1204 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1247 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1205 there was any fuzz.
1248 there was any fuzz.
1206
1249
1207 If 'eolmode' is 'strict', the patch content and patched file are
1250 If 'eolmode' is 'strict', the patch content and patched file are
1208 read in binary mode. Otherwise, line endings are ignored when
1251 read in binary mode. Otherwise, line endings are ignored when
1209 patching then normalized according to 'eolmode'.
1252 patching then normalized according to 'eolmode'.
1210 """
1253 """
1211 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1254 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1212 eolmode=eolmode)
1255 eolmode=eolmode)
1213
1256
1214 def _applydiff(ui, fp, patcher, backend, store, strip=1,
1257 def _applydiff(ui, fp, patcher, backend, store, strip=1,
1215 eolmode='strict'):
1258 eolmode='strict'):
1216
1259
1217 def pstrip(p):
1260 def pstrip(p):
1218 return pathstrip(p, strip - 1)[1]
1261 return pathstrip(p, strip - 1)[1]
1219
1262
1220 rejects = 0
1263 rejects = 0
1221 err = 0
1264 err = 0
1222 current_file = None
1265 current_file = None
1223
1266
1224 for state, values in iterhunks(fp):
1267 for state, values in iterhunks(fp):
1225 if state == 'hunk':
1268 if state == 'hunk':
1226 if not current_file:
1269 if not current_file:
1227 continue
1270 continue
1228 ret = current_file.apply(values)
1271 ret = current_file.apply(values)
1229 if ret > 0:
1272 if ret > 0:
1230 err = 1
1273 err = 1
1231 elif state == 'file':
1274 elif state == 'file':
1232 if current_file:
1275 if current_file:
1233 rejects += current_file.close()
1276 rejects += current_file.close()
1234 current_file = None
1277 current_file = None
1235 afile, bfile, first_hunk, gp = values
1278 afile, bfile, first_hunk, gp = values
1236 if gp:
1279 if gp:
1237 path = pstrip(gp.path)
1280 path = pstrip(gp.path)
1238 gp.path = pstrip(gp.path)
1281 gp.path = pstrip(gp.path)
1239 if gp.oldpath:
1282 if gp.oldpath:
1240 gp.oldpath = pstrip(gp.oldpath)
1283 gp.oldpath = pstrip(gp.oldpath)
1241 else:
1284 else:
1242 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1285 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1243 if gp.op == 'RENAME':
1286 if gp.op == 'RENAME':
1244 backend.unlink(gp.oldpath)
1287 backend.unlink(gp.oldpath)
1245 if not first_hunk:
1288 if not first_hunk:
1246 if gp.op == 'DELETE':
1289 if gp.op == 'DELETE':
1247 backend.unlink(gp.path)
1290 backend.unlink(gp.path)
1248 continue
1291 continue
1249 data, mode = None, None
1292 data, mode = None, None
1250 if gp.op in ('RENAME', 'COPY'):
1293 if gp.op in ('RENAME', 'COPY'):
1251 data, mode = store.getfile(gp.oldpath)[:2]
1294 data, mode = store.getfile(gp.oldpath)[:2]
1252 if gp.mode:
1295 if gp.mode:
1253 mode = gp.mode
1296 mode = gp.mode
1254 if gp.op == 'ADD':
1297 if gp.op == 'ADD':
1255 # Added files without content have no hunk and
1298 # Added files without content have no hunk and
1256 # must be created
1299 # must be created
1257 data = ''
1300 data = ''
1258 if data or mode:
1301 if data or mode:
1259 if (gp.op in ('ADD', 'RENAME', 'COPY')
1302 if (gp.op in ('ADD', 'RENAME', 'COPY')
1260 and backend.exists(gp.path)):
1303 and backend.exists(gp.path)):
1261 raise PatchError(_("cannot create %s: destination "
1304 raise PatchError(_("cannot create %s: destination "
1262 "already exists") % gp.path)
1305 "already exists") % gp.path)
1263 backend.setfile(gp.path, data, mode, gp.oldpath)
1306 backend.setfile(gp.path, data, mode, gp.oldpath)
1264 continue
1307 continue
1265 try:
1308 try:
1266 current_file = patcher(ui, gp, backend, store,
1309 current_file = patcher(ui, gp, backend, store,
1267 eolmode=eolmode)
1310 eolmode=eolmode)
1268 except PatchError, inst:
1311 except PatchError, inst:
1269 ui.warn(str(inst) + '\n')
1312 ui.warn(str(inst) + '\n')
1270 current_file = None
1313 current_file = None
1271 rejects += 1
1314 rejects += 1
1272 continue
1315 continue
1273 elif state == 'git':
1316 elif state == 'git':
1274 for gp in values:
1317 for gp in values:
1275 path = pstrip(gp.oldpath)
1318 path = pstrip(gp.oldpath)
1276 data, mode = backend.getfile(path)
1319 data, mode = backend.getfile(path)
1277 store.setfile(path, data, mode)
1320 store.setfile(path, data, mode)
1278 else:
1321 else:
1279 raise util.Abort(_('unsupported parser state: %s') % state)
1322 raise util.Abort(_('unsupported parser state: %s') % state)
1280
1323
1281 if current_file:
1324 if current_file:
1282 rejects += current_file.close()
1325 rejects += current_file.close()
1283
1326
1284 if rejects:
1327 if rejects:
1285 return -1
1328 return -1
1286 return err
1329 return err
1287
1330
1288 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1331 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1289 similarity):
1332 similarity):
1290 """use <patcher> to apply <patchname> to the working directory.
1333 """use <patcher> to apply <patchname> to the working directory.
1291 returns whether patch was applied with fuzz factor."""
1334 returns whether patch was applied with fuzz factor."""
1292
1335
1293 fuzz = False
1336 fuzz = False
1294 args = []
1337 args = []
1295 cwd = repo.root
1338 cwd = repo.root
1296 if cwd:
1339 if cwd:
1297 args.append('-d %s' % util.shellquote(cwd))
1340 args.append('-d %s' % util.shellquote(cwd))
1298 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1341 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1299 util.shellquote(patchname)))
1342 util.shellquote(patchname)))
1300 try:
1343 try:
1301 for line in fp:
1344 for line in fp:
1302 line = line.rstrip()
1345 line = line.rstrip()
1303 ui.note(line + '\n')
1346 ui.note(line + '\n')
1304 if line.startswith('patching file '):
1347 if line.startswith('patching file '):
1305 pf = util.parsepatchoutput(line)
1348 pf = util.parsepatchoutput(line)
1306 printed_file = False
1349 printed_file = False
1307 files.add(pf)
1350 files.add(pf)
1308 elif line.find('with fuzz') >= 0:
1351 elif line.find('with fuzz') >= 0:
1309 fuzz = True
1352 fuzz = True
1310 if not printed_file:
1353 if not printed_file:
1311 ui.warn(pf + '\n')
1354 ui.warn(pf + '\n')
1312 printed_file = True
1355 printed_file = True
1313 ui.warn(line + '\n')
1356 ui.warn(line + '\n')
1314 elif line.find('saving rejects to file') >= 0:
1357 elif line.find('saving rejects to file') >= 0:
1315 ui.warn(line + '\n')
1358 ui.warn(line + '\n')
1316 elif line.find('FAILED') >= 0:
1359 elif line.find('FAILED') >= 0:
1317 if not printed_file:
1360 if not printed_file:
1318 ui.warn(pf + '\n')
1361 ui.warn(pf + '\n')
1319 printed_file = True
1362 printed_file = True
1320 ui.warn(line + '\n')
1363 ui.warn(line + '\n')
1321 finally:
1364 finally:
1322 if files:
1365 if files:
1323 cfiles = list(files)
1366 cfiles = list(files)
1324 cwd = repo.getcwd()
1367 cwd = repo.getcwd()
1325 if cwd:
1368 if cwd:
1326 cfiles = [util.pathto(repo.root, cwd, f)
1369 cfiles = [util.pathto(repo.root, cwd, f)
1327 for f in cfile]
1370 for f in cfile]
1328 scmutil.addremove(repo, cfiles, similarity=similarity)
1371 scmutil.addremove(repo, cfiles, similarity=similarity)
1329 code = fp.close()
1372 code = fp.close()
1330 if code:
1373 if code:
1331 raise PatchError(_("patch command failed: %s") %
1374 raise PatchError(_("patch command failed: %s") %
1332 util.explainexit(code)[0])
1375 util.explainexit(code)[0])
1333 return fuzz
1376 return fuzz
1334
1377
1335 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1378 def patchbackend(ui, backend, 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
1340 if files is None:
1379 if files is None:
1341 files = set()
1380 files = set()
1342 if eolmode is None:
1381 if eolmode is None:
1343 eolmode = ui.config('patch', 'eol', 'strict')
1382 eolmode = ui.config('patch', 'eol', 'strict')
1344 if eolmode.lower() not in eolmodes:
1383 if eolmode.lower() not in eolmodes:
1345 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1384 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1346 eolmode = eolmode.lower()
1385 eolmode = eolmode.lower()
1347
1386
1348 store = filestore()
1387 store = filestore()
1349 backend = workingbackend(ui, repo, similarity)
1350 try:
1388 try:
1351 fp = open(patchobj, 'rb')
1389 fp = open(patchobj, 'rb')
1352 except TypeError:
1390 except TypeError:
1353 fp = patchobj
1391 fp = patchobj
1354 try:
1392 try:
1355 ret = applydiff(ui, fp, backend, store, strip=strip,
1393 ret = applydiff(ui, fp, backend, store, strip=strip,
1356 eolmode=eolmode)
1394 eolmode=eolmode)
1357 finally:
1395 finally:
1358 if fp != patchobj:
1396 if fp != patchobj:
1359 fp.close()
1397 fp.close()
1360 files.update(backend.close())
1398 files.update(backend.close())
1361 store.close()
1399 store.close()
1362 if ret < 0:
1400 if ret < 0:
1363 raise PatchError(_('patch failed to apply'))
1401 raise PatchError(_('patch failed to apply'))
1364 return ret > 0
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 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1431 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1367 similarity=0):
1432 similarity=0):
1368 """Apply <patchname> to the working directory.
1433 """Apply <patchname> to the working directory.
1369
1434
1370 'eolmode' specifies how end of lines should be handled. It can be:
1435 'eolmode' specifies how end of lines should be handled. It can be:
1371 - 'strict': inputs are read in binary mode, EOLs are preserved
1436 - 'strict': inputs are read in binary mode, EOLs are preserved
1372 - 'crlf': EOLs are ignored when patching and reset to CRLF
1437 - 'crlf': EOLs are ignored when patching and reset to CRLF
1373 - 'lf': EOLs are ignored when patching and reset to LF
1438 - 'lf': EOLs are ignored when patching and reset to LF
1374 - None: get it from user settings, default to 'strict'
1439 - None: get it from user settings, default to 'strict'
1375 'eolmode' is ignored when using an external patcher program.
1440 'eolmode' is ignored when using an external patcher program.
1376
1441
1377 Returns whether patch was applied with fuzz factor.
1442 Returns whether patch was applied with fuzz factor.
1378 """
1443 """
1379 patcher = ui.config('ui', 'patch')
1444 patcher = ui.config('ui', 'patch')
1380 if files is None:
1445 if files is None:
1381 files = set()
1446 files = set()
1382 try:
1447 try:
1383 if patcher:
1448 if patcher:
1384 return _externalpatch(ui, repo, patcher, patchname, strip,
1449 return _externalpatch(ui, repo, patcher, patchname, strip,
1385 files, similarity)
1450 files, similarity)
1386 return internalpatch(ui, repo, patchname, strip, files, eolmode,
1451 return internalpatch(ui, repo, patchname, strip, files, eolmode,
1387 similarity)
1452 similarity)
1388 except PatchError, err:
1453 except PatchError, err:
1389 raise util.Abort(str(err))
1454 raise util.Abort(str(err))
1390
1455
1391 def changedfiles(ui, repo, patchpath, strip=1):
1456 def changedfiles(ui, repo, patchpath, strip=1):
1392 backend = fsbackend(ui, repo.root)
1457 backend = fsbackend(ui, repo.root)
1393 fp = open(patchpath, 'rb')
1458 fp = open(patchpath, 'rb')
1394 try:
1459 try:
1395 changed = set()
1460 changed = set()
1396 for state, values in iterhunks(fp):
1461 for state, values in iterhunks(fp):
1397 if state == 'file':
1462 if state == 'file':
1398 afile, bfile, first_hunk, gp = values
1463 afile, bfile, first_hunk, gp = values
1399 if gp:
1464 if gp:
1400 gp.path = pathstrip(gp.path, strip - 1)[1]
1465 gp.path = pathstrip(gp.path, strip - 1)[1]
1401 if gp.oldpath:
1466 if gp.oldpath:
1402 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1467 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1403 else:
1468 else:
1404 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1469 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1405 changed.add(gp.path)
1470 changed.add(gp.path)
1406 if gp.op == 'RENAME':
1471 if gp.op == 'RENAME':
1407 changed.add(gp.oldpath)
1472 changed.add(gp.oldpath)
1408 elif state not in ('hunk', 'git'):
1473 elif state not in ('hunk', 'git'):
1409 raise util.Abort(_('unsupported parser state: %s') % state)
1474 raise util.Abort(_('unsupported parser state: %s') % state)
1410 return changed
1475 return changed
1411 finally:
1476 finally:
1412 fp.close()
1477 fp.close()
1413
1478
1414 def b85diff(to, tn):
1479 def b85diff(to, tn):
1415 '''print base85-encoded binary diff'''
1480 '''print base85-encoded binary diff'''
1416 def gitindex(text):
1481 def gitindex(text):
1417 if not text:
1482 if not text:
1418 return hex(nullid)
1483 return hex(nullid)
1419 l = len(text)
1484 l = len(text)
1420 s = util.sha1('blob %d\0' % l)
1485 s = util.sha1('blob %d\0' % l)
1421 s.update(text)
1486 s.update(text)
1422 return s.hexdigest()
1487 return s.hexdigest()
1423
1488
1424 def fmtline(line):
1489 def fmtline(line):
1425 l = len(line)
1490 l = len(line)
1426 if l <= 26:
1491 if l <= 26:
1427 l = chr(ord('A') + l - 1)
1492 l = chr(ord('A') + l - 1)
1428 else:
1493 else:
1429 l = chr(l - 26 + ord('a') - 1)
1494 l = chr(l - 26 + ord('a') - 1)
1430 return '%c%s\n' % (l, base85.b85encode(line, True))
1495 return '%c%s\n' % (l, base85.b85encode(line, True))
1431
1496
1432 def chunk(text, csize=52):
1497 def chunk(text, csize=52):
1433 l = len(text)
1498 l = len(text)
1434 i = 0
1499 i = 0
1435 while i < l:
1500 while i < l:
1436 yield text[i:i + csize]
1501 yield text[i:i + csize]
1437 i += csize
1502 i += csize
1438
1503
1439 tohash = gitindex(to)
1504 tohash = gitindex(to)
1440 tnhash = gitindex(tn)
1505 tnhash = gitindex(tn)
1441 if tohash == tnhash:
1506 if tohash == tnhash:
1442 return ""
1507 return ""
1443
1508
1444 # TODO: deltas
1509 # TODO: deltas
1445 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1510 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1446 (tohash, tnhash, len(tn))]
1511 (tohash, tnhash, len(tn))]
1447 for l in chunk(zlib.compress(tn)):
1512 for l in chunk(zlib.compress(tn)):
1448 ret.append(fmtline(l))
1513 ret.append(fmtline(l))
1449 ret.append('\n')
1514 ret.append('\n')
1450 return ''.join(ret)
1515 return ''.join(ret)
1451
1516
1452 class GitDiffRequired(Exception):
1517 class GitDiffRequired(Exception):
1453 pass
1518 pass
1454
1519
1455 def diffopts(ui, opts=None, untrusted=False):
1520 def diffopts(ui, opts=None, untrusted=False):
1456 def get(key, name=None, getter=ui.configbool):
1521 def get(key, name=None, getter=ui.configbool):
1457 return ((opts and opts.get(key)) or
1522 return ((opts and opts.get(key)) or
1458 getter('diff', name or key, None, untrusted=untrusted))
1523 getter('diff', name or key, None, untrusted=untrusted))
1459 return mdiff.diffopts(
1524 return mdiff.diffopts(
1460 text=opts and opts.get('text'),
1525 text=opts and opts.get('text'),
1461 git=get('git'),
1526 git=get('git'),
1462 nodates=get('nodates'),
1527 nodates=get('nodates'),
1463 showfunc=get('show_function', 'showfunc'),
1528 showfunc=get('show_function', 'showfunc'),
1464 ignorews=get('ignore_all_space', 'ignorews'),
1529 ignorews=get('ignore_all_space', 'ignorews'),
1465 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1530 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1466 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1531 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1467 context=get('unified', getter=ui.config))
1532 context=get('unified', getter=ui.config))
1468
1533
1469 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1534 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1470 losedatafn=None, prefix=''):
1535 losedatafn=None, prefix=''):
1471 '''yields diff of changes to files between two nodes, or node and
1536 '''yields diff of changes to files between two nodes, or node and
1472 working directory.
1537 working directory.
1473
1538
1474 if node1 is None, use first dirstate parent instead.
1539 if node1 is None, use first dirstate parent instead.
1475 if node2 is None, compare node1 with working directory.
1540 if node2 is None, compare node1 with working directory.
1476
1541
1477 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1542 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1478 every time some change cannot be represented with the current
1543 every time some change cannot be represented with the current
1479 patch format. Return False to upgrade to git patch format, True to
1544 patch format. Return False to upgrade to git patch format, True to
1480 accept the loss or raise an exception to abort the diff. It is
1545 accept the loss or raise an exception to abort the diff. It is
1481 called with the name of current file being diffed as 'fn'. If set
1546 called with the name of current file being diffed as 'fn'. If set
1482 to None, patches will always be upgraded to git format when
1547 to None, patches will always be upgraded to git format when
1483 necessary.
1548 necessary.
1484
1549
1485 prefix is a filename prefix that is prepended to all filenames on
1550 prefix is a filename prefix that is prepended to all filenames on
1486 display (used for subrepos).
1551 display (used for subrepos).
1487 '''
1552 '''
1488
1553
1489 if opts is None:
1554 if opts is None:
1490 opts = mdiff.defaultopts
1555 opts = mdiff.defaultopts
1491
1556
1492 if not node1 and not node2:
1557 if not node1 and not node2:
1493 node1 = repo.dirstate.p1()
1558 node1 = repo.dirstate.p1()
1494
1559
1495 def lrugetfilectx():
1560 def lrugetfilectx():
1496 cache = {}
1561 cache = {}
1497 order = []
1562 order = []
1498 def getfilectx(f, ctx):
1563 def getfilectx(f, ctx):
1499 fctx = ctx.filectx(f, filelog=cache.get(f))
1564 fctx = ctx.filectx(f, filelog=cache.get(f))
1500 if f not in cache:
1565 if f not in cache:
1501 if len(cache) > 20:
1566 if len(cache) > 20:
1502 del cache[order.pop(0)]
1567 del cache[order.pop(0)]
1503 cache[f] = fctx.filelog()
1568 cache[f] = fctx.filelog()
1504 else:
1569 else:
1505 order.remove(f)
1570 order.remove(f)
1506 order.append(f)
1571 order.append(f)
1507 return fctx
1572 return fctx
1508 return getfilectx
1573 return getfilectx
1509 getfilectx = lrugetfilectx()
1574 getfilectx = lrugetfilectx()
1510
1575
1511 ctx1 = repo[node1]
1576 ctx1 = repo[node1]
1512 ctx2 = repo[node2]
1577 ctx2 = repo[node2]
1513
1578
1514 if not changes:
1579 if not changes:
1515 changes = repo.status(ctx1, ctx2, match=match)
1580 changes = repo.status(ctx1, ctx2, match=match)
1516 modified, added, removed = changes[:3]
1581 modified, added, removed = changes[:3]
1517
1582
1518 if not modified and not added and not removed:
1583 if not modified and not added and not removed:
1519 return []
1584 return []
1520
1585
1521 revs = None
1586 revs = None
1522 if not repo.ui.quiet:
1587 if not repo.ui.quiet:
1523 hexfunc = repo.ui.debugflag and hex or short
1588 hexfunc = repo.ui.debugflag and hex or short
1524 revs = [hexfunc(node) for node in [node1, node2] if node]
1589 revs = [hexfunc(node) for node in [node1, node2] if node]
1525
1590
1526 copy = {}
1591 copy = {}
1527 if opts.git or opts.upgrade:
1592 if opts.git or opts.upgrade:
1528 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1593 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1529
1594
1530 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1595 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1531 modified, added, removed, copy, getfilectx, opts, losedata, prefix)
1596 modified, added, removed, copy, getfilectx, opts, losedata, prefix)
1532 if opts.upgrade and not opts.git:
1597 if opts.upgrade and not opts.git:
1533 try:
1598 try:
1534 def losedata(fn):
1599 def losedata(fn):
1535 if not losedatafn or not losedatafn(fn=fn):
1600 if not losedatafn or not losedatafn(fn=fn):
1536 raise GitDiffRequired()
1601 raise GitDiffRequired()
1537 # Buffer the whole output until we are sure it can be generated
1602 # Buffer the whole output until we are sure it can be generated
1538 return list(difffn(opts.copy(git=False), losedata))
1603 return list(difffn(opts.copy(git=False), losedata))
1539 except GitDiffRequired:
1604 except GitDiffRequired:
1540 return difffn(opts.copy(git=True), None)
1605 return difffn(opts.copy(git=True), None)
1541 else:
1606 else:
1542 return difffn(opts, None)
1607 return difffn(opts, None)
1543
1608
1544 def difflabel(func, *args, **kw):
1609 def difflabel(func, *args, **kw):
1545 '''yields 2-tuples of (output, label) based on the output of func()'''
1610 '''yields 2-tuples of (output, label) based on the output of func()'''
1546 prefixes = [('diff', 'diff.diffline'),
1611 prefixes = [('diff', 'diff.diffline'),
1547 ('copy', 'diff.extended'),
1612 ('copy', 'diff.extended'),
1548 ('rename', 'diff.extended'),
1613 ('rename', 'diff.extended'),
1549 ('old', 'diff.extended'),
1614 ('old', 'diff.extended'),
1550 ('new', 'diff.extended'),
1615 ('new', 'diff.extended'),
1551 ('deleted', 'diff.extended'),
1616 ('deleted', 'diff.extended'),
1552 ('---', 'diff.file_a'),
1617 ('---', 'diff.file_a'),
1553 ('+++', 'diff.file_b'),
1618 ('+++', 'diff.file_b'),
1554 ('@@', 'diff.hunk'),
1619 ('@@', 'diff.hunk'),
1555 ('-', 'diff.deleted'),
1620 ('-', 'diff.deleted'),
1556 ('+', 'diff.inserted')]
1621 ('+', 'diff.inserted')]
1557
1622
1558 for chunk in func(*args, **kw):
1623 for chunk in func(*args, **kw):
1559 lines = chunk.split('\n')
1624 lines = chunk.split('\n')
1560 for i, line in enumerate(lines):
1625 for i, line in enumerate(lines):
1561 if i != 0:
1626 if i != 0:
1562 yield ('\n', '')
1627 yield ('\n', '')
1563 stripline = line
1628 stripline = line
1564 if line and line[0] in '+-':
1629 if line and line[0] in '+-':
1565 # highlight trailing whitespace, but only in changed lines
1630 # highlight trailing whitespace, but only in changed lines
1566 stripline = line.rstrip()
1631 stripline = line.rstrip()
1567 for prefix, label in prefixes:
1632 for prefix, label in prefixes:
1568 if stripline.startswith(prefix):
1633 if stripline.startswith(prefix):
1569 yield (stripline, label)
1634 yield (stripline, label)
1570 break
1635 break
1571 else:
1636 else:
1572 yield (line, '')
1637 yield (line, '')
1573 if line != stripline:
1638 if line != stripline:
1574 yield (line[len(stripline):], 'diff.trailingwhitespace')
1639 yield (line[len(stripline):], 'diff.trailingwhitespace')
1575
1640
1576 def diffui(*args, **kw):
1641 def diffui(*args, **kw):
1577 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1642 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1578 return difflabel(diff, *args, **kw)
1643 return difflabel(diff, *args, **kw)
1579
1644
1580
1645
1581 def _addmodehdr(header, omode, nmode):
1646 def _addmodehdr(header, omode, nmode):
1582 if omode != nmode:
1647 if omode != nmode:
1583 header.append('old mode %s\n' % omode)
1648 header.append('old mode %s\n' % omode)
1584 header.append('new mode %s\n' % nmode)
1649 header.append('new mode %s\n' % nmode)
1585
1650
1586 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1651 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1587 copy, getfilectx, opts, losedatafn, prefix):
1652 copy, getfilectx, opts, losedatafn, prefix):
1588
1653
1589 def join(f):
1654 def join(f):
1590 return os.path.join(prefix, f)
1655 return os.path.join(prefix, f)
1591
1656
1592 date1 = util.datestr(ctx1.date())
1657 date1 = util.datestr(ctx1.date())
1593 man1 = ctx1.manifest()
1658 man1 = ctx1.manifest()
1594
1659
1595 gone = set()
1660 gone = set()
1596 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1661 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1597
1662
1598 copyto = dict([(v, k) for k, v in copy.items()])
1663 copyto = dict([(v, k) for k, v in copy.items()])
1599
1664
1600 if opts.git:
1665 if opts.git:
1601 revs = None
1666 revs = None
1602
1667
1603 for f in sorted(modified + added + removed):
1668 for f in sorted(modified + added + removed):
1604 to = None
1669 to = None
1605 tn = None
1670 tn = None
1606 dodiff = True
1671 dodiff = True
1607 header = []
1672 header = []
1608 if f in man1:
1673 if f in man1:
1609 to = getfilectx(f, ctx1).data()
1674 to = getfilectx(f, ctx1).data()
1610 if f not in removed:
1675 if f not in removed:
1611 tn = getfilectx(f, ctx2).data()
1676 tn = getfilectx(f, ctx2).data()
1612 a, b = f, f
1677 a, b = f, f
1613 if opts.git or losedatafn:
1678 if opts.git or losedatafn:
1614 if f in added:
1679 if f in added:
1615 mode = gitmode[ctx2.flags(f)]
1680 mode = gitmode[ctx2.flags(f)]
1616 if f in copy or f in copyto:
1681 if f in copy or f in copyto:
1617 if opts.git:
1682 if opts.git:
1618 if f in copy:
1683 if f in copy:
1619 a = copy[f]
1684 a = copy[f]
1620 else:
1685 else:
1621 a = copyto[f]
1686 a = copyto[f]
1622 omode = gitmode[man1.flags(a)]
1687 omode = gitmode[man1.flags(a)]
1623 _addmodehdr(header, omode, mode)
1688 _addmodehdr(header, omode, mode)
1624 if a in removed and a not in gone:
1689 if a in removed and a not in gone:
1625 op = 'rename'
1690 op = 'rename'
1626 gone.add(a)
1691 gone.add(a)
1627 else:
1692 else:
1628 op = 'copy'
1693 op = 'copy'
1629 header.append('%s from %s\n' % (op, join(a)))
1694 header.append('%s from %s\n' % (op, join(a)))
1630 header.append('%s to %s\n' % (op, join(f)))
1695 header.append('%s to %s\n' % (op, join(f)))
1631 to = getfilectx(a, ctx1).data()
1696 to = getfilectx(a, ctx1).data()
1632 else:
1697 else:
1633 losedatafn(f)
1698 losedatafn(f)
1634 else:
1699 else:
1635 if opts.git:
1700 if opts.git:
1636 header.append('new file mode %s\n' % mode)
1701 header.append('new file mode %s\n' % mode)
1637 elif ctx2.flags(f):
1702 elif ctx2.flags(f):
1638 losedatafn(f)
1703 losedatafn(f)
1639 # In theory, if tn was copied or renamed we should check
1704 # In theory, if tn was copied or renamed we should check
1640 # if the source is binary too but the copy record already
1705 # if the source is binary too but the copy record already
1641 # forces git mode.
1706 # forces git mode.
1642 if util.binary(tn):
1707 if util.binary(tn):
1643 if opts.git:
1708 if opts.git:
1644 dodiff = 'binary'
1709 dodiff = 'binary'
1645 else:
1710 else:
1646 losedatafn(f)
1711 losedatafn(f)
1647 if not opts.git and not tn:
1712 if not opts.git and not tn:
1648 # regular diffs cannot represent new empty file
1713 # regular diffs cannot represent new empty file
1649 losedatafn(f)
1714 losedatafn(f)
1650 elif f in removed:
1715 elif f in removed:
1651 if opts.git:
1716 if opts.git:
1652 # have we already reported a copy above?
1717 # have we already reported a copy above?
1653 if ((f in copy and copy[f] in added
1718 if ((f in copy and copy[f] in added
1654 and copyto[copy[f]] == f) or
1719 and copyto[copy[f]] == f) or
1655 (f in copyto and copyto[f] in added
1720 (f in copyto and copyto[f] in added
1656 and copy[copyto[f]] == f)):
1721 and copy[copyto[f]] == f)):
1657 dodiff = False
1722 dodiff = False
1658 else:
1723 else:
1659 header.append('deleted file mode %s\n' %
1724 header.append('deleted file mode %s\n' %
1660 gitmode[man1.flags(f)])
1725 gitmode[man1.flags(f)])
1661 elif not to or util.binary(to):
1726 elif not to or util.binary(to):
1662 # regular diffs cannot represent empty file deletion
1727 # regular diffs cannot represent empty file deletion
1663 losedatafn(f)
1728 losedatafn(f)
1664 else:
1729 else:
1665 oflag = man1.flags(f)
1730 oflag = man1.flags(f)
1666 nflag = ctx2.flags(f)
1731 nflag = ctx2.flags(f)
1667 binary = util.binary(to) or util.binary(tn)
1732 binary = util.binary(to) or util.binary(tn)
1668 if opts.git:
1733 if opts.git:
1669 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1734 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1670 if binary:
1735 if binary:
1671 dodiff = 'binary'
1736 dodiff = 'binary'
1672 elif binary or nflag != oflag:
1737 elif binary or nflag != oflag:
1673 losedatafn(f)
1738 losedatafn(f)
1674 if opts.git:
1739 if opts.git:
1675 header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
1740 header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
1676
1741
1677 if dodiff:
1742 if dodiff:
1678 if dodiff == 'binary':
1743 if dodiff == 'binary':
1679 text = b85diff(to, tn)
1744 text = b85diff(to, tn)
1680 else:
1745 else:
1681 text = mdiff.unidiff(to, date1,
1746 text = mdiff.unidiff(to, date1,
1682 # ctx2 date may be dynamic
1747 # ctx2 date may be dynamic
1683 tn, util.datestr(ctx2.date()),
1748 tn, util.datestr(ctx2.date()),
1684 join(a), join(b), revs, opts=opts)
1749 join(a), join(b), revs, opts=opts)
1685 if header and (text or len(header) > 1):
1750 if header and (text or len(header) > 1):
1686 yield ''.join(header)
1751 yield ''.join(header)
1687 if text:
1752 if text:
1688 yield text
1753 yield text
1689
1754
1690 def diffstatsum(stats):
1755 def diffstatsum(stats):
1691 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
1756 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
1692 for f, a, r, b in stats:
1757 for f, a, r, b in stats:
1693 maxfile = max(maxfile, encoding.colwidth(f))
1758 maxfile = max(maxfile, encoding.colwidth(f))
1694 maxtotal = max(maxtotal, a + r)
1759 maxtotal = max(maxtotal, a + r)
1695 addtotal += a
1760 addtotal += a
1696 removetotal += r
1761 removetotal += r
1697 binary = binary or b
1762 binary = binary or b
1698
1763
1699 return maxfile, maxtotal, addtotal, removetotal, binary
1764 return maxfile, maxtotal, addtotal, removetotal, binary
1700
1765
1701 def diffstatdata(lines):
1766 def diffstatdata(lines):
1702 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
1767 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
1703
1768
1704 results = []
1769 results = []
1705 filename, adds, removes = None, 0, 0
1770 filename, adds, removes = None, 0, 0
1706
1771
1707 def addresult():
1772 def addresult():
1708 if filename:
1773 if filename:
1709 isbinary = adds == 0 and removes == 0
1774 isbinary = adds == 0 and removes == 0
1710 results.append((filename, adds, removes, isbinary))
1775 results.append((filename, adds, removes, isbinary))
1711
1776
1712 for line in lines:
1777 for line in lines:
1713 if line.startswith('diff'):
1778 if line.startswith('diff'):
1714 addresult()
1779 addresult()
1715 # set numbers to 0 anyway when starting new file
1780 # set numbers to 0 anyway when starting new file
1716 adds, removes = 0, 0
1781 adds, removes = 0, 0
1717 if line.startswith('diff --git'):
1782 if line.startswith('diff --git'):
1718 filename = gitre.search(line).group(1)
1783 filename = gitre.search(line).group(1)
1719 elif line.startswith('diff -r'):
1784 elif line.startswith('diff -r'):
1720 # format: "diff -r ... -r ... filename"
1785 # format: "diff -r ... -r ... filename"
1721 filename = diffre.search(line).group(1)
1786 filename = diffre.search(line).group(1)
1722 elif line.startswith('+') and not line.startswith('+++'):
1787 elif line.startswith('+') and not line.startswith('+++'):
1723 adds += 1
1788 adds += 1
1724 elif line.startswith('-') and not line.startswith('---'):
1789 elif line.startswith('-') and not line.startswith('---'):
1725 removes += 1
1790 removes += 1
1726 addresult()
1791 addresult()
1727 return results
1792 return results
1728
1793
1729 def diffstat(lines, width=80, git=False):
1794 def diffstat(lines, width=80, git=False):
1730 output = []
1795 output = []
1731 stats = diffstatdata(lines)
1796 stats = diffstatdata(lines)
1732 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
1797 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
1733
1798
1734 countwidth = len(str(maxtotal))
1799 countwidth = len(str(maxtotal))
1735 if hasbinary and countwidth < 3:
1800 if hasbinary and countwidth < 3:
1736 countwidth = 3
1801 countwidth = 3
1737 graphwidth = width - countwidth - maxname - 6
1802 graphwidth = width - countwidth - maxname - 6
1738 if graphwidth < 10:
1803 if graphwidth < 10:
1739 graphwidth = 10
1804 graphwidth = 10
1740
1805
1741 def scale(i):
1806 def scale(i):
1742 if maxtotal <= graphwidth:
1807 if maxtotal <= graphwidth:
1743 return i
1808 return i
1744 # If diffstat runs out of room it doesn't print anything,
1809 # If diffstat runs out of room it doesn't print anything,
1745 # which isn't very useful, so always print at least one + or -
1810 # which isn't very useful, so always print at least one + or -
1746 # if there were at least some changes.
1811 # if there were at least some changes.
1747 return max(i * graphwidth // maxtotal, int(bool(i)))
1812 return max(i * graphwidth // maxtotal, int(bool(i)))
1748
1813
1749 for filename, adds, removes, isbinary in stats:
1814 for filename, adds, removes, isbinary in stats:
1750 if git and isbinary:
1815 if git and isbinary:
1751 count = 'Bin'
1816 count = 'Bin'
1752 else:
1817 else:
1753 count = adds + removes
1818 count = adds + removes
1754 pluses = '+' * scale(adds)
1819 pluses = '+' * scale(adds)
1755 minuses = '-' * scale(removes)
1820 minuses = '-' * scale(removes)
1756 output.append(' %s%s | %*s %s%s\n' %
1821 output.append(' %s%s | %*s %s%s\n' %
1757 (filename, ' ' * (maxname - encoding.colwidth(filename)),
1822 (filename, ' ' * (maxname - encoding.colwidth(filename)),
1758 countwidth, count, pluses, minuses))
1823 countwidth, count, pluses, minuses))
1759
1824
1760 if stats:
1825 if stats:
1761 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1826 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1762 % (len(stats), totaladds, totalremoves))
1827 % (len(stats), totaladds, totalremoves))
1763
1828
1764 return ''.join(output)
1829 return ''.join(output)
1765
1830
1766 def diffstatui(*args, **kw):
1831 def diffstatui(*args, **kw):
1767 '''like diffstat(), but yields 2-tuples of (output, label) for
1832 '''like diffstat(), but yields 2-tuples of (output, label) for
1768 ui.write()
1833 ui.write()
1769 '''
1834 '''
1770
1835
1771 for line in diffstat(*args, **kw).splitlines():
1836 for line in diffstat(*args, **kw).splitlines():
1772 if line and line[-1] in '+-':
1837 if line and line[-1] in '+-':
1773 name, graph = line.rsplit(' ', 1)
1838 name, graph = line.rsplit(' ', 1)
1774 yield (name + ' ', '')
1839 yield (name + ' ', '')
1775 m = re.search(r'\++', graph)
1840 m = re.search(r'\++', graph)
1776 if m:
1841 if m:
1777 yield (m.group(0), 'diffstat.inserted')
1842 yield (m.group(0), 'diffstat.inserted')
1778 m = re.search(r'-+', graph)
1843 m = re.search(r'-+', graph)
1779 if m:
1844 if m:
1780 yield (m.group(0), 'diffstat.deleted')
1845 yield (m.group(0), 'diffstat.deleted')
1781 else:
1846 else:
1782 yield (line, '')
1847 yield (line, '')
1783 yield ('\n', '')
1848 yield ('\n', '')
@@ -1,267 +1,267 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 copy
16 copy
17 diff
17 diff
18 export
18 export
19 forget
19 forget
20 grep
20 grep
21 heads
21 heads
22 help
22 help
23 identify
23 identify
24 import
24 import
25 incoming
25 incoming
26 init
26 init
27 locate
27 locate
28 log
28 log
29 manifest
29 manifest
30 merge
30 merge
31 outgoing
31 outgoing
32 parents
32 parents
33 paths
33 paths
34 pull
34 pull
35 push
35 push
36 recover
36 recover
37 remove
37 remove
38 rename
38 rename
39 resolve
39 resolve
40 revert
40 revert
41 rollback
41 rollback
42 root
42 root
43 serve
43 serve
44 showconfig
44 showconfig
45 status
45 status
46 summary
46 summary
47 tag
47 tag
48 tags
48 tags
49 tip
49 tip
50 unbundle
50 unbundle
51 update
51 update
52 verify
52 verify
53 version
53 version
54
54
55 Show all commands that start with "a"
55 Show all commands that start with "a"
56 $ hg debugcomplete a
56 $ hg debugcomplete a
57 add
57 add
58 addremove
58 addremove
59 annotate
59 annotate
60 archive
60 archive
61
61
62 Do not show debug commands if there are other candidates
62 Do not show debug commands if there are other candidates
63 $ hg debugcomplete d
63 $ hg debugcomplete d
64 diff
64 diff
65
65
66 Show debug commands if there are no other candidates
66 Show debug commands if there are no other candidates
67 $ hg debugcomplete debug
67 $ hg debugcomplete debug
68 debugancestor
68 debugancestor
69 debugbuilddag
69 debugbuilddag
70 debugbundle
70 debugbundle
71 debugcheckstate
71 debugcheckstate
72 debugcommands
72 debugcommands
73 debugcomplete
73 debugcomplete
74 debugconfig
74 debugconfig
75 debugdag
75 debugdag
76 debugdata
76 debugdata
77 debugdate
77 debugdate
78 debugdiscovery
78 debugdiscovery
79 debugfileset
79 debugfileset
80 debugfsinfo
80 debugfsinfo
81 debuggetbundle
81 debuggetbundle
82 debugignore
82 debugignore
83 debugindex
83 debugindex
84 debugindexdot
84 debugindexdot
85 debuginstall
85 debuginstall
86 debugknown
86 debugknown
87 debugpushkey
87 debugpushkey
88 debugrebuildstate
88 debugrebuildstate
89 debugrename
89 debugrename
90 debugrevlog
90 debugrevlog
91 debugrevspec
91 debugrevspec
92 debugsetparents
92 debugsetparents
93 debugstate
93 debugstate
94 debugsub
94 debugsub
95 debugwalk
95 debugwalk
96 debugwireargs
96 debugwireargs
97
97
98 Do not show the alias of a debug command if there are other candidates
98 Do not show the alias of a debug command if there are other candidates
99 (this should hide rawcommit)
99 (this should hide rawcommit)
100 $ hg debugcomplete r
100 $ hg debugcomplete r
101 recover
101 recover
102 remove
102 remove
103 rename
103 rename
104 resolve
104 resolve
105 revert
105 revert
106 rollback
106 rollback
107 root
107 root
108 Show the alias of a debug command if there are no other candidates
108 Show the alias of a debug command if there are no other candidates
109 $ hg debugcomplete rawc
109 $ hg debugcomplete rawc
110
110
111
111
112 Show the global options
112 Show the global options
113 $ hg debugcomplete --options | sort
113 $ hg debugcomplete --options | sort
114 --config
114 --config
115 --cwd
115 --cwd
116 --debug
116 --debug
117 --debugger
117 --debugger
118 --encoding
118 --encoding
119 --encodingmode
119 --encodingmode
120 --help
120 --help
121 --noninteractive
121 --noninteractive
122 --profile
122 --profile
123 --quiet
123 --quiet
124 --repository
124 --repository
125 --time
125 --time
126 --traceback
126 --traceback
127 --verbose
127 --verbose
128 --version
128 --version
129 -R
129 -R
130 -h
130 -h
131 -q
131 -q
132 -v
132 -v
133 -y
133 -y
134
134
135 Show the options for the "serve" command
135 Show the options for the "serve" command
136 $ hg debugcomplete --options serve | sort
136 $ hg debugcomplete --options serve | sort
137 --accesslog
137 --accesslog
138 --address
138 --address
139 --certificate
139 --certificate
140 --config
140 --config
141 --cwd
141 --cwd
142 --daemon
142 --daemon
143 --daemon-pipefds
143 --daemon-pipefds
144 --debug
144 --debug
145 --debugger
145 --debugger
146 --encoding
146 --encoding
147 --encodingmode
147 --encodingmode
148 --errorlog
148 --errorlog
149 --help
149 --help
150 --ipv6
150 --ipv6
151 --name
151 --name
152 --noninteractive
152 --noninteractive
153 --pid-file
153 --pid-file
154 --port
154 --port
155 --prefix
155 --prefix
156 --profile
156 --profile
157 --quiet
157 --quiet
158 --repository
158 --repository
159 --stdio
159 --stdio
160 --style
160 --style
161 --templates
161 --templates
162 --time
162 --time
163 --traceback
163 --traceback
164 --verbose
164 --verbose
165 --version
165 --version
166 --web-conf
166 --web-conf
167 -6
167 -6
168 -A
168 -A
169 -E
169 -E
170 -R
170 -R
171 -a
171 -a
172 -d
172 -d
173 -h
173 -h
174 -n
174 -n
175 -p
175 -p
176 -q
176 -q
177 -t
177 -t
178 -v
178 -v
179 -y
179 -y
180
180
181 Show an error if we use --options with an ambiguous abbreviation
181 Show an error if we use --options with an ambiguous abbreviation
182 $ hg debugcomplete --options s
182 $ hg debugcomplete --options s
183 hg: command 's' is ambiguous:
183 hg: command 's' is ambiguous:
184 serve showconfig status summary
184 serve showconfig status summary
185 [255]
185 [255]
186
186
187 Show all commands + options
187 Show all commands + options
188 $ hg debugcommands
188 $ hg debugcommands
189 add: include, exclude, subrepos, dry-run
189 add: include, exclude, subrepos, dry-run
190 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
190 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
191 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
191 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
192 commit: addremove, close-branch, include, exclude, message, logfile, date, user
192 commit: addremove, close-branch, include, exclude, message, logfile, date, user
193 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
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 export: output, switch-parent, rev, text, git, nodates
194 export: output, switch-parent, rev, text, git, nodates
195 forget: include, exclude
195 forget: include, exclude
196 init: ssh, remotecmd, insecure
196 init: ssh, remotecmd, insecure
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
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 merge: force, tool, rev, preview
198 merge: force, tool, rev, preview
199 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
199 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
200 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
200 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
201 remove: after, force, include, exclude
201 remove: after, force, include, exclude
202 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
202 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
203 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
203 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
204 summary: remote
204 summary: remote
205 update: clean, check, date, rev
205 update: clean, check, date, rev
206 addremove: similarity, include, exclude, dry-run
206 addremove: similarity, include, exclude, dry-run
207 archive: no-decode, prefix, rev, type, subrepos, include, exclude
207 archive: no-decode, prefix, rev, type, subrepos, include, exclude
208 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
208 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
209 bisect: reset, good, bad, skip, extend, command, noupdate
209 bisect: reset, good, bad, skip, extend, command, noupdate
210 bookmarks: force, rev, delete, rename, inactive
210 bookmarks: force, rev, delete, rename, inactive
211 branch: force, clean
211 branch: force, clean
212 branches: active, closed
212 branches: active, closed
213 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
213 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
214 cat: output, rev, decode, include, exclude
214 cat: output, rev, decode, include, exclude
215 copy: after, force, include, exclude, dry-run
215 copy: after, force, include, exclude, dry-run
216 debugancestor:
216 debugancestor:
217 debugbuilddag: mergeable-file, overwritten-file, new-file
217 debugbuilddag: mergeable-file, overwritten-file, new-file
218 debugbundle: all
218 debugbundle: all
219 debugcheckstate:
219 debugcheckstate:
220 debugcommands:
220 debugcommands:
221 debugcomplete: options
221 debugcomplete: options
222 debugdag: tags, branches, dots, spaces
222 debugdag: tags, branches, dots, spaces
223 debugdata: changelog, manifest
223 debugdata: changelog, manifest
224 debugdate: extended
224 debugdate: extended
225 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
225 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
226 debugfileset:
226 debugfileset:
227 debugfsinfo:
227 debugfsinfo:
228 debuggetbundle: head, common, type
228 debuggetbundle: head, common, type
229 debugignore:
229 debugignore:
230 debugindex: changelog, manifest, format
230 debugindex: changelog, manifest, format
231 debugindexdot:
231 debugindexdot:
232 debuginstall:
232 debuginstall:
233 debugknown:
233 debugknown:
234 debugpushkey:
234 debugpushkey:
235 debugrebuildstate: rev
235 debugrebuildstate: rev
236 debugrename: rev
236 debugrename: rev
237 debugrevlog: changelog, manifest, dump
237 debugrevlog: changelog, manifest, dump
238 debugrevspec:
238 debugrevspec:
239 debugsetparents:
239 debugsetparents:
240 debugstate: nodates, datesort
240 debugstate: nodates, datesort
241 debugsub: rev
241 debugsub: rev
242 debugwalk: include, exclude
242 debugwalk: include, exclude
243 debugwireargs: three, four, five, ssh, remotecmd, insecure
243 debugwireargs: three, four, five, ssh, remotecmd, insecure
244 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
244 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
245 heads: rev, topo, active, closed, style, template
245 heads: rev, topo, active, closed, style, template
246 help: extension, command
246 help: extension, command
247 identify: rev, num, id, branch, tags, bookmarks
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 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
249 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
250 locate: rev, print0, fullpath, include, exclude
250 locate: rev, print0, fullpath, include, exclude
251 manifest: rev, all
251 manifest: rev, all
252 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
252 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
253 parents: rev, style, template
253 parents: rev, style, template
254 paths:
254 paths:
255 recover:
255 recover:
256 rename: after, force, include, exclude, dry-run
256 rename: after, force, include, exclude, dry-run
257 resolve: all, list, mark, unmark, tool, no-status, include, exclude
257 resolve: all, list, mark, unmark, tool, no-status, include, exclude
258 revert: all, date, rev, no-backup, include, exclude, dry-run
258 revert: all, date, rev, no-backup, include, exclude, dry-run
259 rollback: dry-run
259 rollback: dry-run
260 root:
260 root:
261 showconfig: untrusted
261 showconfig: untrusted
262 tag: force, local, rev, remove, edit, message, date, user
262 tag: force, local, rev, remove, edit, message, date, user
263 tags:
263 tags:
264 tip: patch, git, style, template
264 tip: patch, git, style, template
265 unbundle: update
265 unbundle: update
266 verify:
266 verify:
267 version:
267 version:
General Comments 0
You need to be logged in to leave comments. Login now