##// END OF EJS Templates
add pretag and tag hooks....
Vadim Gelfer -
r1720:55017891 default
parent child Browse files
Show More
@@ -1,259 +1,268 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed.
21 21
22 22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 23 (Unix) <install-root>/etc/mercurial/hgrc::
24 24 Per-installation configuration files, searched for in the
25 25 directory where Mercurial is installed. For example, if installed
26 26 in /shared/tools, Mercurial will look in
27 27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 28 all Mercurial commands executed by any user in any directory.
29 29
30 30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 31 (Unix) /etc/mercurial/hgrc::
32 32 (Windows) C:\Mercurial\Mercurial.ini::
33 33 Per-system configuration files, for the system on which Mercurial
34 34 is running. Options in these files apply to all Mercurial
35 35 commands executed by any user in any directory. Options in these
36 36 files override per-installation options.
37 37
38 38 (Unix) $HOME/.hgrc::
39 39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini
40 40 Per-user configuration file, for the user running Mercurial.
41 41 Options in this file apply to all Mercurial commands executed by
42 42 any user in any directory. Options in this file override
43 43 per-installation and per-system options.
44 44
45 45 (Unix, Windows) <repo>/.hg/hgrc::
46 46 Per-repository configuration options that only apply in a
47 47 particular repository. This file is not version-controlled, and
48 48 will not get transferred during a "clone" operation. Options in
49 49 this file override options in all other configuration files.
50 50
51 51 SYNTAX
52 52 ------
53 53
54 54 A configuration file consists of sections, led by a "[section]" header
55 55 and followed by "name: value" entries; "name=value" is also accepted.
56 56
57 57 [spam]
58 58 eggs=ham
59 59 green=
60 60 eggs
61 61
62 62 Each line contains one entry. If the lines that follow are indented,
63 63 they are treated as continuations of that entry.
64 64
65 65 Leading whitespace is removed from values. Empty lines are skipped.
66 66
67 67 The optional values can contain format strings which refer to other
68 68 values in the same section, or values in a special DEFAULT section.
69 69
70 70 Lines beginning with "#" or ";" are ignored and may be used to provide
71 71 comments.
72 72
73 73 SECTIONS
74 74 --------
75 75
76 76 This section describes the different sections that may appear in a
77 77 Mercurial "hgrc" file, the purpose of each section, its possible
78 78 keys, and their possible values.
79 79
80 80 decode/encode::
81 81 Filters for transforming files on checkout/checkin. This would
82 82 typically be used for newline processing or other
83 83 localization/canonicalization of files.
84 84
85 85 Filters consist of a filter pattern followed by a filter command.
86 86 Filter patterns are globs by default, rooted at the repository
87 87 root. For example, to match any file ending in ".txt" in the root
88 88 directory only, use the pattern "*.txt". To match any file ending
89 89 in ".c" anywhere in the repository, use the pattern "**.c".
90 90
91 91 The filter command can start with a specifier, either "pipe:" or
92 92 "tempfile:". If no specifier is given, "pipe:" is used by default.
93 93
94 94 A "pipe:" command must accept data on stdin and return the
95 95 transformed data on stdout.
96 96
97 97 Pipe example:
98 98
99 99 [encode]
100 100 # uncompress gzip files on checkin to improve delta compression
101 101 # note: not necessarily a good idea, just an example
102 102 *.gz = pipe: gunzip
103 103
104 104 [decode]
105 105 # recompress gzip files when writing them to the working dir (we
106 106 # can safely omit "pipe:", because it's the default)
107 107 *.gz = gzip
108 108
109 109 A "tempfile:" command is a template. The string INFILE is replaced
110 110 with the name of a temporary file that contains the data to be
111 111 filtered by the command. The string OUTFILE is replaced with the
112 112 name of an empty temporary file, where the filtered data must be
113 113 written by the command.
114 114
115 115 NOTE: the tempfile mechanism is recommended for Windows systems,
116 116 where the standard shell I/O redirection operators often have
117 117 strange effects. In particular, if you are doing line ending
118 118 conversion on Windows using the popular dos2unix and unix2dos
119 119 programs, you *must* use the tempfile mechanism, as using pipes will
120 120 corrupt the contents of your files.
121 121
122 122 Tempfile example:
123 123
124 124 [encode]
125 125 # convert files to unix line ending conventions on checkin
126 126 **.txt = tempfile: dos2unix -n INFILE OUTFILE
127 127
128 128 [decode]
129 129 # convert files to windows line ending conventions when writing
130 130 # them to the working dir
131 131 **.txt = tempfile: unix2dos -n INFILE OUTFILE
132 132
133 133 hooks::
134 134 Commands that get automatically executed by various actions such as
135 135 starting or finishing a commit. Multiple commands can be run for
136 136 the same action by appending a suffix to the action. Overriding a
137 137 site-wide hook can be done by changing its value or setting it to
138 138 an empty string.
139 139
140 140 Example .hg/hgrc:
141 141
142 142 [hooks]
143 143 # do not use the site-wide hook
144 144 incoming =
145 145 incoming.email = /my/email/hook
146 146 incoming.autobuild = /my/build/hook
147 147
148 148 changegroup;;
149 149 Run after a changegroup has been added via push or pull. Passed
150 150 the ID of the first new changeset in $NODE.
151 151 commit;;
152 152 Run after a changeset has been created in the local repository.
153 153 Passed the ID of the newly created changeset in environment
154 154 variable $NODE.
155 155 incoming;;
156 156 Run after a changeset has been pulled, pushed, or unbundled into
157 157 the local repository. Passed the ID of the newly arrived
158 158 changeset in environment variable $NODE.
159 159 precommit;;
160 160 Run before starting a commit. Exit status 0 allows the commit to
161 161 proceed. Non-zero status will cause the commit to fail.
162 pretag;;
163 Run before creating a tag. Exit status 0 allows the tag to be
164 created. Non-zero status will cause the tag to fail. ID of
165 changeset to tag in $NODE. Name of tag in $TAG. Tag is local if
166 $LOCAL=1, in repo if $LOCAL=0.
167 tag;;
168 Run after a tag is created. ID of tagged changeset in $NODE.
169 Name of tag in $TAG. Tag is local if $LOCAL=1, in repo if
170 $LOCAL=0.
162 171
163 172 http_proxy::
164 173 Used to access web-based Mercurial repositories through a HTTP
165 174 proxy.
166 175 host;;
167 176 Host name and (optional) port of the proxy server, for example
168 177 "myproxy:8000".
169 178 no;;
170 179 Optional. Comma-separated list of host names that should bypass
171 180 the proxy.
172 181 passwd;;
173 182 Optional. Password to authenticate with at the proxy server.
174 183 user;;
175 184 Optional. User name to authenticate with at the proxy server.
176 185
177 186 paths::
178 187 Assigns symbolic names to repositories. The left side is the
179 188 symbolic name, and the right gives the directory or URL that is the
180 189 location of the repository.
181 190
182 191 ui::
183 192 User interface controls.
184 193 debug;;
185 194 Print debugging information. True or False. Default is False.
186 195 editor;;
187 196 The editor to use during a commit. Default is $EDITOR or "vi".
188 197 interactive;;
189 198 Allow to prompt the user. True or False. Default is True.
190 199 merge;;
191 200 The conflict resolution program to use during a manual merge.
192 201 Default is "hgmerge".
193 202 quiet;;
194 203 Reduce the amount of output printed. True or False. Default is False.
195 204 remotecmd;;
196 205 remote command to use for clone/push/pull operations. Default is 'hg'.
197 206 ssh;;
198 207 command to use for SSH connections. Default is 'ssh'.
199 208 username;;
200 209 The committer of a changeset created when running "commit".
201 210 Typically a person's name and email address, e.g. "Fred Widget
202 211 <fred@example.com>". Default is $EMAIL or username@hostname.
203 212 verbose;;
204 213 Increase the amount of output printed. True or False. Default is False.
205 214
206 215
207 216 web::
208 217 Web interface configuration.
209 218 accesslog;;
210 219 Where to output the access log. Default is stdout.
211 220 address;;
212 221 Interface address to bind to. Default is all.
213 222 allowbz2;;
214 223 Whether to allow .tar.bz2 downloading of repo revisions. Default is false.
215 224 allowgz;;
216 225 Whether to allow .tar.gz downloading of repo revisions. Default is false.
217 226 allowpull;;
218 227 Whether to allow pulling from the repository. Default is true.
219 228 allowzip;;
220 229 Whether to allow .zip downloading of repo revisions. Default is false.
221 230 This feature creates temporary files.
222 231 description;;
223 232 Textual description of the repository's purpose or contents.
224 233 Default is "unknown".
225 234 errorlog;;
226 235 Where to output the error log. Default is stderr.
227 236 ipv6;;
228 237 Whether to use IPv6. Default is false.
229 238 name;;
230 239 Repository name to use in the web interface. Default is current
231 240 working directory.
232 241 maxchanges;;
233 242 Maximum number of changes to list on the changelog. Default is 10.
234 243 maxfiles;;
235 244 Maximum number of files to list per changeset. Default is 10.
236 245 port;;
237 246 Port to listen on. Default is 8000.
238 247 style;;
239 248 Which template map style to use.
240 249 templates;;
241 250 Where to find the HTML templates. Default is install path.
242 251
243 252
244 253 AUTHOR
245 254 ------
246 255 Bryan O'Sullivan <bos@serpentine.com>.
247 256
248 257 Mercurial was written by Matt Mackall <mpm@selenic.com>.
249 258
250 259 SEE ALSO
251 260 --------
252 261 hg(1)
253 262
254 263 COPYING
255 264 -------
256 265 This manual page is copyright 2005 Bryan O'Sullivan.
257 266 Mercurial is copyright 2005 Matt Mackall.
258 267 Free use of this software is granted under the terms of the GNU General
259 268 Public License (GPL).
@@ -1,2794 +1,2799 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 14 demandload(globals(), "errno socket version struct atexit sets bz2")
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18 class AmbiguousCommand(Exception):
19 19 """Exception raised if command shortcut matches more than one command."""
20 20
21 21 def filterfiles(filters, files):
22 22 l = [x for x in files if x in filters]
23 23
24 24 for t in filters:
25 25 if t and t[-1] != "/":
26 26 t += "/"
27 27 l += [x for x in files if x.startswith(t)]
28 28 return l
29 29
30 30 def relpath(repo, args):
31 31 cwd = repo.getcwd()
32 32 if cwd:
33 33 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 34 return args
35 35
36 36 def matchpats(repo, pats=[], opts={}, head=''):
37 37 cwd = repo.getcwd()
38 38 if not pats and cwd:
39 39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 41 cwd = ''
42 42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 43 opts.get('exclude'), head)
44 44
45 45 def makewalk(repo, pats, opts, node=None, head=''):
46 46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 47 exact = dict(zip(files, files))
48 48 def walk():
49 49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 51 return files, matchfn, walk()
52 52
53 53 def walk(repo, pats, opts, node=None, head=''):
54 54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 55 for r in results:
56 56 yield r
57 57
58 58 def walkchangerevs(ui, repo, pats, opts):
59 59 '''Iterate over files and the revs they changed in.
60 60
61 61 Callers most commonly need to iterate backwards over the history
62 62 it is interested in. Doing so has awful (quadratic-looking)
63 63 performance, so we use iterators in a "windowed" way.
64 64
65 65 We walk a window of revisions in the desired order. Within the
66 66 window, we first walk forwards to gather data, then in the desired
67 67 order (usually backwards) to display it.
68 68
69 69 This function returns an (iterator, getchange, matchfn) tuple. The
70 70 getchange function returns the changelog entry for a numeric
71 71 revision. The iterator yields 3-tuples. They will be of one of
72 72 the following forms:
73 73
74 74 "window", incrementing, lastrev: stepping through a window,
75 75 positive if walking forwards through revs, last rev in the
76 76 sequence iterated over - use to reset state for the current window
77 77
78 78 "add", rev, fns: out-of-order traversal of the given file names
79 79 fns, which changed during revision rev - use to gather data for
80 80 possible display
81 81
82 82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 83 over with "add" - use to display data'''
84 84
85 85 files, matchfn, anypats = matchpats(repo, pats, opts)
86 86
87 87 if repo.changelog.count() == 0:
88 88 return [], False, matchfn
89 89
90 90 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
91 91 wanted = {}
92 92 slowpath = anypats
93 93 window = 300
94 94 fncache = {}
95 95
96 96 chcache = {}
97 97 def getchange(rev):
98 98 ch = chcache.get(rev)
99 99 if ch is None:
100 100 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
101 101 return ch
102 102
103 103 if not slowpath and not files:
104 104 # No files, no patterns. Display all revs.
105 105 wanted = dict(zip(revs, revs))
106 106 if not slowpath:
107 107 # Only files, no patterns. Check the history of each file.
108 108 def filerevgen(filelog):
109 109 for i in xrange(filelog.count() - 1, -1, -window):
110 110 revs = []
111 111 for j in xrange(max(0, i - window), i + 1):
112 112 revs.append(filelog.linkrev(filelog.node(j)))
113 113 revs.reverse()
114 114 for rev in revs:
115 115 yield rev
116 116
117 117 minrev, maxrev = min(revs), max(revs)
118 118 for file in files:
119 119 filelog = repo.file(file)
120 120 # A zero count may be a directory or deleted file, so
121 121 # try to find matching entries on the slow path.
122 122 if filelog.count() == 0:
123 123 slowpath = True
124 124 break
125 125 for rev in filerevgen(filelog):
126 126 if rev <= maxrev:
127 127 if rev < minrev:
128 128 break
129 129 fncache.setdefault(rev, [])
130 130 fncache[rev].append(file)
131 131 wanted[rev] = 1
132 132 if slowpath:
133 133 # The slow path checks files modified in every changeset.
134 134 def changerevgen():
135 135 for i in xrange(repo.changelog.count() - 1, -1, -window):
136 136 for j in xrange(max(0, i - window), i + 1):
137 137 yield j, getchange(j)[3]
138 138
139 139 for rev, changefiles in changerevgen():
140 140 matches = filter(matchfn, changefiles)
141 141 if matches:
142 142 fncache[rev] = matches
143 143 wanted[rev] = 1
144 144
145 145 def iterate():
146 146 for i in xrange(0, len(revs), window):
147 147 yield 'window', revs[0] < revs[-1], revs[-1]
148 148 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
149 149 if rev in wanted]
150 150 srevs = list(nrevs)
151 151 srevs.sort()
152 152 for rev in srevs:
153 153 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
154 154 yield 'add', rev, fns
155 155 for rev in nrevs:
156 156 yield 'iter', rev, None
157 157 return iterate(), getchange, matchfn
158 158
159 159 revrangesep = ':'
160 160
161 161 def revrange(ui, repo, revs, revlog=None):
162 162 """Yield revision as strings from a list of revision specifications."""
163 163 if revlog is None:
164 164 revlog = repo.changelog
165 165 revcount = revlog.count()
166 166 def fix(val, defval):
167 167 if not val:
168 168 return defval
169 169 try:
170 170 num = int(val)
171 171 if str(num) != val:
172 172 raise ValueError
173 173 if num < 0:
174 174 num += revcount
175 175 if num < 0:
176 176 num = 0
177 177 elif num >= revcount:
178 178 raise ValueError
179 179 except ValueError:
180 180 try:
181 181 num = repo.changelog.rev(repo.lookup(val))
182 182 except KeyError:
183 183 try:
184 184 num = revlog.rev(revlog.lookup(val))
185 185 except KeyError:
186 186 raise util.Abort(_('invalid revision identifier %s'), val)
187 187 return num
188 188 seen = {}
189 189 for spec in revs:
190 190 if spec.find(revrangesep) >= 0:
191 191 start, end = spec.split(revrangesep, 1)
192 192 start = fix(start, 0)
193 193 end = fix(end, revcount - 1)
194 194 step = start > end and -1 or 1
195 195 for rev in xrange(start, end+step, step):
196 196 if rev in seen:
197 197 continue
198 198 seen[rev] = 1
199 199 yield str(rev)
200 200 else:
201 201 rev = fix(spec, None)
202 202 if rev in seen:
203 203 continue
204 204 seen[rev] = 1
205 205 yield str(rev)
206 206
207 207 def make_filename(repo, r, pat, node=None,
208 208 total=None, seqno=None, revwidth=None, pathname=None):
209 209 node_expander = {
210 210 'H': lambda: hex(node),
211 211 'R': lambda: str(r.rev(node)),
212 212 'h': lambda: short(node),
213 213 }
214 214 expander = {
215 215 '%': lambda: '%',
216 216 'b': lambda: os.path.basename(repo.root),
217 217 }
218 218
219 219 try:
220 220 if node:
221 221 expander.update(node_expander)
222 222 if node and revwidth is not None:
223 223 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
224 224 if total is not None:
225 225 expander['N'] = lambda: str(total)
226 226 if seqno is not None:
227 227 expander['n'] = lambda: str(seqno)
228 228 if total is not None and seqno is not None:
229 229 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
230 230 if pathname is not None:
231 231 expander['s'] = lambda: os.path.basename(pathname)
232 232 expander['d'] = lambda: os.path.dirname(pathname) or '.'
233 233 expander['p'] = lambda: pathname
234 234
235 235 newname = []
236 236 patlen = len(pat)
237 237 i = 0
238 238 while i < patlen:
239 239 c = pat[i]
240 240 if c == '%':
241 241 i += 1
242 242 c = pat[i]
243 243 c = expander[c]()
244 244 newname.append(c)
245 245 i += 1
246 246 return ''.join(newname)
247 247 except KeyError, inst:
248 248 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
249 249 inst.args[0])
250 250
251 251 def make_file(repo, r, pat, node=None,
252 252 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
253 253 if not pat or pat == '-':
254 254 return 'w' in mode and sys.stdout or sys.stdin
255 255 if hasattr(pat, 'write') and 'w' in mode:
256 256 return pat
257 257 if hasattr(pat, 'read') and 'r' in mode:
258 258 return pat
259 259 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
260 260 pathname),
261 261 mode)
262 262
263 263 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
264 264 changes=None, text=False):
265 265 if not changes:
266 266 changes = repo.changes(node1, node2, files, match=match)
267 267 modified, added, removed, deleted, unknown = changes
268 268 if files:
269 269 modified, added, removed = map(lambda x: filterfiles(files, x),
270 270 (modified, added, removed))
271 271
272 272 if not modified and not added and not removed:
273 273 return
274 274
275 275 if node2:
276 276 change = repo.changelog.read(node2)
277 277 mmap2 = repo.manifest.read(change[0])
278 278 date2 = util.datestr(change[2])
279 279 def read(f):
280 280 return repo.file(f).read(mmap2[f])
281 281 else:
282 282 date2 = util.datestr()
283 283 if not node1:
284 284 node1 = repo.dirstate.parents()[0]
285 285 def read(f):
286 286 return repo.wread(f)
287 287
288 288 if ui.quiet:
289 289 r = None
290 290 else:
291 291 hexfunc = ui.verbose and hex or short
292 292 r = [hexfunc(node) for node in [node1, node2] if node]
293 293
294 294 change = repo.changelog.read(node1)
295 295 mmap = repo.manifest.read(change[0])
296 296 date1 = util.datestr(change[2])
297 297
298 298 diffopts = ui.diffopts()
299 299 showfunc = diffopts['showfunc']
300 300 ignorews = diffopts['ignorews']
301 301 for f in modified:
302 302 to = None
303 303 if f in mmap:
304 304 to = repo.file(f).read(mmap[f])
305 305 tn = read(f)
306 306 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
307 307 showfunc=showfunc, ignorews=ignorews))
308 308 for f in added:
309 309 to = None
310 310 tn = read(f)
311 311 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
312 312 showfunc=showfunc, ignorews=ignorews))
313 313 for f in removed:
314 314 to = repo.file(f).read(mmap[f])
315 315 tn = None
316 316 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
317 317 showfunc=showfunc, ignorews=ignorews))
318 318
319 319 def trimuser(ui, name, rev, revcache):
320 320 """trim the name of the user who committed a change"""
321 321 user = revcache.get(rev)
322 322 if user is None:
323 323 user = revcache[rev] = ui.shortuser(name)
324 324 return user
325 325
326 326 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
327 327 """show a single changeset or file revision"""
328 328 log = repo.changelog
329 329 if changenode is None:
330 330 changenode = log.node(rev)
331 331 elif not rev:
332 332 rev = log.rev(changenode)
333 333
334 334 if ui.quiet:
335 335 ui.write("%d:%s\n" % (rev, short(changenode)))
336 336 return
337 337
338 338 changes = log.read(changenode)
339 339 date = util.datestr(changes[2])
340 340
341 341 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
342 342 for p in log.parents(changenode)
343 343 if ui.debugflag or p != nullid]
344 344 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
345 345 parents = []
346 346
347 347 if ui.verbose:
348 348 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
349 349 else:
350 350 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
351 351
352 352 for tag in repo.nodetags(changenode):
353 353 ui.status(_("tag: %s\n") % tag)
354 354 for parent in parents:
355 355 ui.write(_("parent: %d:%s\n") % parent)
356 356
357 357 if brinfo and changenode in brinfo:
358 358 br = brinfo[changenode]
359 359 ui.write(_("branch: %s\n") % " ".join(br))
360 360
361 361 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
362 362 hex(changes[0])))
363 363 ui.status(_("user: %s\n") % changes[1])
364 364 ui.status(_("date: %s\n") % date)
365 365
366 366 if ui.debugflag:
367 367 files = repo.changes(log.parents(changenode)[0], changenode)
368 368 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
369 369 if value:
370 370 ui.note("%-12s %s\n" % (key, " ".join(value)))
371 371 else:
372 372 ui.note(_("files: %s\n") % " ".join(changes[3]))
373 373
374 374 description = changes[4].strip()
375 375 if description:
376 376 if ui.verbose:
377 377 ui.status(_("description:\n"))
378 378 ui.status(description)
379 379 ui.status("\n\n")
380 380 else:
381 381 ui.status(_("summary: %s\n") % description.splitlines()[0])
382 382 ui.status("\n")
383 383
384 384 def show_version(ui):
385 385 """output version and copyright information"""
386 386 ui.write(_("Mercurial Distributed SCM (version %s)\n")
387 387 % version.get_version())
388 388 ui.status(_(
389 389 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
390 390 "This is free software; see the source for copying conditions. "
391 391 "There is NO\nwarranty; "
392 392 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
393 393 ))
394 394
395 395 def help_(ui, cmd=None, with_version=False):
396 396 """show help for a given command or all commands"""
397 397 option_lists = []
398 398 if cmd and cmd != 'shortlist':
399 399 if with_version:
400 400 show_version(ui)
401 401 ui.write('\n')
402 402 aliases, i = find(cmd)
403 403 # synopsis
404 404 ui.write("%s\n\n" % i[2])
405 405
406 406 # description
407 407 doc = i[0].__doc__
408 408 if not doc:
409 409 doc = _("(No help text available)")
410 410 if ui.quiet:
411 411 doc = doc.splitlines(0)[0]
412 412 ui.write("%s\n" % doc.rstrip())
413 413
414 414 if not ui.quiet:
415 415 # aliases
416 416 if len(aliases) > 1:
417 417 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
418 418
419 419 # options
420 420 if i[1]:
421 421 option_lists.append(("options", i[1]))
422 422
423 423 else:
424 424 # program name
425 425 if ui.verbose or with_version:
426 426 show_version(ui)
427 427 else:
428 428 ui.status(_("Mercurial Distributed SCM\n"))
429 429 ui.status('\n')
430 430
431 431 # list of commands
432 432 if cmd == "shortlist":
433 433 ui.status(_('basic commands (use "hg help" '
434 434 'for the full list or option "-v" for details):\n\n'))
435 435 elif ui.verbose:
436 436 ui.status(_('list of commands:\n\n'))
437 437 else:
438 438 ui.status(_('list of commands (use "hg help -v" '
439 439 'to show aliases and global options):\n\n'))
440 440
441 441 h = {}
442 442 cmds = {}
443 443 for c, e in table.items():
444 444 f = c.split("|")[0]
445 445 if cmd == "shortlist" and not f.startswith("^"):
446 446 continue
447 447 f = f.lstrip("^")
448 448 if not ui.debugflag and f.startswith("debug"):
449 449 continue
450 450 d = ""
451 451 doc = e[0].__doc__
452 452 if not doc:
453 453 doc = _("(No help text available)")
454 454 h[f] = doc.splitlines(0)[0].rstrip()
455 455 cmds[f] = c.lstrip("^")
456 456
457 457 fns = h.keys()
458 458 fns.sort()
459 459 m = max(map(len, fns))
460 460 for f in fns:
461 461 if ui.verbose:
462 462 commands = cmds[f].replace("|",", ")
463 463 ui.write(" %s:\n %s\n"%(commands, h[f]))
464 464 else:
465 465 ui.write(' %-*s %s\n' % (m, f, h[f]))
466 466
467 467 # global options
468 468 if ui.verbose:
469 469 option_lists.append(("global options", globalopts))
470 470
471 471 # list all option lists
472 472 opt_output = []
473 473 for title, options in option_lists:
474 474 opt_output.append(("\n%s:\n" % title, None))
475 475 for shortopt, longopt, default, desc in options:
476 476 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
477 477 longopt and " --%s" % longopt),
478 478 "%s%s" % (desc,
479 479 default
480 480 and _(" (default: %s)") % default
481 481 or "")))
482 482
483 483 if opt_output:
484 484 opts_len = max([len(line[0]) for line in opt_output if line[1]])
485 485 for first, second in opt_output:
486 486 if second:
487 487 ui.write(" %-*s %s\n" % (opts_len, first, second))
488 488 else:
489 489 ui.write("%s\n" % first)
490 490
491 491 # Commands start here, listed alphabetically
492 492
493 493 def add(ui, repo, *pats, **opts):
494 494 """add the specified files on the next commit
495 495
496 496 Schedule files to be version controlled and added to the repository.
497 497
498 498 The files will be added to the repository at the next commit.
499 499
500 500 If no names are given, add all files in the repository.
501 501 """
502 502
503 503 names = []
504 504 for src, abs, rel, exact in walk(repo, pats, opts):
505 505 if exact:
506 506 if ui.verbose:
507 507 ui.status(_('adding %s\n') % rel)
508 508 names.append(abs)
509 509 elif repo.dirstate.state(abs) == '?':
510 510 ui.status(_('adding %s\n') % rel)
511 511 names.append(abs)
512 512 repo.add(names)
513 513
514 514 def addremove(ui, repo, *pats, **opts):
515 515 """add all new files, delete all missing files
516 516
517 517 Add all new files and remove all missing files from the repository.
518 518
519 519 New files are ignored if they match any of the patterns in .hgignore. As
520 520 with add, these changes take effect at the next commit.
521 521 """
522 522 return addremove_lock(ui, repo, pats, opts)
523 523
524 524 def addremove_lock(ui, repo, pats, opts, wlock=None):
525 525 add, remove = [], []
526 526 for src, abs, rel, exact in walk(repo, pats, opts):
527 527 if src == 'f' and repo.dirstate.state(abs) == '?':
528 528 add.append(abs)
529 529 if ui.verbose or not exact:
530 530 ui.status(_('adding %s\n') % ((pats and rel) or abs))
531 531 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
532 532 remove.append(abs)
533 533 if ui.verbose or not exact:
534 534 ui.status(_('removing %s\n') % ((pats and rel) or abs))
535 535 repo.add(add, wlock=wlock)
536 536 repo.remove(remove, wlock=wlock)
537 537
538 538 def annotate(ui, repo, *pats, **opts):
539 539 """show changeset information per file line
540 540
541 541 List changes in files, showing the revision id responsible for each line
542 542
543 543 This command is useful to discover who did a change or when a change took
544 544 place.
545 545
546 546 Without the -a option, annotate will avoid processing files it
547 547 detects as binary. With -a, annotate will generate an annotation
548 548 anyway, probably with undesirable results.
549 549 """
550 550 def getnode(rev):
551 551 return short(repo.changelog.node(rev))
552 552
553 553 ucache = {}
554 554 def getname(rev):
555 555 cl = repo.changelog.read(repo.changelog.node(rev))
556 556 return trimuser(ui, cl[1], rev, ucache)
557 557
558 558 dcache = {}
559 559 def getdate(rev):
560 560 datestr = dcache.get(rev)
561 561 if datestr is None:
562 562 cl = repo.changelog.read(repo.changelog.node(rev))
563 563 datestr = dcache[rev] = util.datestr(cl[2])
564 564 return datestr
565 565
566 566 if not pats:
567 567 raise util.Abort(_('at least one file name or pattern required'))
568 568
569 569 opmap = [['user', getname], ['number', str], ['changeset', getnode],
570 570 ['date', getdate]]
571 571 if not opts['user'] and not opts['changeset'] and not opts['date']:
572 572 opts['number'] = 1
573 573
574 574 if opts['rev']:
575 575 node = repo.changelog.lookup(opts['rev'])
576 576 else:
577 577 node = repo.dirstate.parents()[0]
578 578 change = repo.changelog.read(node)
579 579 mmap = repo.manifest.read(change[0])
580 580
581 581 for src, abs, rel, exact in walk(repo, pats, opts):
582 582 if abs not in mmap:
583 583 ui.warn(_("warning: %s is not in the repository!\n") %
584 584 ((pats and rel) or abs))
585 585 continue
586 586
587 587 f = repo.file(abs)
588 588 if not opts['text'] and util.binary(f.read(mmap[abs])):
589 589 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
590 590 continue
591 591
592 592 lines = f.annotate(mmap[abs])
593 593 pieces = []
594 594
595 595 for o, f in opmap:
596 596 if opts[o]:
597 597 l = [f(n) for n, dummy in lines]
598 598 if l:
599 599 m = max(map(len, l))
600 600 pieces.append(["%*s" % (m, x) for x in l])
601 601
602 602 if pieces:
603 603 for p, l in zip(zip(*pieces), lines):
604 604 ui.write("%s: %s" % (" ".join(p), l[1]))
605 605
606 606 def bundle(ui, repo, fname, dest="default-push", **opts):
607 607 """create a changegroup file
608 608
609 609 Generate a compressed changegroup file collecting all changesets
610 610 not found in the other repository.
611 611
612 612 This file can then be transferred using conventional means and
613 613 applied to another repository with the unbundle command. This is
614 614 useful when native push and pull are not available or when
615 615 exporting an entire repository is undesirable. The standard file
616 616 extension is ".hg".
617 617
618 618 Unlike import/export, this exactly preserves all changeset
619 619 contents including permissions, rename data, and revision history.
620 620 """
621 621 f = open(fname, "wb")
622 622 dest = ui.expandpath(dest, repo.root)
623 623 other = hg.repository(ui, dest)
624 624 o = repo.findoutgoing(other)
625 625 cg = repo.changegroup(o)
626 626
627 627 try:
628 628 f.write("HG10")
629 629 z = bz2.BZ2Compressor(9)
630 630 while 1:
631 631 chunk = cg.read(4096)
632 632 if not chunk:
633 633 break
634 634 f.write(z.compress(chunk))
635 635 f.write(z.flush())
636 636 except:
637 637 os.unlink(fname)
638 638 raise
639 639
640 640 def cat(ui, repo, file1, *pats, **opts):
641 641 """output the latest or given revisions of files
642 642
643 643 Print the specified files as they were at the given revision.
644 644 If no revision is given then the tip is used.
645 645
646 646 Output may be to a file, in which case the name of the file is
647 647 given using a format string. The formatting rules are the same as
648 648 for the export command, with the following additions:
649 649
650 650 %s basename of file being printed
651 651 %d dirname of file being printed, or '.' if in repo root
652 652 %p root-relative path name of file being printed
653 653 """
654 654 mf = {}
655 655 rev = opts['rev']
656 656 if rev:
657 657 node = repo.lookup(rev)
658 658 else:
659 659 node = repo.changelog.tip()
660 660 change = repo.changelog.read(node)
661 661 mf = repo.manifest.read(change[0])
662 662 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
663 663 r = repo.file(abs)
664 664 n = mf[abs]
665 665 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
666 666 fp.write(r.read(n))
667 667
668 668 def clone(ui, source, dest=None, **opts):
669 669 """make a copy of an existing repository
670 670
671 671 Create a copy of an existing repository in a new directory.
672 672
673 673 If no destination directory name is specified, it defaults to the
674 674 basename of the source.
675 675
676 676 The location of the source is added to the new repository's
677 677 .hg/hgrc file, as the default to be used for future pulls.
678 678
679 679 For efficiency, hardlinks are used for cloning whenever the source
680 680 and destination are on the same filesystem. Some filesystems,
681 681 such as AFS, implement hardlinking incorrectly, but do not report
682 682 errors. In these cases, use the --pull option to avoid
683 683 hardlinking.
684 684 """
685 685 if dest is None:
686 686 dest = os.path.basename(os.path.normpath(source))
687 687
688 688 if os.path.exists(dest):
689 689 raise util.Abort(_("destination '%s' already exists"), dest)
690 690
691 691 dest = os.path.realpath(dest)
692 692
693 693 class Dircleanup(object):
694 694 def __init__(self, dir_):
695 695 self.rmtree = shutil.rmtree
696 696 self.dir_ = dir_
697 697 os.mkdir(dir_)
698 698 def close(self):
699 699 self.dir_ = None
700 700 def __del__(self):
701 701 if self.dir_:
702 702 self.rmtree(self.dir_, True)
703 703
704 704 if opts['ssh']:
705 705 ui.setconfig("ui", "ssh", opts['ssh'])
706 706 if opts['remotecmd']:
707 707 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
708 708
709 709 if not os.path.exists(source):
710 710 source = ui.expandpath(source)
711 711
712 712 d = Dircleanup(dest)
713 713 abspath = source
714 714 other = hg.repository(ui, source)
715 715
716 716 copy = False
717 717 if other.dev() != -1:
718 718 abspath = os.path.abspath(source)
719 719 if not opts['pull'] and not opts['rev']:
720 720 copy = True
721 721
722 722 if copy:
723 723 try:
724 724 # we use a lock here because if we race with commit, we
725 725 # can end up with extra data in the cloned revlogs that's
726 726 # not pointed to by changesets, thus causing verify to
727 727 # fail
728 728 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
729 729 except OSError:
730 730 copy = False
731 731
732 732 if copy:
733 733 # we lock here to avoid premature writing to the target
734 734 os.mkdir(os.path.join(dest, ".hg"))
735 735 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
736 736
737 737 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
738 738 for f in files.split():
739 739 src = os.path.join(source, ".hg", f)
740 740 dst = os.path.join(dest, ".hg", f)
741 741 try:
742 742 util.copyfiles(src, dst)
743 743 except OSError, inst:
744 744 if inst.errno != errno.ENOENT:
745 745 raise
746 746
747 747 repo = hg.repository(ui, dest)
748 748
749 749 else:
750 750 revs = None
751 751 if opts['rev']:
752 752 if not other.local():
753 753 error = _("clone -r not supported yet for remote repositories.")
754 754 raise util.Abort(error)
755 755 else:
756 756 revs = [other.lookup(rev) for rev in opts['rev']]
757 757 repo = hg.repository(ui, dest, create=1)
758 758 repo.pull(other, heads = revs)
759 759
760 760 f = repo.opener("hgrc", "w", text=True)
761 761 f.write("[paths]\n")
762 762 f.write("default = %s\n" % abspath)
763 763 f.close()
764 764
765 765 if not opts['noupdate']:
766 766 update(ui, repo)
767 767
768 768 d.close()
769 769
770 770 def commit(ui, repo, *pats, **opts):
771 771 """commit the specified files or all outstanding changes
772 772
773 773 Commit changes to the given files into the repository.
774 774
775 775 If a list of files is omitted, all changes reported by "hg status"
776 776 will be commited.
777 777
778 778 The HGEDITOR or EDITOR environment variables are used to start an
779 779 editor to add a commit comment.
780 780 """
781 781 message = opts['message']
782 782 logfile = opts['logfile']
783 783
784 784 if message and logfile:
785 785 raise util.Abort(_('options --message and --logfile are mutually '
786 786 'exclusive'))
787 787 if not message and logfile:
788 788 try:
789 789 if logfile == '-':
790 790 message = sys.stdin.read()
791 791 else:
792 792 message = open(logfile).read()
793 793 except IOError, inst:
794 794 raise util.Abort(_("can't read commit message '%s': %s") %
795 795 (logfile, inst.strerror))
796 796
797 797 if opts['addremove']:
798 798 addremove(ui, repo, *pats, **opts)
799 799 fns, match, anypats = matchpats(repo, pats, opts)
800 800 if pats:
801 801 modified, added, removed, deleted, unknown = (
802 802 repo.changes(files=fns, match=match))
803 803 files = modified + added + removed
804 804 else:
805 805 files = []
806 806 try:
807 807 repo.commit(files, message, opts['user'], opts['date'], match)
808 808 except ValueError, inst:
809 809 raise util.Abort(str(inst))
810 810
811 811 def docopy(ui, repo, pats, opts):
812 812 cwd = repo.getcwd()
813 813 errors = 0
814 814 copied = []
815 815 targets = {}
816 816
817 817 def okaytocopy(abs, rel, exact):
818 818 reasons = {'?': _('is not managed'),
819 819 'a': _('has been marked for add'),
820 820 'r': _('has been marked for remove')}
821 821 reason = reasons.get(repo.dirstate.state(abs))
822 822 if reason:
823 823 if exact:
824 824 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
825 825 else:
826 826 return True
827 827
828 828 def copy(abssrc, relsrc, target, exact):
829 829 abstarget = util.canonpath(repo.root, cwd, target)
830 830 reltarget = util.pathto(cwd, abstarget)
831 831 prevsrc = targets.get(abstarget)
832 832 if prevsrc is not None:
833 833 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
834 834 (reltarget, abssrc, prevsrc))
835 835 return
836 836 if (not opts['after'] and os.path.exists(reltarget) or
837 837 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
838 838 if not opts['force']:
839 839 ui.warn(_('%s: not overwriting - file exists\n') %
840 840 reltarget)
841 841 return
842 842 if not opts['after']:
843 843 os.unlink(reltarget)
844 844 if opts['after']:
845 845 if not os.path.exists(reltarget):
846 846 return
847 847 else:
848 848 targetdir = os.path.dirname(reltarget) or '.'
849 849 if not os.path.isdir(targetdir):
850 850 os.makedirs(targetdir)
851 851 try:
852 852 shutil.copyfile(relsrc, reltarget)
853 853 shutil.copymode(relsrc, reltarget)
854 854 except shutil.Error, inst:
855 855 raise util.Abort(str(inst))
856 856 except IOError, inst:
857 857 if inst.errno == errno.ENOENT:
858 858 ui.warn(_('%s: deleted in working copy\n') % relsrc)
859 859 else:
860 860 ui.warn(_('%s: cannot copy - %s\n') %
861 861 (relsrc, inst.strerror))
862 862 errors += 1
863 863 return
864 864 if ui.verbose or not exact:
865 865 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
866 866 targets[abstarget] = abssrc
867 867 repo.copy(abssrc, abstarget)
868 868 copied.append((abssrc, relsrc, exact))
869 869
870 870 def targetpathfn(pat, dest, srcs):
871 871 if os.path.isdir(pat):
872 872 abspfx = util.canonpath(repo.root, cwd, pat)
873 873 if destdirexists:
874 874 striplen = len(os.path.split(abspfx)[0])
875 875 else:
876 876 striplen = len(abspfx)
877 877 if striplen:
878 878 striplen += len(os.sep)
879 879 res = lambda p: os.path.join(dest, p[striplen:])
880 880 elif destdirexists:
881 881 res = lambda p: os.path.join(dest, os.path.basename(p))
882 882 else:
883 883 res = lambda p: dest
884 884 return res
885 885
886 886 def targetpathafterfn(pat, dest, srcs):
887 887 if util.patkind(pat, None)[0]:
888 888 # a mercurial pattern
889 889 res = lambda p: os.path.join(dest, os.path.basename(p))
890 890 else:
891 891 abspfx = util.canonpath(repo.root, cwd, pat)
892 892 if len(abspfx) < len(srcs[0][0]):
893 893 # A directory. Either the target path contains the last
894 894 # component of the source path or it does not.
895 895 def evalpath(striplen):
896 896 score = 0
897 897 for s in srcs:
898 898 t = os.path.join(dest, s[0][striplen:])
899 899 if os.path.exists(t):
900 900 score += 1
901 901 return score
902 902
903 903 striplen = len(abspfx)
904 904 if striplen:
905 905 striplen += len(os.sep)
906 906 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
907 907 score = evalpath(striplen)
908 908 striplen1 = len(os.path.split(abspfx)[0])
909 909 if striplen1:
910 910 striplen1 += len(os.sep)
911 911 if evalpath(striplen1) > score:
912 912 striplen = striplen1
913 913 res = lambda p: os.path.join(dest, p[striplen:])
914 914 else:
915 915 # a file
916 916 if destdirexists:
917 917 res = lambda p: os.path.join(dest, os.path.basename(p))
918 918 else:
919 919 res = lambda p: dest
920 920 return res
921 921
922 922
923 923 pats = list(pats)
924 924 if not pats:
925 925 raise util.Abort(_('no source or destination specified'))
926 926 if len(pats) == 1:
927 927 raise util.Abort(_('no destination specified'))
928 928 dest = pats.pop()
929 929 destdirexists = os.path.isdir(dest)
930 930 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
931 931 raise util.Abort(_('with multiple sources, destination must be an '
932 932 'existing directory'))
933 933 if opts['after']:
934 934 tfn = targetpathafterfn
935 935 else:
936 936 tfn = targetpathfn
937 937 copylist = []
938 938 for pat in pats:
939 939 srcs = []
940 940 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
941 941 if okaytocopy(abssrc, relsrc, exact):
942 942 srcs.append((abssrc, relsrc, exact))
943 943 if not srcs:
944 944 continue
945 945 copylist.append((tfn(pat, dest, srcs), srcs))
946 946 if not copylist:
947 947 raise util.Abort(_('no files to copy'))
948 948
949 949 for targetpath, srcs in copylist:
950 950 for abssrc, relsrc, exact in srcs:
951 951 copy(abssrc, relsrc, targetpath(abssrc), exact)
952 952
953 953 if errors:
954 954 ui.warn(_('(consider using --after)\n'))
955 955 return errors, copied
956 956
957 957 def copy(ui, repo, *pats, **opts):
958 958 """mark files as copied for the next commit
959 959
960 960 Mark dest as having copies of source files. If dest is a
961 961 directory, copies are put in that directory. If dest is a file,
962 962 there can only be one source.
963 963
964 964 By default, this command copies the contents of files as they
965 965 stand in the working directory. If invoked with --after, the
966 966 operation is recorded, but no copying is performed.
967 967
968 968 This command takes effect in the next commit.
969 969
970 970 NOTE: This command should be treated as experimental. While it
971 971 should properly record copied files, this information is not yet
972 972 fully used by merge, nor fully reported by log.
973 973 """
974 974 errs, copied = docopy(ui, repo, pats, opts)
975 975 return errs
976 976
977 977 def debugancestor(ui, index, rev1, rev2):
978 978 """find the ancestor revision of two revisions in a given index"""
979 979 r = revlog.revlog(util.opener(os.getcwd()), index, "")
980 980 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
981 981 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
982 982
983 983 def debugcheckstate(ui, repo):
984 984 """validate the correctness of the current dirstate"""
985 985 parent1, parent2 = repo.dirstate.parents()
986 986 repo.dirstate.read()
987 987 dc = repo.dirstate.map
988 988 keys = dc.keys()
989 989 keys.sort()
990 990 m1n = repo.changelog.read(parent1)[0]
991 991 m2n = repo.changelog.read(parent2)[0]
992 992 m1 = repo.manifest.read(m1n)
993 993 m2 = repo.manifest.read(m2n)
994 994 errors = 0
995 995 for f in dc:
996 996 state = repo.dirstate.state(f)
997 997 if state in "nr" and f not in m1:
998 998 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
999 999 errors += 1
1000 1000 if state in "a" and f in m1:
1001 1001 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1002 1002 errors += 1
1003 1003 if state in "m" and f not in m1 and f not in m2:
1004 1004 ui.warn(_("%s in state %s, but not in either manifest\n") %
1005 1005 (f, state))
1006 1006 errors += 1
1007 1007 for f in m1:
1008 1008 state = repo.dirstate.state(f)
1009 1009 if state not in "nrm":
1010 1010 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1011 1011 errors += 1
1012 1012 if errors:
1013 1013 error = _(".hg/dirstate inconsistent with current parent's manifest")
1014 1014 raise util.Abort(error)
1015 1015
1016 1016 def debugconfig(ui):
1017 1017 """show combined config settings from all hgrc files"""
1018 1018 try:
1019 1019 repo = hg.repository(ui)
1020 1020 except hg.RepoError:
1021 1021 pass
1022 1022 for section, name, value in ui.walkconfig():
1023 1023 ui.write('%s.%s=%s\n' % (section, name, value))
1024 1024
1025 1025 def debugsetparents(ui, repo, rev1, rev2=None):
1026 1026 """manually set the parents of the current working directory
1027 1027
1028 1028 This is useful for writing repository conversion tools, but should
1029 1029 be used with care.
1030 1030 """
1031 1031
1032 1032 if not rev2:
1033 1033 rev2 = hex(nullid)
1034 1034
1035 1035 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1036 1036
1037 1037 def debugstate(ui, repo):
1038 1038 """show the contents of the current dirstate"""
1039 1039 repo.dirstate.read()
1040 1040 dc = repo.dirstate.map
1041 1041 keys = dc.keys()
1042 1042 keys.sort()
1043 1043 for file_ in keys:
1044 1044 ui.write("%c %3o %10d %s %s\n"
1045 1045 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1046 1046 time.strftime("%x %X",
1047 1047 time.localtime(dc[file_][3])), file_))
1048 1048 for f in repo.dirstate.copies:
1049 1049 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1050 1050
1051 1051 def debugdata(ui, file_, rev):
1052 1052 """dump the contents of an data file revision"""
1053 1053 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1054 1054 try:
1055 1055 ui.write(r.revision(r.lookup(rev)))
1056 1056 except KeyError:
1057 1057 raise util.Abort(_('invalid revision identifier %s'), rev)
1058 1058
1059 1059 def debugindex(ui, file_):
1060 1060 """dump the contents of an index file"""
1061 1061 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1062 1062 ui.write(" rev offset length base linkrev" +
1063 1063 " nodeid p1 p2\n")
1064 1064 for i in range(r.count()):
1065 1065 e = r.index[i]
1066 1066 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1067 1067 i, e[0], e[1], e[2], e[3],
1068 1068 short(e[6]), short(e[4]), short(e[5])))
1069 1069
1070 1070 def debugindexdot(ui, file_):
1071 1071 """dump an index DAG as a .dot file"""
1072 1072 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1073 1073 ui.write("digraph G {\n")
1074 1074 for i in range(r.count()):
1075 1075 e = r.index[i]
1076 1076 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1077 1077 if e[5] != nullid:
1078 1078 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1079 1079 ui.write("}\n")
1080 1080
1081 1081 def debugrename(ui, repo, file, rev=None):
1082 1082 """dump rename information"""
1083 1083 r = repo.file(relpath(repo, [file])[0])
1084 1084 if rev:
1085 1085 try:
1086 1086 # assume all revision numbers are for changesets
1087 1087 n = repo.lookup(rev)
1088 1088 change = repo.changelog.read(n)
1089 1089 m = repo.manifest.read(change[0])
1090 1090 n = m[relpath(repo, [file])[0]]
1091 1091 except (hg.RepoError, KeyError):
1092 1092 n = r.lookup(rev)
1093 1093 else:
1094 1094 n = r.tip()
1095 1095 m = r.renamed(n)
1096 1096 if m:
1097 1097 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1098 1098 else:
1099 1099 ui.write(_("not renamed\n"))
1100 1100
1101 1101 def debugwalk(ui, repo, *pats, **opts):
1102 1102 """show how files match on given patterns"""
1103 1103 items = list(walk(repo, pats, opts))
1104 1104 if not items:
1105 1105 return
1106 1106 fmt = '%%s %%-%ds %%-%ds %%s' % (
1107 1107 max([len(abs) for (src, abs, rel, exact) in items]),
1108 1108 max([len(rel) for (src, abs, rel, exact) in items]))
1109 1109 for src, abs, rel, exact in items:
1110 1110 line = fmt % (src, abs, rel, exact and 'exact' or '')
1111 1111 ui.write("%s\n" % line.rstrip())
1112 1112
1113 1113 def diff(ui, repo, *pats, **opts):
1114 1114 """diff repository (or selected files)
1115 1115
1116 1116 Show differences between revisions for the specified files.
1117 1117
1118 1118 Differences between files are shown using the unified diff format.
1119 1119
1120 1120 When two revision arguments are given, then changes are shown
1121 1121 between those revisions. If only one revision is specified then
1122 1122 that revision is compared to the working directory, and, when no
1123 1123 revisions are specified, the working directory files are compared
1124 1124 to its parent.
1125 1125
1126 1126 Without the -a option, diff will avoid generating diffs of files
1127 1127 it detects as binary. With -a, diff will generate a diff anyway,
1128 1128 probably with undesirable results.
1129 1129 """
1130 1130 node1, node2 = None, None
1131 1131 revs = [repo.lookup(x) for x in opts['rev']]
1132 1132
1133 1133 if len(revs) > 0:
1134 1134 node1 = revs[0]
1135 1135 if len(revs) > 1:
1136 1136 node2 = revs[1]
1137 1137 if len(revs) > 2:
1138 1138 raise util.Abort(_("too many revisions to diff"))
1139 1139
1140 1140 fns, matchfn, anypats = matchpats(repo, pats, opts)
1141 1141
1142 1142 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1143 1143 text=opts['text'])
1144 1144
1145 1145 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1146 1146 node = repo.lookup(changeset)
1147 1147 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1148 1148 if opts['switch_parent']:
1149 1149 parents.reverse()
1150 1150 prev = (parents and parents[0]) or nullid
1151 1151 change = repo.changelog.read(node)
1152 1152
1153 1153 fp = make_file(repo, repo.changelog, opts['output'],
1154 1154 node=node, total=total, seqno=seqno,
1155 1155 revwidth=revwidth)
1156 1156 if fp != sys.stdout:
1157 1157 ui.note("%s\n" % fp.name)
1158 1158
1159 1159 fp.write("# HG changeset patch\n")
1160 1160 fp.write("# User %s\n" % change[1])
1161 1161 fp.write("# Node ID %s\n" % hex(node))
1162 1162 fp.write("# Parent %s\n" % hex(prev))
1163 1163 if len(parents) > 1:
1164 1164 fp.write("# Parent %s\n" % hex(parents[1]))
1165 1165 fp.write(change[4].rstrip())
1166 1166 fp.write("\n\n")
1167 1167
1168 1168 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1169 1169 if fp != sys.stdout:
1170 1170 fp.close()
1171 1171
1172 1172 def export(ui, repo, *changesets, **opts):
1173 1173 """dump the header and diffs for one or more changesets
1174 1174
1175 1175 Print the changeset header and diffs for one or more revisions.
1176 1176
1177 1177 The information shown in the changeset header is: author,
1178 1178 changeset hash, parent and commit comment.
1179 1179
1180 1180 Output may be to a file, in which case the name of the file is
1181 1181 given using a format string. The formatting rules are as follows:
1182 1182
1183 1183 %% literal "%" character
1184 1184 %H changeset hash (40 bytes of hexadecimal)
1185 1185 %N number of patches being generated
1186 1186 %R changeset revision number
1187 1187 %b basename of the exporting repository
1188 1188 %h short-form changeset hash (12 bytes of hexadecimal)
1189 1189 %n zero-padded sequence number, starting at 1
1190 1190 %r zero-padded changeset revision number
1191 1191
1192 1192 Without the -a option, export will avoid generating diffs of files
1193 1193 it detects as binary. With -a, export will generate a diff anyway,
1194 1194 probably with undesirable results.
1195 1195
1196 1196 With the --switch-parent option, the diff will be against the second
1197 1197 parent. It can be useful to review a merge.
1198 1198 """
1199 1199 if not changesets:
1200 1200 raise util.Abort(_("export requires at least one changeset"))
1201 1201 seqno = 0
1202 1202 revs = list(revrange(ui, repo, changesets))
1203 1203 total = len(revs)
1204 1204 revwidth = max(map(len, revs))
1205 1205 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1206 1206 ui.note(msg)
1207 1207 for cset in revs:
1208 1208 seqno += 1
1209 1209 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1210 1210
1211 1211 def forget(ui, repo, *pats, **opts):
1212 1212 """don't add the specified files on the next commit
1213 1213
1214 1214 Undo an 'hg add' scheduled for the next commit.
1215 1215 """
1216 1216 forget = []
1217 1217 for src, abs, rel, exact in walk(repo, pats, opts):
1218 1218 if repo.dirstate.state(abs) == 'a':
1219 1219 forget.append(abs)
1220 1220 if ui.verbose or not exact:
1221 1221 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1222 1222 repo.forget(forget)
1223 1223
1224 1224 def grep(ui, repo, pattern, *pats, **opts):
1225 1225 """search for a pattern in specified files and revisions
1226 1226
1227 1227 Search revisions of files for a regular expression.
1228 1228
1229 1229 This command behaves differently than Unix grep. It only accepts
1230 1230 Python/Perl regexps. It searches repository history, not the
1231 1231 working directory. It always prints the revision number in which
1232 1232 a match appears.
1233 1233
1234 1234 By default, grep only prints output for the first revision of a
1235 1235 file in which it finds a match. To get it to print every revision
1236 1236 that contains a change in match status ("-" for a match that
1237 1237 becomes a non-match, or "+" for a non-match that becomes a match),
1238 1238 use the --all flag.
1239 1239 """
1240 1240 reflags = 0
1241 1241 if opts['ignore_case']:
1242 1242 reflags |= re.I
1243 1243 regexp = re.compile(pattern, reflags)
1244 1244 sep, eol = ':', '\n'
1245 1245 if opts['print0']:
1246 1246 sep = eol = '\0'
1247 1247
1248 1248 fcache = {}
1249 1249 def getfile(fn):
1250 1250 if fn not in fcache:
1251 1251 fcache[fn] = repo.file(fn)
1252 1252 return fcache[fn]
1253 1253
1254 1254 def matchlines(body):
1255 1255 begin = 0
1256 1256 linenum = 0
1257 1257 while True:
1258 1258 match = regexp.search(body, begin)
1259 1259 if not match:
1260 1260 break
1261 1261 mstart, mend = match.span()
1262 1262 linenum += body.count('\n', begin, mstart) + 1
1263 1263 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1264 1264 lend = body.find('\n', mend)
1265 1265 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1266 1266 begin = lend + 1
1267 1267
1268 1268 class linestate(object):
1269 1269 def __init__(self, line, linenum, colstart, colend):
1270 1270 self.line = line
1271 1271 self.linenum = linenum
1272 1272 self.colstart = colstart
1273 1273 self.colend = colend
1274 1274 def __eq__(self, other):
1275 1275 return self.line == other.line
1276 1276 def __hash__(self):
1277 1277 return hash(self.line)
1278 1278
1279 1279 matches = {}
1280 1280 def grepbody(fn, rev, body):
1281 1281 matches[rev].setdefault(fn, {})
1282 1282 m = matches[rev][fn]
1283 1283 for lnum, cstart, cend, line in matchlines(body):
1284 1284 s = linestate(line, lnum, cstart, cend)
1285 1285 m[s] = s
1286 1286
1287 1287 prev = {}
1288 1288 ucache = {}
1289 1289 def display(fn, rev, states, prevstates):
1290 1290 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1291 1291 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1292 1292 counts = {'-': 0, '+': 0}
1293 1293 filerevmatches = {}
1294 1294 for l in diff:
1295 1295 if incrementing or not opts['all']:
1296 1296 change = ((l in prevstates) and '-') or '+'
1297 1297 r = rev
1298 1298 else:
1299 1299 change = ((l in states) and '-') or '+'
1300 1300 r = prev[fn]
1301 1301 cols = [fn, str(rev)]
1302 1302 if opts['line_number']:
1303 1303 cols.append(str(l.linenum))
1304 1304 if opts['all']:
1305 1305 cols.append(change)
1306 1306 if opts['user']:
1307 1307 cols.append(trimuser(ui, getchange(rev)[1], rev,
1308 1308 ucache))
1309 1309 if opts['files_with_matches']:
1310 1310 c = (fn, rev)
1311 1311 if c in filerevmatches:
1312 1312 continue
1313 1313 filerevmatches[c] = 1
1314 1314 else:
1315 1315 cols.append(l.line)
1316 1316 ui.write(sep.join(cols), eol)
1317 1317 counts[change] += 1
1318 1318 return counts['+'], counts['-']
1319 1319
1320 1320 fstate = {}
1321 1321 skip = {}
1322 1322 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1323 1323 count = 0
1324 1324 incrementing = False
1325 1325 for st, rev, fns in changeiter:
1326 1326 if st == 'window':
1327 1327 incrementing = rev
1328 1328 matches.clear()
1329 1329 elif st == 'add':
1330 1330 change = repo.changelog.read(repo.lookup(str(rev)))
1331 1331 mf = repo.manifest.read(change[0])
1332 1332 matches[rev] = {}
1333 1333 for fn in fns:
1334 1334 if fn in skip:
1335 1335 continue
1336 1336 fstate.setdefault(fn, {})
1337 1337 try:
1338 1338 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1339 1339 except KeyError:
1340 1340 pass
1341 1341 elif st == 'iter':
1342 1342 states = matches[rev].items()
1343 1343 states.sort()
1344 1344 for fn, m in states:
1345 1345 if fn in skip:
1346 1346 continue
1347 1347 if incrementing or not opts['all'] or fstate[fn]:
1348 1348 pos, neg = display(fn, rev, m, fstate[fn])
1349 1349 count += pos + neg
1350 1350 if pos and not opts['all']:
1351 1351 skip[fn] = True
1352 1352 fstate[fn] = m
1353 1353 prev[fn] = rev
1354 1354
1355 1355 if not incrementing:
1356 1356 fstate = fstate.items()
1357 1357 fstate.sort()
1358 1358 for fn, state in fstate:
1359 1359 if fn in skip:
1360 1360 continue
1361 1361 display(fn, rev, {}, state)
1362 1362 return (count == 0 and 1) or 0
1363 1363
1364 1364 def heads(ui, repo, **opts):
1365 1365 """show current repository heads
1366 1366
1367 1367 Show all repository head changesets.
1368 1368
1369 1369 Repository "heads" are changesets that don't have children
1370 1370 changesets. They are where development generally takes place and
1371 1371 are the usual targets for update and merge operations.
1372 1372 """
1373 1373 if opts['rev']:
1374 1374 heads = repo.heads(repo.lookup(opts['rev']))
1375 1375 else:
1376 1376 heads = repo.heads()
1377 1377 br = None
1378 1378 if opts['branches']:
1379 1379 br = repo.branchlookup(heads)
1380 1380 for n in heads:
1381 1381 show_changeset(ui, repo, changenode=n, brinfo=br)
1382 1382
1383 1383 def identify(ui, repo):
1384 1384 """print information about the working copy
1385 1385
1386 1386 Print a short summary of the current state of the repo.
1387 1387
1388 1388 This summary identifies the repository state using one or two parent
1389 1389 hash identifiers, followed by a "+" if there are uncommitted changes
1390 1390 in the working directory, followed by a list of tags for this revision.
1391 1391 """
1392 1392 parents = [p for p in repo.dirstate.parents() if p != nullid]
1393 1393 if not parents:
1394 1394 ui.write(_("unknown\n"))
1395 1395 return
1396 1396
1397 1397 hexfunc = ui.verbose and hex or short
1398 1398 modified, added, removed, deleted, unknown = repo.changes()
1399 1399 output = ["%s%s" %
1400 1400 ('+'.join([hexfunc(parent) for parent in parents]),
1401 1401 (modified or added or removed or deleted) and "+" or "")]
1402 1402
1403 1403 if not ui.quiet:
1404 1404 # multiple tags for a single parent separated by '/'
1405 1405 parenttags = ['/'.join(tags)
1406 1406 for tags in map(repo.nodetags, parents) if tags]
1407 1407 # tags for multiple parents separated by ' + '
1408 1408 if parenttags:
1409 1409 output.append(' + '.join(parenttags))
1410 1410
1411 1411 ui.write("%s\n" % ' '.join(output))
1412 1412
1413 1413 def import_(ui, repo, patch1, *patches, **opts):
1414 1414 """import an ordered set of patches
1415 1415
1416 1416 Import a list of patches and commit them individually.
1417 1417
1418 1418 If there are outstanding changes in the working directory, import
1419 1419 will abort unless given the -f flag.
1420 1420
1421 1421 If a patch looks like a mail message (its first line starts with
1422 1422 "From " or looks like an RFC822 header), it will not be applied
1423 1423 unless the -f option is used. The importer neither parses nor
1424 1424 discards mail headers, so use -f only to override the "mailness"
1425 1425 safety check, not to import a real mail message.
1426 1426 """
1427 1427 patches = (patch1,) + patches
1428 1428
1429 1429 if not opts['force']:
1430 1430 modified, added, removed, deleted, unknown = repo.changes()
1431 1431 if modified or added or removed or deleted:
1432 1432 raise util.Abort(_("outstanding uncommitted changes"))
1433 1433
1434 1434 d = opts["base"]
1435 1435 strip = opts["strip"]
1436 1436
1437 1437 mailre = re.compile(r'(?:From |[\w-]+:)')
1438 1438
1439 1439 # attempt to detect the start of a patch
1440 1440 # (this heuristic is borrowed from quilt)
1441 1441 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1442 1442 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1443 1443 '(---|\*\*\*)[ \t])')
1444 1444
1445 1445 for patch in patches:
1446 1446 ui.status(_("applying %s\n") % patch)
1447 1447 pf = os.path.join(d, patch)
1448 1448
1449 1449 message = []
1450 1450 user = None
1451 1451 hgpatch = False
1452 1452 for line in file(pf):
1453 1453 line = line.rstrip()
1454 1454 if (not message and not hgpatch and
1455 1455 mailre.match(line) and not opts['force']):
1456 1456 if len(line) > 35:
1457 1457 line = line[:32] + '...'
1458 1458 raise util.Abort(_('first line looks like a '
1459 1459 'mail header: ') + line)
1460 1460 if diffre.match(line):
1461 1461 break
1462 1462 elif hgpatch:
1463 1463 # parse values when importing the result of an hg export
1464 1464 if line.startswith("# User "):
1465 1465 user = line[7:]
1466 1466 ui.debug(_('User: %s\n') % user)
1467 1467 elif not line.startswith("# ") and line:
1468 1468 message.append(line)
1469 1469 hgpatch = False
1470 1470 elif line == '# HG changeset patch':
1471 1471 hgpatch = True
1472 1472 message = [] # We may have collected garbage
1473 1473 else:
1474 1474 message.append(line)
1475 1475
1476 1476 # make sure message isn't empty
1477 1477 if not message:
1478 1478 message = _("imported patch %s\n") % patch
1479 1479 else:
1480 1480 message = "%s\n" % '\n'.join(message)
1481 1481 ui.debug(_('message:\n%s\n') % message)
1482 1482
1483 1483 files = util.patch(strip, pf, ui)
1484 1484
1485 1485 if len(files) > 0:
1486 1486 addremove(ui, repo, *files)
1487 1487 repo.commit(files, message, user)
1488 1488
1489 1489 def incoming(ui, repo, source="default", **opts):
1490 1490 """show new changesets found in source
1491 1491
1492 1492 Show new changesets found in the specified repo or the default
1493 1493 pull repo. These are the changesets that would be pulled if a pull
1494 1494 was requested.
1495 1495
1496 1496 Currently only local repositories are supported.
1497 1497 """
1498 1498 source = ui.expandpath(source, repo.root)
1499 1499 other = hg.repository(ui, source)
1500 1500 if not other.local():
1501 1501 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1502 1502 o = repo.findincoming(other)
1503 1503 if not o:
1504 1504 return
1505 1505 o = other.changelog.nodesbetween(o)[0]
1506 1506 if opts['newest_first']:
1507 1507 o.reverse()
1508 1508 for n in o:
1509 1509 parents = [p for p in other.changelog.parents(n) if p != nullid]
1510 1510 if opts['no_merges'] and len(parents) == 2:
1511 1511 continue
1512 1512 show_changeset(ui, other, changenode=n)
1513 1513 if opts['patch']:
1514 1514 prev = (parents and parents[0]) or nullid
1515 1515 dodiff(ui, ui, other, prev, n)
1516 1516 ui.write("\n")
1517 1517
1518 1518 def init(ui, dest="."):
1519 1519 """create a new repository in the given directory
1520 1520
1521 1521 Initialize a new repository in the given directory. If the given
1522 1522 directory does not exist, it is created.
1523 1523
1524 1524 If no directory is given, the current directory is used.
1525 1525 """
1526 1526 if not os.path.exists(dest):
1527 1527 os.mkdir(dest)
1528 1528 hg.repository(ui, dest, create=1)
1529 1529
1530 1530 def locate(ui, repo, *pats, **opts):
1531 1531 """locate files matching specific patterns
1532 1532
1533 1533 Print all files under Mercurial control whose names match the
1534 1534 given patterns.
1535 1535
1536 1536 This command searches the current directory and its
1537 1537 subdirectories. To search an entire repository, move to the root
1538 1538 of the repository.
1539 1539
1540 1540 If no patterns are given to match, this command prints all file
1541 1541 names.
1542 1542
1543 1543 If you want to feed the output of this command into the "xargs"
1544 1544 command, use the "-0" option to both this command and "xargs".
1545 1545 This will avoid the problem of "xargs" treating single filenames
1546 1546 that contain white space as multiple filenames.
1547 1547 """
1548 1548 end = opts['print0'] and '\0' or '\n'
1549 1549 rev = opts['rev']
1550 1550 if rev:
1551 1551 node = repo.lookup(rev)
1552 1552 else:
1553 1553 node = None
1554 1554
1555 1555 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1556 1556 head='(?:.*/|)'):
1557 1557 if not node and repo.dirstate.state(abs) == '?':
1558 1558 continue
1559 1559 if opts['fullpath']:
1560 1560 ui.write(os.path.join(repo.root, abs), end)
1561 1561 else:
1562 1562 ui.write(((pats and rel) or abs), end)
1563 1563
1564 1564 def log(ui, repo, *pats, **opts):
1565 1565 """show revision history of entire repository or files
1566 1566
1567 1567 Print the revision history of the specified files or the entire project.
1568 1568
1569 1569 By default this command outputs: changeset id and hash, tags,
1570 1570 non-trivial parents, user, date and time, and a summary for each
1571 1571 commit. When the -v/--verbose switch is used, the list of changed
1572 1572 files and full commit message is shown.
1573 1573 """
1574 1574 class dui(object):
1575 1575 # Implement and delegate some ui protocol. Save hunks of
1576 1576 # output for later display in the desired order.
1577 1577 def __init__(self, ui):
1578 1578 self.ui = ui
1579 1579 self.hunk = {}
1580 1580 def bump(self, rev):
1581 1581 self.rev = rev
1582 1582 self.hunk[rev] = []
1583 1583 def note(self, *args):
1584 1584 if self.verbose:
1585 1585 self.write(*args)
1586 1586 def status(self, *args):
1587 1587 if not self.quiet:
1588 1588 self.write(*args)
1589 1589 def write(self, *args):
1590 1590 self.hunk[self.rev].append(args)
1591 1591 def debug(self, *args):
1592 1592 if self.debugflag:
1593 1593 self.write(*args)
1594 1594 def __getattr__(self, key):
1595 1595 return getattr(self.ui, key)
1596 1596 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1597 1597 for st, rev, fns in changeiter:
1598 1598 if st == 'window':
1599 1599 du = dui(ui)
1600 1600 elif st == 'add':
1601 1601 du.bump(rev)
1602 1602 changenode = repo.changelog.node(rev)
1603 1603 parents = [p for p in repo.changelog.parents(changenode)
1604 1604 if p != nullid]
1605 1605 if opts['no_merges'] and len(parents) == 2:
1606 1606 continue
1607 1607 if opts['only_merges'] and len(parents) != 2:
1608 1608 continue
1609 1609
1610 1610 br = None
1611 1611 if opts['keyword']:
1612 1612 changes = getchange(rev)
1613 1613 miss = 0
1614 1614 for k in [kw.lower() for kw in opts['keyword']]:
1615 1615 if not (k in changes[1].lower() or
1616 1616 k in changes[4].lower() or
1617 1617 k in " ".join(changes[3][:20]).lower()):
1618 1618 miss = 1
1619 1619 break
1620 1620 if miss:
1621 1621 continue
1622 1622
1623 1623 if opts['branch']:
1624 1624 br = repo.branchlookup([repo.changelog.node(rev)])
1625 1625
1626 1626 show_changeset(du, repo, rev, brinfo=br)
1627 1627 if opts['patch']:
1628 1628 prev = (parents and parents[0]) or nullid
1629 1629 dodiff(du, du, repo, prev, changenode, match=matchfn)
1630 1630 du.write("\n\n")
1631 1631 elif st == 'iter':
1632 1632 for args in du.hunk[rev]:
1633 1633 ui.write(*args)
1634 1634
1635 1635 def manifest(ui, repo, rev=None):
1636 1636 """output the latest or given revision of the project manifest
1637 1637
1638 1638 Print a list of version controlled files for the given revision.
1639 1639
1640 1640 The manifest is the list of files being version controlled. If no revision
1641 1641 is given then the tip is used.
1642 1642 """
1643 1643 if rev:
1644 1644 try:
1645 1645 # assume all revision numbers are for changesets
1646 1646 n = repo.lookup(rev)
1647 1647 change = repo.changelog.read(n)
1648 1648 n = change[0]
1649 1649 except hg.RepoError:
1650 1650 n = repo.manifest.lookup(rev)
1651 1651 else:
1652 1652 n = repo.manifest.tip()
1653 1653 m = repo.manifest.read(n)
1654 1654 mf = repo.manifest.readflags(n)
1655 1655 files = m.keys()
1656 1656 files.sort()
1657 1657
1658 1658 for f in files:
1659 1659 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1660 1660
1661 1661 def outgoing(ui, repo, dest="default-push", **opts):
1662 1662 """show changesets not found in destination
1663 1663
1664 1664 Show changesets not found in the specified destination repo or the
1665 1665 default push repo. These are the changesets that would be pushed
1666 1666 if a push was requested.
1667 1667 """
1668 1668 dest = ui.expandpath(dest, repo.root)
1669 1669 other = hg.repository(ui, dest)
1670 1670 o = repo.findoutgoing(other)
1671 1671 o = repo.changelog.nodesbetween(o)[0]
1672 1672 if opts['newest_first']:
1673 1673 o.reverse()
1674 1674 for n in o:
1675 1675 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1676 1676 if opts['no_merges'] and len(parents) == 2:
1677 1677 continue
1678 1678 show_changeset(ui, repo, changenode=n)
1679 1679 if opts['patch']:
1680 1680 prev = (parents and parents[0]) or nullid
1681 1681 dodiff(ui, ui, repo, prev, n)
1682 1682 ui.write("\n")
1683 1683
1684 1684 def parents(ui, repo, rev=None):
1685 1685 """show the parents of the working dir or revision
1686 1686
1687 1687 Print the working directory's parent revisions.
1688 1688 """
1689 1689 if rev:
1690 1690 p = repo.changelog.parents(repo.lookup(rev))
1691 1691 else:
1692 1692 p = repo.dirstate.parents()
1693 1693
1694 1694 for n in p:
1695 1695 if n != nullid:
1696 1696 show_changeset(ui, repo, changenode=n)
1697 1697
1698 1698 def paths(ui, search=None):
1699 1699 """show definition of symbolic path names
1700 1700
1701 1701 Show definition of symbolic path name NAME. If no name is given, show
1702 1702 definition of available names.
1703 1703
1704 1704 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1705 1705 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1706 1706 """
1707 1707 try:
1708 1708 repo = hg.repository(ui=ui)
1709 1709 except hg.RepoError:
1710 1710 pass
1711 1711
1712 1712 if search:
1713 1713 for name, path in ui.configitems("paths"):
1714 1714 if name == search:
1715 1715 ui.write("%s\n" % path)
1716 1716 return
1717 1717 ui.warn(_("not found!\n"))
1718 1718 return 1
1719 1719 else:
1720 1720 for name, path in ui.configitems("paths"):
1721 1721 ui.write("%s = %s\n" % (name, path))
1722 1722
1723 1723 def pull(ui, repo, source="default", **opts):
1724 1724 """pull changes from the specified source
1725 1725
1726 1726 Pull changes from a remote repository to a local one.
1727 1727
1728 1728 This finds all changes from the repository at the specified path
1729 1729 or URL and adds them to the local repository. By default, this
1730 1730 does not update the copy of the project in the working directory.
1731 1731
1732 1732 Valid URLs are of the form:
1733 1733
1734 1734 local/filesystem/path
1735 1735 http://[user@]host[:port][/path]
1736 1736 https://[user@]host[:port][/path]
1737 1737 ssh://[user@]host[:port][/path]
1738 1738
1739 1739 SSH requires an accessible shell account on the destination machine
1740 1740 and a copy of hg in the remote path. With SSH, paths are relative
1741 1741 to the remote user's home directory by default; use two slashes at
1742 1742 the start of a path to specify it as relative to the filesystem root.
1743 1743 """
1744 1744 source = ui.expandpath(source, repo.root)
1745 1745 ui.status(_('pulling from %s\n') % (source))
1746 1746
1747 1747 if opts['ssh']:
1748 1748 ui.setconfig("ui", "ssh", opts['ssh'])
1749 1749 if opts['remotecmd']:
1750 1750 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1751 1751
1752 1752 other = hg.repository(ui, source)
1753 1753 revs = None
1754 1754 if opts['rev'] and not other.local():
1755 1755 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
1756 1756 elif opts['rev']:
1757 1757 revs = [other.lookup(rev) for rev in opts['rev']]
1758 1758 r = repo.pull(other, heads=revs)
1759 1759 if not r:
1760 1760 if opts['update']:
1761 1761 return update(ui, repo)
1762 1762 else:
1763 1763 ui.status(_("(run 'hg update' to get a working copy)\n"))
1764 1764
1765 1765 return r
1766 1766
1767 1767 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1768 1768 """push changes to the specified destination
1769 1769
1770 1770 Push changes from the local repository to the given destination.
1771 1771
1772 1772 This is the symmetrical operation for pull. It helps to move
1773 1773 changes from the current repository to a different one. If the
1774 1774 destination is local this is identical to a pull in that directory
1775 1775 from the current one.
1776 1776
1777 1777 By default, push will refuse to run if it detects the result would
1778 1778 increase the number of remote heads. This generally indicates the
1779 1779 the client has forgotten to sync and merge before pushing.
1780 1780
1781 1781 Valid URLs are of the form:
1782 1782
1783 1783 local/filesystem/path
1784 1784 ssh://[user@]host[:port][/path]
1785 1785
1786 1786 SSH requires an accessible shell account on the destination
1787 1787 machine and a copy of hg in the remote path.
1788 1788 """
1789 1789 dest = ui.expandpath(dest, repo.root)
1790 1790 ui.status('pushing to %s\n' % (dest))
1791 1791
1792 1792 if ssh:
1793 1793 ui.setconfig("ui", "ssh", ssh)
1794 1794 if remotecmd:
1795 1795 ui.setconfig("ui", "remotecmd", remotecmd)
1796 1796
1797 1797 other = hg.repository(ui, dest)
1798 1798 r = repo.push(other, force)
1799 1799 return r
1800 1800
1801 1801 def rawcommit(ui, repo, *flist, **rc):
1802 1802 """raw commit interface (DEPRECATED)
1803 1803
1804 1804 Lowlevel commit, for use in helper scripts.
1805 1805
1806 1806 This command is not intended to be used by normal users, as it is
1807 1807 primarily useful for importing from other SCMs.
1808 1808
1809 1809 This command is now deprecated and will be removed in a future
1810 1810 release, please use debugsetparents and commit instead.
1811 1811 """
1812 1812
1813 1813 ui.warn(_("(the rawcommit command is deprecated)\n"))
1814 1814
1815 1815 message = rc['message']
1816 1816 if not message and rc['logfile']:
1817 1817 try:
1818 1818 message = open(rc['logfile']).read()
1819 1819 except IOError:
1820 1820 pass
1821 1821 if not message and not rc['logfile']:
1822 1822 raise util.Abort(_("missing commit message"))
1823 1823
1824 1824 files = relpath(repo, list(flist))
1825 1825 if rc['files']:
1826 1826 files += open(rc['files']).read().splitlines()
1827 1827
1828 1828 rc['parent'] = map(repo.lookup, rc['parent'])
1829 1829
1830 1830 try:
1831 1831 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1832 1832 except ValueError, inst:
1833 1833 raise util.Abort(str(inst))
1834 1834
1835 1835 def recover(ui, repo):
1836 1836 """roll back an interrupted transaction
1837 1837
1838 1838 Recover from an interrupted commit or pull.
1839 1839
1840 1840 This command tries to fix the repository status after an interrupted
1841 1841 operation. It should only be necessary when Mercurial suggests it.
1842 1842 """
1843 1843 if repo.recover():
1844 1844 return repo.verify()
1845 1845 return False
1846 1846
1847 1847 def remove(ui, repo, pat, *pats, **opts):
1848 1848 """remove the specified files on the next commit
1849 1849
1850 1850 Schedule the indicated files for removal from the repository.
1851 1851
1852 1852 This command schedules the files to be removed at the next commit.
1853 1853 This only removes files from the current branch, not from the
1854 1854 entire project history. If the files still exist in the working
1855 1855 directory, they will be deleted from it.
1856 1856 """
1857 1857 names = []
1858 1858 def okaytoremove(abs, rel, exact):
1859 1859 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
1860 1860 reason = None
1861 1861 if modified:
1862 1862 reason = _('is modified')
1863 1863 elif added:
1864 1864 reason = _('has been marked for add')
1865 1865 elif unknown:
1866 1866 reason = _('is not managed')
1867 1867 if reason:
1868 1868 if exact:
1869 1869 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1870 1870 else:
1871 1871 return True
1872 1872 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1873 1873 if okaytoremove(abs, rel, exact):
1874 1874 if ui.verbose or not exact:
1875 1875 ui.status(_('removing %s\n') % rel)
1876 1876 names.append(abs)
1877 1877 repo.remove(names, unlink=True)
1878 1878
1879 1879 def rename(ui, repo, *pats, **opts):
1880 1880 """rename files; equivalent of copy + remove
1881 1881
1882 1882 Mark dest as copies of sources; mark sources for deletion. If
1883 1883 dest is a directory, copies are put in that directory. If dest is
1884 1884 a file, there can only be one source.
1885 1885
1886 1886 By default, this command copies the contents of files as they
1887 1887 stand in the working directory. If invoked with --after, the
1888 1888 operation is recorded, but no copying is performed.
1889 1889
1890 1890 This command takes effect in the next commit.
1891 1891
1892 1892 NOTE: This command should be treated as experimental. While it
1893 1893 should properly record rename files, this information is not yet
1894 1894 fully used by merge, nor fully reported by log.
1895 1895 """
1896 1896 errs, copied = docopy(ui, repo, pats, opts)
1897 1897 names = []
1898 1898 for abs, rel, exact in copied:
1899 1899 if ui.verbose or not exact:
1900 1900 ui.status(_('removing %s\n') % rel)
1901 1901 names.append(abs)
1902 1902 repo.remove(names, unlink=True)
1903 1903 return errs
1904 1904
1905 1905 def revert(ui, repo, *pats, **opts):
1906 1906 """revert modified files or dirs back to their unmodified states
1907 1907
1908 1908 Revert any uncommitted modifications made to the named files or
1909 1909 directories. This restores the contents of the affected files to
1910 1910 an unmodified state.
1911 1911
1912 1912 If a file has been deleted, it is recreated. If the executable
1913 1913 mode of a file was changed, it is reset.
1914 1914
1915 1915 If names are given, all files matching the names are reverted.
1916 1916
1917 1917 If no arguments are given, all files in the repository are reverted.
1918 1918 """
1919 1919 node = opts['rev'] and repo.lookup(opts['rev']) or \
1920 1920 repo.dirstate.parents()[0]
1921 1921
1922 1922 files, choose, anypats = matchpats(repo, pats, opts)
1923 1923 modified, added, removed, deleted, unknown = repo.changes(match=choose)
1924 1924 repo.forget(added)
1925 1925 repo.undelete(removed + deleted)
1926 1926
1927 1927 return repo.update(node, False, True, choose, False)
1928 1928
1929 1929 def root(ui, repo):
1930 1930 """print the root (top) of the current working dir
1931 1931
1932 1932 Print the root directory of the current repository.
1933 1933 """
1934 1934 ui.write(repo.root + "\n")
1935 1935
1936 1936 def serve(ui, repo, **opts):
1937 1937 """export the repository via HTTP
1938 1938
1939 1939 Start a local HTTP repository browser and pull server.
1940 1940
1941 1941 By default, the server logs accesses to stdout and errors to
1942 1942 stderr. Use the "-A" and "-E" options to log to files.
1943 1943 """
1944 1944
1945 1945 if opts["stdio"]:
1946 1946 fin, fout = sys.stdin, sys.stdout
1947 1947 sys.stdout = sys.stderr
1948 1948
1949 1949 # Prevent insertion/deletion of CRs
1950 1950 util.set_binary(fin)
1951 1951 util.set_binary(fout)
1952 1952
1953 1953 def getarg():
1954 1954 argline = fin.readline()[:-1]
1955 1955 arg, l = argline.split()
1956 1956 val = fin.read(int(l))
1957 1957 return arg, val
1958 1958 def respond(v):
1959 1959 fout.write("%d\n" % len(v))
1960 1960 fout.write(v)
1961 1961 fout.flush()
1962 1962
1963 1963 lock = None
1964 1964
1965 1965 while 1:
1966 1966 cmd = fin.readline()[:-1]
1967 1967 if cmd == '':
1968 1968 return
1969 1969 if cmd == "heads":
1970 1970 h = repo.heads()
1971 1971 respond(" ".join(map(hex, h)) + "\n")
1972 1972 if cmd == "lock":
1973 1973 lock = repo.lock()
1974 1974 respond("")
1975 1975 if cmd == "unlock":
1976 1976 if lock:
1977 1977 lock.release()
1978 1978 lock = None
1979 1979 respond("")
1980 1980 elif cmd == "branches":
1981 1981 arg, nodes = getarg()
1982 1982 nodes = map(bin, nodes.split(" "))
1983 1983 r = []
1984 1984 for b in repo.branches(nodes):
1985 1985 r.append(" ".join(map(hex, b)) + "\n")
1986 1986 respond("".join(r))
1987 1987 elif cmd == "between":
1988 1988 arg, pairs = getarg()
1989 1989 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1990 1990 r = []
1991 1991 for b in repo.between(pairs):
1992 1992 r.append(" ".join(map(hex, b)) + "\n")
1993 1993 respond("".join(r))
1994 1994 elif cmd == "changegroup":
1995 1995 nodes = []
1996 1996 arg, roots = getarg()
1997 1997 nodes = map(bin, roots.split(" "))
1998 1998
1999 1999 cg = repo.changegroup(nodes)
2000 2000 while 1:
2001 2001 d = cg.read(4096)
2002 2002 if not d:
2003 2003 break
2004 2004 fout.write(d)
2005 2005
2006 2006 fout.flush()
2007 2007
2008 2008 elif cmd == "addchangegroup":
2009 2009 if not lock:
2010 2010 respond("not locked")
2011 2011 continue
2012 2012 respond("")
2013 2013
2014 2014 r = repo.addchangegroup(fin)
2015 2015 respond("")
2016 2016
2017 2017 optlist = "name templates style address port ipv6 accesslog errorlog"
2018 2018 for o in optlist.split():
2019 2019 if opts[o]:
2020 2020 ui.setconfig("web", o, opts[o])
2021 2021
2022 2022 try:
2023 2023 httpd = hgweb.create_server(repo)
2024 2024 except socket.error, inst:
2025 2025 raise util.Abort(_('cannot start server: ') + inst.args[1])
2026 2026
2027 2027 if ui.verbose:
2028 2028 addr, port = httpd.socket.getsockname()
2029 2029 if addr == '0.0.0.0':
2030 2030 addr = socket.gethostname()
2031 2031 else:
2032 2032 try:
2033 2033 addr = socket.gethostbyaddr(addr)[0]
2034 2034 except socket.error:
2035 2035 pass
2036 2036 if port != 80:
2037 2037 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2038 2038 else:
2039 2039 ui.status(_('listening at http://%s/\n') % addr)
2040 2040 httpd.serve_forever()
2041 2041
2042 2042 def status(ui, repo, *pats, **opts):
2043 2043 """show changed files in the working directory
2044 2044
2045 2045 Show changed files in the repository. If names are
2046 2046 given, only files that match are shown.
2047 2047
2048 2048 The codes used to show the status of files are:
2049 2049 M = modified
2050 2050 A = added
2051 2051 R = removed
2052 2052 ! = deleted, but still tracked
2053 2053 ? = not tracked
2054 2054 """
2055 2055
2056 2056 files, matchfn, anypats = matchpats(repo, pats, opts)
2057 2057 cwd = (pats and repo.getcwd()) or ''
2058 2058 modified, added, removed, deleted, unknown = [
2059 2059 [util.pathto(cwd, x) for x in n]
2060 2060 for n in repo.changes(files=files, match=matchfn)]
2061 2061
2062 2062 changetypes = [(_('modified'), 'M', modified),
2063 2063 (_('added'), 'A', added),
2064 2064 (_('removed'), 'R', removed),
2065 2065 (_('deleted'), '!', deleted),
2066 2066 (_('unknown'), '?', unknown)]
2067 2067
2068 2068 end = opts['print0'] and '\0' or '\n'
2069 2069
2070 2070 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2071 2071 or changetypes):
2072 2072 if opts['no_status']:
2073 2073 format = "%%s%s" % end
2074 2074 else:
2075 2075 format = "%s %%s%s" % (char, end);
2076 2076
2077 2077 for f in changes:
2078 2078 ui.write(format % f)
2079 2079
2080 2080 def tag(ui, repo, name, rev_=None, **opts):
2081 2081 """add a tag for the current tip or a given revision
2082 2082
2083 2083 Name a particular revision using <name>.
2084 2084
2085 2085 Tags are used to name particular revisions of the repository and are
2086 2086 very useful to compare different revision, to go back to significant
2087 2087 earlier versions or to mark branch points as releases, etc.
2088 2088
2089 2089 If no revision is given, the tip is used.
2090 2090
2091 2091 To facilitate version control, distribution, and merging of tags,
2092 2092 they are stored as a file named ".hgtags" which is managed
2093 2093 similarly to other project files and can be hand-edited if
2094 2094 necessary. The file '.hg/localtags' is used for local tags (not
2095 2095 shared among repositories).
2096 2096 """
2097 2097 if name == "tip":
2098 2098 raise util.Abort(_("the name 'tip' is reserved"))
2099 2099 if rev_ is not None:
2100 2100 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2101 2101 "please use 'hg tag [-r REV] NAME' instead\n"))
2102 2102 if opts['rev']:
2103 2103 raise util.Abort(_("use only one form to specify the revision"))
2104 2104 if opts['rev']:
2105 2105 rev_ = opts['rev']
2106 2106 if rev_:
2107 2107 r = hex(repo.lookup(rev_))
2108 2108 else:
2109 2109 r = hex(repo.changelog.tip())
2110 2110
2111 2111 disallowed = (revrangesep, '\r', '\n')
2112 2112 for c in disallowed:
2113 2113 if name.find(c) >= 0:
2114 2114 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2115 2115
2116 repo.hook('pretag', throw=True, node=r, tag=name,
2117 local=not not opts['local'])
2118
2116 2119 if opts['local']:
2117 2120 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2121 repo.hook('tag', node=r, tag=name, local=1)
2118 2122 return
2119 2123
2120 2124 for x in repo.changes():
2121 2125 if ".hgtags" in x:
2122 2126 raise util.Abort(_("working copy of .hgtags is changed "
2123 2127 "(please commit .hgtags manually)"))
2124 2128
2125 2129 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2126 2130 if repo.dirstate.state(".hgtags") == '?':
2127 2131 repo.add([".hgtags"])
2128 2132
2129 2133 message = (opts['message'] or
2130 2134 _("Added tag %s for changeset %s") % (name, r))
2131 2135 try:
2132 2136 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2137 repo.hook('tag', node=r, tag=name, local=0)
2133 2138 except ValueError, inst:
2134 2139 raise util.Abort(str(inst))
2135 2140
2136 2141 def tags(ui, repo):
2137 2142 """list repository tags
2138 2143
2139 2144 List the repository tags.
2140 2145
2141 2146 This lists both regular and local tags.
2142 2147 """
2143 2148
2144 2149 l = repo.tagslist()
2145 2150 l.reverse()
2146 2151 for t, n in l:
2147 2152 try:
2148 2153 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2149 2154 except KeyError:
2150 2155 r = " ?:?"
2151 2156 ui.write("%-30s %s\n" % (t, r))
2152 2157
2153 2158 def tip(ui, repo):
2154 2159 """show the tip revision
2155 2160
2156 2161 Show the tip revision.
2157 2162 """
2158 2163 n = repo.changelog.tip()
2159 2164 show_changeset(ui, repo, changenode=n)
2160 2165
2161 2166 def unbundle(ui, repo, fname, **opts):
2162 2167 """apply a changegroup file
2163 2168
2164 2169 Apply a compressed changegroup file generated by the bundle
2165 2170 command.
2166 2171 """
2167 2172 f = urllib.urlopen(fname)
2168 2173
2169 2174 if f.read(4) != "HG10":
2170 2175 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2171 2176
2172 2177 def bzgenerator(f):
2173 2178 zd = bz2.BZ2Decompressor()
2174 2179 for chunk in f:
2175 2180 yield zd.decompress(chunk)
2176 2181
2177 2182 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2178 2183 if repo.addchangegroup(util.chunkbuffer(bzgen)):
2179 2184 return 1
2180 2185
2181 2186 if opts['update']:
2182 2187 return update(ui, repo)
2183 2188 else:
2184 2189 ui.status(_("(run 'hg update' to get a working copy)\n"))
2185 2190
2186 2191 def undo(ui, repo):
2187 2192 """undo the last commit or pull
2188 2193
2189 2194 Roll back the last pull or commit transaction on the
2190 2195 repository, restoring the project to its earlier state.
2191 2196
2192 2197 This command should be used with care. There is only one level of
2193 2198 undo and there is no redo.
2194 2199
2195 2200 This command is not intended for use on public repositories. Once
2196 2201 a change is visible for pull by other users, undoing it locally is
2197 2202 ineffective.
2198 2203 """
2199 2204 repo.undo()
2200 2205
2201 2206 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2202 2207 branch=None):
2203 2208 """update or merge working directory
2204 2209
2205 2210 Update the working directory to the specified revision.
2206 2211
2207 2212 If there are no outstanding changes in the working directory and
2208 2213 there is a linear relationship between the current version and the
2209 2214 requested version, the result is the requested version.
2210 2215
2211 2216 Otherwise the result is a merge between the contents of the
2212 2217 current working directory and the requested version. Files that
2213 2218 changed between either parent are marked as changed for the next
2214 2219 commit and a commit must be performed before any further updates
2215 2220 are allowed.
2216 2221
2217 2222 By default, update will refuse to run if doing so would require
2218 2223 merging or discarding local changes.
2219 2224 """
2220 2225 if branch:
2221 2226 br = repo.branchlookup(branch=branch)
2222 2227 found = []
2223 2228 for x in br:
2224 2229 if branch in br[x]:
2225 2230 found.append(x)
2226 2231 if len(found) > 1:
2227 2232 ui.warn(_("Found multiple heads for %s\n") % branch)
2228 2233 for x in found:
2229 2234 show_changeset(ui, repo, changenode=x, brinfo=br)
2230 2235 return 1
2231 2236 if len(found) == 1:
2232 2237 node = found[0]
2233 2238 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2234 2239 else:
2235 2240 ui.warn(_("branch %s not found\n") % (branch))
2236 2241 return 1
2237 2242 else:
2238 2243 node = node and repo.lookup(node) or repo.changelog.tip()
2239 2244 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2240 2245
2241 2246 def verify(ui, repo):
2242 2247 """verify the integrity of the repository
2243 2248
2244 2249 Verify the integrity of the current repository.
2245 2250
2246 2251 This will perform an extensive check of the repository's
2247 2252 integrity, validating the hashes and checksums of each entry in
2248 2253 the changelog, manifest, and tracked files, as well as the
2249 2254 integrity of their crosslinks and indices.
2250 2255 """
2251 2256 return repo.verify()
2252 2257
2253 2258 # Command options and aliases are listed here, alphabetically
2254 2259
2255 2260 table = {
2256 2261 "^add":
2257 2262 (add,
2258 2263 [('I', 'include', [], _('include names matching the given patterns')),
2259 2264 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2260 2265 _('hg add [OPTION]... [FILE]...')),
2261 2266 "addremove":
2262 2267 (addremove,
2263 2268 [('I', 'include', [], _('include names matching the given patterns')),
2264 2269 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2265 2270 _('hg addremove [OPTION]... [FILE]...')),
2266 2271 "^annotate":
2267 2272 (annotate,
2268 2273 [('r', 'rev', '', _('annotate the specified revision')),
2269 2274 ('a', 'text', None, _('treat all files as text')),
2270 2275 ('u', 'user', None, _('list the author')),
2271 2276 ('d', 'date', None, _('list the date')),
2272 2277 ('n', 'number', None, _('list the revision number (default)')),
2273 2278 ('c', 'changeset', None, _('list the changeset')),
2274 2279 ('I', 'include', [], _('include names matching the given patterns')),
2275 2280 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2276 2281 _('hg annotate [OPTION]... FILE...')),
2277 2282 "bundle":
2278 2283 (bundle,
2279 2284 [],
2280 2285 _('hg bundle FILE DEST')),
2281 2286 "cat":
2282 2287 (cat,
2283 2288 [('I', 'include', [], _('include names matching the given patterns')),
2284 2289 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2285 2290 ('o', 'output', '', _('print output to file with formatted name')),
2286 2291 ('r', 'rev', '', _('print the given revision'))],
2287 2292 _('hg cat [OPTION]... FILE...')),
2288 2293 "^clone":
2289 2294 (clone,
2290 2295 [('U', 'noupdate', None, _('do not update the new working directory')),
2291 2296 ('e', 'ssh', '', _('specify ssh command to use')),
2292 2297 ('', 'pull', None, _('use pull protocol to copy metadata')),
2293 2298 ('r', 'rev', [],
2294 2299 _('a changeset you would like to have after cloning')),
2295 2300 ('', 'remotecmd', '',
2296 2301 _('specify hg command to run on the remote side'))],
2297 2302 _('hg clone [OPTION]... SOURCE [DEST]')),
2298 2303 "^commit|ci":
2299 2304 (commit,
2300 2305 [('A', 'addremove', None, _('run addremove during commit')),
2301 2306 ('I', 'include', [], _('include names matching the given patterns')),
2302 2307 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2303 2308 ('m', 'message', '', _('use <text> as commit message')),
2304 2309 ('l', 'logfile', '', _('read the commit message from <file>')),
2305 2310 ('d', 'date', '', _('record datecode as commit date')),
2306 2311 ('u', 'user', '', _('record user as commiter'))],
2307 2312 _('hg commit [OPTION]... [FILE]...')),
2308 2313 "copy|cp":
2309 2314 (copy,
2310 2315 [('I', 'include', [], _('include names matching the given patterns')),
2311 2316 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2312 2317 ('A', 'after', None, _('record a copy that has already occurred')),
2313 2318 ('f', 'force', None,
2314 2319 _('forcibly copy over an existing managed file'))],
2315 2320 _('hg copy [OPTION]... [SOURCE]... DEST')),
2316 2321 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2317 2322 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2318 2323 "debugconfig": (debugconfig, [], _('debugconfig')),
2319 2324 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2320 2325 "debugstate": (debugstate, [], _('debugstate')),
2321 2326 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2322 2327 "debugindex": (debugindex, [], _('debugindex FILE')),
2323 2328 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2324 2329 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2325 2330 "debugwalk":
2326 2331 (debugwalk,
2327 2332 [('I', 'include', [], _('include names matching the given patterns')),
2328 2333 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2329 2334 _('debugwalk [OPTION]... [FILE]...')),
2330 2335 "^diff":
2331 2336 (diff,
2332 2337 [('r', 'rev', [], _('revision')),
2333 2338 ('a', 'text', None, _('treat all files as text')),
2334 2339 ('I', 'include', [], _('include names matching the given patterns')),
2335 2340 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2336 2341 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2337 2342 "^export":
2338 2343 (export,
2339 2344 [('o', 'output', '', _('print output to file with formatted name')),
2340 2345 ('a', 'text', None, _('treat all files as text')),
2341 2346 ('', 'switch-parent', None, _('diff against the second parent'))],
2342 2347 _('hg export [-a] [-o OUTFILE] REV...')),
2343 2348 "forget":
2344 2349 (forget,
2345 2350 [('I', 'include', [], _('include names matching the given patterns')),
2346 2351 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2347 2352 _('hg forget [OPTION]... FILE...')),
2348 2353 "grep":
2349 2354 (grep,
2350 2355 [('0', 'print0', None, _('end fields with NUL')),
2351 2356 ('I', 'include', [], _('include names matching the given patterns')),
2352 2357 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2353 2358 ('', 'all', None, _('print all revisions that match')),
2354 2359 ('i', 'ignore-case', None, _('ignore case when matching')),
2355 2360 ('l', 'files-with-matches', None,
2356 2361 _('print only filenames and revs that match')),
2357 2362 ('n', 'line-number', None, _('print matching line numbers')),
2358 2363 ('r', 'rev', [], _('search in given revision range')),
2359 2364 ('u', 'user', None, _('print user who committed change'))],
2360 2365 _('hg grep [OPTION]... PATTERN [FILE]...')),
2361 2366 "heads":
2362 2367 (heads,
2363 2368 [('b', 'branches', None, _('find branch info')),
2364 2369 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2365 2370 _('hg heads [-b] [-r <rev>]')),
2366 2371 "help": (help_, [], _('hg help [COMMAND]')),
2367 2372 "identify|id": (identify, [], _('hg identify')),
2368 2373 "import|patch":
2369 2374 (import_,
2370 2375 [('p', 'strip', 1,
2371 2376 _('directory strip option for patch. This has the same\n') +
2372 2377 _('meaning as the corresponding patch option')),
2373 2378 ('f', 'force', None,
2374 2379 _('skip check for outstanding uncommitted changes')),
2375 2380 ('b', 'base', '', _('base path'))],
2376 2381 _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
2377 2382 "incoming|in": (incoming,
2378 2383 [('M', 'no-merges', None, _('do not show merges')),
2379 2384 ('p', 'patch', None, _('show patch')),
2380 2385 ('n', 'newest-first', None, _('show newest record first'))],
2381 2386 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2382 2387 "^init": (init, [], _('hg init [DEST]')),
2383 2388 "locate":
2384 2389 (locate,
2385 2390 [('r', 'rev', '', _('search the repository as it stood at rev')),
2386 2391 ('0', 'print0', None,
2387 2392 _('end filenames with NUL, for use with xargs')),
2388 2393 ('f', 'fullpath', None,
2389 2394 _('print complete paths from the filesystem root')),
2390 2395 ('I', 'include', [], _('include names matching the given patterns')),
2391 2396 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2392 2397 _('hg locate [OPTION]... [PATTERN]...')),
2393 2398 "^log|history":
2394 2399 (log,
2395 2400 [('I', 'include', [], _('include names matching the given patterns')),
2396 2401 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2397 2402 ('b', 'branch', None, _('show branches')),
2398 2403 ('k', 'keyword', [], _('search for a keyword')),
2399 2404 ('r', 'rev', [], _('show the specified revision or range')),
2400 2405 ('M', 'no-merges', None, _('do not show merges')),
2401 2406 ('m', 'only-merges', None, _('show only merges')),
2402 2407 ('p', 'patch', None, _('show patch'))],
2403 2408 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2404 2409 "manifest": (manifest, [], _('hg manifest [REV]')),
2405 2410 "outgoing|out": (outgoing,
2406 2411 [('M', 'no-merges', None, _('do not show merges')),
2407 2412 ('p', 'patch', None, _('show patch')),
2408 2413 ('n', 'newest-first', None, _('show newest record first'))],
2409 2414 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2410 2415 "^parents": (parents, [], _('hg parents [REV]')),
2411 2416 "paths": (paths, [], _('hg paths [NAME]')),
2412 2417 "^pull":
2413 2418 (pull,
2414 2419 [('u', 'update', None,
2415 2420 _('update the working directory to tip after pull')),
2416 2421 ('e', 'ssh', '', _('specify ssh command to use')),
2417 2422 ('r', 'rev', [], _('a specific revision you would like to pull')),
2418 2423 ('', 'remotecmd', '',
2419 2424 _('specify hg command to run on the remote side'))],
2420 2425 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2421 2426 "^push":
2422 2427 (push,
2423 2428 [('f', 'force', None, _('force push')),
2424 2429 ('e', 'ssh', '', _('specify ssh command to use')),
2425 2430 ('', 'remotecmd', '',
2426 2431 _('specify hg command to run on the remote side'))],
2427 2432 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2428 2433 "rawcommit":
2429 2434 (rawcommit,
2430 2435 [('p', 'parent', [], _('parent')),
2431 2436 ('d', 'date', '', _('date code')),
2432 2437 ('u', 'user', '', _('user')),
2433 2438 ('F', 'files', '', _('file list')),
2434 2439 ('m', 'message', '', _('commit message')),
2435 2440 ('l', 'logfile', '', _('commit message file'))],
2436 2441 _('hg rawcommit [OPTION]... [FILE]...')),
2437 2442 "recover": (recover, [], _('hg recover')),
2438 2443 "^remove|rm":
2439 2444 (remove,
2440 2445 [('I', 'include', [], _('include names matching the given patterns')),
2441 2446 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2442 2447 _('hg remove [OPTION]... FILE...')),
2443 2448 "rename|mv":
2444 2449 (rename,
2445 2450 [('I', 'include', [], _('include names matching the given patterns')),
2446 2451 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2447 2452 ('A', 'after', None, _('record a rename that has already occurred')),
2448 2453 ('f', 'force', None,
2449 2454 _('forcibly copy over an existing managed file'))],
2450 2455 _('hg rename [OPTION]... [SOURCE]... DEST')),
2451 2456 "^revert":
2452 2457 (revert,
2453 2458 [('I', 'include', [], _('include names matching the given patterns')),
2454 2459 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2455 2460 ('r', 'rev', '', _('revision to revert to'))],
2456 2461 _('hg revert [-n] [-r REV] [NAME]...')),
2457 2462 "root": (root, [], _('hg root')),
2458 2463 "^serve":
2459 2464 (serve,
2460 2465 [('A', 'accesslog', '', _('name of access log file to write to')),
2461 2466 ('E', 'errorlog', '', _('name of error log file to write to')),
2462 2467 ('p', 'port', 0, _('port to use (default: 8000)')),
2463 2468 ('a', 'address', '', _('address to use')),
2464 2469 ('n', 'name', '',
2465 2470 _('name to show in web pages (default: working dir)')),
2466 2471 ('', 'stdio', None, _('for remote clients')),
2467 2472 ('t', 'templates', '', _('web templates to use')),
2468 2473 ('', 'style', '', _('template style to use')),
2469 2474 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2470 2475 _('hg serve [OPTION]...')),
2471 2476 "^status|st":
2472 2477 (status,
2473 2478 [('m', 'modified', None, _('show only modified files')),
2474 2479 ('a', 'added', None, _('show only added files')),
2475 2480 ('r', 'removed', None, _('show only removed files')),
2476 2481 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2477 2482 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2478 2483 ('n', 'no-status', None, _('hide status prefix')),
2479 2484 ('0', 'print0', None,
2480 2485 _('end filenames with NUL, for use with xargs')),
2481 2486 ('I', 'include', [], _('include names matching the given patterns')),
2482 2487 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2483 2488 _('hg status [OPTION]... [FILE]...')),
2484 2489 "tag":
2485 2490 (tag,
2486 2491 [('l', 'local', None, _('make the tag local')),
2487 2492 ('m', 'message', '', _('message for tag commit log entry')),
2488 2493 ('d', 'date', '', _('record datecode as commit date')),
2489 2494 ('u', 'user', '', _('record user as commiter')),
2490 2495 ('r', 'rev', '', _('revision to tag'))],
2491 2496 _('hg tag [-r REV] [OPTION]... NAME')),
2492 2497 "tags": (tags, [], _('hg tags')),
2493 2498 "tip": (tip, [], _('hg tip')),
2494 2499 "unbundle":
2495 2500 (unbundle,
2496 2501 [('u', 'update', None,
2497 2502 _('update the working directory to tip after unbundle'))],
2498 2503 _('hg unbundle [-u] FILE')),
2499 2504 "undo": (undo, [], _('hg undo')),
2500 2505 "^update|up|checkout|co":
2501 2506 (update,
2502 2507 [('b', 'branch', '', _('checkout the head of a specific branch')),
2503 2508 ('m', 'merge', None, _('allow merging of branches')),
2504 2509 ('C', 'clean', None, _('overwrite locally modified files')),
2505 2510 ('f', 'force', None, _('force a merge with outstanding changes'))],
2506 2511 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2507 2512 "verify": (verify, [], _('hg verify')),
2508 2513 "version": (show_version, [], _('hg version')),
2509 2514 }
2510 2515
2511 2516 globalopts = [
2512 2517 ('R', 'repository', '', _('repository root directory')),
2513 2518 ('', 'cwd', '', _('change working directory')),
2514 2519 ('y', 'noninteractive', None,
2515 2520 _('do not prompt, assume \'yes\' for any required answers')),
2516 2521 ('q', 'quiet', None, _('suppress output')),
2517 2522 ('v', 'verbose', None, _('enable additional output')),
2518 2523 ('', 'debug', None, _('enable debugging output')),
2519 2524 ('', 'debugger', None, _('start debugger')),
2520 2525 ('', 'traceback', None, _('print traceback on exception')),
2521 2526 ('', 'time', None, _('time how long the command takes')),
2522 2527 ('', 'profile', None, _('print command execution profile')),
2523 2528 ('', 'version', None, _('output version information and exit')),
2524 2529 ('h', 'help', None, _('display help and exit')),
2525 2530 ]
2526 2531
2527 2532 norepo = ("clone init version help debugancestor debugconfig debugdata"
2528 2533 " debugindex debugindexdot paths")
2529 2534
2530 2535 def find(cmd):
2531 2536 """Return (aliases, command table entry) for command string."""
2532 2537 choice = None
2533 2538 for e in table.keys():
2534 2539 aliases = e.lstrip("^").split("|")
2535 2540 if cmd in aliases:
2536 2541 return aliases, table[e]
2537 2542 for a in aliases:
2538 2543 if a.startswith(cmd):
2539 2544 if choice:
2540 2545 raise AmbiguousCommand(cmd)
2541 2546 else:
2542 2547 choice = aliases, table[e]
2543 2548 break
2544 2549 if choice:
2545 2550 return choice
2546 2551
2547 2552 raise UnknownCommand(cmd)
2548 2553
2549 2554 class SignalInterrupt(Exception):
2550 2555 """Exception raised on SIGTERM and SIGHUP."""
2551 2556
2552 2557 def catchterm(*args):
2553 2558 raise SignalInterrupt
2554 2559
2555 2560 def run():
2556 2561 sys.exit(dispatch(sys.argv[1:]))
2557 2562
2558 2563 class ParseError(Exception):
2559 2564 """Exception raised on errors in parsing the command line."""
2560 2565
2561 2566 def parse(ui, args):
2562 2567 options = {}
2563 2568 cmdoptions = {}
2564 2569
2565 2570 try:
2566 2571 args = fancyopts.fancyopts(args, globalopts, options)
2567 2572 except fancyopts.getopt.GetoptError, inst:
2568 2573 raise ParseError(None, inst)
2569 2574
2570 2575 if args:
2571 2576 cmd, args = args[0], args[1:]
2572 2577 aliases, i = find(cmd)
2573 2578 cmd = aliases[0]
2574 2579 defaults = ui.config("defaults", cmd)
2575 2580 if defaults:
2576 2581 args = defaults.split() + args
2577 2582 c = list(i[1])
2578 2583 else:
2579 2584 cmd = None
2580 2585 c = []
2581 2586
2582 2587 # combine global options into local
2583 2588 for o in globalopts:
2584 2589 c.append((o[0], o[1], options[o[1]], o[3]))
2585 2590
2586 2591 try:
2587 2592 args = fancyopts.fancyopts(args, c, cmdoptions)
2588 2593 except fancyopts.getopt.GetoptError, inst:
2589 2594 raise ParseError(cmd, inst)
2590 2595
2591 2596 # separate global options back out
2592 2597 for o in globalopts:
2593 2598 n = o[1]
2594 2599 options[n] = cmdoptions[n]
2595 2600 del cmdoptions[n]
2596 2601
2597 2602 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2598 2603
2599 2604 def dispatch(args):
2600 2605 signal.signal(signal.SIGTERM, catchterm)
2601 2606 try:
2602 2607 signal.signal(signal.SIGHUP, catchterm)
2603 2608 except AttributeError:
2604 2609 pass
2605 2610
2606 2611 try:
2607 2612 u = ui.ui()
2608 2613 except util.Abort, inst:
2609 2614 sys.stderr.write(_("abort: %s\n") % inst)
2610 2615 sys.exit(1)
2611 2616
2612 2617 external = []
2613 2618 for x in u.extensions():
2614 2619 def on_exception(exc, inst):
2615 2620 u.warn(_("*** failed to import extension %s\n") % x[1])
2616 2621 u.warn("%s\n" % inst)
2617 2622 if "--traceback" in sys.argv[1:]:
2618 2623 traceback.print_exc()
2619 2624 if x[1]:
2620 2625 try:
2621 2626 mod = imp.load_source(x[0], x[1])
2622 2627 except Exception, inst:
2623 2628 on_exception(Exception, inst)
2624 2629 continue
2625 2630 else:
2626 2631 def importh(name):
2627 2632 mod = __import__(name)
2628 2633 components = name.split('.')
2629 2634 for comp in components[1:]:
2630 2635 mod = getattr(mod, comp)
2631 2636 return mod
2632 2637 try:
2633 2638 mod = importh(x[0])
2634 2639 except Exception, inst:
2635 2640 on_exception(Exception, inst)
2636 2641 continue
2637 2642
2638 2643 external.append(mod)
2639 2644 for x in external:
2640 2645 cmdtable = getattr(x, 'cmdtable', {})
2641 2646 for t in cmdtable:
2642 2647 if t in table:
2643 2648 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2644 2649 table.update(cmdtable)
2645 2650
2646 2651 try:
2647 2652 cmd, func, args, options, cmdoptions = parse(u, args)
2648 2653 except ParseError, inst:
2649 2654 if inst.args[0]:
2650 2655 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2651 2656 help_(u, inst.args[0])
2652 2657 else:
2653 2658 u.warn(_("hg: %s\n") % inst.args[1])
2654 2659 help_(u, 'shortlist')
2655 2660 sys.exit(-1)
2656 2661 except AmbiguousCommand, inst:
2657 2662 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2658 2663 sys.exit(1)
2659 2664 except UnknownCommand, inst:
2660 2665 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2661 2666 help_(u, 'shortlist')
2662 2667 sys.exit(1)
2663 2668
2664 2669 if options["time"]:
2665 2670 def get_times():
2666 2671 t = os.times()
2667 2672 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2668 2673 t = (t[0], t[1], t[2], t[3], time.clock())
2669 2674 return t
2670 2675 s = get_times()
2671 2676 def print_time():
2672 2677 t = get_times()
2673 2678 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2674 2679 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2675 2680 atexit.register(print_time)
2676 2681
2677 2682 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2678 2683 not options["noninteractive"])
2679 2684
2680 2685 # enter the debugger before command execution
2681 2686 if options['debugger']:
2682 2687 pdb.set_trace()
2683 2688
2684 2689 try:
2685 2690 try:
2686 2691 if options['help']:
2687 2692 help_(u, cmd, options['version'])
2688 2693 sys.exit(0)
2689 2694 elif options['version']:
2690 2695 show_version(u)
2691 2696 sys.exit(0)
2692 2697 elif not cmd:
2693 2698 help_(u, 'shortlist')
2694 2699 sys.exit(0)
2695 2700
2696 2701 if options['cwd']:
2697 2702 try:
2698 2703 os.chdir(options['cwd'])
2699 2704 except OSError, inst:
2700 2705 raise util.Abort('%s: %s' %
2701 2706 (options['cwd'], inst.strerror))
2702 2707
2703 2708 if cmd not in norepo.split():
2704 2709 path = options["repository"] or ""
2705 2710 repo = hg.repository(ui=u, path=path)
2706 2711 for x in external:
2707 2712 if hasattr(x, 'reposetup'):
2708 2713 x.reposetup(u, repo)
2709 2714 d = lambda: func(u, repo, *args, **cmdoptions)
2710 2715 else:
2711 2716 d = lambda: func(u, *args, **cmdoptions)
2712 2717
2713 2718 if options['profile']:
2714 2719 import hotshot, hotshot.stats
2715 2720 prof = hotshot.Profile("hg.prof")
2716 2721 r = prof.runcall(d)
2717 2722 prof.close()
2718 2723 stats = hotshot.stats.load("hg.prof")
2719 2724 stats.strip_dirs()
2720 2725 stats.sort_stats('time', 'calls')
2721 2726 stats.print_stats(40)
2722 2727 return r
2723 2728 else:
2724 2729 return d()
2725 2730 except:
2726 2731 # enter the debugger when we hit an exception
2727 2732 if options['debugger']:
2728 2733 pdb.post_mortem(sys.exc_info()[2])
2729 2734 if options['traceback']:
2730 2735 traceback.print_exc()
2731 2736 raise
2732 2737 except hg.RepoError, inst:
2733 2738 u.warn(_("abort: "), inst, "!\n")
2734 2739 except revlog.RevlogError, inst:
2735 2740 u.warn(_("abort: "), inst, "!\n")
2736 2741 except SignalInterrupt:
2737 2742 u.warn(_("killed!\n"))
2738 2743 except KeyboardInterrupt:
2739 2744 try:
2740 2745 u.warn(_("interrupted!\n"))
2741 2746 except IOError, inst:
2742 2747 if inst.errno == errno.EPIPE:
2743 2748 if u.debugflag:
2744 2749 u.warn(_("\nbroken pipe\n"))
2745 2750 else:
2746 2751 raise
2747 2752 except IOError, inst:
2748 2753 if hasattr(inst, "code"):
2749 2754 u.warn(_("abort: %s\n") % inst)
2750 2755 elif hasattr(inst, "reason"):
2751 2756 u.warn(_("abort: error: %s\n") % inst.reason[1])
2752 2757 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2753 2758 if u.debugflag:
2754 2759 u.warn(_("broken pipe\n"))
2755 2760 elif getattr(inst, "strerror", None):
2756 2761 if getattr(inst, "filename", None):
2757 2762 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2758 2763 else:
2759 2764 u.warn(_("abort: %s\n") % inst.strerror)
2760 2765 else:
2761 2766 raise
2762 2767 except OSError, inst:
2763 2768 if hasattr(inst, "filename"):
2764 2769 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2765 2770 else:
2766 2771 u.warn(_("abort: %s\n") % inst.strerror)
2767 2772 except util.Abort, inst:
2768 2773 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2769 2774 sys.exit(1)
2770 2775 except TypeError, inst:
2771 2776 # was this an argument error?
2772 2777 tb = traceback.extract_tb(sys.exc_info()[2])
2773 2778 if len(tb) > 2: # no
2774 2779 raise
2775 2780 u.debug(inst, "\n")
2776 2781 u.warn(_("%s: invalid arguments\n") % cmd)
2777 2782 help_(u, cmd)
2778 2783 except AmbiguousCommand, inst:
2779 2784 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2780 2785 help_(u, 'shortlist')
2781 2786 except UnknownCommand, inst:
2782 2787 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2783 2788 help_(u, 'shortlist')
2784 2789 except SystemExit:
2785 2790 # don't catch this in the catch-all below
2786 2791 raise
2787 2792 except:
2788 2793 u.warn(_("** unknown exception encountered, details follow\n"))
2789 2794 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2790 2795 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2791 2796 % version.get_version())
2792 2797 raise
2793 2798
2794 2799 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now