##// END OF EJS Templates
rename checkfolding to checkcase
Matt Mackall -
r6746:1dca460e default
parent child Browse files
Show More
@@ -1,137 +1,137
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 #
2 #
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 # that removes files not known to mercurial
4 # that removes files not known to mercurial
5 #
5 #
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 # (http://www.red-bean.com/cvsutils/).
7 # (http://www.red-bean.com/cvsutils/).
8 #
8 #
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 # [extensions]
10 # [extensions]
11 # hgext.purge =
11 # hgext.purge =
12 #
12 #
13 # For help on the usage of "hg purge" use:
13 # For help on the usage of "hg purge" use:
14 # hg help purge
14 # hg help purge
15 #
15 #
16 # This program is free software; you can redistribute it and/or modify
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
19 # (at your option) any later version.
20 #
20 #
21 # This program is distributed in the hope that it will be useful,
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
24 # GNU General Public License for more details.
25 #
25 #
26 # You should have received a copy of the GNU General Public License
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
29
30 from mercurial import util, commands, cmdutil
30 from mercurial import util, commands, cmdutil
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 import os
32 import os
33
33
34 def purge(ui, repo, *dirs, **opts):
34 def purge(ui, repo, *dirs, **opts):
35 '''removes files not tracked by mercurial
35 '''removes files not tracked by mercurial
36
36
37 Delete files not known to mercurial, this is useful to test local and
37 Delete files not known to mercurial, this is useful to test local and
38 uncommitted changes in the otherwise clean source tree.
38 uncommitted changes in the otherwise clean source tree.
39
39
40 This means that purge will delete:
40 This means that purge will delete:
41 - Unknown files: files marked with "?" by "hg status"
41 - Unknown files: files marked with "?" by "hg status"
42 - Ignored files: files usually ignored by Mercurial because they match
42 - Ignored files: files usually ignored by Mercurial because they match
43 a pattern in a ".hgignore" file
43 a pattern in a ".hgignore" file
44 - Empty directories: in fact Mercurial ignores directories unless they
44 - Empty directories: in fact Mercurial ignores directories unless they
45 contain files under source control managment
45 contain files under source control managment
46 But it will leave untouched:
46 But it will leave untouched:
47 - Unmodified tracked files
47 - Unmodified tracked files
48 - Modified tracked files
48 - Modified tracked files
49 - New files added to the repository (with "hg add")
49 - New files added to the repository (with "hg add")
50
50
51 If directories are given on the command line, only files in these
51 If directories are given on the command line, only files in these
52 directories are considered.
52 directories are considered.
53
53
54 Be careful with purge, you could irreversibly delete some files you
54 Be careful with purge, you could irreversibly delete some files you
55 forgot to add to the repository. If you only want to print the list of
55 forgot to add to the repository. If you only want to print the list of
56 files that this program would delete use the --print option.
56 files that this program would delete use the --print option.
57 '''
57 '''
58 act = not opts['print']
58 act = not opts['print']
59 ignored = bool(opts['all'])
59 ignored = bool(opts['all'])
60 abort_on_err = bool(opts['abort_on_err'])
60 abort_on_err = bool(opts['abort_on_err'])
61 eol = opts['print0'] and '\0' or '\n'
61 eol = opts['print0'] and '\0' or '\n'
62 if eol == '\0':
62 if eol == '\0':
63 # --print0 implies --print
63 # --print0 implies --print
64 act = False
64 act = False
65 force = bool(opts['force'])
65 force = bool(opts['force'])
66
66
67 def error(msg):
67 def error(msg):
68 if abort_on_err:
68 if abort_on_err:
69 raise util.Abort(msg)
69 raise util.Abort(msg)
70 else:
70 else:
71 ui.warn(_('warning: %s\n') % msg)
71 ui.warn(_('warning: %s\n') % msg)
72
72
73 def remove(remove_func, name):
73 def remove(remove_func, name):
74 if act:
74 if act:
75 try:
75 try:
76 remove_func(os.path.join(repo.root, name))
76 remove_func(os.path.join(repo.root, name))
77 except OSError, e:
77 except OSError, e:
78 error(_('%s cannot be removed') % name)
78 error(_('%s cannot be removed') % name)
79 else:
79 else:
80 ui.write('%s%s' % (name, eol))
80 ui.write('%s%s' % (name, eol))
81
81
82 if not force:
82 if not force:
83 _check_fs(ui, repo)
83 _check_fs(ui, repo)
84
84
85 directories = []
85 directories = []
86 files = []
86 files = []
87 match = cmdutil.match(repo, dirs, opts)
87 match = cmdutil.match(repo, dirs, opts)
88 match.dir = directories.append
88 match.dir = directories.append
89 for src, f, st in repo.dirstate.statwalk(match, ignored=ignored):
89 for src, f, st in repo.dirstate.statwalk(match, ignored=ignored):
90 if src == 'f' and f not in repo.dirstate:
90 if src == 'f' and f not in repo.dirstate:
91 files.append(f)
91 files.append(f)
92
92
93 directories.sort()
93 directories.sort()
94
94
95 for f in files:
95 for f in files:
96 if f not in repo.dirstate:
96 if f not in repo.dirstate:
97 ui.note(_('Removing file %s\n') % f)
97 ui.note(_('Removing file %s\n') % f)
98 remove(os.remove, f)
98 remove(os.remove, f)
99
99
100 for f in directories[::-1]:
100 for f in directories[::-1]:
101 if match(f) and not os.listdir(repo.wjoin(f)):
101 if match(f) and not os.listdir(repo.wjoin(f)):
102 ui.note(_('Removing directory %s\n') % f)
102 ui.note(_('Removing directory %s\n') % f)
103 remove(os.rmdir, f)
103 remove(os.rmdir, f)
104
104
105 def _check_fs(ui, repo):
105 def _check_fs(ui, repo):
106 """Abort if there is the chance of having problems with name-mangling fs
106 """Abort if there is the chance of having problems with name-mangling fs
107
107
108 In a name mangling filesystem (e.g. a case insensitive one)
108 In a name mangling filesystem (e.g. a case insensitive one)
109 dirstate.walk() can yield filenames different from the ones
109 dirstate.walk() can yield filenames different from the ones
110 stored in the dirstate. This already confuses the status and
110 stored in the dirstate. This already confuses the status and
111 add commands, but with purge this may cause data loss.
111 add commands, but with purge this may cause data loss.
112
112
113 To prevent this, this function will abort if there are uncommitted
113 To prevent this, this function will abort if there are uncommitted
114 changes.
114 changes.
115 """
115 """
116
116
117 # We can't use (files, match) to do a partial walk here - we wouldn't
117 # We can't use (files, match) to do a partial walk here - we wouldn't
118 # notice a modified README file if the user ran "hg purge readme"
118 # notice a modified README file if the user ran "hg purge readme"
119 modified, added, removed, deleted = repo.status()[:4]
119 modified, added, removed, deleted = repo.status()[:4]
120 if modified or added or removed or deleted:
120 if modified or added or removed or deleted:
121 if not util.checkfolding(repo.path) and not ui.quiet:
121 if not util.checkcase(repo.path) and not ui.quiet:
122 ui.warn(_("Purging on name mangling filesystems is not "
122 ui.warn(_("Purging on case-insensitive filesystems is not "
123 "fully supported.\n"))
123 "fully supported.\n"))
124 raise util.Abort(_("outstanding uncommitted changes"))
124 raise util.Abort(_("outstanding uncommitted changes"))
125
125
126 cmdtable = {
126 cmdtable = {
127 'purge|clean':
127 'purge|clean':
128 (purge,
128 (purge,
129 [('a', 'abort-on-err', None, _('abort if an error occurs')),
129 [('a', 'abort-on-err', None, _('abort if an error occurs')),
130 ('', 'all', None, _('purge ignored files too')),
130 ('', 'all', None, _('purge ignored files too')),
131 ('f', 'force', None, _('purge even when there are uncommitted changes')),
131 ('f', 'force', None, _('purge even when there are uncommitted changes')),
132 ('p', 'print', None, _('print the file names instead of deleting them')),
132 ('p', 'print', None, _('print the file names instead of deleting them')),
133 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
133 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
134 ' (implies -p)')),
134 ' (implies -p)')),
135 ] + commands.walkopts,
135 ] + commands.walkopts,
136 _('hg purge [OPTION]... [DIR]...'))
136 _('hg purge [OPTION]... [DIR]...'))
137 }
137 }
@@ -1,3339 +1,3339
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from repo import RepoError, NoCapability
9 from repo import RepoError, NoCapability
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import hg, util, revlog, bundlerepo, extensions, copies
12 import hg, util, revlog, bundlerepo, extensions, copies
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import version, socket
14 import version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the repository.
23 Schedule files to be version controlled and added to the repository.
24
24
25 The files will be added to the repository at the next commit. To
25 The files will be added to the repository at the next commit. To
26 undo an add before that, see hg revert.
26 undo an add before that, see hg revert.
27
27
28 If no names are given, add all files in the repository.
28 If no names are given, add all files in the repository.
29 """
29 """
30
30
31 rejected = None
31 rejected = None
32 exacts = {}
32 exacts = {}
33 names = []
33 names = []
34 m = cmdutil.match(repo, pats, opts)
34 m = cmdutil.match(repo, pats, opts)
35 m.bad = lambda x,y: True
35 m.bad = lambda x,y: True
36 for abs in repo.walk(m):
36 for abs in repo.walk(m):
37 if m.exact(abs):
37 if m.exact(abs):
38 if ui.verbose:
38 if ui.verbose:
39 ui.status(_('adding %s\n') % m.rel(abs))
39 ui.status(_('adding %s\n') % m.rel(abs))
40 names.append(abs)
40 names.append(abs)
41 exacts[abs] = 1
41 exacts[abs] = 1
42 elif abs not in repo.dirstate:
42 elif abs not in repo.dirstate:
43 ui.status(_('adding %s\n') % m.rel(abs))
43 ui.status(_('adding %s\n') % m.rel(abs))
44 names.append(abs)
44 names.append(abs)
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 rejected = repo.add(names)
46 rejected = repo.add(names)
47 rejected = [p for p in rejected if p in exacts]
47 rejected = [p for p in rejected if p in exacts]
48 return rejected and 1 or 0
48 return rejected and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the repository.
53 Add all new files and remove all missing files from the repository.
54
54
55 New files are ignored if they match any of the patterns in .hgignore. As
55 New files are ignored if they match any of the patterns in .hgignore. As
56 with add, these changes take effect at the next commit.
56 with add, these changes take effect at the next commit.
57
57
58 Use the -s option to detect renamed files. With a parameter > 0,
58 Use the -s option to detect renamed files. With a parameter > 0,
59 this compares every removed file with every added file and records
59 this compares every removed file with every added file and records
60 those similar enough as renames. This option takes a percentage
60 those similar enough as renames. This option takes a percentage
61 between 0 (disabled) and 100 (files must be identical) as its
61 between 0 (disabled) and 100 (files must be identical) as its
62 parameter. Detecting renamed files this way can be expensive.
62 parameter. Detecting renamed files this way can be expensive.
63 """
63 """
64 try:
64 try:
65 sim = float(opts.get('similarity') or 0)
65 sim = float(opts.get('similarity') or 0)
66 except ValueError:
66 except ValueError:
67 raise util.Abort(_('similarity must be a number'))
67 raise util.Abort(_('similarity must be a number'))
68 if sim < 0 or sim > 100:
68 if sim < 0 or sim > 100:
69 raise util.Abort(_('similarity must be between 0 and 100'))
69 raise util.Abort(_('similarity must be between 0 and 100'))
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71
71
72 def annotate(ui, repo, *pats, **opts):
72 def annotate(ui, repo, *pats, **opts):
73 """show changeset information per file line
73 """show changeset information per file line
74
74
75 List changes in files, showing the revision id responsible for each line
75 List changes in files, showing the revision id responsible for each line
76
76
77 This command is useful to discover who did a change or when a change took
77 This command is useful to discover who did a change or when a change took
78 place.
78 place.
79
79
80 Without the -a option, annotate will avoid processing files it
80 Without the -a option, annotate will avoid processing files it
81 detects as binary. With -a, annotate will generate an annotation
81 detects as binary. With -a, annotate will generate an annotation
82 anyway, probably with undesirable results.
82 anyway, probably with undesirable results.
83 """
83 """
84 datefunc = ui.quiet and util.shortdate or util.datestr
84 datefunc = ui.quiet and util.shortdate or util.datestr
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86
86
87 if not pats:
87 if not pats:
88 raise util.Abort(_('at least one file name or pattern required'))
88 raise util.Abort(_('at least one file name or pattern required'))
89
89
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 ('number', lambda x: str(x[0].rev())),
91 ('number', lambda x: str(x[0].rev())),
92 ('changeset', lambda x: short(x[0].node())),
92 ('changeset', lambda x: short(x[0].node())),
93 ('date', getdate),
93 ('date', getdate),
94 ('follow', lambda x: x[0].path()),
94 ('follow', lambda x: x[0].path()),
95 ]
95 ]
96
96
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 and not opts['follow']):
98 and not opts['follow']):
99 opts['number'] = 1
99 opts['number'] = 1
100
100
101 linenumber = opts.get('line_number') is not None
101 linenumber = opts.get('line_number') is not None
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104
104
105 funcmap = [func for op, func in opmap if opts.get(op)]
105 funcmap = [func for op, func in opmap if opts.get(op)]
106 if linenumber:
106 if linenumber:
107 lastfunc = funcmap[-1]
107 lastfunc = funcmap[-1]
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109
109
110 ctx = repo.changectx(opts['rev'])
110 ctx = repo.changectx(opts['rev'])
111
111
112 m = cmdutil.match(repo, pats, opts)
112 m = cmdutil.match(repo, pats, opts)
113 for abs in repo.walk(m, ctx.node()):
113 for abs in repo.walk(m, ctx.node()):
114 fctx = ctx.filectx(abs)
114 fctx = ctx.filectx(abs)
115 if not opts['text'] and util.binary(fctx.data()):
115 if not opts['text'] and util.binary(fctx.data()):
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
117 continue
117 continue
118
118
119 lines = fctx.annotate(follow=opts.get('follow'),
119 lines = fctx.annotate(follow=opts.get('follow'),
120 linenumber=linenumber)
120 linenumber=linenumber)
121 pieces = []
121 pieces = []
122
122
123 for f in funcmap:
123 for f in funcmap:
124 l = [f(n) for n, dummy in lines]
124 l = [f(n) for n, dummy in lines]
125 if l:
125 if l:
126 m = max(map(len, l))
126 m = max(map(len, l))
127 pieces.append(["%*s" % (m, x) for x in l])
127 pieces.append(["%*s" % (m, x) for x in l])
128
128
129 if pieces:
129 if pieces:
130 for p, l in zip(zip(*pieces), lines):
130 for p, l in zip(zip(*pieces), lines):
131 ui.write("%s: %s" % (" ".join(p), l[1]))
131 ui.write("%s: %s" % (" ".join(p), l[1]))
132
132
133 def archive(ui, repo, dest, **opts):
133 def archive(ui, repo, dest, **opts):
134 '''create unversioned archive of a repository revision
134 '''create unversioned archive of a repository revision
135
135
136 By default, the revision used is the parent of the working
136 By default, the revision used is the parent of the working
137 directory; use "-r" to specify a different revision.
137 directory; use "-r" to specify a different revision.
138
138
139 To specify the type of archive to create, use "-t". Valid
139 To specify the type of archive to create, use "-t". Valid
140 types are:
140 types are:
141
141
142 "files" (default): a directory full of files
142 "files" (default): a directory full of files
143 "tar": tar archive, uncompressed
143 "tar": tar archive, uncompressed
144 "tbz2": tar archive, compressed using bzip2
144 "tbz2": tar archive, compressed using bzip2
145 "tgz": tar archive, compressed using gzip
145 "tgz": tar archive, compressed using gzip
146 "uzip": zip archive, uncompressed
146 "uzip": zip archive, uncompressed
147 "zip": zip archive, compressed using deflate
147 "zip": zip archive, compressed using deflate
148
148
149 The exact name of the destination archive or directory is given
149 The exact name of the destination archive or directory is given
150 using a format string; see "hg help export" for details.
150 using a format string; see "hg help export" for details.
151
151
152 Each member added to an archive file has a directory prefix
152 Each member added to an archive file has a directory prefix
153 prepended. Use "-p" to specify a format string for the prefix.
153 prepended. Use "-p" to specify a format string for the prefix.
154 The default is the basename of the archive, with suffixes removed.
154 The default is the basename of the archive, with suffixes removed.
155 '''
155 '''
156
156
157 ctx = repo.changectx(opts['rev'])
157 ctx = repo.changectx(opts['rev'])
158 if not ctx:
158 if not ctx:
159 raise util.Abort(_('repository has no revisions'))
159 raise util.Abort(_('repository has no revisions'))
160 node = ctx.node()
160 node = ctx.node()
161 dest = cmdutil.make_filename(repo, dest, node)
161 dest = cmdutil.make_filename(repo, dest, node)
162 if os.path.realpath(dest) == repo.root:
162 if os.path.realpath(dest) == repo.root:
163 raise util.Abort(_('repository root cannot be destination'))
163 raise util.Abort(_('repository root cannot be destination'))
164 matchfn = cmdutil.match(repo, [], opts)
164 matchfn = cmdutil.match(repo, [], opts)
165 kind = opts.get('type') or 'files'
165 kind = opts.get('type') or 'files'
166 prefix = opts['prefix']
166 prefix = opts['prefix']
167 if dest == '-':
167 if dest == '-':
168 if kind == 'files':
168 if kind == 'files':
169 raise util.Abort(_('cannot archive plain files to stdout'))
169 raise util.Abort(_('cannot archive plain files to stdout'))
170 dest = sys.stdout
170 dest = sys.stdout
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 prefix = cmdutil.make_filename(repo, prefix, node)
172 prefix = cmdutil.make_filename(repo, prefix, node)
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 matchfn, prefix)
174 matchfn, prefix)
175
175
176 def backout(ui, repo, node=None, rev=None, **opts):
176 def backout(ui, repo, node=None, rev=None, **opts):
177 '''reverse effect of earlier changeset
177 '''reverse effect of earlier changeset
178
178
179 Commit the backed out changes as a new changeset. The new
179 Commit the backed out changes as a new changeset. The new
180 changeset is a child of the backed out changeset.
180 changeset is a child of the backed out changeset.
181
181
182 If you back out a changeset other than the tip, a new head is
182 If you back out a changeset other than the tip, a new head is
183 created. This head will be the new tip and you should merge this
183 created. This head will be the new tip and you should merge this
184 backout changeset with another head (current one by default).
184 backout changeset with another head (current one by default).
185
185
186 The --merge option remembers the parent of the working directory
186 The --merge option remembers the parent of the working directory
187 before starting the backout, then merges the new head with that
187 before starting the backout, then merges the new head with that
188 changeset afterwards. This saves you from doing the merge by
188 changeset afterwards. This saves you from doing the merge by
189 hand. The result of this merge is not committed, as for a normal
189 hand. The result of this merge is not committed, as for a normal
190 merge.
190 merge.
191
191
192 See \'hg help dates\' for a list of formats valid for -d/--date.
192 See \'hg help dates\' for a list of formats valid for -d/--date.
193 '''
193 '''
194 if rev and node:
194 if rev and node:
195 raise util.Abort(_("please specify just one revision"))
195 raise util.Abort(_("please specify just one revision"))
196
196
197 if not rev:
197 if not rev:
198 rev = node
198 rev = node
199
199
200 if not rev:
200 if not rev:
201 raise util.Abort(_("please specify a revision to backout"))
201 raise util.Abort(_("please specify a revision to backout"))
202
202
203 date = opts.get('date')
203 date = opts.get('date')
204 if date:
204 if date:
205 opts['date'] = util.parsedate(date)
205 opts['date'] = util.parsedate(date)
206
206
207 cmdutil.bail_if_changed(repo)
207 cmdutil.bail_if_changed(repo)
208 node = repo.lookup(rev)
208 node = repo.lookup(rev)
209
209
210 op1, op2 = repo.dirstate.parents()
210 op1, op2 = repo.dirstate.parents()
211 a = repo.changelog.ancestor(op1, node)
211 a = repo.changelog.ancestor(op1, node)
212 if a != node:
212 if a != node:
213 raise util.Abort(_('cannot back out change on a different branch'))
213 raise util.Abort(_('cannot back out change on a different branch'))
214
214
215 p1, p2 = repo.changelog.parents(node)
215 p1, p2 = repo.changelog.parents(node)
216 if p1 == nullid:
216 if p1 == nullid:
217 raise util.Abort(_('cannot back out a change with no parents'))
217 raise util.Abort(_('cannot back out a change with no parents'))
218 if p2 != nullid:
218 if p2 != nullid:
219 if not opts['parent']:
219 if not opts['parent']:
220 raise util.Abort(_('cannot back out a merge changeset without '
220 raise util.Abort(_('cannot back out a merge changeset without '
221 '--parent'))
221 '--parent'))
222 p = repo.lookup(opts['parent'])
222 p = repo.lookup(opts['parent'])
223 if p not in (p1, p2):
223 if p not in (p1, p2):
224 raise util.Abort(_('%s is not a parent of %s') %
224 raise util.Abort(_('%s is not a parent of %s') %
225 (short(p), short(node)))
225 (short(p), short(node)))
226 parent = p
226 parent = p
227 else:
227 else:
228 if opts['parent']:
228 if opts['parent']:
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 parent = p1
230 parent = p1
231
231
232 # the backout should appear on the same branch
232 # the backout should appear on the same branch
233 branch = repo.dirstate.branch()
233 branch = repo.dirstate.branch()
234 hg.clean(repo, node, show_stats=False)
234 hg.clean(repo, node, show_stats=False)
235 repo.dirstate.setbranch(branch)
235 repo.dirstate.setbranch(branch)
236 revert_opts = opts.copy()
236 revert_opts = opts.copy()
237 revert_opts['date'] = None
237 revert_opts['date'] = None
238 revert_opts['all'] = True
238 revert_opts['all'] = True
239 revert_opts['rev'] = hex(parent)
239 revert_opts['rev'] = hex(parent)
240 revert_opts['no_backup'] = None
240 revert_opts['no_backup'] = None
241 revert(ui, repo, **revert_opts)
241 revert(ui, repo, **revert_opts)
242 commit_opts = opts.copy()
242 commit_opts = opts.copy()
243 commit_opts['addremove'] = False
243 commit_opts['addremove'] = False
244 if not commit_opts['message'] and not commit_opts['logfile']:
244 if not commit_opts['message'] and not commit_opts['logfile']:
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 commit_opts['force_editor'] = True
246 commit_opts['force_editor'] = True
247 commit(ui, repo, **commit_opts)
247 commit(ui, repo, **commit_opts)
248 def nice(node):
248 def nice(node):
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 ui.status(_('changeset %s backs out changeset %s\n') %
250 ui.status(_('changeset %s backs out changeset %s\n') %
251 (nice(repo.changelog.tip()), nice(node)))
251 (nice(repo.changelog.tip()), nice(node)))
252 if op1 != node:
252 if op1 != node:
253 hg.clean(repo, op1, show_stats=False)
253 hg.clean(repo, op1, show_stats=False)
254 if opts['merge']:
254 if opts['merge']:
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
257 else:
257 else:
258 ui.status(_('the backout changeset is a new head - '
258 ui.status(_('the backout changeset is a new head - '
259 'do not forget to merge\n'))
259 'do not forget to merge\n'))
260 ui.status(_('(use "backout --merge" '
260 ui.status(_('(use "backout --merge" '
261 'if you want to auto-merge)\n'))
261 'if you want to auto-merge)\n'))
262
262
263 def bisect(ui, repo, rev=None, extra=None,
263 def bisect(ui, repo, rev=None, extra=None,
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 """subdivision search of changesets
265 """subdivision search of changesets
266
266
267 This command helps to find changesets which introduce problems.
267 This command helps to find changesets which introduce problems.
268 To use, mark the earliest changeset you know exhibits the problem
268 To use, mark the earliest changeset you know exhibits the problem
269 as bad, then mark the latest changeset which is free from the
269 as bad, then mark the latest changeset which is free from the
270 problem as good. Bisect will update your working directory to a
270 problem as good. Bisect will update your working directory to a
271 revision for testing. Once you have performed tests, mark the
271 revision for testing. Once you have performed tests, mark the
272 working directory as bad or good and bisect will either update to
272 working directory as bad or good and bisect will either update to
273 another candidate changeset or announce that it has found the bad
273 another candidate changeset or announce that it has found the bad
274 revision.
274 revision.
275 """
275 """
276 # backward compatibility
276 # backward compatibility
277 if rev in "good bad reset init".split():
277 if rev in "good bad reset init".split():
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 cmd, rev, extra = rev, extra, None
279 cmd, rev, extra = rev, extra, None
280 if cmd == "good":
280 if cmd == "good":
281 good = True
281 good = True
282 elif cmd == "bad":
282 elif cmd == "bad":
283 bad = True
283 bad = True
284 else:
284 else:
285 reset = True
285 reset = True
286 elif extra or good + bad + skip + reset > 1:
286 elif extra or good + bad + skip + reset > 1:
287 raise util.Abort("Incompatible arguments")
287 raise util.Abort("Incompatible arguments")
288
288
289 if reset:
289 if reset:
290 p = repo.join("bisect.state")
290 p = repo.join("bisect.state")
291 if os.path.exists(p):
291 if os.path.exists(p):
292 os.unlink(p)
292 os.unlink(p)
293 return
293 return
294
294
295 # load state
295 # load state
296 state = {'good': [], 'bad': [], 'skip': []}
296 state = {'good': [], 'bad': [], 'skip': []}
297 if os.path.exists(repo.join("bisect.state")):
297 if os.path.exists(repo.join("bisect.state")):
298 for l in repo.opener("bisect.state"):
298 for l in repo.opener("bisect.state"):
299 kind, node = l[:-1].split()
299 kind, node = l[:-1].split()
300 node = repo.lookup(node)
300 node = repo.lookup(node)
301 if kind not in state:
301 if kind not in state:
302 raise util.Abort(_("unknown bisect kind %s") % kind)
302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 state[kind].append(node)
303 state[kind].append(node)
304
304
305 # update state
305 # update state
306 node = repo.lookup(rev or '.')
306 node = repo.lookup(rev or '.')
307 if good:
307 if good:
308 state['good'].append(node)
308 state['good'].append(node)
309 elif bad:
309 elif bad:
310 state['bad'].append(node)
310 state['bad'].append(node)
311 elif skip:
311 elif skip:
312 state['skip'].append(node)
312 state['skip'].append(node)
313
313
314 # save state
314 # save state
315 f = repo.opener("bisect.state", "w", atomictemp=True)
315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 wlock = repo.wlock()
316 wlock = repo.wlock()
317 try:
317 try:
318 for kind in state:
318 for kind in state:
319 for node in state[kind]:
319 for node in state[kind]:
320 f.write("%s %s\n" % (kind, hex(node)))
320 f.write("%s %s\n" % (kind, hex(node)))
321 f.rename()
321 f.rename()
322 finally:
322 finally:
323 del wlock
323 del wlock
324
324
325 if not state['good'] or not state['bad']:
325 if not state['good'] or not state['bad']:
326 return
326 return
327
327
328 # actually bisect
328 # actually bisect
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 if changesets == 0:
330 if changesets == 0:
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 displayer = cmdutil.show_changeset(ui, repo, {})
332 displayer = cmdutil.show_changeset(ui, repo, {})
333 displayer.show(changenode=node)
333 displayer.show(changenode=node)
334 elif node is not None:
334 elif node is not None:
335 # compute the approximate number of remaining tests
335 # compute the approximate number of remaining tests
336 tests, size = 0, 2
336 tests, size = 0, 2
337 while size <= changesets:
337 while size <= changesets:
338 tests, size = tests + 1, size * 2
338 tests, size = tests + 1, size * 2
339 rev = repo.changelog.rev(node)
339 rev = repo.changelog.rev(node)
340 ui.write(_("Testing changeset %s:%s "
340 ui.write(_("Testing changeset %s:%s "
341 "(%s changesets remaining, ~%s tests)\n")
341 "(%s changesets remaining, ~%s tests)\n")
342 % (rev, short(node), changesets, tests))
342 % (rev, short(node), changesets, tests))
343 if not noupdate:
343 if not noupdate:
344 cmdutil.bail_if_changed(repo)
344 cmdutil.bail_if_changed(repo)
345 return hg.clean(repo, node)
345 return hg.clean(repo, node)
346
346
347 def branch(ui, repo, label=None, **opts):
347 def branch(ui, repo, label=None, **opts):
348 """set or show the current branch name
348 """set or show the current branch name
349
349
350 With no argument, show the current branch name. With one argument,
350 With no argument, show the current branch name. With one argument,
351 set the working directory branch name (the branch does not exist in
351 set the working directory branch name (the branch does not exist in
352 the repository until the next commit).
352 the repository until the next commit).
353
353
354 Unless --force is specified, branch will not let you set a
354 Unless --force is specified, branch will not let you set a
355 branch name that shadows an existing branch.
355 branch name that shadows an existing branch.
356
356
357 Use the command 'hg update' to switch to an existing branch.
357 Use the command 'hg update' to switch to an existing branch.
358 """
358 """
359
359
360 if label:
360 if label:
361 if not opts.get('force') and label in repo.branchtags():
361 if not opts.get('force') and label in repo.branchtags():
362 if label not in [p.branch() for p in repo.changectx(None).parents()]:
362 if label not in [p.branch() for p in repo.changectx(None).parents()]:
363 raise util.Abort(_('a branch of the same name already exists'
363 raise util.Abort(_('a branch of the same name already exists'
364 ' (use --force to override)'))
364 ' (use --force to override)'))
365 repo.dirstate.setbranch(util.fromlocal(label))
365 repo.dirstate.setbranch(util.fromlocal(label))
366 ui.status(_('marked working directory as branch %s\n') % label)
366 ui.status(_('marked working directory as branch %s\n') % label)
367 else:
367 else:
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369
369
370 def branches(ui, repo, active=False):
370 def branches(ui, repo, active=False):
371 """list repository named branches
371 """list repository named branches
372
372
373 List the repository's named branches, indicating which ones are
373 List the repository's named branches, indicating which ones are
374 inactive. If active is specified, only show active branches.
374 inactive. If active is specified, only show active branches.
375
375
376 A branch is considered active if it contains repository heads.
376 A branch is considered active if it contains repository heads.
377
377
378 Use the command 'hg update' to switch to an existing branch.
378 Use the command 'hg update' to switch to an existing branch.
379 """
379 """
380 hexfunc = ui.debugflag and hex or short
380 hexfunc = ui.debugflag and hex or short
381 activebranches = [util.tolocal(repo.changectx(n).branch())
381 activebranches = [util.tolocal(repo.changectx(n).branch())
382 for n in repo.heads()]
382 for n in repo.heads()]
383 branches = [(tag in activebranches, repo.changelog.rev(node), tag)
383 branches = [(tag in activebranches, repo.changelog.rev(node), tag)
384 for tag, node in repo.branchtags().items()]
384 for tag, node in repo.branchtags().items()]
385 branches.sort()
385 branches.sort()
386 branches.reverse()
386 branches.reverse()
387
387
388 for isactive, node, tag in branches:
388 for isactive, node, tag in branches:
389 if (not active) or isactive:
389 if (not active) or isactive:
390 if ui.quiet:
390 if ui.quiet:
391 ui.write("%s\n" % tag)
391 ui.write("%s\n" % tag)
392 else:
392 else:
393 rev = str(node).rjust(32 - util.locallen(tag))
393 rev = str(node).rjust(32 - util.locallen(tag))
394 isinactive = ((not isactive) and " (inactive)") or ''
394 isinactive = ((not isactive) and " (inactive)") or ''
395 data = tag, rev, hexfunc(repo.lookup(node)), isinactive
395 data = tag, rev, hexfunc(repo.lookup(node)), isinactive
396 ui.write("%s%s:%s%s\n" % data)
396 ui.write("%s%s:%s%s\n" % data)
397
397
398 def bundle(ui, repo, fname, dest=None, **opts):
398 def bundle(ui, repo, fname, dest=None, **opts):
399 """create a changegroup file
399 """create a changegroup file
400
400
401 Generate a compressed changegroup file collecting changesets not
401 Generate a compressed changegroup file collecting changesets not
402 found in the other repository.
402 found in the other repository.
403
403
404 If no destination repository is specified the destination is
404 If no destination repository is specified the destination is
405 assumed to have all the nodes specified by one or more --base
405 assumed to have all the nodes specified by one or more --base
406 parameters. To create a bundle containing all changesets, use
406 parameters. To create a bundle containing all changesets, use
407 --all (or --base null). To change the compression method applied,
407 --all (or --base null). To change the compression method applied,
408 use the -t option (by default, bundles are compressed using bz2).
408 use the -t option (by default, bundles are compressed using bz2).
409
409
410 The bundle file can then be transferred using conventional means and
410 The bundle file can then be transferred using conventional means and
411 applied to another repository with the unbundle or pull command.
411 applied to another repository with the unbundle or pull command.
412 This is useful when direct push and pull are not available or when
412 This is useful when direct push and pull are not available or when
413 exporting an entire repository is undesirable.
413 exporting an entire repository is undesirable.
414
414
415 Applying bundles preserves all changeset contents including
415 Applying bundles preserves all changeset contents including
416 permissions, copy/rename information, and revision history.
416 permissions, copy/rename information, and revision history.
417 """
417 """
418 revs = opts.get('rev') or None
418 revs = opts.get('rev') or None
419 if revs:
419 if revs:
420 revs = [repo.lookup(rev) for rev in revs]
420 revs = [repo.lookup(rev) for rev in revs]
421 if opts.get('all'):
421 if opts.get('all'):
422 base = ['null']
422 base = ['null']
423 else:
423 else:
424 base = opts.get('base')
424 base = opts.get('base')
425 if base:
425 if base:
426 if dest:
426 if dest:
427 raise util.Abort(_("--base is incompatible with specifiying "
427 raise util.Abort(_("--base is incompatible with specifiying "
428 "a destination"))
428 "a destination"))
429 base = [repo.lookup(rev) for rev in base]
429 base = [repo.lookup(rev) for rev in base]
430 # create the right base
430 # create the right base
431 # XXX: nodesbetween / changegroup* should be "fixed" instead
431 # XXX: nodesbetween / changegroup* should be "fixed" instead
432 o = []
432 o = []
433 has = {nullid: None}
433 has = {nullid: None}
434 for n in base:
434 for n in base:
435 has.update(repo.changelog.reachable(n))
435 has.update(repo.changelog.reachable(n))
436 if revs:
436 if revs:
437 visit = list(revs)
437 visit = list(revs)
438 else:
438 else:
439 visit = repo.changelog.heads()
439 visit = repo.changelog.heads()
440 seen = {}
440 seen = {}
441 while visit:
441 while visit:
442 n = visit.pop(0)
442 n = visit.pop(0)
443 parents = [p for p in repo.changelog.parents(n) if p not in has]
443 parents = [p for p in repo.changelog.parents(n) if p not in has]
444 if len(parents) == 0:
444 if len(parents) == 0:
445 o.insert(0, n)
445 o.insert(0, n)
446 else:
446 else:
447 for p in parents:
447 for p in parents:
448 if p not in seen:
448 if p not in seen:
449 seen[p] = 1
449 seen[p] = 1
450 visit.append(p)
450 visit.append(p)
451 else:
451 else:
452 cmdutil.setremoteconfig(ui, opts)
452 cmdutil.setremoteconfig(ui, opts)
453 dest, revs, checkout = hg.parseurl(
453 dest, revs, checkout = hg.parseurl(
454 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
454 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
455 other = hg.repository(ui, dest)
455 other = hg.repository(ui, dest)
456 o = repo.findoutgoing(other, force=opts['force'])
456 o = repo.findoutgoing(other, force=opts['force'])
457
457
458 if revs:
458 if revs:
459 cg = repo.changegroupsubset(o, revs, 'bundle')
459 cg = repo.changegroupsubset(o, revs, 'bundle')
460 else:
460 else:
461 cg = repo.changegroup(o, 'bundle')
461 cg = repo.changegroup(o, 'bundle')
462
462
463 bundletype = opts.get('type', 'bzip2').lower()
463 bundletype = opts.get('type', 'bzip2').lower()
464 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
464 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
465 bundletype = btypes.get(bundletype)
465 bundletype = btypes.get(bundletype)
466 if bundletype not in changegroup.bundletypes:
466 if bundletype not in changegroup.bundletypes:
467 raise util.Abort(_('unknown bundle type specified with --type'))
467 raise util.Abort(_('unknown bundle type specified with --type'))
468
468
469 changegroup.writebundle(cg, fname, bundletype)
469 changegroup.writebundle(cg, fname, bundletype)
470
470
471 def cat(ui, repo, file1, *pats, **opts):
471 def cat(ui, repo, file1, *pats, **opts):
472 """output the current or given revision of files
472 """output the current or given revision of files
473
473
474 Print the specified files as they were at the given revision.
474 Print the specified files as they were at the given revision.
475 If no revision is given, the parent of the working directory is used,
475 If no revision is given, the parent of the working directory is used,
476 or tip if no revision is checked out.
476 or tip if no revision is checked out.
477
477
478 Output may be to a file, in which case the name of the file is
478 Output may be to a file, in which case the name of the file is
479 given using a format string. The formatting rules are the same as
479 given using a format string. The formatting rules are the same as
480 for the export command, with the following additions:
480 for the export command, with the following additions:
481
481
482 %s basename of file being printed
482 %s basename of file being printed
483 %d dirname of file being printed, or '.' if in repo root
483 %d dirname of file being printed, or '.' if in repo root
484 %p root-relative path name of file being printed
484 %p root-relative path name of file being printed
485 """
485 """
486 ctx = repo.changectx(opts['rev'])
486 ctx = repo.changectx(opts['rev'])
487 err = 1
487 err = 1
488 m = cmdutil.match(repo, (file1,) + pats, opts)
488 m = cmdutil.match(repo, (file1,) + pats, opts)
489 for abs in repo.walk(m, ctx.node()):
489 for abs in repo.walk(m, ctx.node()):
490 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
490 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
491 data = ctx.filectx(abs).data()
491 data = ctx.filectx(abs).data()
492 if opts.get('decode'):
492 if opts.get('decode'):
493 data = repo.wwritedata(abs, data)
493 data = repo.wwritedata(abs, data)
494 fp.write(data)
494 fp.write(data)
495 err = 0
495 err = 0
496 return err
496 return err
497
497
498 def clone(ui, source, dest=None, **opts):
498 def clone(ui, source, dest=None, **opts):
499 """make a copy of an existing repository
499 """make a copy of an existing repository
500
500
501 Create a copy of an existing repository in a new directory.
501 Create a copy of an existing repository in a new directory.
502
502
503 If no destination directory name is specified, it defaults to the
503 If no destination directory name is specified, it defaults to the
504 basename of the source.
504 basename of the source.
505
505
506 The location of the source is added to the new repository's
506 The location of the source is added to the new repository's
507 .hg/hgrc file, as the default to be used for future pulls.
507 .hg/hgrc file, as the default to be used for future pulls.
508
508
509 For efficiency, hardlinks are used for cloning whenever the source
509 For efficiency, hardlinks are used for cloning whenever the source
510 and destination are on the same filesystem (note this applies only
510 and destination are on the same filesystem (note this applies only
511 to the repository data, not to the checked out files). Some
511 to the repository data, not to the checked out files). Some
512 filesystems, such as AFS, implement hardlinking incorrectly, but
512 filesystems, such as AFS, implement hardlinking incorrectly, but
513 do not report errors. In these cases, use the --pull option to
513 do not report errors. In these cases, use the --pull option to
514 avoid hardlinking.
514 avoid hardlinking.
515
515
516 In some cases, you can clone repositories and checked out files
516 In some cases, you can clone repositories and checked out files
517 using full hardlinks with
517 using full hardlinks with
518
518
519 $ cp -al REPO REPOCLONE
519 $ cp -al REPO REPOCLONE
520
520
521 This is the fastest way to clone, but it is not always safe. The
521 This is the fastest way to clone, but it is not always safe. The
522 operation is not atomic (making sure REPO is not modified during
522 operation is not atomic (making sure REPO is not modified during
523 the operation is up to you) and you have to make sure your editor
523 the operation is up to you) and you have to make sure your editor
524 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
524 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
525 this is not compatible with certain extensions that place their
525 this is not compatible with certain extensions that place their
526 metadata under the .hg directory, such as mq.
526 metadata under the .hg directory, such as mq.
527
527
528 If you use the -r option to clone up to a specific revision, no
528 If you use the -r option to clone up to a specific revision, no
529 subsequent revisions will be present in the cloned repository.
529 subsequent revisions will be present in the cloned repository.
530 This option implies --pull, even on local repositories.
530 This option implies --pull, even on local repositories.
531
531
532 If the -U option is used, the new clone will contain only a repository
532 If the -U option is used, the new clone will contain only a repository
533 (.hg) and no working copy (the working copy parent is the null revision).
533 (.hg) and no working copy (the working copy parent is the null revision).
534
534
535 See pull for valid source format details.
535 See pull for valid source format details.
536
536
537 It is possible to specify an ssh:// URL as the destination, but no
537 It is possible to specify an ssh:// URL as the destination, but no
538 .hg/hgrc and working directory will be created on the remote side.
538 .hg/hgrc and working directory will be created on the remote side.
539 Look at the help text for the pull command for important details
539 Look at the help text for the pull command for important details
540 about ssh:// URLs.
540 about ssh:// URLs.
541 """
541 """
542 cmdutil.setremoteconfig(ui, opts)
542 cmdutil.setremoteconfig(ui, opts)
543 hg.clone(ui, source, dest,
543 hg.clone(ui, source, dest,
544 pull=opts['pull'],
544 pull=opts['pull'],
545 stream=opts['uncompressed'],
545 stream=opts['uncompressed'],
546 rev=opts['rev'],
546 rev=opts['rev'],
547 update=not opts['noupdate'])
547 update=not opts['noupdate'])
548
548
549 def commit(ui, repo, *pats, **opts):
549 def commit(ui, repo, *pats, **opts):
550 """commit the specified files or all outstanding changes
550 """commit the specified files or all outstanding changes
551
551
552 Commit changes to the given files into the repository.
552 Commit changes to the given files into the repository.
553
553
554 If a list of files is omitted, all changes reported by "hg status"
554 If a list of files is omitted, all changes reported by "hg status"
555 will be committed.
555 will be committed.
556
556
557 If you are committing the result of a merge, do not provide any
557 If you are committing the result of a merge, do not provide any
558 file names or -I/-X filters.
558 file names or -I/-X filters.
559
559
560 If no commit message is specified, the configured editor is started to
560 If no commit message is specified, the configured editor is started to
561 enter a message.
561 enter a message.
562
562
563 See 'hg help dates' for a list of formats valid for -d/--date.
563 See 'hg help dates' for a list of formats valid for -d/--date.
564 """
564 """
565 def commitfunc(ui, repo, message, match, opts):
565 def commitfunc(ui, repo, message, match, opts):
566 return repo.commit(match.files(), message, opts['user'], opts['date'],
566 return repo.commit(match.files(), message, opts['user'], opts['date'],
567 match, force_editor=opts.get('force_editor'))
567 match, force_editor=opts.get('force_editor'))
568
568
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
570 if not node:
570 if not node:
571 return
571 return
572 cl = repo.changelog
572 cl = repo.changelog
573 rev = cl.rev(node)
573 rev = cl.rev(node)
574 parents = cl.parentrevs(rev)
574 parents = cl.parentrevs(rev)
575 if rev - 1 in parents:
575 if rev - 1 in parents:
576 # one of the parents was the old tip
576 # one of the parents was the old tip
577 return
577 return
578 if (parents == (nullrev, nullrev) or
578 if (parents == (nullrev, nullrev) or
579 len(cl.heads(cl.node(parents[0]))) > 1 and
579 len(cl.heads(cl.node(parents[0]))) > 1 and
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
581 ui.status(_('created new head\n'))
581 ui.status(_('created new head\n'))
582
582
583 def copy(ui, repo, *pats, **opts):
583 def copy(ui, repo, *pats, **opts):
584 """mark files as copied for the next commit
584 """mark files as copied for the next commit
585
585
586 Mark dest as having copies of source files. If dest is a
586 Mark dest as having copies of source files. If dest is a
587 directory, copies are put in that directory. If dest is a file,
587 directory, copies are put in that directory. If dest is a file,
588 there can only be one source.
588 there can only be one source.
589
589
590 By default, this command copies the contents of files as they
590 By default, this command copies the contents of files as they
591 stand in the working directory. If invoked with --after, the
591 stand in the working directory. If invoked with --after, the
592 operation is recorded, but no copying is performed.
592 operation is recorded, but no copying is performed.
593
593
594 This command takes effect in the next commit. To undo a copy
594 This command takes effect in the next commit. To undo a copy
595 before that, see hg revert.
595 before that, see hg revert.
596 """
596 """
597 wlock = repo.wlock(False)
597 wlock = repo.wlock(False)
598 try:
598 try:
599 return cmdutil.copy(ui, repo, pats, opts)
599 return cmdutil.copy(ui, repo, pats, opts)
600 finally:
600 finally:
601 del wlock
601 del wlock
602
602
603 def debugancestor(ui, repo, *args):
603 def debugancestor(ui, repo, *args):
604 """find the ancestor revision of two revisions in a given index"""
604 """find the ancestor revision of two revisions in a given index"""
605 if len(args) == 3:
605 if len(args) == 3:
606 index, rev1, rev2 = args
606 index, rev1, rev2 = args
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
608 lookup = r.lookup
608 lookup = r.lookup
609 elif len(args) == 2:
609 elif len(args) == 2:
610 if not repo:
610 if not repo:
611 raise util.Abort(_("There is no Mercurial repository here "
611 raise util.Abort(_("There is no Mercurial repository here "
612 "(.hg not found)"))
612 "(.hg not found)"))
613 rev1, rev2 = args
613 rev1, rev2 = args
614 r = repo.changelog
614 r = repo.changelog
615 lookup = repo.lookup
615 lookup = repo.lookup
616 else:
616 else:
617 raise util.Abort(_('either two or three arguments required'))
617 raise util.Abort(_('either two or three arguments required'))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
620
620
621 def debugcomplete(ui, cmd='', **opts):
621 def debugcomplete(ui, cmd='', **opts):
622 """returns the completion list associated with the given command"""
622 """returns the completion list associated with the given command"""
623
623
624 if opts['options']:
624 if opts['options']:
625 options = []
625 options = []
626 otables = [globalopts]
626 otables = [globalopts]
627 if cmd:
627 if cmd:
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
629 otables.append(entry[1])
629 otables.append(entry[1])
630 for t in otables:
630 for t in otables:
631 for o in t:
631 for o in t:
632 if o[0]:
632 if o[0]:
633 options.append('-%s' % o[0])
633 options.append('-%s' % o[0])
634 options.append('--%s' % o[1])
634 options.append('--%s' % o[1])
635 ui.write("%s\n" % "\n".join(options))
635 ui.write("%s\n" % "\n".join(options))
636 return
636 return
637
637
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
639 clist.sort()
639 clist.sort()
640 ui.write("%s\n" % "\n".join(clist))
640 ui.write("%s\n" % "\n".join(clist))
641
641
642 def debugfsinfo(ui, path = "."):
642 def debugfsinfo(ui, path = "."):
643 file('.debugfsinfo', 'w').write('')
643 file('.debugfsinfo', 'w').write('')
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
646 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
647 and 'yes' or 'no'))
647 and 'yes' or 'no'))
648 os.unlink('.debugfsinfo')
648 os.unlink('.debugfsinfo')
649
649
650 def debugrebuildstate(ui, repo, rev=""):
650 def debugrebuildstate(ui, repo, rev=""):
651 """rebuild the dirstate as it would look like for the given revision"""
651 """rebuild the dirstate as it would look like for the given revision"""
652 if rev == "":
652 if rev == "":
653 rev = repo.changelog.tip()
653 rev = repo.changelog.tip()
654 ctx = repo.changectx(rev)
654 ctx = repo.changectx(rev)
655 files = ctx.manifest()
655 files = ctx.manifest()
656 wlock = repo.wlock()
656 wlock = repo.wlock()
657 try:
657 try:
658 repo.dirstate.rebuild(rev, files)
658 repo.dirstate.rebuild(rev, files)
659 finally:
659 finally:
660 del wlock
660 del wlock
661
661
662 def debugcheckstate(ui, repo):
662 def debugcheckstate(ui, repo):
663 """validate the correctness of the current dirstate"""
663 """validate the correctness of the current dirstate"""
664 parent1, parent2 = repo.dirstate.parents()
664 parent1, parent2 = repo.dirstate.parents()
665 m1 = repo.changectx(parent1).manifest()
665 m1 = repo.changectx(parent1).manifest()
666 m2 = repo.changectx(parent2).manifest()
666 m2 = repo.changectx(parent2).manifest()
667 errors = 0
667 errors = 0
668 for f in repo.dirstate:
668 for f in repo.dirstate:
669 state = repo.dirstate[f]
669 state = repo.dirstate[f]
670 if state in "nr" and f not in m1:
670 if state in "nr" and f not in m1:
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
672 errors += 1
672 errors += 1
673 if state in "a" and f in m1:
673 if state in "a" and f in m1:
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
675 errors += 1
675 errors += 1
676 if state in "m" and f not in m1 and f not in m2:
676 if state in "m" and f not in m1 and f not in m2:
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
678 (f, state))
678 (f, state))
679 errors += 1
679 errors += 1
680 for f in m1:
680 for f in m1:
681 state = repo.dirstate[f]
681 state = repo.dirstate[f]
682 if state not in "nrm":
682 if state not in "nrm":
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
684 errors += 1
684 errors += 1
685 if errors:
685 if errors:
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
687 raise util.Abort(error)
687 raise util.Abort(error)
688
688
689 def showconfig(ui, repo, *values, **opts):
689 def showconfig(ui, repo, *values, **opts):
690 """show combined config settings from all hgrc files
690 """show combined config settings from all hgrc files
691
691
692 With no args, print names and values of all config items.
692 With no args, print names and values of all config items.
693
693
694 With one arg of the form section.name, print just the value of
694 With one arg of the form section.name, print just the value of
695 that config item.
695 that config item.
696
696
697 With multiple args, print names and values of all config items
697 With multiple args, print names and values of all config items
698 with matching section names."""
698 with matching section names."""
699
699
700 untrusted = bool(opts.get('untrusted'))
700 untrusted = bool(opts.get('untrusted'))
701 if values:
701 if values:
702 if len([v for v in values if '.' in v]) > 1:
702 if len([v for v in values if '.' in v]) > 1:
703 raise util.Abort(_('only one config item permitted'))
703 raise util.Abort(_('only one config item permitted'))
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
705 sectname = section + '.' + name
705 sectname = section + '.' + name
706 if values:
706 if values:
707 for v in values:
707 for v in values:
708 if v == section:
708 if v == section:
709 ui.write('%s=%s\n' % (sectname, value))
709 ui.write('%s=%s\n' % (sectname, value))
710 elif v == sectname:
710 elif v == sectname:
711 ui.write(value, '\n')
711 ui.write(value, '\n')
712 else:
712 else:
713 ui.write('%s=%s\n' % (sectname, value))
713 ui.write('%s=%s\n' % (sectname, value))
714
714
715 def debugsetparents(ui, repo, rev1, rev2=None):
715 def debugsetparents(ui, repo, rev1, rev2=None):
716 """manually set the parents of the current working directory
716 """manually set the parents of the current working directory
717
717
718 This is useful for writing repository conversion tools, but should
718 This is useful for writing repository conversion tools, but should
719 be used with care.
719 be used with care.
720 """
720 """
721
721
722 if not rev2:
722 if not rev2:
723 rev2 = hex(nullid)
723 rev2 = hex(nullid)
724
724
725 wlock = repo.wlock()
725 wlock = repo.wlock()
726 try:
726 try:
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
728 finally:
728 finally:
729 del wlock
729 del wlock
730
730
731 def debugstate(ui, repo, nodates=None):
731 def debugstate(ui, repo, nodates=None):
732 """show the contents of the current dirstate"""
732 """show the contents of the current dirstate"""
733 k = repo.dirstate._map.items()
733 k = repo.dirstate._map.items()
734 k.sort()
734 k.sort()
735 timestr = ""
735 timestr = ""
736 showdate = not nodates
736 showdate = not nodates
737 for file_, ent in k:
737 for file_, ent in k:
738 if showdate:
738 if showdate:
739 if ent[3] == -1:
739 if ent[3] == -1:
740 # Pad or slice to locale representation
740 # Pad or slice to locale representation
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
742 timestr = 'unset'
742 timestr = 'unset'
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
744 else:
744 else:
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
746 if ent[1] & 020000:
746 if ent[1] & 020000:
747 mode = 'lnk'
747 mode = 'lnk'
748 else:
748 else:
749 mode = '%3o' % (ent[1] & 0777)
749 mode = '%3o' % (ent[1] & 0777)
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 for f in repo.dirstate.copies():
751 for f in repo.dirstate.copies():
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753
753
754 def debugdata(ui, file_, rev):
754 def debugdata(ui, file_, rev):
755 """dump the contents of a data file revision"""
755 """dump the contents of a data file revision"""
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
757 try:
757 try:
758 ui.write(r.revision(r.lookup(rev)))
758 ui.write(r.revision(r.lookup(rev)))
759 except KeyError:
759 except KeyError:
760 raise util.Abort(_('invalid revision identifier %s') % rev)
760 raise util.Abort(_('invalid revision identifier %s') % rev)
761
761
762 def debugdate(ui, date, range=None, **opts):
762 def debugdate(ui, date, range=None, **opts):
763 """parse and display a date"""
763 """parse and display a date"""
764 if opts["extended"]:
764 if opts["extended"]:
765 d = util.parsedate(date, util.extendeddateformats)
765 d = util.parsedate(date, util.extendeddateformats)
766 else:
766 else:
767 d = util.parsedate(date)
767 d = util.parsedate(date)
768 ui.write("internal: %s %s\n" % d)
768 ui.write("internal: %s %s\n" % d)
769 ui.write("standard: %s\n" % util.datestr(d))
769 ui.write("standard: %s\n" % util.datestr(d))
770 if range:
770 if range:
771 m = util.matchdate(range)
771 m = util.matchdate(range)
772 ui.write("match: %s\n" % m(d[0]))
772 ui.write("match: %s\n" % m(d[0]))
773
773
774 def debugindex(ui, file_):
774 def debugindex(ui, file_):
775 """dump the contents of an index file"""
775 """dump the contents of an index file"""
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 ui.write(" rev offset length base linkrev" +
777 ui.write(" rev offset length base linkrev" +
778 " nodeid p1 p2\n")
778 " nodeid p1 p2\n")
779 for i in xrange(r.count()):
779 for i in xrange(r.count()):
780 node = r.node(i)
780 node = r.node(i)
781 try:
781 try:
782 pp = r.parents(node)
782 pp = r.parents(node)
783 except:
783 except:
784 pp = [nullid, nullid]
784 pp = [nullid, nullid]
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
787 short(node), short(pp[0]), short(pp[1])))
787 short(node), short(pp[0]), short(pp[1])))
788
788
789 def debugindexdot(ui, file_):
789 def debugindexdot(ui, file_):
790 """dump an index DAG as a .dot file"""
790 """dump an index DAG as a .dot file"""
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
792 ui.write("digraph G {\n")
792 ui.write("digraph G {\n")
793 for i in xrange(r.count()):
793 for i in xrange(r.count()):
794 node = r.node(i)
794 node = r.node(i)
795 pp = r.parents(node)
795 pp = r.parents(node)
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
797 if pp[1] != nullid:
797 if pp[1] != nullid:
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
799 ui.write("}\n")
799 ui.write("}\n")
800
800
801 def debuginstall(ui):
801 def debuginstall(ui):
802 '''test Mercurial installation'''
802 '''test Mercurial installation'''
803
803
804 def writetemp(contents):
804 def writetemp(contents):
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
806 f = os.fdopen(fd, "wb")
806 f = os.fdopen(fd, "wb")
807 f.write(contents)
807 f.write(contents)
808 f.close()
808 f.close()
809 return name
809 return name
810
810
811 problems = 0
811 problems = 0
812
812
813 # encoding
813 # encoding
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
815 try:
815 try:
816 util.fromlocal("test")
816 util.fromlocal("test")
817 except util.Abort, inst:
817 except util.Abort, inst:
818 ui.write(" %s\n" % inst)
818 ui.write(" %s\n" % inst)
819 ui.write(_(" (check that your locale is properly set)\n"))
819 ui.write(_(" (check that your locale is properly set)\n"))
820 problems += 1
820 problems += 1
821
821
822 # compiled modules
822 # compiled modules
823 ui.status(_("Checking extensions...\n"))
823 ui.status(_("Checking extensions...\n"))
824 try:
824 try:
825 import bdiff, mpatch, base85
825 import bdiff, mpatch, base85
826 except Exception, inst:
826 except Exception, inst:
827 ui.write(" %s\n" % inst)
827 ui.write(" %s\n" % inst)
828 ui.write(_(" One or more extensions could not be found"))
828 ui.write(_(" One or more extensions could not be found"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
830 problems += 1
830 problems += 1
831
831
832 # templates
832 # templates
833 ui.status(_("Checking templates...\n"))
833 ui.status(_("Checking templates...\n"))
834 try:
834 try:
835 import templater
835 import templater
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
837 except Exception, inst:
837 except Exception, inst:
838 ui.write(" %s\n" % inst)
838 ui.write(" %s\n" % inst)
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
840 problems += 1
840 problems += 1
841
841
842 # patch
842 # patch
843 ui.status(_("Checking patch...\n"))
843 ui.status(_("Checking patch...\n"))
844 patchproblems = 0
844 patchproblems = 0
845 a = "1\n2\n3\n4\n"
845 a = "1\n2\n3\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
847 fa = writetemp(a)
847 fa = writetemp(a)
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
849 os.path.basename(fa))
849 os.path.basename(fa))
850 fd = writetemp(d)
850 fd = writetemp(d)
851
851
852 files = {}
852 files = {}
853 try:
853 try:
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
855 except util.Abort, e:
855 except util.Abort, e:
856 ui.write(_(" patch call failed:\n"))
856 ui.write(_(" patch call failed:\n"))
857 ui.write(" " + str(e) + "\n")
857 ui.write(" " + str(e) + "\n")
858 patchproblems += 1
858 patchproblems += 1
859 else:
859 else:
860 if list(files) != [os.path.basename(fa)]:
860 if list(files) != [os.path.basename(fa)]:
861 ui.write(_(" unexpected patch output!\n"))
861 ui.write(_(" unexpected patch output!\n"))
862 patchproblems += 1
862 patchproblems += 1
863 a = file(fa).read()
863 a = file(fa).read()
864 if a != b:
864 if a != b:
865 ui.write(_(" patch test failed!\n"))
865 ui.write(_(" patch test failed!\n"))
866 patchproblems += 1
866 patchproblems += 1
867
867
868 if patchproblems:
868 if patchproblems:
869 if ui.config('ui', 'patch'):
869 if ui.config('ui', 'patch'):
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
871 " or misconfigured. Please check your .hgrc file)\n"))
871 " or misconfigured. Please check your .hgrc file)\n"))
872 else:
872 else:
873 ui.write(_(" Internal patcher failure, please report this error"
873 ui.write(_(" Internal patcher failure, please report this error"
874 " to http://www.selenic.com/mercurial/bts\n"))
874 " to http://www.selenic.com/mercurial/bts\n"))
875 problems += patchproblems
875 problems += patchproblems
876
876
877 os.unlink(fa)
877 os.unlink(fa)
878 os.unlink(fd)
878 os.unlink(fd)
879
879
880 # editor
880 # editor
881 ui.status(_("Checking commit editor...\n"))
881 ui.status(_("Checking commit editor...\n"))
882 editor = ui.geteditor()
882 editor = ui.geteditor()
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
884 if not cmdpath:
884 if not cmdpath:
885 if editor == 'vi':
885 if editor == 'vi':
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
888 else:
888 else:
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
891 problems += 1
891 problems += 1
892
892
893 # check username
893 # check username
894 ui.status(_("Checking username...\n"))
894 ui.status(_("Checking username...\n"))
895 user = os.environ.get("HGUSER")
895 user = os.environ.get("HGUSER")
896 if user is None:
896 if user is None:
897 user = ui.config("ui", "username")
897 user = ui.config("ui", "username")
898 if user is None:
898 if user is None:
899 user = os.environ.get("EMAIL")
899 user = os.environ.get("EMAIL")
900 if not user:
900 if not user:
901 ui.warn(" ")
901 ui.warn(" ")
902 ui.username()
902 ui.username()
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
904
904
905 if not problems:
905 if not problems:
906 ui.status(_("No problems detected\n"))
906 ui.status(_("No problems detected\n"))
907 else:
907 else:
908 ui.write(_("%s problems detected,"
908 ui.write(_("%s problems detected,"
909 " please check your install!\n") % problems)
909 " please check your install!\n") % problems)
910
910
911 return problems
911 return problems
912
912
913 def debugrename(ui, repo, file1, *pats, **opts):
913 def debugrename(ui, repo, file1, *pats, **opts):
914 """dump rename information"""
914 """dump rename information"""
915
915
916 ctx = repo.changectx(opts.get('rev', 'tip'))
916 ctx = repo.changectx(opts.get('rev', 'tip'))
917 m = cmdutil.match(repo, (file1,) + pats, opts)
917 m = cmdutil.match(repo, (file1,) + pats, opts)
918 for abs in repo.walk(m, ctx.node()):
918 for abs in repo.walk(m, ctx.node()):
919 fctx = ctx.filectx(abs)
919 fctx = ctx.filectx(abs)
920 o = fctx.filelog().renamed(fctx.filenode())
920 o = fctx.filelog().renamed(fctx.filenode())
921 rel = m.rel(abs)
921 rel = m.rel(abs)
922 if o:
922 if o:
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
924 else:
924 else:
925 ui.write(_("%s not renamed\n") % rel)
925 ui.write(_("%s not renamed\n") % rel)
926
926
927 def debugwalk(ui, repo, *pats, **opts):
927 def debugwalk(ui, repo, *pats, **opts):
928 """show how files match on given patterns"""
928 """show how files match on given patterns"""
929 m = cmdutil.match(repo, pats, opts)
929 m = cmdutil.match(repo, pats, opts)
930 items = list(repo.walk(m))
930 items = list(repo.walk(m))
931 if not items:
931 if not items:
932 return
932 return
933 fmt = 'f %%-%ds %%-%ds %%s' % (
933 fmt = 'f %%-%ds %%-%ds %%s' % (
934 max([len(abs) for abs in items]),
934 max([len(abs) for abs in items]),
935 max([len(m.rel(abs)) for abs in items]))
935 max([len(m.rel(abs)) for abs in items]))
936 for abs in items:
936 for abs in items:
937 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
937 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
938 ui.write("%s\n" % line.rstrip())
938 ui.write("%s\n" % line.rstrip())
939
939
940 def diff(ui, repo, *pats, **opts):
940 def diff(ui, repo, *pats, **opts):
941 """diff repository (or selected files)
941 """diff repository (or selected files)
942
942
943 Show differences between revisions for the specified files.
943 Show differences between revisions for the specified files.
944
944
945 Differences between files are shown using the unified diff format.
945 Differences between files are shown using the unified diff format.
946
946
947 NOTE: diff may generate unexpected results for merges, as it will
947 NOTE: diff may generate unexpected results for merges, as it will
948 default to comparing against the working directory's first parent
948 default to comparing against the working directory's first parent
949 changeset if no revisions are specified.
949 changeset if no revisions are specified.
950
950
951 When two revision arguments are given, then changes are shown
951 When two revision arguments are given, then changes are shown
952 between those revisions. If only one revision is specified then
952 between those revisions. If only one revision is specified then
953 that revision is compared to the working directory, and, when no
953 that revision is compared to the working directory, and, when no
954 revisions are specified, the working directory files are compared
954 revisions are specified, the working directory files are compared
955 to its parent.
955 to its parent.
956
956
957 Without the -a option, diff will avoid generating diffs of files
957 Without the -a option, diff will avoid generating diffs of files
958 it detects as binary. With -a, diff will generate a diff anyway,
958 it detects as binary. With -a, diff will generate a diff anyway,
959 probably with undesirable results.
959 probably with undesirable results.
960 """
960 """
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
962
962
963 m = cmdutil.match(repo, pats, opts)
963 m = cmdutil.match(repo, pats, opts)
964 patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
964 patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
965
965
966 def export(ui, repo, *changesets, **opts):
966 def export(ui, repo, *changesets, **opts):
967 """dump the header and diffs for one or more changesets
967 """dump the header and diffs for one or more changesets
968
968
969 Print the changeset header and diffs for one or more revisions.
969 Print the changeset header and diffs for one or more revisions.
970
970
971 The information shown in the changeset header is: author,
971 The information shown in the changeset header is: author,
972 changeset hash, parent(s) and commit comment.
972 changeset hash, parent(s) and commit comment.
973
973
974 NOTE: export may generate unexpected diff output for merge changesets,
974 NOTE: export may generate unexpected diff output for merge changesets,
975 as it will compare the merge changeset against its first parent only.
975 as it will compare the merge changeset against its first parent only.
976
976
977 Output may be to a file, in which case the name of the file is
977 Output may be to a file, in which case the name of the file is
978 given using a format string. The formatting rules are as follows:
978 given using a format string. The formatting rules are as follows:
979
979
980 %% literal "%" character
980 %% literal "%" character
981 %H changeset hash (40 bytes of hexadecimal)
981 %H changeset hash (40 bytes of hexadecimal)
982 %N number of patches being generated
982 %N number of patches being generated
983 %R changeset revision number
983 %R changeset revision number
984 %b basename of the exporting repository
984 %b basename of the exporting repository
985 %h short-form changeset hash (12 bytes of hexadecimal)
985 %h short-form changeset hash (12 bytes of hexadecimal)
986 %n zero-padded sequence number, starting at 1
986 %n zero-padded sequence number, starting at 1
987 %r zero-padded changeset revision number
987 %r zero-padded changeset revision number
988
988
989 Without the -a option, export will avoid generating diffs of files
989 Without the -a option, export will avoid generating diffs of files
990 it detects as binary. With -a, export will generate a diff anyway,
990 it detects as binary. With -a, export will generate a diff anyway,
991 probably with undesirable results.
991 probably with undesirable results.
992
992
993 With the --switch-parent option, the diff will be against the second
993 With the --switch-parent option, the diff will be against the second
994 parent. It can be useful to review a merge.
994 parent. It can be useful to review a merge.
995 """
995 """
996 if not changesets:
996 if not changesets:
997 raise util.Abort(_("export requires at least one changeset"))
997 raise util.Abort(_("export requires at least one changeset"))
998 revs = cmdutil.revrange(repo, changesets)
998 revs = cmdutil.revrange(repo, changesets)
999 if len(revs) > 1:
999 if len(revs) > 1:
1000 ui.note(_('exporting patches:\n'))
1000 ui.note(_('exporting patches:\n'))
1001 else:
1001 else:
1002 ui.note(_('exporting patch:\n'))
1002 ui.note(_('exporting patch:\n'))
1003 patch.export(repo, revs, template=opts['output'],
1003 patch.export(repo, revs, template=opts['output'],
1004 switch_parent=opts['switch_parent'],
1004 switch_parent=opts['switch_parent'],
1005 opts=patch.diffopts(ui, opts))
1005 opts=patch.diffopts(ui, opts))
1006
1006
1007 def grep(ui, repo, pattern, *pats, **opts):
1007 def grep(ui, repo, pattern, *pats, **opts):
1008 """search for a pattern in specified files and revisions
1008 """search for a pattern in specified files and revisions
1009
1009
1010 Search revisions of files for a regular expression.
1010 Search revisions of files for a regular expression.
1011
1011
1012 This command behaves differently than Unix grep. It only accepts
1012 This command behaves differently than Unix grep. It only accepts
1013 Python/Perl regexps. It searches repository history, not the
1013 Python/Perl regexps. It searches repository history, not the
1014 working directory. It always prints the revision number in which
1014 working directory. It always prints the revision number in which
1015 a match appears.
1015 a match appears.
1016
1016
1017 By default, grep only prints output for the first revision of a
1017 By default, grep only prints output for the first revision of a
1018 file in which it finds a match. To get it to print every revision
1018 file in which it finds a match. To get it to print every revision
1019 that contains a change in match status ("-" for a match that
1019 that contains a change in match status ("-" for a match that
1020 becomes a non-match, or "+" for a non-match that becomes a match),
1020 becomes a non-match, or "+" for a non-match that becomes a match),
1021 use the --all flag.
1021 use the --all flag.
1022 """
1022 """
1023 reflags = 0
1023 reflags = 0
1024 if opts['ignore_case']:
1024 if opts['ignore_case']:
1025 reflags |= re.I
1025 reflags |= re.I
1026 try:
1026 try:
1027 regexp = re.compile(pattern, reflags)
1027 regexp = re.compile(pattern, reflags)
1028 except Exception, inst:
1028 except Exception, inst:
1029 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1029 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1030 return None
1030 return None
1031 sep, eol = ':', '\n'
1031 sep, eol = ':', '\n'
1032 if opts['print0']:
1032 if opts['print0']:
1033 sep = eol = '\0'
1033 sep = eol = '\0'
1034
1034
1035 fcache = {}
1035 fcache = {}
1036 def getfile(fn):
1036 def getfile(fn):
1037 if fn not in fcache:
1037 if fn not in fcache:
1038 fcache[fn] = repo.file(fn)
1038 fcache[fn] = repo.file(fn)
1039 return fcache[fn]
1039 return fcache[fn]
1040
1040
1041 def matchlines(body):
1041 def matchlines(body):
1042 begin = 0
1042 begin = 0
1043 linenum = 0
1043 linenum = 0
1044 while True:
1044 while True:
1045 match = regexp.search(body, begin)
1045 match = regexp.search(body, begin)
1046 if not match:
1046 if not match:
1047 break
1047 break
1048 mstart, mend = match.span()
1048 mstart, mend = match.span()
1049 linenum += body.count('\n', begin, mstart) + 1
1049 linenum += body.count('\n', begin, mstart) + 1
1050 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1050 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1051 lend = body.find('\n', mend)
1051 lend = body.find('\n', mend)
1052 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1052 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1053 begin = lend + 1
1053 begin = lend + 1
1054
1054
1055 class linestate(object):
1055 class linestate(object):
1056 def __init__(self, line, linenum, colstart, colend):
1056 def __init__(self, line, linenum, colstart, colend):
1057 self.line = line
1057 self.line = line
1058 self.linenum = linenum
1058 self.linenum = linenum
1059 self.colstart = colstart
1059 self.colstart = colstart
1060 self.colend = colend
1060 self.colend = colend
1061
1061
1062 def __hash__(self):
1062 def __hash__(self):
1063 return hash((self.linenum, self.line))
1063 return hash((self.linenum, self.line))
1064
1064
1065 def __eq__(self, other):
1065 def __eq__(self, other):
1066 return self.line == other.line
1066 return self.line == other.line
1067
1067
1068 matches = {}
1068 matches = {}
1069 copies = {}
1069 copies = {}
1070 def grepbody(fn, rev, body):
1070 def grepbody(fn, rev, body):
1071 matches[rev].setdefault(fn, [])
1071 matches[rev].setdefault(fn, [])
1072 m = matches[rev][fn]
1072 m = matches[rev][fn]
1073 for lnum, cstart, cend, line in matchlines(body):
1073 for lnum, cstart, cend, line in matchlines(body):
1074 s = linestate(line, lnum, cstart, cend)
1074 s = linestate(line, lnum, cstart, cend)
1075 m.append(s)
1075 m.append(s)
1076
1076
1077 def difflinestates(a, b):
1077 def difflinestates(a, b):
1078 sm = difflib.SequenceMatcher(None, a, b)
1078 sm = difflib.SequenceMatcher(None, a, b)
1079 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1079 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1080 if tag == 'insert':
1080 if tag == 'insert':
1081 for i in xrange(blo, bhi):
1081 for i in xrange(blo, bhi):
1082 yield ('+', b[i])
1082 yield ('+', b[i])
1083 elif tag == 'delete':
1083 elif tag == 'delete':
1084 for i in xrange(alo, ahi):
1084 for i in xrange(alo, ahi):
1085 yield ('-', a[i])
1085 yield ('-', a[i])
1086 elif tag == 'replace':
1086 elif tag == 'replace':
1087 for i in xrange(alo, ahi):
1087 for i in xrange(alo, ahi):
1088 yield ('-', a[i])
1088 yield ('-', a[i])
1089 for i in xrange(blo, bhi):
1089 for i in xrange(blo, bhi):
1090 yield ('+', b[i])
1090 yield ('+', b[i])
1091
1091
1092 prev = {}
1092 prev = {}
1093 def display(fn, rev, states, prevstates):
1093 def display(fn, rev, states, prevstates):
1094 datefunc = ui.quiet and util.shortdate or util.datestr
1094 datefunc = ui.quiet and util.shortdate or util.datestr
1095 found = False
1095 found = False
1096 filerevmatches = {}
1096 filerevmatches = {}
1097 r = prev.get(fn, -1)
1097 r = prev.get(fn, -1)
1098 if opts['all']:
1098 if opts['all']:
1099 iter = difflinestates(states, prevstates)
1099 iter = difflinestates(states, prevstates)
1100 else:
1100 else:
1101 iter = [('', l) for l in prevstates]
1101 iter = [('', l) for l in prevstates]
1102 for change, l in iter:
1102 for change, l in iter:
1103 cols = [fn, str(r)]
1103 cols = [fn, str(r)]
1104 if opts['line_number']:
1104 if opts['line_number']:
1105 cols.append(str(l.linenum))
1105 cols.append(str(l.linenum))
1106 if opts['all']:
1106 if opts['all']:
1107 cols.append(change)
1107 cols.append(change)
1108 if opts['user']:
1108 if opts['user']:
1109 cols.append(ui.shortuser(get(r)[1]))
1109 cols.append(ui.shortuser(get(r)[1]))
1110 if opts.get('date'):
1110 if opts.get('date'):
1111 cols.append(datefunc(get(r)[2]))
1111 cols.append(datefunc(get(r)[2]))
1112 if opts['files_with_matches']:
1112 if opts['files_with_matches']:
1113 c = (fn, r)
1113 c = (fn, r)
1114 if c in filerevmatches:
1114 if c in filerevmatches:
1115 continue
1115 continue
1116 filerevmatches[c] = 1
1116 filerevmatches[c] = 1
1117 else:
1117 else:
1118 cols.append(l.line)
1118 cols.append(l.line)
1119 ui.write(sep.join(cols), eol)
1119 ui.write(sep.join(cols), eol)
1120 found = True
1120 found = True
1121 return found
1121 return found
1122
1122
1123 fstate = {}
1123 fstate = {}
1124 skip = {}
1124 skip = {}
1125 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1125 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1126 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1126 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1127 found = False
1127 found = False
1128 follow = opts.get('follow')
1128 follow = opts.get('follow')
1129 for st, rev, fns in changeiter:
1129 for st, rev, fns in changeiter:
1130 if st == 'window':
1130 if st == 'window':
1131 matches.clear()
1131 matches.clear()
1132 elif st == 'add':
1132 elif st == 'add':
1133 ctx = repo.changectx(rev)
1133 ctx = repo.changectx(rev)
1134 matches[rev] = {}
1134 matches[rev] = {}
1135 for fn in fns:
1135 for fn in fns:
1136 if fn in skip:
1136 if fn in skip:
1137 continue
1137 continue
1138 try:
1138 try:
1139 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1139 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1140 fstate.setdefault(fn, [])
1140 fstate.setdefault(fn, [])
1141 if follow:
1141 if follow:
1142 copied = getfile(fn).renamed(ctx.filenode(fn))
1142 copied = getfile(fn).renamed(ctx.filenode(fn))
1143 if copied:
1143 if copied:
1144 copies.setdefault(rev, {})[fn] = copied[0]
1144 copies.setdefault(rev, {})[fn] = copied[0]
1145 except revlog.LookupError:
1145 except revlog.LookupError:
1146 pass
1146 pass
1147 elif st == 'iter':
1147 elif st == 'iter':
1148 states = matches[rev].items()
1148 states = matches[rev].items()
1149 states.sort()
1149 states.sort()
1150 for fn, m in states:
1150 for fn, m in states:
1151 copy = copies.get(rev, {}).get(fn)
1151 copy = copies.get(rev, {}).get(fn)
1152 if fn in skip:
1152 if fn in skip:
1153 if copy:
1153 if copy:
1154 skip[copy] = True
1154 skip[copy] = True
1155 continue
1155 continue
1156 if fn in prev or fstate[fn]:
1156 if fn in prev or fstate[fn]:
1157 r = display(fn, rev, m, fstate[fn])
1157 r = display(fn, rev, m, fstate[fn])
1158 found = found or r
1158 found = found or r
1159 if r and not opts['all']:
1159 if r and not opts['all']:
1160 skip[fn] = True
1160 skip[fn] = True
1161 if copy:
1161 if copy:
1162 skip[copy] = True
1162 skip[copy] = True
1163 fstate[fn] = m
1163 fstate[fn] = m
1164 if copy:
1164 if copy:
1165 fstate[copy] = m
1165 fstate[copy] = m
1166 prev[fn] = rev
1166 prev[fn] = rev
1167
1167
1168 fstate = fstate.items()
1168 fstate = fstate.items()
1169 fstate.sort()
1169 fstate.sort()
1170 for fn, state in fstate:
1170 for fn, state in fstate:
1171 if fn in skip:
1171 if fn in skip:
1172 continue
1172 continue
1173 if fn not in copies.get(prev[fn], {}):
1173 if fn not in copies.get(prev[fn], {}):
1174 found = display(fn, rev, {}, state) or found
1174 found = display(fn, rev, {}, state) or found
1175 return (not found and 1) or 0
1175 return (not found and 1) or 0
1176
1176
1177 def heads(ui, repo, *branchrevs, **opts):
1177 def heads(ui, repo, *branchrevs, **opts):
1178 """show current repository heads or show branch heads
1178 """show current repository heads or show branch heads
1179
1179
1180 With no arguments, show all repository head changesets.
1180 With no arguments, show all repository head changesets.
1181
1181
1182 If branch or revisions names are given this will show the heads of
1182 If branch or revisions names are given this will show the heads of
1183 the specified branches or the branches those revisions are tagged
1183 the specified branches or the branches those revisions are tagged
1184 with.
1184 with.
1185
1185
1186 Repository "heads" are changesets that don't have child
1186 Repository "heads" are changesets that don't have child
1187 changesets. They are where development generally takes place and
1187 changesets. They are where development generally takes place and
1188 are the usual targets for update and merge operations.
1188 are the usual targets for update and merge operations.
1189
1189
1190 Branch heads are changesets that have a given branch tag, but have
1190 Branch heads are changesets that have a given branch tag, but have
1191 no child changesets with that tag. They are usually where
1191 no child changesets with that tag. They are usually where
1192 development on the given branch takes place.
1192 development on the given branch takes place.
1193 """
1193 """
1194 if opts['rev']:
1194 if opts['rev']:
1195 start = repo.lookup(opts['rev'])
1195 start = repo.lookup(opts['rev'])
1196 else:
1196 else:
1197 start = None
1197 start = None
1198 if not branchrevs:
1198 if not branchrevs:
1199 # Assume we're looking repo-wide heads if no revs were specified.
1199 # Assume we're looking repo-wide heads if no revs were specified.
1200 heads = repo.heads(start)
1200 heads = repo.heads(start)
1201 else:
1201 else:
1202 heads = []
1202 heads = []
1203 visitedset = util.set()
1203 visitedset = util.set()
1204 for branchrev in branchrevs:
1204 for branchrev in branchrevs:
1205 branch = repo.changectx(branchrev).branch()
1205 branch = repo.changectx(branchrev).branch()
1206 if branch in visitedset:
1206 if branch in visitedset:
1207 continue
1207 continue
1208 visitedset.add(branch)
1208 visitedset.add(branch)
1209 bheads = repo.branchheads(branch, start)
1209 bheads = repo.branchheads(branch, start)
1210 if not bheads:
1210 if not bheads:
1211 if branch != branchrev:
1211 if branch != branchrev:
1212 ui.warn(_("no changes on branch %s containing %s are "
1212 ui.warn(_("no changes on branch %s containing %s are "
1213 "reachable from %s\n")
1213 "reachable from %s\n")
1214 % (branch, branchrev, opts['rev']))
1214 % (branch, branchrev, opts['rev']))
1215 else:
1215 else:
1216 ui.warn(_("no changes on branch %s are reachable from %s\n")
1216 ui.warn(_("no changes on branch %s are reachable from %s\n")
1217 % (branch, opts['rev']))
1217 % (branch, opts['rev']))
1218 heads.extend(bheads)
1218 heads.extend(bheads)
1219 if not heads:
1219 if not heads:
1220 return 1
1220 return 1
1221 displayer = cmdutil.show_changeset(ui, repo, opts)
1221 displayer = cmdutil.show_changeset(ui, repo, opts)
1222 for n in heads:
1222 for n in heads:
1223 displayer.show(changenode=n)
1223 displayer.show(changenode=n)
1224
1224
1225 def help_(ui, name=None, with_version=False):
1225 def help_(ui, name=None, with_version=False):
1226 """show help for a command, extension, or list of commands
1226 """show help for a command, extension, or list of commands
1227
1227
1228 With no arguments, print a list of commands and short help.
1228 With no arguments, print a list of commands and short help.
1229
1229
1230 Given a command name, print help for that command.
1230 Given a command name, print help for that command.
1231
1231
1232 Given an extension name, print help for that extension, and the
1232 Given an extension name, print help for that extension, and the
1233 commands it provides."""
1233 commands it provides."""
1234 option_lists = []
1234 option_lists = []
1235
1235
1236 def addglobalopts(aliases):
1236 def addglobalopts(aliases):
1237 if ui.verbose:
1237 if ui.verbose:
1238 option_lists.append((_("global options:"), globalopts))
1238 option_lists.append((_("global options:"), globalopts))
1239 if name == 'shortlist':
1239 if name == 'shortlist':
1240 option_lists.append((_('use "hg help" for the full list '
1240 option_lists.append((_('use "hg help" for the full list '
1241 'of commands'), ()))
1241 'of commands'), ()))
1242 else:
1242 else:
1243 if name == 'shortlist':
1243 if name == 'shortlist':
1244 msg = _('use "hg help" for the full list of commands '
1244 msg = _('use "hg help" for the full list of commands '
1245 'or "hg -v" for details')
1245 'or "hg -v" for details')
1246 elif aliases:
1246 elif aliases:
1247 msg = _('use "hg -v help%s" to show aliases and '
1247 msg = _('use "hg -v help%s" to show aliases and '
1248 'global options') % (name and " " + name or "")
1248 'global options') % (name and " " + name or "")
1249 else:
1249 else:
1250 msg = _('use "hg -v help %s" to show global options') % name
1250 msg = _('use "hg -v help %s" to show global options') % name
1251 option_lists.append((msg, ()))
1251 option_lists.append((msg, ()))
1252
1252
1253 def helpcmd(name):
1253 def helpcmd(name):
1254 if with_version:
1254 if with_version:
1255 version_(ui)
1255 version_(ui)
1256 ui.write('\n')
1256 ui.write('\n')
1257
1257
1258 try:
1258 try:
1259 aliases, i = cmdutil.findcmd(ui, name, table)
1259 aliases, i = cmdutil.findcmd(ui, name, table)
1260 except cmdutil.AmbiguousCommand, inst:
1260 except cmdutil.AmbiguousCommand, inst:
1261 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1261 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1262 helplist(_('list of commands:\n\n'), select)
1262 helplist(_('list of commands:\n\n'), select)
1263 return
1263 return
1264
1264
1265 # synopsis
1265 # synopsis
1266 ui.write("%s\n" % i[2])
1266 ui.write("%s\n" % i[2])
1267
1267
1268 # aliases
1268 # aliases
1269 if not ui.quiet and len(aliases) > 1:
1269 if not ui.quiet and len(aliases) > 1:
1270 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1270 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1271
1271
1272 # description
1272 # description
1273 doc = i[0].__doc__
1273 doc = i[0].__doc__
1274 if not doc:
1274 if not doc:
1275 doc = _("(No help text available)")
1275 doc = _("(No help text available)")
1276 if ui.quiet:
1276 if ui.quiet:
1277 doc = doc.splitlines(0)[0]
1277 doc = doc.splitlines(0)[0]
1278 ui.write("\n%s\n" % doc.rstrip())
1278 ui.write("\n%s\n" % doc.rstrip())
1279
1279
1280 if not ui.quiet:
1280 if not ui.quiet:
1281 # options
1281 # options
1282 if i[1]:
1282 if i[1]:
1283 option_lists.append((_("options:\n"), i[1]))
1283 option_lists.append((_("options:\n"), i[1]))
1284
1284
1285 addglobalopts(False)
1285 addglobalopts(False)
1286
1286
1287 def helplist(header, select=None):
1287 def helplist(header, select=None):
1288 h = {}
1288 h = {}
1289 cmds = {}
1289 cmds = {}
1290 for c, e in table.items():
1290 for c, e in table.items():
1291 f = c.split("|", 1)[0]
1291 f = c.split("|", 1)[0]
1292 if select and not select(f):
1292 if select and not select(f):
1293 continue
1293 continue
1294 if name == "shortlist" and not f.startswith("^"):
1294 if name == "shortlist" and not f.startswith("^"):
1295 continue
1295 continue
1296 f = f.lstrip("^")
1296 f = f.lstrip("^")
1297 if not ui.debugflag and f.startswith("debug"):
1297 if not ui.debugflag and f.startswith("debug"):
1298 continue
1298 continue
1299 doc = e[0].__doc__
1299 doc = e[0].__doc__
1300 if not doc:
1300 if not doc:
1301 doc = _("(No help text available)")
1301 doc = _("(No help text available)")
1302 h[f] = doc.splitlines(0)[0].rstrip()
1302 h[f] = doc.splitlines(0)[0].rstrip()
1303 cmds[f] = c.lstrip("^")
1303 cmds[f] = c.lstrip("^")
1304
1304
1305 if not h:
1305 if not h:
1306 ui.status(_('no commands defined\n'))
1306 ui.status(_('no commands defined\n'))
1307 return
1307 return
1308
1308
1309 ui.status(header)
1309 ui.status(header)
1310 fns = h.keys()
1310 fns = h.keys()
1311 fns.sort()
1311 fns.sort()
1312 m = max(map(len, fns))
1312 m = max(map(len, fns))
1313 for f in fns:
1313 for f in fns:
1314 if ui.verbose:
1314 if ui.verbose:
1315 commands = cmds[f].replace("|",", ")
1315 commands = cmds[f].replace("|",", ")
1316 ui.write(" %s:\n %s\n"%(commands, h[f]))
1316 ui.write(" %s:\n %s\n"%(commands, h[f]))
1317 else:
1317 else:
1318 ui.write(' %-*s %s\n' % (m, f, h[f]))
1318 ui.write(' %-*s %s\n' % (m, f, h[f]))
1319
1319
1320 if not ui.quiet:
1320 if not ui.quiet:
1321 addglobalopts(True)
1321 addglobalopts(True)
1322
1322
1323 def helptopic(name):
1323 def helptopic(name):
1324 v = None
1324 v = None
1325 for i, d in help.helptable:
1325 for i, d in help.helptable:
1326 l = i.split('|')
1326 l = i.split('|')
1327 if name in l:
1327 if name in l:
1328 v = i
1328 v = i
1329 header = l[-1]
1329 header = l[-1]
1330 doc = d
1330 doc = d
1331 if not v:
1331 if not v:
1332 raise cmdutil.UnknownCommand(name)
1332 raise cmdutil.UnknownCommand(name)
1333
1333
1334 # description
1334 # description
1335 if not doc:
1335 if not doc:
1336 doc = _("(No help text available)")
1336 doc = _("(No help text available)")
1337 if callable(doc):
1337 if callable(doc):
1338 doc = doc()
1338 doc = doc()
1339
1339
1340 ui.write("%s\n" % header)
1340 ui.write("%s\n" % header)
1341 ui.write("%s\n" % doc.rstrip())
1341 ui.write("%s\n" % doc.rstrip())
1342
1342
1343 def helpext(name):
1343 def helpext(name):
1344 try:
1344 try:
1345 mod = extensions.find(name)
1345 mod = extensions.find(name)
1346 except KeyError:
1346 except KeyError:
1347 raise cmdutil.UnknownCommand(name)
1347 raise cmdutil.UnknownCommand(name)
1348
1348
1349 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1349 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1350 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1350 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1351 for d in doc[1:]:
1351 for d in doc[1:]:
1352 ui.write(d, '\n')
1352 ui.write(d, '\n')
1353
1353
1354 ui.status('\n')
1354 ui.status('\n')
1355
1355
1356 try:
1356 try:
1357 ct = mod.cmdtable
1357 ct = mod.cmdtable
1358 except AttributeError:
1358 except AttributeError:
1359 ct = {}
1359 ct = {}
1360
1360
1361 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1361 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1362 helplist(_('list of commands:\n\n'), modcmds.has_key)
1362 helplist(_('list of commands:\n\n'), modcmds.has_key)
1363
1363
1364 if name and name != 'shortlist':
1364 if name and name != 'shortlist':
1365 i = None
1365 i = None
1366 for f in (helpcmd, helptopic, helpext):
1366 for f in (helpcmd, helptopic, helpext):
1367 try:
1367 try:
1368 f(name)
1368 f(name)
1369 i = None
1369 i = None
1370 break
1370 break
1371 except cmdutil.UnknownCommand, inst:
1371 except cmdutil.UnknownCommand, inst:
1372 i = inst
1372 i = inst
1373 if i:
1373 if i:
1374 raise i
1374 raise i
1375
1375
1376 else:
1376 else:
1377 # program name
1377 # program name
1378 if ui.verbose or with_version:
1378 if ui.verbose or with_version:
1379 version_(ui)
1379 version_(ui)
1380 else:
1380 else:
1381 ui.status(_("Mercurial Distributed SCM\n"))
1381 ui.status(_("Mercurial Distributed SCM\n"))
1382 ui.status('\n')
1382 ui.status('\n')
1383
1383
1384 # list of commands
1384 # list of commands
1385 if name == "shortlist":
1385 if name == "shortlist":
1386 header = _('basic commands:\n\n')
1386 header = _('basic commands:\n\n')
1387 else:
1387 else:
1388 header = _('list of commands:\n\n')
1388 header = _('list of commands:\n\n')
1389
1389
1390 helplist(header)
1390 helplist(header)
1391
1391
1392 # list all option lists
1392 # list all option lists
1393 opt_output = []
1393 opt_output = []
1394 for title, options in option_lists:
1394 for title, options in option_lists:
1395 opt_output.append(("\n%s" % title, None))
1395 opt_output.append(("\n%s" % title, None))
1396 for shortopt, longopt, default, desc in options:
1396 for shortopt, longopt, default, desc in options:
1397 if "DEPRECATED" in desc and not ui.verbose: continue
1397 if "DEPRECATED" in desc and not ui.verbose: continue
1398 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1398 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1399 longopt and " --%s" % longopt),
1399 longopt and " --%s" % longopt),
1400 "%s%s" % (desc,
1400 "%s%s" % (desc,
1401 default
1401 default
1402 and _(" (default: %s)") % default
1402 and _(" (default: %s)") % default
1403 or "")))
1403 or "")))
1404
1404
1405 if ui.verbose:
1405 if ui.verbose:
1406 ui.write(_("\nspecial help topics:\n"))
1406 ui.write(_("\nspecial help topics:\n"))
1407 topics = []
1407 topics = []
1408 for i, d in help.helptable:
1408 for i, d in help.helptable:
1409 l = i.split('|')
1409 l = i.split('|')
1410 topics.append((", ".join(l[:-1]), l[-1]))
1410 topics.append((", ".join(l[:-1]), l[-1]))
1411 topics_len = max([len(s[0]) for s in topics])
1411 topics_len = max([len(s[0]) for s in topics])
1412 for t, desc in topics:
1412 for t, desc in topics:
1413 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1413 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1414
1414
1415 if opt_output:
1415 if opt_output:
1416 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1416 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1417 for first, second in opt_output:
1417 for first, second in opt_output:
1418 if second:
1418 if second:
1419 ui.write(" %-*s %s\n" % (opts_len, first, second))
1419 ui.write(" %-*s %s\n" % (opts_len, first, second))
1420 else:
1420 else:
1421 ui.write("%s\n" % first)
1421 ui.write("%s\n" % first)
1422
1422
1423 def identify(ui, repo, source=None,
1423 def identify(ui, repo, source=None,
1424 rev=None, num=None, id=None, branch=None, tags=None):
1424 rev=None, num=None, id=None, branch=None, tags=None):
1425 """identify the working copy or specified revision
1425 """identify the working copy or specified revision
1426
1426
1427 With no revision, print a summary of the current state of the repo.
1427 With no revision, print a summary of the current state of the repo.
1428
1428
1429 With a path, do a lookup in another repository.
1429 With a path, do a lookup in another repository.
1430
1430
1431 This summary identifies the repository state using one or two parent
1431 This summary identifies the repository state using one or two parent
1432 hash identifiers, followed by a "+" if there are uncommitted changes
1432 hash identifiers, followed by a "+" if there are uncommitted changes
1433 in the working directory, a list of tags for this revision and a branch
1433 in the working directory, a list of tags for this revision and a branch
1434 name for non-default branches.
1434 name for non-default branches.
1435 """
1435 """
1436
1436
1437 if not repo and not source:
1437 if not repo and not source:
1438 raise util.Abort(_("There is no Mercurial repository here "
1438 raise util.Abort(_("There is no Mercurial repository here "
1439 "(.hg not found)"))
1439 "(.hg not found)"))
1440
1440
1441 hexfunc = ui.debugflag and hex or short
1441 hexfunc = ui.debugflag and hex or short
1442 default = not (num or id or branch or tags)
1442 default = not (num or id or branch or tags)
1443 output = []
1443 output = []
1444
1444
1445 if source:
1445 if source:
1446 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1446 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1447 srepo = hg.repository(ui, source)
1447 srepo = hg.repository(ui, source)
1448 if not rev and revs:
1448 if not rev and revs:
1449 rev = revs[0]
1449 rev = revs[0]
1450 if not rev:
1450 if not rev:
1451 rev = "tip"
1451 rev = "tip"
1452 if num or branch or tags:
1452 if num or branch or tags:
1453 raise util.Abort(
1453 raise util.Abort(
1454 "can't query remote revision number, branch, or tags")
1454 "can't query remote revision number, branch, or tags")
1455 output = [hexfunc(srepo.lookup(rev))]
1455 output = [hexfunc(srepo.lookup(rev))]
1456 elif not rev:
1456 elif not rev:
1457 ctx = repo.changectx(None)
1457 ctx = repo.changectx(None)
1458 parents = ctx.parents()
1458 parents = ctx.parents()
1459 changed = False
1459 changed = False
1460 if default or id or num:
1460 if default or id or num:
1461 changed = ctx.files() + ctx.deleted()
1461 changed = ctx.files() + ctx.deleted()
1462 if default or id:
1462 if default or id:
1463 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1463 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1464 (changed) and "+" or "")]
1464 (changed) and "+" or "")]
1465 if num:
1465 if num:
1466 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1466 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1467 (changed) and "+" or ""))
1467 (changed) and "+" or ""))
1468 else:
1468 else:
1469 ctx = repo.changectx(rev)
1469 ctx = repo.changectx(rev)
1470 if default or id:
1470 if default or id:
1471 output = [hexfunc(ctx.node())]
1471 output = [hexfunc(ctx.node())]
1472 if num:
1472 if num:
1473 output.append(str(ctx.rev()))
1473 output.append(str(ctx.rev()))
1474
1474
1475 if not source and default and not ui.quiet:
1475 if not source and default and not ui.quiet:
1476 b = util.tolocal(ctx.branch())
1476 b = util.tolocal(ctx.branch())
1477 if b != 'default':
1477 if b != 'default':
1478 output.append("(%s)" % b)
1478 output.append("(%s)" % b)
1479
1479
1480 # multiple tags for a single parent separated by '/'
1480 # multiple tags for a single parent separated by '/'
1481 t = "/".join(ctx.tags())
1481 t = "/".join(ctx.tags())
1482 if t:
1482 if t:
1483 output.append(t)
1483 output.append(t)
1484
1484
1485 if branch:
1485 if branch:
1486 output.append(util.tolocal(ctx.branch()))
1486 output.append(util.tolocal(ctx.branch()))
1487
1487
1488 if tags:
1488 if tags:
1489 output.extend(ctx.tags())
1489 output.extend(ctx.tags())
1490
1490
1491 ui.write("%s\n" % ' '.join(output))
1491 ui.write("%s\n" % ' '.join(output))
1492
1492
1493 def import_(ui, repo, patch1, *patches, **opts):
1493 def import_(ui, repo, patch1, *patches, **opts):
1494 """import an ordered set of patches
1494 """import an ordered set of patches
1495
1495
1496 Import a list of patches and commit them individually.
1496 Import a list of patches and commit them individually.
1497
1497
1498 If there are outstanding changes in the working directory, import
1498 If there are outstanding changes in the working directory, import
1499 will abort unless given the -f flag.
1499 will abort unless given the -f flag.
1500
1500
1501 You can import a patch straight from a mail message. Even patches
1501 You can import a patch straight from a mail message. Even patches
1502 as attachments work (body part must be type text/plain or
1502 as attachments work (body part must be type text/plain or
1503 text/x-patch to be used). From and Subject headers of email
1503 text/x-patch to be used). From and Subject headers of email
1504 message are used as default committer and commit message. All
1504 message are used as default committer and commit message. All
1505 text/plain body parts before first diff are added to commit
1505 text/plain body parts before first diff are added to commit
1506 message.
1506 message.
1507
1507
1508 If the imported patch was generated by hg export, user and description
1508 If the imported patch was generated by hg export, user and description
1509 from patch override values from message headers and body. Values
1509 from patch override values from message headers and body. Values
1510 given on command line with -m and -u override these.
1510 given on command line with -m and -u override these.
1511
1511
1512 If --exact is specified, import will set the working directory
1512 If --exact is specified, import will set the working directory
1513 to the parent of each patch before applying it, and will abort
1513 to the parent of each patch before applying it, and will abort
1514 if the resulting changeset has a different ID than the one
1514 if the resulting changeset has a different ID than the one
1515 recorded in the patch. This may happen due to character set
1515 recorded in the patch. This may happen due to character set
1516 problems or other deficiencies in the text patch format.
1516 problems or other deficiencies in the text patch format.
1517
1517
1518 To read a patch from standard input, use patch name "-".
1518 To read a patch from standard input, use patch name "-".
1519 See 'hg help dates' for a list of formats valid for -d/--date.
1519 See 'hg help dates' for a list of formats valid for -d/--date.
1520 """
1520 """
1521 patches = (patch1,) + patches
1521 patches = (patch1,) + patches
1522
1522
1523 date = opts.get('date')
1523 date = opts.get('date')
1524 if date:
1524 if date:
1525 opts['date'] = util.parsedate(date)
1525 opts['date'] = util.parsedate(date)
1526
1526
1527 if opts.get('exact') or not opts['force']:
1527 if opts.get('exact') or not opts['force']:
1528 cmdutil.bail_if_changed(repo)
1528 cmdutil.bail_if_changed(repo)
1529
1529
1530 d = opts["base"]
1530 d = opts["base"]
1531 strip = opts["strip"]
1531 strip = opts["strip"]
1532 wlock = lock = None
1532 wlock = lock = None
1533 try:
1533 try:
1534 wlock = repo.wlock()
1534 wlock = repo.wlock()
1535 lock = repo.lock()
1535 lock = repo.lock()
1536 for p in patches:
1536 for p in patches:
1537 pf = os.path.join(d, p)
1537 pf = os.path.join(d, p)
1538
1538
1539 if pf == '-':
1539 if pf == '-':
1540 ui.status(_("applying patch from stdin\n"))
1540 ui.status(_("applying patch from stdin\n"))
1541 data = patch.extract(ui, sys.stdin)
1541 data = patch.extract(ui, sys.stdin)
1542 else:
1542 else:
1543 ui.status(_("applying %s\n") % p)
1543 ui.status(_("applying %s\n") % p)
1544 if os.path.exists(pf):
1544 if os.path.exists(pf):
1545 data = patch.extract(ui, file(pf, 'rb'))
1545 data = patch.extract(ui, file(pf, 'rb'))
1546 else:
1546 else:
1547 data = patch.extract(ui, urllib.urlopen(pf))
1547 data = patch.extract(ui, urllib.urlopen(pf))
1548 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1548 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1549
1549
1550 if tmpname is None:
1550 if tmpname is None:
1551 raise util.Abort(_('no diffs found'))
1551 raise util.Abort(_('no diffs found'))
1552
1552
1553 try:
1553 try:
1554 cmdline_message = cmdutil.logmessage(opts)
1554 cmdline_message = cmdutil.logmessage(opts)
1555 if cmdline_message:
1555 if cmdline_message:
1556 # pickup the cmdline msg
1556 # pickup the cmdline msg
1557 message = cmdline_message
1557 message = cmdline_message
1558 elif message:
1558 elif message:
1559 # pickup the patch msg
1559 # pickup the patch msg
1560 message = message.strip()
1560 message = message.strip()
1561 else:
1561 else:
1562 # launch the editor
1562 # launch the editor
1563 message = None
1563 message = None
1564 ui.debug(_('message:\n%s\n') % message)
1564 ui.debug(_('message:\n%s\n') % message)
1565
1565
1566 wp = repo.changectx(None).parents()
1566 wp = repo.changectx(None).parents()
1567 if opts.get('exact'):
1567 if opts.get('exact'):
1568 if not nodeid or not p1:
1568 if not nodeid or not p1:
1569 raise util.Abort(_('not a mercurial patch'))
1569 raise util.Abort(_('not a mercurial patch'))
1570 p1 = repo.lookup(p1)
1570 p1 = repo.lookup(p1)
1571 p2 = repo.lookup(p2 or hex(nullid))
1571 p2 = repo.lookup(p2 or hex(nullid))
1572
1572
1573 if p1 != wp[0].node():
1573 if p1 != wp[0].node():
1574 hg.clean(repo, p1)
1574 hg.clean(repo, p1)
1575 repo.dirstate.setparents(p1, p2)
1575 repo.dirstate.setparents(p1, p2)
1576 elif p2:
1576 elif p2:
1577 try:
1577 try:
1578 p1 = repo.lookup(p1)
1578 p1 = repo.lookup(p1)
1579 p2 = repo.lookup(p2)
1579 p2 = repo.lookup(p2)
1580 if p1 == wp[0].node():
1580 if p1 == wp[0].node():
1581 repo.dirstate.setparents(p1, p2)
1581 repo.dirstate.setparents(p1, p2)
1582 except RepoError:
1582 except RepoError:
1583 pass
1583 pass
1584 if opts.get('exact') or opts.get('import_branch'):
1584 if opts.get('exact') or opts.get('import_branch'):
1585 repo.dirstate.setbranch(branch or 'default')
1585 repo.dirstate.setbranch(branch or 'default')
1586
1586
1587 files = {}
1587 files = {}
1588 try:
1588 try:
1589 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1589 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1590 files=files)
1590 files=files)
1591 finally:
1591 finally:
1592 files = patch.updatedir(ui, repo, files)
1592 files = patch.updatedir(ui, repo, files)
1593 if not opts.get('no_commit'):
1593 if not opts.get('no_commit'):
1594 n = repo.commit(files, message, opts.get('user') or user,
1594 n = repo.commit(files, message, opts.get('user') or user,
1595 opts.get('date') or date)
1595 opts.get('date') or date)
1596 if opts.get('exact'):
1596 if opts.get('exact'):
1597 if hex(n) != nodeid:
1597 if hex(n) != nodeid:
1598 repo.rollback()
1598 repo.rollback()
1599 raise util.Abort(_('patch is damaged'
1599 raise util.Abort(_('patch is damaged'
1600 ' or loses information'))
1600 ' or loses information'))
1601 # Force a dirstate write so that the next transaction
1601 # Force a dirstate write so that the next transaction
1602 # backups an up-do-date file.
1602 # backups an up-do-date file.
1603 repo.dirstate.write()
1603 repo.dirstate.write()
1604 finally:
1604 finally:
1605 os.unlink(tmpname)
1605 os.unlink(tmpname)
1606 finally:
1606 finally:
1607 del lock, wlock
1607 del lock, wlock
1608
1608
1609 def incoming(ui, repo, source="default", **opts):
1609 def incoming(ui, repo, source="default", **opts):
1610 """show new changesets found in source
1610 """show new changesets found in source
1611
1611
1612 Show new changesets found in the specified path/URL or the default
1612 Show new changesets found in the specified path/URL or the default
1613 pull location. These are the changesets that would be pulled if a pull
1613 pull location. These are the changesets that would be pulled if a pull
1614 was requested.
1614 was requested.
1615
1615
1616 For remote repository, using --bundle avoids downloading the changesets
1616 For remote repository, using --bundle avoids downloading the changesets
1617 twice if the incoming is followed by a pull.
1617 twice if the incoming is followed by a pull.
1618
1618
1619 See pull for valid source format details.
1619 See pull for valid source format details.
1620 """
1620 """
1621 limit = cmdutil.loglimit(opts)
1621 limit = cmdutil.loglimit(opts)
1622 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1622 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1623 cmdutil.setremoteconfig(ui, opts)
1623 cmdutil.setremoteconfig(ui, opts)
1624
1624
1625 other = hg.repository(ui, source)
1625 other = hg.repository(ui, source)
1626 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1626 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1627 if revs:
1627 if revs:
1628 revs = [other.lookup(rev) for rev in revs]
1628 revs = [other.lookup(rev) for rev in revs]
1629 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1629 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1630 if not incoming:
1630 if not incoming:
1631 try:
1631 try:
1632 os.unlink(opts["bundle"])
1632 os.unlink(opts["bundle"])
1633 except:
1633 except:
1634 pass
1634 pass
1635 ui.status(_("no changes found\n"))
1635 ui.status(_("no changes found\n"))
1636 return 1
1636 return 1
1637
1637
1638 cleanup = None
1638 cleanup = None
1639 try:
1639 try:
1640 fname = opts["bundle"]
1640 fname = opts["bundle"]
1641 if fname or not other.local():
1641 if fname or not other.local():
1642 # create a bundle (uncompressed if other repo is not local)
1642 # create a bundle (uncompressed if other repo is not local)
1643 if revs is None:
1643 if revs is None:
1644 cg = other.changegroup(incoming, "incoming")
1644 cg = other.changegroup(incoming, "incoming")
1645 else:
1645 else:
1646 cg = other.changegroupsubset(incoming, revs, 'incoming')
1646 cg = other.changegroupsubset(incoming, revs, 'incoming')
1647 bundletype = other.local() and "HG10BZ" or "HG10UN"
1647 bundletype = other.local() and "HG10BZ" or "HG10UN"
1648 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1648 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1649 # keep written bundle?
1649 # keep written bundle?
1650 if opts["bundle"]:
1650 if opts["bundle"]:
1651 cleanup = None
1651 cleanup = None
1652 if not other.local():
1652 if not other.local():
1653 # use the created uncompressed bundlerepo
1653 # use the created uncompressed bundlerepo
1654 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1654 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1655
1655
1656 o = other.changelog.nodesbetween(incoming, revs)[0]
1656 o = other.changelog.nodesbetween(incoming, revs)[0]
1657 if opts['newest_first']:
1657 if opts['newest_first']:
1658 o.reverse()
1658 o.reverse()
1659 displayer = cmdutil.show_changeset(ui, other, opts)
1659 displayer = cmdutil.show_changeset(ui, other, opts)
1660 count = 0
1660 count = 0
1661 for n in o:
1661 for n in o:
1662 if count >= limit:
1662 if count >= limit:
1663 break
1663 break
1664 parents = [p for p in other.changelog.parents(n) if p != nullid]
1664 parents = [p for p in other.changelog.parents(n) if p != nullid]
1665 if opts['no_merges'] and len(parents) == 2:
1665 if opts['no_merges'] and len(parents) == 2:
1666 continue
1666 continue
1667 count += 1
1667 count += 1
1668 displayer.show(changenode=n)
1668 displayer.show(changenode=n)
1669 finally:
1669 finally:
1670 if hasattr(other, 'close'):
1670 if hasattr(other, 'close'):
1671 other.close()
1671 other.close()
1672 if cleanup:
1672 if cleanup:
1673 os.unlink(cleanup)
1673 os.unlink(cleanup)
1674
1674
1675 def init(ui, dest=".", **opts):
1675 def init(ui, dest=".", **opts):
1676 """create a new repository in the given directory
1676 """create a new repository in the given directory
1677
1677
1678 Initialize a new repository in the given directory. If the given
1678 Initialize a new repository in the given directory. If the given
1679 directory does not exist, it is created.
1679 directory does not exist, it is created.
1680
1680
1681 If no directory is given, the current directory is used.
1681 If no directory is given, the current directory is used.
1682
1682
1683 It is possible to specify an ssh:// URL as the destination.
1683 It is possible to specify an ssh:// URL as the destination.
1684 Look at the help text for the pull command for important details
1684 Look at the help text for the pull command for important details
1685 about ssh:// URLs.
1685 about ssh:// URLs.
1686 """
1686 """
1687 cmdutil.setremoteconfig(ui, opts)
1687 cmdutil.setremoteconfig(ui, opts)
1688 hg.repository(ui, dest, create=1)
1688 hg.repository(ui, dest, create=1)
1689
1689
1690 def locate(ui, repo, *pats, **opts):
1690 def locate(ui, repo, *pats, **opts):
1691 """locate files matching specific patterns
1691 """locate files matching specific patterns
1692
1692
1693 Print all files under Mercurial control whose names match the
1693 Print all files under Mercurial control whose names match the
1694 given patterns.
1694 given patterns.
1695
1695
1696 This command searches the entire repository by default. To search
1696 This command searches the entire repository by default. To search
1697 just the current directory and its subdirectories, use
1697 just the current directory and its subdirectories, use
1698 "--include .".
1698 "--include .".
1699
1699
1700 If no patterns are given to match, this command prints all file
1700 If no patterns are given to match, this command prints all file
1701 names.
1701 names.
1702
1702
1703 If you want to feed the output of this command into the "xargs"
1703 If you want to feed the output of this command into the "xargs"
1704 command, use the "-0" option to both this command and "xargs".
1704 command, use the "-0" option to both this command and "xargs".
1705 This will avoid the problem of "xargs" treating single filenames
1705 This will avoid the problem of "xargs" treating single filenames
1706 that contain white space as multiple filenames.
1706 that contain white space as multiple filenames.
1707 """
1707 """
1708 end = opts['print0'] and '\0' or '\n'
1708 end = opts['print0'] and '\0' or '\n'
1709 rev = opts['rev']
1709 rev = opts['rev']
1710 if rev:
1710 if rev:
1711 node = repo.lookup(rev)
1711 node = repo.lookup(rev)
1712 else:
1712 else:
1713 node = None
1713 node = None
1714
1714
1715 ret = 1
1715 ret = 1
1716 m = cmdutil.match(repo, pats, opts, default='relglob')
1716 m = cmdutil.match(repo, pats, opts, default='relglob')
1717 m.bad = lambda x,y: False
1717 m.bad = lambda x,y: False
1718 for abs in repo.walk(m, node):
1718 for abs in repo.walk(m, node):
1719 if not node and abs not in repo.dirstate:
1719 if not node and abs not in repo.dirstate:
1720 continue
1720 continue
1721 if opts['fullpath']:
1721 if opts['fullpath']:
1722 ui.write(os.path.join(repo.root, abs), end)
1722 ui.write(os.path.join(repo.root, abs), end)
1723 else:
1723 else:
1724 ui.write(((pats and m.rel(abs)) or abs), end)
1724 ui.write(((pats and m.rel(abs)) or abs), end)
1725 ret = 0
1725 ret = 0
1726
1726
1727 return ret
1727 return ret
1728
1728
1729 def log(ui, repo, *pats, **opts):
1729 def log(ui, repo, *pats, **opts):
1730 """show revision history of entire repository or files
1730 """show revision history of entire repository or files
1731
1731
1732 Print the revision history of the specified files or the entire
1732 Print the revision history of the specified files or the entire
1733 project.
1733 project.
1734
1734
1735 File history is shown without following rename or copy history of
1735 File history is shown without following rename or copy history of
1736 files. Use -f/--follow with a file name to follow history across
1736 files. Use -f/--follow with a file name to follow history across
1737 renames and copies. --follow without a file name will only show
1737 renames and copies. --follow without a file name will only show
1738 ancestors or descendants of the starting revision. --follow-first
1738 ancestors or descendants of the starting revision. --follow-first
1739 only follows the first parent of merge revisions.
1739 only follows the first parent of merge revisions.
1740
1740
1741 If no revision range is specified, the default is tip:0 unless
1741 If no revision range is specified, the default is tip:0 unless
1742 --follow is set, in which case the working directory parent is
1742 --follow is set, in which case the working directory parent is
1743 used as the starting revision.
1743 used as the starting revision.
1744
1744
1745 See 'hg help dates' for a list of formats valid for -d/--date.
1745 See 'hg help dates' for a list of formats valid for -d/--date.
1746
1746
1747 By default this command outputs: changeset id and hash, tags,
1747 By default this command outputs: changeset id and hash, tags,
1748 non-trivial parents, user, date and time, and a summary for each
1748 non-trivial parents, user, date and time, and a summary for each
1749 commit. When the -v/--verbose switch is used, the list of changed
1749 commit. When the -v/--verbose switch is used, the list of changed
1750 files and full commit message is shown.
1750 files and full commit message is shown.
1751
1751
1752 NOTE: log -p may generate unexpected diff output for merge
1752 NOTE: log -p may generate unexpected diff output for merge
1753 changesets, as it will compare the merge changeset against its
1753 changesets, as it will compare the merge changeset against its
1754 first parent only. Also, the files: list will only reflect files
1754 first parent only. Also, the files: list will only reflect files
1755 that are different from BOTH parents.
1755 that are different from BOTH parents.
1756
1756
1757 """
1757 """
1758
1758
1759 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1759 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1760 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1760 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1761
1761
1762 limit = cmdutil.loglimit(opts)
1762 limit = cmdutil.loglimit(opts)
1763 count = 0
1763 count = 0
1764
1764
1765 if opts['copies'] and opts['rev']:
1765 if opts['copies'] and opts['rev']:
1766 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1766 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1767 else:
1767 else:
1768 endrev = repo.changelog.count()
1768 endrev = repo.changelog.count()
1769 rcache = {}
1769 rcache = {}
1770 ncache = {}
1770 ncache = {}
1771 def getrenamed(fn, rev):
1771 def getrenamed(fn, rev):
1772 '''looks up all renames for a file (up to endrev) the first
1772 '''looks up all renames for a file (up to endrev) the first
1773 time the file is given. It indexes on the changerev and only
1773 time the file is given. It indexes on the changerev and only
1774 parses the manifest if linkrev != changerev.
1774 parses the manifest if linkrev != changerev.
1775 Returns rename info for fn at changerev rev.'''
1775 Returns rename info for fn at changerev rev.'''
1776 if fn not in rcache:
1776 if fn not in rcache:
1777 rcache[fn] = {}
1777 rcache[fn] = {}
1778 ncache[fn] = {}
1778 ncache[fn] = {}
1779 fl = repo.file(fn)
1779 fl = repo.file(fn)
1780 for i in xrange(fl.count()):
1780 for i in xrange(fl.count()):
1781 node = fl.node(i)
1781 node = fl.node(i)
1782 lr = fl.linkrev(node)
1782 lr = fl.linkrev(node)
1783 renamed = fl.renamed(node)
1783 renamed = fl.renamed(node)
1784 rcache[fn][lr] = renamed
1784 rcache[fn][lr] = renamed
1785 if renamed:
1785 if renamed:
1786 ncache[fn][node] = renamed
1786 ncache[fn][node] = renamed
1787 if lr >= endrev:
1787 if lr >= endrev:
1788 break
1788 break
1789 if rev in rcache[fn]:
1789 if rev in rcache[fn]:
1790 return rcache[fn][rev]
1790 return rcache[fn][rev]
1791
1791
1792 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1792 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1793 # filectx logic.
1793 # filectx logic.
1794
1794
1795 try:
1795 try:
1796 return repo.changectx(rev).filectx(fn).renamed()
1796 return repo.changectx(rev).filectx(fn).renamed()
1797 except revlog.LookupError:
1797 except revlog.LookupError:
1798 pass
1798 pass
1799 return None
1799 return None
1800
1800
1801 df = False
1801 df = False
1802 if opts["date"]:
1802 if opts["date"]:
1803 df = util.matchdate(opts["date"])
1803 df = util.matchdate(opts["date"])
1804
1804
1805 only_branches = opts['only_branch']
1805 only_branches = opts['only_branch']
1806
1806
1807 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1807 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1808 for st, rev, fns in changeiter:
1808 for st, rev, fns in changeiter:
1809 if st == 'add':
1809 if st == 'add':
1810 changenode = repo.changelog.node(rev)
1810 changenode = repo.changelog.node(rev)
1811 parents = [p for p in repo.changelog.parentrevs(rev)
1811 parents = [p for p in repo.changelog.parentrevs(rev)
1812 if p != nullrev]
1812 if p != nullrev]
1813 if opts['no_merges'] and len(parents) == 2:
1813 if opts['no_merges'] and len(parents) == 2:
1814 continue
1814 continue
1815 if opts['only_merges'] and len(parents) != 2:
1815 if opts['only_merges'] and len(parents) != 2:
1816 continue
1816 continue
1817
1817
1818 if only_branches:
1818 if only_branches:
1819 revbranch = get(rev)[5]['branch']
1819 revbranch = get(rev)[5]['branch']
1820 if revbranch not in only_branches:
1820 if revbranch not in only_branches:
1821 continue
1821 continue
1822
1822
1823 if df:
1823 if df:
1824 changes = get(rev)
1824 changes = get(rev)
1825 if not df(changes[2][0]):
1825 if not df(changes[2][0]):
1826 continue
1826 continue
1827
1827
1828 if opts['keyword']:
1828 if opts['keyword']:
1829 changes = get(rev)
1829 changes = get(rev)
1830 miss = 0
1830 miss = 0
1831 for k in [kw.lower() for kw in opts['keyword']]:
1831 for k in [kw.lower() for kw in opts['keyword']]:
1832 if not (k in changes[1].lower() or
1832 if not (k in changes[1].lower() or
1833 k in changes[4].lower() or
1833 k in changes[4].lower() or
1834 k in " ".join(changes[3]).lower()):
1834 k in " ".join(changes[3]).lower()):
1835 miss = 1
1835 miss = 1
1836 break
1836 break
1837 if miss:
1837 if miss:
1838 continue
1838 continue
1839
1839
1840 copies = []
1840 copies = []
1841 if opts.get('copies') and rev:
1841 if opts.get('copies') and rev:
1842 for fn in get(rev)[3]:
1842 for fn in get(rev)[3]:
1843 rename = getrenamed(fn, rev)
1843 rename = getrenamed(fn, rev)
1844 if rename:
1844 if rename:
1845 copies.append((fn, rename[0]))
1845 copies.append((fn, rename[0]))
1846 displayer.show(rev, changenode, copies=copies)
1846 displayer.show(rev, changenode, copies=copies)
1847 elif st == 'iter':
1847 elif st == 'iter':
1848 if count == limit: break
1848 if count == limit: break
1849 if displayer.flush(rev):
1849 if displayer.flush(rev):
1850 count += 1
1850 count += 1
1851
1851
1852 def manifest(ui, repo, node=None, rev=None):
1852 def manifest(ui, repo, node=None, rev=None):
1853 """output the current or given revision of the project manifest
1853 """output the current or given revision of the project manifest
1854
1854
1855 Print a list of version controlled files for the given revision.
1855 Print a list of version controlled files for the given revision.
1856 If no revision is given, the parent of the working directory is used,
1856 If no revision is given, the parent of the working directory is used,
1857 or tip if no revision is checked out.
1857 or tip if no revision is checked out.
1858
1858
1859 The manifest is the list of files being version controlled. If no revision
1859 The manifest is the list of files being version controlled. If no revision
1860 is given then the first parent of the working directory is used.
1860 is given then the first parent of the working directory is used.
1861
1861
1862 With -v flag, print file permissions, symlink and executable bits. With
1862 With -v flag, print file permissions, symlink and executable bits. With
1863 --debug flag, print file revision hashes.
1863 --debug flag, print file revision hashes.
1864 """
1864 """
1865
1865
1866 if rev and node:
1866 if rev and node:
1867 raise util.Abort(_("please specify just one revision"))
1867 raise util.Abort(_("please specify just one revision"))
1868
1868
1869 if not node:
1869 if not node:
1870 node = rev
1870 node = rev
1871
1871
1872 m = repo.changectx(node).manifest()
1872 m = repo.changectx(node).manifest()
1873 files = m.keys()
1873 files = m.keys()
1874 files.sort()
1874 files.sort()
1875
1875
1876 for f in files:
1876 for f in files:
1877 if ui.debugflag:
1877 if ui.debugflag:
1878 ui.write("%40s " % hex(m[f]))
1878 ui.write("%40s " % hex(m[f]))
1879 if ui.verbose:
1879 if ui.verbose:
1880 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1880 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1881 perm = m.execf(f) and "755" or "644"
1881 perm = m.execf(f) and "755" or "644"
1882 ui.write("%3s %1s " % (perm, type))
1882 ui.write("%3s %1s " % (perm, type))
1883 ui.write("%s\n" % f)
1883 ui.write("%s\n" % f)
1884
1884
1885 def merge(ui, repo, node=None, force=None, rev=None):
1885 def merge(ui, repo, node=None, force=None, rev=None):
1886 """merge working directory with another revision
1886 """merge working directory with another revision
1887
1887
1888 Merge the contents of the current working directory and the
1888 Merge the contents of the current working directory and the
1889 requested revision. Files that changed between either parent are
1889 requested revision. Files that changed between either parent are
1890 marked as changed for the next commit and a commit must be
1890 marked as changed for the next commit and a commit must be
1891 performed before any further updates are allowed.
1891 performed before any further updates are allowed.
1892
1892
1893 If no revision is specified, the working directory's parent is a
1893 If no revision is specified, the working directory's parent is a
1894 head revision, and the current branch contains exactly one other head,
1894 head revision, and the current branch contains exactly one other head,
1895 the other head is merged with by default. Otherwise, an explicit
1895 the other head is merged with by default. Otherwise, an explicit
1896 revision to merge with must be provided.
1896 revision to merge with must be provided.
1897 """
1897 """
1898
1898
1899 if rev and node:
1899 if rev and node:
1900 raise util.Abort(_("please specify just one revision"))
1900 raise util.Abort(_("please specify just one revision"))
1901 if not node:
1901 if not node:
1902 node = rev
1902 node = rev
1903
1903
1904 if not node:
1904 if not node:
1905 branch = repo.changectx(None).branch()
1905 branch = repo.changectx(None).branch()
1906 bheads = repo.branchheads()
1906 bheads = repo.branchheads()
1907 if len(bheads) > 2:
1907 if len(bheads) > 2:
1908 raise util.Abort(_("branch '%s' has %d heads - "
1908 raise util.Abort(_("branch '%s' has %d heads - "
1909 "please merge with an explicit rev") %
1909 "please merge with an explicit rev") %
1910 (branch, len(bheads)))
1910 (branch, len(bheads)))
1911
1911
1912 parent = repo.dirstate.parents()[0]
1912 parent = repo.dirstate.parents()[0]
1913 if len(bheads) == 1:
1913 if len(bheads) == 1:
1914 if len(repo.heads()) > 1:
1914 if len(repo.heads()) > 1:
1915 raise util.Abort(_("branch '%s' has one head - "
1915 raise util.Abort(_("branch '%s' has one head - "
1916 "please merge with an explicit rev") %
1916 "please merge with an explicit rev") %
1917 branch)
1917 branch)
1918 msg = _('there is nothing to merge')
1918 msg = _('there is nothing to merge')
1919 if parent != repo.lookup(repo.changectx(None).branch()):
1919 if parent != repo.lookup(repo.changectx(None).branch()):
1920 msg = _('%s - use "hg update" instead') % msg
1920 msg = _('%s - use "hg update" instead') % msg
1921 raise util.Abort(msg)
1921 raise util.Abort(msg)
1922
1922
1923 if parent not in bheads:
1923 if parent not in bheads:
1924 raise util.Abort(_('working dir not at a head rev - '
1924 raise util.Abort(_('working dir not at a head rev - '
1925 'use "hg update" or merge with an explicit rev'))
1925 'use "hg update" or merge with an explicit rev'))
1926 node = parent == bheads[0] and bheads[-1] or bheads[0]
1926 node = parent == bheads[0] and bheads[-1] or bheads[0]
1927 return hg.merge(repo, node, force=force)
1927 return hg.merge(repo, node, force=force)
1928
1928
1929 def outgoing(ui, repo, dest=None, **opts):
1929 def outgoing(ui, repo, dest=None, **opts):
1930 """show changesets not found in destination
1930 """show changesets not found in destination
1931
1931
1932 Show changesets not found in the specified destination repository or
1932 Show changesets not found in the specified destination repository or
1933 the default push location. These are the changesets that would be pushed
1933 the default push location. These are the changesets that would be pushed
1934 if a push was requested.
1934 if a push was requested.
1935
1935
1936 See pull for valid destination format details.
1936 See pull for valid destination format details.
1937 """
1937 """
1938 limit = cmdutil.loglimit(opts)
1938 limit = cmdutil.loglimit(opts)
1939 dest, revs, checkout = hg.parseurl(
1939 dest, revs, checkout = hg.parseurl(
1940 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1940 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1941 cmdutil.setremoteconfig(ui, opts)
1941 cmdutil.setremoteconfig(ui, opts)
1942 if revs:
1942 if revs:
1943 revs = [repo.lookup(rev) for rev in revs]
1943 revs = [repo.lookup(rev) for rev in revs]
1944
1944
1945 other = hg.repository(ui, dest)
1945 other = hg.repository(ui, dest)
1946 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1946 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1947 o = repo.findoutgoing(other, force=opts['force'])
1947 o = repo.findoutgoing(other, force=opts['force'])
1948 if not o:
1948 if not o:
1949 ui.status(_("no changes found\n"))
1949 ui.status(_("no changes found\n"))
1950 return 1
1950 return 1
1951 o = repo.changelog.nodesbetween(o, revs)[0]
1951 o = repo.changelog.nodesbetween(o, revs)[0]
1952 if opts['newest_first']:
1952 if opts['newest_first']:
1953 o.reverse()
1953 o.reverse()
1954 displayer = cmdutil.show_changeset(ui, repo, opts)
1954 displayer = cmdutil.show_changeset(ui, repo, opts)
1955 count = 0
1955 count = 0
1956 for n in o:
1956 for n in o:
1957 if count >= limit:
1957 if count >= limit:
1958 break
1958 break
1959 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1959 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1960 if opts['no_merges'] and len(parents) == 2:
1960 if opts['no_merges'] and len(parents) == 2:
1961 continue
1961 continue
1962 count += 1
1962 count += 1
1963 displayer.show(changenode=n)
1963 displayer.show(changenode=n)
1964
1964
1965 def parents(ui, repo, file_=None, **opts):
1965 def parents(ui, repo, file_=None, **opts):
1966 """show the parents of the working dir or revision
1966 """show the parents of the working dir or revision
1967
1967
1968 Print the working directory's parent revisions. If a
1968 Print the working directory's parent revisions. If a
1969 revision is given via --rev, the parent of that revision
1969 revision is given via --rev, the parent of that revision
1970 will be printed. If a file argument is given, revision in
1970 will be printed. If a file argument is given, revision in
1971 which the file was last changed (before the working directory
1971 which the file was last changed (before the working directory
1972 revision or the argument to --rev if given) is printed.
1972 revision or the argument to --rev if given) is printed.
1973 """
1973 """
1974 rev = opts.get('rev')
1974 rev = opts.get('rev')
1975 if rev:
1975 if rev:
1976 ctx = repo.changectx(rev)
1976 ctx = repo.changectx(rev)
1977 else:
1977 else:
1978 ctx = repo.changectx(None)
1978 ctx = repo.changectx(None)
1979
1979
1980 if file_:
1980 if file_:
1981 m = cmdutil.match(repo, (file_,), opts)
1981 m = cmdutil.match(repo, (file_,), opts)
1982 if m.anypats() or len(m.files()) != 1:
1982 if m.anypats() or len(m.files()) != 1:
1983 raise util.Abort(_('can only specify an explicit file name'))
1983 raise util.Abort(_('can only specify an explicit file name'))
1984 file_ = m.files()[0]
1984 file_ = m.files()[0]
1985 filenodes = []
1985 filenodes = []
1986 for cp in ctx.parents():
1986 for cp in ctx.parents():
1987 if not cp:
1987 if not cp:
1988 continue
1988 continue
1989 try:
1989 try:
1990 filenodes.append(cp.filenode(file_))
1990 filenodes.append(cp.filenode(file_))
1991 except revlog.LookupError:
1991 except revlog.LookupError:
1992 pass
1992 pass
1993 if not filenodes:
1993 if not filenodes:
1994 raise util.Abort(_("'%s' not found in manifest!") % file_)
1994 raise util.Abort(_("'%s' not found in manifest!") % file_)
1995 fl = repo.file(file_)
1995 fl = repo.file(file_)
1996 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1996 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1997 else:
1997 else:
1998 p = [cp.node() for cp in ctx.parents()]
1998 p = [cp.node() for cp in ctx.parents()]
1999
1999
2000 displayer = cmdutil.show_changeset(ui, repo, opts)
2000 displayer = cmdutil.show_changeset(ui, repo, opts)
2001 for n in p:
2001 for n in p:
2002 if n != nullid:
2002 if n != nullid:
2003 displayer.show(changenode=n)
2003 displayer.show(changenode=n)
2004
2004
2005 def paths(ui, repo, search=None):
2005 def paths(ui, repo, search=None):
2006 """show definition of symbolic path names
2006 """show definition of symbolic path names
2007
2007
2008 Show definition of symbolic path name NAME. If no name is given, show
2008 Show definition of symbolic path name NAME. If no name is given, show
2009 definition of available names.
2009 definition of available names.
2010
2010
2011 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2011 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2012 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2012 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2013 """
2013 """
2014 if search:
2014 if search:
2015 for name, path in ui.configitems("paths"):
2015 for name, path in ui.configitems("paths"):
2016 if name == search:
2016 if name == search:
2017 ui.write("%s\n" % util.hidepassword(path))
2017 ui.write("%s\n" % util.hidepassword(path))
2018 return
2018 return
2019 ui.warn(_("not found!\n"))
2019 ui.warn(_("not found!\n"))
2020 return 1
2020 return 1
2021 else:
2021 else:
2022 for name, path in ui.configitems("paths"):
2022 for name, path in ui.configitems("paths"):
2023 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2023 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2024
2024
2025 def postincoming(ui, repo, modheads, optupdate, checkout):
2025 def postincoming(ui, repo, modheads, optupdate, checkout):
2026 if modheads == 0:
2026 if modheads == 0:
2027 return
2027 return
2028 if optupdate:
2028 if optupdate:
2029 if modheads <= 1 or checkout:
2029 if modheads <= 1 or checkout:
2030 return hg.update(repo, checkout)
2030 return hg.update(repo, checkout)
2031 else:
2031 else:
2032 ui.status(_("not updating, since new heads added\n"))
2032 ui.status(_("not updating, since new heads added\n"))
2033 if modheads > 1:
2033 if modheads > 1:
2034 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2034 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2035 else:
2035 else:
2036 ui.status(_("(run 'hg update' to get a working copy)\n"))
2036 ui.status(_("(run 'hg update' to get a working copy)\n"))
2037
2037
2038 def pull(ui, repo, source="default", **opts):
2038 def pull(ui, repo, source="default", **opts):
2039 """pull changes from the specified source
2039 """pull changes from the specified source
2040
2040
2041 Pull changes from a remote repository to a local one.
2041 Pull changes from a remote repository to a local one.
2042
2042
2043 This finds all changes from the repository at the specified path
2043 This finds all changes from the repository at the specified path
2044 or URL and adds them to the local repository. By default, this
2044 or URL and adds them to the local repository. By default, this
2045 does not update the copy of the project in the working directory.
2045 does not update the copy of the project in the working directory.
2046
2046
2047 Valid URLs are of the form:
2047 Valid URLs are of the form:
2048
2048
2049 local/filesystem/path (or file://local/filesystem/path)
2049 local/filesystem/path (or file://local/filesystem/path)
2050 http://[user@]host[:port]/[path]
2050 http://[user@]host[:port]/[path]
2051 https://[user@]host[:port]/[path]
2051 https://[user@]host[:port]/[path]
2052 ssh://[user@]host[:port]/[path]
2052 ssh://[user@]host[:port]/[path]
2053 static-http://host[:port]/[path]
2053 static-http://host[:port]/[path]
2054
2054
2055 Paths in the local filesystem can either point to Mercurial
2055 Paths in the local filesystem can either point to Mercurial
2056 repositories or to bundle files (as created by 'hg bundle' or
2056 repositories or to bundle files (as created by 'hg bundle' or
2057 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2057 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2058 allows access to a Mercurial repository where you simply use a web
2058 allows access to a Mercurial repository where you simply use a web
2059 server to publish the .hg directory as static content.
2059 server to publish the .hg directory as static content.
2060
2060
2061 An optional identifier after # indicates a particular branch, tag,
2061 An optional identifier after # indicates a particular branch, tag,
2062 or changeset to pull.
2062 or changeset to pull.
2063
2063
2064 Some notes about using SSH with Mercurial:
2064 Some notes about using SSH with Mercurial:
2065 - SSH requires an accessible shell account on the destination machine
2065 - SSH requires an accessible shell account on the destination machine
2066 and a copy of hg in the remote path or specified with as remotecmd.
2066 and a copy of hg in the remote path or specified with as remotecmd.
2067 - path is relative to the remote user's home directory by default.
2067 - path is relative to the remote user's home directory by default.
2068 Use an extra slash at the start of a path to specify an absolute path:
2068 Use an extra slash at the start of a path to specify an absolute path:
2069 ssh://example.com//tmp/repository
2069 ssh://example.com//tmp/repository
2070 - Mercurial doesn't use its own compression via SSH; the right thing
2070 - Mercurial doesn't use its own compression via SSH; the right thing
2071 to do is to configure it in your ~/.ssh/config, e.g.:
2071 to do is to configure it in your ~/.ssh/config, e.g.:
2072 Host *.mylocalnetwork.example.com
2072 Host *.mylocalnetwork.example.com
2073 Compression no
2073 Compression no
2074 Host *
2074 Host *
2075 Compression yes
2075 Compression yes
2076 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2076 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2077 with the --ssh command line option.
2077 with the --ssh command line option.
2078 """
2078 """
2079 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2079 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2080 cmdutil.setremoteconfig(ui, opts)
2080 cmdutil.setremoteconfig(ui, opts)
2081
2081
2082 other = hg.repository(ui, source)
2082 other = hg.repository(ui, source)
2083 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2083 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2084 if revs:
2084 if revs:
2085 try:
2085 try:
2086 revs = [other.lookup(rev) for rev in revs]
2086 revs = [other.lookup(rev) for rev in revs]
2087 except NoCapability:
2087 except NoCapability:
2088 error = _("Other repository doesn't support revision lookup, "
2088 error = _("Other repository doesn't support revision lookup, "
2089 "so a rev cannot be specified.")
2089 "so a rev cannot be specified.")
2090 raise util.Abort(error)
2090 raise util.Abort(error)
2091
2091
2092 modheads = repo.pull(other, heads=revs, force=opts['force'])
2092 modheads = repo.pull(other, heads=revs, force=opts['force'])
2093 return postincoming(ui, repo, modheads, opts['update'], checkout)
2093 return postincoming(ui, repo, modheads, opts['update'], checkout)
2094
2094
2095 def push(ui, repo, dest=None, **opts):
2095 def push(ui, repo, dest=None, **opts):
2096 """push changes to the specified destination
2096 """push changes to the specified destination
2097
2097
2098 Push changes from the local repository to the given destination.
2098 Push changes from the local repository to the given destination.
2099
2099
2100 This is the symmetrical operation for pull. It helps to move
2100 This is the symmetrical operation for pull. It helps to move
2101 changes from the current repository to a different one. If the
2101 changes from the current repository to a different one. If the
2102 destination is local this is identical to a pull in that directory
2102 destination is local this is identical to a pull in that directory
2103 from the current one.
2103 from the current one.
2104
2104
2105 By default, push will refuse to run if it detects the result would
2105 By default, push will refuse to run if it detects the result would
2106 increase the number of remote heads. This generally indicates the
2106 increase the number of remote heads. This generally indicates the
2107 the client has forgotten to pull and merge before pushing.
2107 the client has forgotten to pull and merge before pushing.
2108
2108
2109 Valid URLs are of the form:
2109 Valid URLs are of the form:
2110
2110
2111 local/filesystem/path (or file://local/filesystem/path)
2111 local/filesystem/path (or file://local/filesystem/path)
2112 ssh://[user@]host[:port]/[path]
2112 ssh://[user@]host[:port]/[path]
2113 http://[user@]host[:port]/[path]
2113 http://[user@]host[:port]/[path]
2114 https://[user@]host[:port]/[path]
2114 https://[user@]host[:port]/[path]
2115
2115
2116 An optional identifier after # indicates a particular branch, tag,
2116 An optional identifier after # indicates a particular branch, tag,
2117 or changeset to push. If -r is used, the named changeset and all its
2117 or changeset to push. If -r is used, the named changeset and all its
2118 ancestors will be pushed to the remote repository.
2118 ancestors will be pushed to the remote repository.
2119
2119
2120 Look at the help text for the pull command for important details
2120 Look at the help text for the pull command for important details
2121 about ssh:// URLs.
2121 about ssh:// URLs.
2122
2122
2123 Pushing to http:// and https:// URLs is only possible, if this
2123 Pushing to http:// and https:// URLs is only possible, if this
2124 feature is explicitly enabled on the remote Mercurial server.
2124 feature is explicitly enabled on the remote Mercurial server.
2125 """
2125 """
2126 dest, revs, checkout = hg.parseurl(
2126 dest, revs, checkout = hg.parseurl(
2127 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2127 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2128 cmdutil.setremoteconfig(ui, opts)
2128 cmdutil.setremoteconfig(ui, opts)
2129
2129
2130 other = hg.repository(ui, dest)
2130 other = hg.repository(ui, dest)
2131 ui.status('pushing to %s\n' % util.hidepassword(dest))
2131 ui.status('pushing to %s\n' % util.hidepassword(dest))
2132 if revs:
2132 if revs:
2133 revs = [repo.lookup(rev) for rev in revs]
2133 revs = [repo.lookup(rev) for rev in revs]
2134 r = repo.push(other, opts['force'], revs=revs)
2134 r = repo.push(other, opts['force'], revs=revs)
2135 return r == 0
2135 return r == 0
2136
2136
2137 def rawcommit(ui, repo, *pats, **opts):
2137 def rawcommit(ui, repo, *pats, **opts):
2138 """raw commit interface (DEPRECATED)
2138 """raw commit interface (DEPRECATED)
2139
2139
2140 (DEPRECATED)
2140 (DEPRECATED)
2141 Lowlevel commit, for use in helper scripts.
2141 Lowlevel commit, for use in helper scripts.
2142
2142
2143 This command is not intended to be used by normal users, as it is
2143 This command is not intended to be used by normal users, as it is
2144 primarily useful for importing from other SCMs.
2144 primarily useful for importing from other SCMs.
2145
2145
2146 This command is now deprecated and will be removed in a future
2146 This command is now deprecated and will be removed in a future
2147 release, please use debugsetparents and commit instead.
2147 release, please use debugsetparents and commit instead.
2148 """
2148 """
2149
2149
2150 ui.warn(_("(the rawcommit command is deprecated)\n"))
2150 ui.warn(_("(the rawcommit command is deprecated)\n"))
2151
2151
2152 message = cmdutil.logmessage(opts)
2152 message = cmdutil.logmessage(opts)
2153
2153
2154 files = cmdutil.match(repo, pats, opts).files()
2154 files = cmdutil.match(repo, pats, opts).files()
2155 if opts['files']:
2155 if opts['files']:
2156 files += open(opts['files']).read().splitlines()
2156 files += open(opts['files']).read().splitlines()
2157
2157
2158 parents = [repo.lookup(p) for p in opts['parent']]
2158 parents = [repo.lookup(p) for p in opts['parent']]
2159
2159
2160 try:
2160 try:
2161 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2161 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2162 except ValueError, inst:
2162 except ValueError, inst:
2163 raise util.Abort(str(inst))
2163 raise util.Abort(str(inst))
2164
2164
2165 def recover(ui, repo):
2165 def recover(ui, repo):
2166 """roll back an interrupted transaction
2166 """roll back an interrupted transaction
2167
2167
2168 Recover from an interrupted commit or pull.
2168 Recover from an interrupted commit or pull.
2169
2169
2170 This command tries to fix the repository status after an interrupted
2170 This command tries to fix the repository status after an interrupted
2171 operation. It should only be necessary when Mercurial suggests it.
2171 operation. It should only be necessary when Mercurial suggests it.
2172 """
2172 """
2173 if repo.recover():
2173 if repo.recover():
2174 return hg.verify(repo)
2174 return hg.verify(repo)
2175 return 1
2175 return 1
2176
2176
2177 def remove(ui, repo, *pats, **opts):
2177 def remove(ui, repo, *pats, **opts):
2178 """remove the specified files on the next commit
2178 """remove the specified files on the next commit
2179
2179
2180 Schedule the indicated files for removal from the repository.
2180 Schedule the indicated files for removal from the repository.
2181
2181
2182 This only removes files from the current branch, not from the entire
2182 This only removes files from the current branch, not from the entire
2183 project history. -A can be used to remove only files that have already
2183 project history. -A can be used to remove only files that have already
2184 been deleted, -f can be used to force deletion, and -Af can be used
2184 been deleted, -f can be used to force deletion, and -Af can be used
2185 to remove files from the next revision without deleting them.
2185 to remove files from the next revision without deleting them.
2186
2186
2187 The following table details the behavior of remove for different file
2187 The following table details the behavior of remove for different file
2188 states (columns) and option combinations (rows). The file states are
2188 states (columns) and option combinations (rows). The file states are
2189 Added, Clean, Modified and Missing (as reported by hg status). The
2189 Added, Clean, Modified and Missing (as reported by hg status). The
2190 actions are Warn, Remove (from branch) and Delete (from disk).
2190 actions are Warn, Remove (from branch) and Delete (from disk).
2191
2191
2192 A C M !
2192 A C M !
2193 none W RD W R
2193 none W RD W R
2194 -f R RD RD R
2194 -f R RD RD R
2195 -A W W W R
2195 -A W W W R
2196 -Af R R R R
2196 -Af R R R R
2197
2197
2198 This command schedules the files to be removed at the next commit.
2198 This command schedules the files to be removed at the next commit.
2199 To undo a remove before that, see hg revert.
2199 To undo a remove before that, see hg revert.
2200 """
2200 """
2201
2201
2202 after, force = opts.get('after'), opts.get('force')
2202 after, force = opts.get('after'), opts.get('force')
2203 if not pats and not after:
2203 if not pats and not after:
2204 raise util.Abort(_('no files specified'))
2204 raise util.Abort(_('no files specified'))
2205
2205
2206 m = cmdutil.match(repo, pats, opts)
2206 m = cmdutil.match(repo, pats, opts)
2207 mardu = map(dict.fromkeys, repo.status(match=m))[:5]
2207 mardu = map(dict.fromkeys, repo.status(match=m))[:5]
2208 modified, added, removed, deleted, unknown = mardu
2208 modified, added, removed, deleted, unknown = mardu
2209
2209
2210 remove, forget = [], []
2210 remove, forget = [], []
2211 for abs in repo.walk(m):
2211 for abs in repo.walk(m):
2212
2212
2213 reason = None
2213 reason = None
2214 if abs in removed or abs in unknown:
2214 if abs in removed or abs in unknown:
2215 continue
2215 continue
2216
2216
2217 # last column
2217 # last column
2218 elif abs in deleted:
2218 elif abs in deleted:
2219 remove.append(abs)
2219 remove.append(abs)
2220
2220
2221 # rest of the third row
2221 # rest of the third row
2222 elif after and not force:
2222 elif after and not force:
2223 reason = _('still exists (use -f to force removal)')
2223 reason = _('still exists (use -f to force removal)')
2224
2224
2225 # rest of the first column
2225 # rest of the first column
2226 elif abs in added:
2226 elif abs in added:
2227 if not force:
2227 if not force:
2228 reason = _('has been marked for add (use -f to force removal)')
2228 reason = _('has been marked for add (use -f to force removal)')
2229 else:
2229 else:
2230 forget.append(abs)
2230 forget.append(abs)
2231
2231
2232 # rest of the third column
2232 # rest of the third column
2233 elif abs in modified:
2233 elif abs in modified:
2234 if not force:
2234 if not force:
2235 reason = _('is modified (use -f to force removal)')
2235 reason = _('is modified (use -f to force removal)')
2236 else:
2236 else:
2237 remove.append(abs)
2237 remove.append(abs)
2238
2238
2239 # rest of the second column
2239 # rest of the second column
2240 elif not reason:
2240 elif not reason:
2241 remove.append(abs)
2241 remove.append(abs)
2242
2242
2243 if reason:
2243 if reason:
2244 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2244 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2245 elif ui.verbose or not m.exact(abs):
2245 elif ui.verbose or not m.exact(abs):
2246 ui.status(_('removing %s\n') % m.rel(abs))
2246 ui.status(_('removing %s\n') % m.rel(abs))
2247
2247
2248 repo.forget(forget)
2248 repo.forget(forget)
2249 repo.remove(remove, unlink=not after)
2249 repo.remove(remove, unlink=not after)
2250
2250
2251 def rename(ui, repo, *pats, **opts):
2251 def rename(ui, repo, *pats, **opts):
2252 """rename files; equivalent of copy + remove
2252 """rename files; equivalent of copy + remove
2253
2253
2254 Mark dest as copies of sources; mark sources for deletion. If
2254 Mark dest as copies of sources; mark sources for deletion. If
2255 dest is a directory, copies are put in that directory. If dest is
2255 dest is a directory, copies are put in that directory. If dest is
2256 a file, there can only be one source.
2256 a file, there can only be one source.
2257
2257
2258 By default, this command copies the contents of files as they
2258 By default, this command copies the contents of files as they
2259 stand in the working directory. If invoked with --after, the
2259 stand in the working directory. If invoked with --after, the
2260 operation is recorded, but no copying is performed.
2260 operation is recorded, but no copying is performed.
2261
2261
2262 This command takes effect in the next commit. To undo a rename
2262 This command takes effect in the next commit. To undo a rename
2263 before that, see hg revert.
2263 before that, see hg revert.
2264 """
2264 """
2265 wlock = repo.wlock(False)
2265 wlock = repo.wlock(False)
2266 try:
2266 try:
2267 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2267 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2268 finally:
2268 finally:
2269 del wlock
2269 del wlock
2270
2270
2271 def resolve(ui, repo, *pats, **opts):
2271 def resolve(ui, repo, *pats, **opts):
2272 """resolve file merges from a branch merge or update
2272 """resolve file merges from a branch merge or update
2273
2273
2274 This command will attempt to resolve unresolved merges from the
2274 This command will attempt to resolve unresolved merges from the
2275 last update or merge command. This will use the local file
2275 last update or merge command. This will use the local file
2276 revision preserved at the last update or merge to cleanly retry
2276 revision preserved at the last update or merge to cleanly retry
2277 the file merge attempt. With no file or options specified, this
2277 the file merge attempt. With no file or options specified, this
2278 command will attempt to resolve all unresolved files.
2278 command will attempt to resolve all unresolved files.
2279
2279
2280 The codes used to show the status of files are:
2280 The codes used to show the status of files are:
2281 U = unresolved
2281 U = unresolved
2282 R = resolved
2282 R = resolved
2283 """
2283 """
2284
2284
2285 if len([x for x in opts if opts[x]]) > 1:
2285 if len([x for x in opts if opts[x]]) > 1:
2286 raise util.Abort(_("too many options specified"))
2286 raise util.Abort(_("too many options specified"))
2287
2287
2288 ms = merge_.mergestate(repo)
2288 ms = merge_.mergestate(repo)
2289 m = cmdutil.match(repo, pats, opts)
2289 m = cmdutil.match(repo, pats, opts)
2290
2290
2291 for f in ms:
2291 for f in ms:
2292 if m(f):
2292 if m(f):
2293 if opts.get("list"):
2293 if opts.get("list"):
2294 ui.write("%s %s\n" % (ms[f].upper(), f))
2294 ui.write("%s %s\n" % (ms[f].upper(), f))
2295 elif opts.get("mark"):
2295 elif opts.get("mark"):
2296 ms.mark(f, "r")
2296 ms.mark(f, "r")
2297 elif opts.get("unmark"):
2297 elif opts.get("unmark"):
2298 ms.mark(f, "u")
2298 ms.mark(f, "u")
2299 else:
2299 else:
2300 wctx = repo.changectx(None)
2300 wctx = repo.changectx(None)
2301 mctx = wctx.parents()[-1]
2301 mctx = wctx.parents()[-1]
2302 ms.resolve(f, wctx, mctx)
2302 ms.resolve(f, wctx, mctx)
2303
2303
2304 def revert(ui, repo, *pats, **opts):
2304 def revert(ui, repo, *pats, **opts):
2305 """restore individual files or dirs to an earlier state
2305 """restore individual files or dirs to an earlier state
2306
2306
2307 (use update -r to check out earlier revisions, revert does not
2307 (use update -r to check out earlier revisions, revert does not
2308 change the working dir parents)
2308 change the working dir parents)
2309
2309
2310 With no revision specified, revert the named files or directories
2310 With no revision specified, revert the named files or directories
2311 to the contents they had in the parent of the working directory.
2311 to the contents they had in the parent of the working directory.
2312 This restores the contents of the affected files to an unmodified
2312 This restores the contents of the affected files to an unmodified
2313 state and unschedules adds, removes, copies, and renames. If the
2313 state and unschedules adds, removes, copies, and renames. If the
2314 working directory has two parents, you must explicitly specify the
2314 working directory has two parents, you must explicitly specify the
2315 revision to revert to.
2315 revision to revert to.
2316
2316
2317 Using the -r option, revert the given files or directories to their
2317 Using the -r option, revert the given files or directories to their
2318 contents as of a specific revision. This can be helpful to "roll
2318 contents as of a specific revision. This can be helpful to "roll
2319 back" some or all of an earlier change.
2319 back" some or all of an earlier change.
2320 See 'hg help dates' for a list of formats valid for -d/--date.
2320 See 'hg help dates' for a list of formats valid for -d/--date.
2321
2321
2322 Revert modifies the working directory. It does not commit any
2322 Revert modifies the working directory. It does not commit any
2323 changes, or change the parent of the working directory. If you
2323 changes, or change the parent of the working directory. If you
2324 revert to a revision other than the parent of the working
2324 revert to a revision other than the parent of the working
2325 directory, the reverted files will thus appear modified
2325 directory, the reverted files will thus appear modified
2326 afterwards.
2326 afterwards.
2327
2327
2328 If a file has been deleted, it is restored. If the executable
2328 If a file has been deleted, it is restored. If the executable
2329 mode of a file was changed, it is reset.
2329 mode of a file was changed, it is reset.
2330
2330
2331 If names are given, all files matching the names are reverted.
2331 If names are given, all files matching the names are reverted.
2332 If no arguments are given, no files are reverted.
2332 If no arguments are given, no files are reverted.
2333
2333
2334 Modified files are saved with a .orig suffix before reverting.
2334 Modified files are saved with a .orig suffix before reverting.
2335 To disable these backups, use --no-backup.
2335 To disable these backups, use --no-backup.
2336 """
2336 """
2337
2337
2338 if opts["date"]:
2338 if opts["date"]:
2339 if opts["rev"]:
2339 if opts["rev"]:
2340 raise util.Abort(_("you can't specify a revision and a date"))
2340 raise util.Abort(_("you can't specify a revision and a date"))
2341 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2341 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2342
2342
2343 if not pats and not opts['all']:
2343 if not pats and not opts['all']:
2344 raise util.Abort(_('no files or directories specified; '
2344 raise util.Abort(_('no files or directories specified; '
2345 'use --all to revert the whole repo'))
2345 'use --all to revert the whole repo'))
2346
2346
2347 parent, p2 = repo.dirstate.parents()
2347 parent, p2 = repo.dirstate.parents()
2348 if not opts['rev'] and p2 != nullid:
2348 if not opts['rev'] and p2 != nullid:
2349 raise util.Abort(_('uncommitted merge - please provide a '
2349 raise util.Abort(_('uncommitted merge - please provide a '
2350 'specific revision'))
2350 'specific revision'))
2351 ctx = repo.changectx(opts['rev'])
2351 ctx = repo.changectx(opts['rev'])
2352 node = ctx.node()
2352 node = ctx.node()
2353 mf = ctx.manifest()
2353 mf = ctx.manifest()
2354 if node == parent:
2354 if node == parent:
2355 pmf = mf
2355 pmf = mf
2356 else:
2356 else:
2357 pmf = None
2357 pmf = None
2358
2358
2359 # need all matching names in dirstate and manifest of target rev,
2359 # need all matching names in dirstate and manifest of target rev,
2360 # so have to walk both. do not print errors if files exist in one
2360 # so have to walk both. do not print errors if files exist in one
2361 # but not other.
2361 # but not other.
2362
2362
2363 names = {}
2363 names = {}
2364
2364
2365 wlock = repo.wlock()
2365 wlock = repo.wlock()
2366 try:
2366 try:
2367 # walk dirstate.
2367 # walk dirstate.
2368 files = []
2368 files = []
2369
2369
2370 m = cmdutil.match(repo, pats, opts)
2370 m = cmdutil.match(repo, pats, opts)
2371 m.bad = lambda x,y: False
2371 m.bad = lambda x,y: False
2372 for abs in repo.walk(m):
2372 for abs in repo.walk(m):
2373 names[abs] = m.rel(abs), m.exact(abs)
2373 names[abs] = m.rel(abs), m.exact(abs)
2374
2374
2375 # walk target manifest.
2375 # walk target manifest.
2376
2376
2377 def badfn(path, msg):
2377 def badfn(path, msg):
2378 if path in names:
2378 if path in names:
2379 return False
2379 return False
2380 path_ = path + '/'
2380 path_ = path + '/'
2381 for f in names:
2381 for f in names:
2382 if f.startswith(path_):
2382 if f.startswith(path_):
2383 return False
2383 return False
2384 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2384 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2385 return False
2385 return False
2386
2386
2387 m = cmdutil.match(repo, pats, opts)
2387 m = cmdutil.match(repo, pats, opts)
2388 m.bad = badfn
2388 m.bad = badfn
2389 for abs in repo.walk(m, node=node):
2389 for abs in repo.walk(m, node=node):
2390 if abs not in names:
2390 if abs not in names:
2391 names[abs] = m.rel(abs), m.exact(abs)
2391 names[abs] = m.rel(abs), m.exact(abs)
2392
2392
2393 m = cmdutil.matchfiles(repo, names)
2393 m = cmdutil.matchfiles(repo, names)
2394 changes = repo.status(match=m)[:4]
2394 changes = repo.status(match=m)[:4]
2395 modified, added, removed, deleted = map(dict.fromkeys, changes)
2395 modified, added, removed, deleted = map(dict.fromkeys, changes)
2396
2396
2397 # if f is a rename, also revert the source
2397 # if f is a rename, also revert the source
2398 cwd = repo.getcwd()
2398 cwd = repo.getcwd()
2399 for f in added:
2399 for f in added:
2400 src = repo.dirstate.copied(f)
2400 src = repo.dirstate.copied(f)
2401 if src and src not in names and repo.dirstate[src] == 'r':
2401 if src and src not in names and repo.dirstate[src] == 'r':
2402 removed[src] = None
2402 removed[src] = None
2403 names[src] = (repo.pathto(src, cwd), True)
2403 names[src] = (repo.pathto(src, cwd), True)
2404
2404
2405 def removeforget(abs):
2405 def removeforget(abs):
2406 if repo.dirstate[abs] == 'a':
2406 if repo.dirstate[abs] == 'a':
2407 return _('forgetting %s\n')
2407 return _('forgetting %s\n')
2408 return _('removing %s\n')
2408 return _('removing %s\n')
2409
2409
2410 revert = ([], _('reverting %s\n'))
2410 revert = ([], _('reverting %s\n'))
2411 add = ([], _('adding %s\n'))
2411 add = ([], _('adding %s\n'))
2412 remove = ([], removeforget)
2412 remove = ([], removeforget)
2413 undelete = ([], _('undeleting %s\n'))
2413 undelete = ([], _('undeleting %s\n'))
2414
2414
2415 disptable = (
2415 disptable = (
2416 # dispatch table:
2416 # dispatch table:
2417 # file state
2417 # file state
2418 # action if in target manifest
2418 # action if in target manifest
2419 # action if not in target manifest
2419 # action if not in target manifest
2420 # make backup if in target manifest
2420 # make backup if in target manifest
2421 # make backup if not in target manifest
2421 # make backup if not in target manifest
2422 (modified, revert, remove, True, True),
2422 (modified, revert, remove, True, True),
2423 (added, revert, remove, True, False),
2423 (added, revert, remove, True, False),
2424 (removed, undelete, None, False, False),
2424 (removed, undelete, None, False, False),
2425 (deleted, revert, remove, False, False),
2425 (deleted, revert, remove, False, False),
2426 )
2426 )
2427
2427
2428 entries = names.items()
2428 entries = names.items()
2429 entries.sort()
2429 entries.sort()
2430
2430
2431 for abs, (rel, exact) in entries:
2431 for abs, (rel, exact) in entries:
2432 mfentry = mf.get(abs)
2432 mfentry = mf.get(abs)
2433 target = repo.wjoin(abs)
2433 target = repo.wjoin(abs)
2434 def handle(xlist, dobackup):
2434 def handle(xlist, dobackup):
2435 xlist[0].append(abs)
2435 xlist[0].append(abs)
2436 if dobackup and not opts['no_backup'] and util.lexists(target):
2436 if dobackup and not opts['no_backup'] and util.lexists(target):
2437 bakname = "%s.orig" % rel
2437 bakname = "%s.orig" % rel
2438 ui.note(_('saving current version of %s as %s\n') %
2438 ui.note(_('saving current version of %s as %s\n') %
2439 (rel, bakname))
2439 (rel, bakname))
2440 if not opts.get('dry_run'):
2440 if not opts.get('dry_run'):
2441 util.copyfile(target, bakname)
2441 util.copyfile(target, bakname)
2442 if ui.verbose or not exact:
2442 if ui.verbose or not exact:
2443 msg = xlist[1]
2443 msg = xlist[1]
2444 if not isinstance(msg, basestring):
2444 if not isinstance(msg, basestring):
2445 msg = msg(abs)
2445 msg = msg(abs)
2446 ui.status(msg % rel)
2446 ui.status(msg % rel)
2447 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2447 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2448 if abs not in table: continue
2448 if abs not in table: continue
2449 # file has changed in dirstate
2449 # file has changed in dirstate
2450 if mfentry:
2450 if mfentry:
2451 handle(hitlist, backuphit)
2451 handle(hitlist, backuphit)
2452 elif misslist is not None:
2452 elif misslist is not None:
2453 handle(misslist, backupmiss)
2453 handle(misslist, backupmiss)
2454 break
2454 break
2455 else:
2455 else:
2456 if abs not in repo.dirstate:
2456 if abs not in repo.dirstate:
2457 if mfentry:
2457 if mfentry:
2458 handle(add, True)
2458 handle(add, True)
2459 elif exact:
2459 elif exact:
2460 ui.warn(_('file not managed: %s\n') % rel)
2460 ui.warn(_('file not managed: %s\n') % rel)
2461 continue
2461 continue
2462 # file has not changed in dirstate
2462 # file has not changed in dirstate
2463 if node == parent:
2463 if node == parent:
2464 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2464 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2465 continue
2465 continue
2466 if pmf is None:
2466 if pmf is None:
2467 # only need parent manifest in this unlikely case,
2467 # only need parent manifest in this unlikely case,
2468 # so do not read by default
2468 # so do not read by default
2469 pmf = repo.changectx(parent).manifest()
2469 pmf = repo.changectx(parent).manifest()
2470 if abs in pmf:
2470 if abs in pmf:
2471 if mfentry:
2471 if mfentry:
2472 # if version of file is same in parent and target
2472 # if version of file is same in parent and target
2473 # manifests, do nothing
2473 # manifests, do nothing
2474 if (pmf[abs] != mfentry or
2474 if (pmf[abs] != mfentry or
2475 pmf.flags(abs) != mf.flags(abs)):
2475 pmf.flags(abs) != mf.flags(abs)):
2476 handle(revert, False)
2476 handle(revert, False)
2477 else:
2477 else:
2478 handle(remove, False)
2478 handle(remove, False)
2479
2479
2480 if not opts.get('dry_run'):
2480 if not opts.get('dry_run'):
2481 def checkout(f):
2481 def checkout(f):
2482 fc = ctx[f]
2482 fc = ctx[f]
2483 repo.wwrite(f, fc.data(), fc.flags())
2483 repo.wwrite(f, fc.data(), fc.flags())
2484
2484
2485 audit_path = util.path_auditor(repo.root)
2485 audit_path = util.path_auditor(repo.root)
2486 for f in remove[0]:
2486 for f in remove[0]:
2487 if repo.dirstate[f] == 'a':
2487 if repo.dirstate[f] == 'a':
2488 repo.dirstate.forget(f)
2488 repo.dirstate.forget(f)
2489 continue
2489 continue
2490 audit_path(f)
2490 audit_path(f)
2491 try:
2491 try:
2492 util.unlink(repo.wjoin(f))
2492 util.unlink(repo.wjoin(f))
2493 except OSError:
2493 except OSError:
2494 pass
2494 pass
2495 repo.dirstate.remove(f)
2495 repo.dirstate.remove(f)
2496
2496
2497 normal = None
2497 normal = None
2498 if node == parent:
2498 if node == parent:
2499 # We're reverting to our parent. If possible, we'd like status
2499 # We're reverting to our parent. If possible, we'd like status
2500 # to report the file as clean. We have to use normallookup for
2500 # to report the file as clean. We have to use normallookup for
2501 # merges to avoid losing information about merged/dirty files.
2501 # merges to avoid losing information about merged/dirty files.
2502 if p2 != nullid:
2502 if p2 != nullid:
2503 normal = repo.dirstate.normallookup
2503 normal = repo.dirstate.normallookup
2504 else:
2504 else:
2505 normal = repo.dirstate.normal
2505 normal = repo.dirstate.normal
2506 for f in revert[0]:
2506 for f in revert[0]:
2507 checkout(f)
2507 checkout(f)
2508 if normal:
2508 if normal:
2509 normal(f)
2509 normal(f)
2510
2510
2511 for f in add[0]:
2511 for f in add[0]:
2512 checkout(f)
2512 checkout(f)
2513 repo.dirstate.add(f)
2513 repo.dirstate.add(f)
2514
2514
2515 normal = repo.dirstate.normallookup
2515 normal = repo.dirstate.normallookup
2516 if node == parent and p2 == nullid:
2516 if node == parent and p2 == nullid:
2517 normal = repo.dirstate.normal
2517 normal = repo.dirstate.normal
2518 for f in undelete[0]:
2518 for f in undelete[0]:
2519 checkout(f)
2519 checkout(f)
2520 normal(f)
2520 normal(f)
2521
2521
2522 finally:
2522 finally:
2523 del wlock
2523 del wlock
2524
2524
2525 def rollback(ui, repo):
2525 def rollback(ui, repo):
2526 """roll back the last transaction
2526 """roll back the last transaction
2527
2527
2528 This command should be used with care. There is only one level of
2528 This command should be used with care. There is only one level of
2529 rollback, and there is no way to undo a rollback. It will also
2529 rollback, and there is no way to undo a rollback. It will also
2530 restore the dirstate at the time of the last transaction, losing
2530 restore the dirstate at the time of the last transaction, losing
2531 any dirstate changes since that time.
2531 any dirstate changes since that time.
2532
2532
2533 Transactions are used to encapsulate the effects of all commands
2533 Transactions are used to encapsulate the effects of all commands
2534 that create new changesets or propagate existing changesets into a
2534 that create new changesets or propagate existing changesets into a
2535 repository. For example, the following commands are transactional,
2535 repository. For example, the following commands are transactional,
2536 and their effects can be rolled back:
2536 and their effects can be rolled back:
2537
2537
2538 commit
2538 commit
2539 import
2539 import
2540 pull
2540 pull
2541 push (with this repository as destination)
2541 push (with this repository as destination)
2542 unbundle
2542 unbundle
2543
2543
2544 This command is not intended for use on public repositories. Once
2544 This command is not intended for use on public repositories. Once
2545 changes are visible for pull by other users, rolling a transaction
2545 changes are visible for pull by other users, rolling a transaction
2546 back locally is ineffective (someone else may already have pulled
2546 back locally is ineffective (someone else may already have pulled
2547 the changes). Furthermore, a race is possible with readers of the
2547 the changes). Furthermore, a race is possible with readers of the
2548 repository; for example an in-progress pull from the repository
2548 repository; for example an in-progress pull from the repository
2549 may fail if a rollback is performed.
2549 may fail if a rollback is performed.
2550 """
2550 """
2551 repo.rollback()
2551 repo.rollback()
2552
2552
2553 def root(ui, repo):
2553 def root(ui, repo):
2554 """print the root (top) of the current working dir
2554 """print the root (top) of the current working dir
2555
2555
2556 Print the root directory of the current repository.
2556 Print the root directory of the current repository.
2557 """
2557 """
2558 ui.write(repo.root + "\n")
2558 ui.write(repo.root + "\n")
2559
2559
2560 def serve(ui, repo, **opts):
2560 def serve(ui, repo, **opts):
2561 """export the repository via HTTP
2561 """export the repository via HTTP
2562
2562
2563 Start a local HTTP repository browser and pull server.
2563 Start a local HTTP repository browser and pull server.
2564
2564
2565 By default, the server logs accesses to stdout and errors to
2565 By default, the server logs accesses to stdout and errors to
2566 stderr. Use the "-A" and "-E" options to log to files.
2566 stderr. Use the "-A" and "-E" options to log to files.
2567 """
2567 """
2568
2568
2569 if opts["stdio"]:
2569 if opts["stdio"]:
2570 if repo is None:
2570 if repo is None:
2571 raise RepoError(_("There is no Mercurial repository here"
2571 raise RepoError(_("There is no Mercurial repository here"
2572 " (.hg not found)"))
2572 " (.hg not found)"))
2573 s = sshserver.sshserver(ui, repo)
2573 s = sshserver.sshserver(ui, repo)
2574 s.serve_forever()
2574 s.serve_forever()
2575
2575
2576 parentui = ui.parentui or ui
2576 parentui = ui.parentui or ui
2577 optlist = ("name templates style address port prefix ipv6"
2577 optlist = ("name templates style address port prefix ipv6"
2578 " accesslog errorlog webdir_conf certificate")
2578 " accesslog errorlog webdir_conf certificate")
2579 for o in optlist.split():
2579 for o in optlist.split():
2580 if opts[o]:
2580 if opts[o]:
2581 parentui.setconfig("web", o, str(opts[o]))
2581 parentui.setconfig("web", o, str(opts[o]))
2582 if (repo is not None) and (repo.ui != parentui):
2582 if (repo is not None) and (repo.ui != parentui):
2583 repo.ui.setconfig("web", o, str(opts[o]))
2583 repo.ui.setconfig("web", o, str(opts[o]))
2584
2584
2585 if repo is None and not ui.config("web", "webdir_conf"):
2585 if repo is None and not ui.config("web", "webdir_conf"):
2586 raise RepoError(_("There is no Mercurial repository here"
2586 raise RepoError(_("There is no Mercurial repository here"
2587 " (.hg not found)"))
2587 " (.hg not found)"))
2588
2588
2589 class service:
2589 class service:
2590 def init(self):
2590 def init(self):
2591 util.set_signal_handler()
2591 util.set_signal_handler()
2592 self.httpd = hgweb.server.create_server(parentui, repo)
2592 self.httpd = hgweb.server.create_server(parentui, repo)
2593
2593
2594 if not ui.verbose: return
2594 if not ui.verbose: return
2595
2595
2596 if self.httpd.prefix:
2596 if self.httpd.prefix:
2597 prefix = self.httpd.prefix.strip('/') + '/'
2597 prefix = self.httpd.prefix.strip('/') + '/'
2598 else:
2598 else:
2599 prefix = ''
2599 prefix = ''
2600
2600
2601 port = ':%d' % self.httpd.port
2601 port = ':%d' % self.httpd.port
2602 if port == ':80':
2602 if port == ':80':
2603 port = ''
2603 port = ''
2604
2604
2605 bindaddr = self.httpd.addr
2605 bindaddr = self.httpd.addr
2606 if bindaddr == '0.0.0.0':
2606 if bindaddr == '0.0.0.0':
2607 bindaddr = '*'
2607 bindaddr = '*'
2608 elif ':' in bindaddr: # IPv6
2608 elif ':' in bindaddr: # IPv6
2609 bindaddr = '[%s]' % bindaddr
2609 bindaddr = '[%s]' % bindaddr
2610
2610
2611 fqaddr = self.httpd.fqaddr
2611 fqaddr = self.httpd.fqaddr
2612 if ':' in fqaddr:
2612 if ':' in fqaddr:
2613 fqaddr = '[%s]' % fqaddr
2613 fqaddr = '[%s]' % fqaddr
2614 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2614 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2615 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2615 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2616
2616
2617 def run(self):
2617 def run(self):
2618 self.httpd.serve_forever()
2618 self.httpd.serve_forever()
2619
2619
2620 service = service()
2620 service = service()
2621
2621
2622 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2622 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2623
2623
2624 def status(ui, repo, *pats, **opts):
2624 def status(ui, repo, *pats, **opts):
2625 """show changed files in the working directory
2625 """show changed files in the working directory
2626
2626
2627 Show status of files in the repository. If names are given, only
2627 Show status of files in the repository. If names are given, only
2628 files that match are shown. Files that are clean or ignored or
2628 files that match are shown. Files that are clean or ignored or
2629 source of a copy/move operation, are not listed unless -c (clean),
2629 source of a copy/move operation, are not listed unless -c (clean),
2630 -i (ignored), -C (copies) or -A is given. Unless options described
2630 -i (ignored), -C (copies) or -A is given. Unless options described
2631 with "show only ..." are given, the options -mardu are used.
2631 with "show only ..." are given, the options -mardu are used.
2632
2632
2633 Option -q/--quiet hides untracked (unknown and ignored) files
2633 Option -q/--quiet hides untracked (unknown and ignored) files
2634 unless explicitly requested with -u/--unknown or -i/-ignored.
2634 unless explicitly requested with -u/--unknown or -i/-ignored.
2635
2635
2636 NOTE: status may appear to disagree with diff if permissions have
2636 NOTE: status may appear to disagree with diff if permissions have
2637 changed or a merge has occurred. The standard diff format does not
2637 changed or a merge has occurred. The standard diff format does not
2638 report permission changes and diff only reports changes relative
2638 report permission changes and diff only reports changes relative
2639 to one merge parent.
2639 to one merge parent.
2640
2640
2641 If one revision is given, it is used as the base revision.
2641 If one revision is given, it is used as the base revision.
2642 If two revisions are given, the difference between them is shown.
2642 If two revisions are given, the difference between them is shown.
2643
2643
2644 The codes used to show the status of files are:
2644 The codes used to show the status of files are:
2645 M = modified
2645 M = modified
2646 A = added
2646 A = added
2647 R = removed
2647 R = removed
2648 C = clean
2648 C = clean
2649 ! = deleted, but still tracked
2649 ! = deleted, but still tracked
2650 ? = not tracked
2650 ? = not tracked
2651 I = ignored
2651 I = ignored
2652 = the previous added file was copied from here
2652 = the previous added file was copied from here
2653 """
2653 """
2654
2654
2655 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2655 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2656 cwd = (pats and repo.getcwd()) or ''
2656 cwd = (pats and repo.getcwd()) or ''
2657 end = opts['print0'] and '\0' or '\n'
2657 end = opts['print0'] and '\0' or '\n'
2658 copy = {}
2658 copy = {}
2659 states = 'modified added removed deleted unknown ignored clean'.split()
2659 states = 'modified added removed deleted unknown ignored clean'.split()
2660 show = [k for k in states if opts[k]]
2660 show = [k for k in states if opts[k]]
2661 if opts['all']:
2661 if opts['all']:
2662 show += ui.quiet and (states[:4] + ['clean']) or states
2662 show += ui.quiet and (states[:4] + ['clean']) or states
2663 if not show:
2663 if not show:
2664 show = ui.quiet and states[:4] or states[:5]
2664 show = ui.quiet and states[:4] or states[:5]
2665
2665
2666 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2666 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2667 'ignored' in show, 'clean' in show, 'unknown' in show)
2667 'ignored' in show, 'clean' in show, 'unknown' in show)
2668 changestates = zip(states, 'MAR!?IC', stat)
2668 changestates = zip(states, 'MAR!?IC', stat)
2669
2669
2670 if (opts['all'] or opts['copies']) and not opts['no_status']:
2670 if (opts['all'] or opts['copies']) and not opts['no_status']:
2671 ctxn = repo.changectx(nullid)
2671 ctxn = repo.changectx(nullid)
2672 ctx1 = repo.changectx(node1)
2672 ctx1 = repo.changectx(node1)
2673 ctx2 = repo.changectx(node2)
2673 ctx2 = repo.changectx(node2)
2674 added = stat[1]
2674 added = stat[1]
2675 if node2 is None:
2675 if node2 is None:
2676 added = stat[0] + stat[1] # merged?
2676 added = stat[0] + stat[1] # merged?
2677
2677
2678 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
2678 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
2679 if k in added:
2679 if k in added:
2680 copy[k] = v
2680 copy[k] = v
2681 elif v in added:
2681 elif v in added:
2682 copy[v] = k
2682 copy[v] = k
2683
2683
2684 for state, char, files in changestates:
2684 for state, char, files in changestates:
2685 if state in show:
2685 if state in show:
2686 format = "%s %%s%s" % (char, end)
2686 format = "%s %%s%s" % (char, end)
2687 if opts['no_status']:
2687 if opts['no_status']:
2688 format = "%%s%s" % end
2688 format = "%%s%s" % end
2689
2689
2690 for f in files:
2690 for f in files:
2691 ui.write(format % repo.pathto(f, cwd))
2691 ui.write(format % repo.pathto(f, cwd))
2692 if f in copy:
2692 if f in copy:
2693 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2693 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2694
2694
2695 def tag(ui, repo, name1, *names, **opts):
2695 def tag(ui, repo, name1, *names, **opts):
2696 """add one or more tags for the current or given revision
2696 """add one or more tags for the current or given revision
2697
2697
2698 Name a particular revision using <name>.
2698 Name a particular revision using <name>.
2699
2699
2700 Tags are used to name particular revisions of the repository and are
2700 Tags are used to name particular revisions of the repository and are
2701 very useful to compare different revisions, to go back to significant
2701 very useful to compare different revisions, to go back to significant
2702 earlier versions or to mark branch points as releases, etc.
2702 earlier versions or to mark branch points as releases, etc.
2703
2703
2704 If no revision is given, the parent of the working directory is used,
2704 If no revision is given, the parent of the working directory is used,
2705 or tip if no revision is checked out.
2705 or tip if no revision is checked out.
2706
2706
2707 To facilitate version control, distribution, and merging of tags,
2707 To facilitate version control, distribution, and merging of tags,
2708 they are stored as a file named ".hgtags" which is managed
2708 they are stored as a file named ".hgtags" which is managed
2709 similarly to other project files and can be hand-edited if
2709 similarly to other project files and can be hand-edited if
2710 necessary. The file '.hg/localtags' is used for local tags (not
2710 necessary. The file '.hg/localtags' is used for local tags (not
2711 shared among repositories).
2711 shared among repositories).
2712
2712
2713 See 'hg help dates' for a list of formats valid for -d/--date.
2713 See 'hg help dates' for a list of formats valid for -d/--date.
2714 """
2714 """
2715
2715
2716 rev_ = "."
2716 rev_ = "."
2717 names = (name1,) + names
2717 names = (name1,) + names
2718 if len(names) != len(dict.fromkeys(names)):
2718 if len(names) != len(dict.fromkeys(names)):
2719 raise util.Abort(_('tag names must be unique'))
2719 raise util.Abort(_('tag names must be unique'))
2720 for n in names:
2720 for n in names:
2721 if n in ['tip', '.', 'null']:
2721 if n in ['tip', '.', 'null']:
2722 raise util.Abort(_('the name \'%s\' is reserved') % n)
2722 raise util.Abort(_('the name \'%s\' is reserved') % n)
2723 if opts['rev'] and opts['remove']:
2723 if opts['rev'] and opts['remove']:
2724 raise util.Abort(_("--rev and --remove are incompatible"))
2724 raise util.Abort(_("--rev and --remove are incompatible"))
2725 if opts['rev']:
2725 if opts['rev']:
2726 rev_ = opts['rev']
2726 rev_ = opts['rev']
2727 message = opts['message']
2727 message = opts['message']
2728 if opts['remove']:
2728 if opts['remove']:
2729 expectedtype = opts['local'] and 'local' or 'global'
2729 expectedtype = opts['local'] and 'local' or 'global'
2730 for n in names:
2730 for n in names:
2731 if not repo.tagtype(n):
2731 if not repo.tagtype(n):
2732 raise util.Abort(_('tag \'%s\' does not exist') % n)
2732 raise util.Abort(_('tag \'%s\' does not exist') % n)
2733 if repo.tagtype(n) != expectedtype:
2733 if repo.tagtype(n) != expectedtype:
2734 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2734 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2735 (n, expectedtype))
2735 (n, expectedtype))
2736 rev_ = nullid
2736 rev_ = nullid
2737 if not message:
2737 if not message:
2738 message = _('Removed tag %s') % ', '.join(names)
2738 message = _('Removed tag %s') % ', '.join(names)
2739 elif not opts['force']:
2739 elif not opts['force']:
2740 for n in names:
2740 for n in names:
2741 if n in repo.tags():
2741 if n in repo.tags():
2742 raise util.Abort(_('tag \'%s\' already exists '
2742 raise util.Abort(_('tag \'%s\' already exists '
2743 '(use -f to force)') % n)
2743 '(use -f to force)') % n)
2744 if not rev_ and repo.dirstate.parents()[1] != nullid:
2744 if not rev_ and repo.dirstate.parents()[1] != nullid:
2745 raise util.Abort(_('uncommitted merge - please provide a '
2745 raise util.Abort(_('uncommitted merge - please provide a '
2746 'specific revision'))
2746 'specific revision'))
2747 r = repo.changectx(rev_).node()
2747 r = repo.changectx(rev_).node()
2748
2748
2749 if not message:
2749 if not message:
2750 message = (_('Added tag %s for changeset %s') %
2750 message = (_('Added tag %s for changeset %s') %
2751 (', '.join(names), short(r)))
2751 (', '.join(names), short(r)))
2752
2752
2753 date = opts.get('date')
2753 date = opts.get('date')
2754 if date:
2754 if date:
2755 date = util.parsedate(date)
2755 date = util.parsedate(date)
2756
2756
2757 repo.tag(names, r, message, opts['local'], opts['user'], date)
2757 repo.tag(names, r, message, opts['local'], opts['user'], date)
2758
2758
2759 def tags(ui, repo):
2759 def tags(ui, repo):
2760 """list repository tags
2760 """list repository tags
2761
2761
2762 List the repository tags.
2762 List the repository tags.
2763
2763
2764 This lists both regular and local tags. When the -v/--verbose switch
2764 This lists both regular and local tags. When the -v/--verbose switch
2765 is used, a third column "local" is printed for local tags.
2765 is used, a third column "local" is printed for local tags.
2766 """
2766 """
2767
2767
2768 l = repo.tagslist()
2768 l = repo.tagslist()
2769 l.reverse()
2769 l.reverse()
2770 hexfunc = ui.debugflag and hex or short
2770 hexfunc = ui.debugflag and hex or short
2771 tagtype = ""
2771 tagtype = ""
2772
2772
2773 for t, n in l:
2773 for t, n in l:
2774 if ui.quiet:
2774 if ui.quiet:
2775 ui.write("%s\n" % t)
2775 ui.write("%s\n" % t)
2776 continue
2776 continue
2777
2777
2778 try:
2778 try:
2779 hn = hexfunc(n)
2779 hn = hexfunc(n)
2780 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2780 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2781 except revlog.LookupError:
2781 except revlog.LookupError:
2782 r = " ?:%s" % hn
2782 r = " ?:%s" % hn
2783 else:
2783 else:
2784 spaces = " " * (30 - util.locallen(t))
2784 spaces = " " * (30 - util.locallen(t))
2785 if ui.verbose:
2785 if ui.verbose:
2786 if repo.tagtype(t) == 'local':
2786 if repo.tagtype(t) == 'local':
2787 tagtype = " local"
2787 tagtype = " local"
2788 else:
2788 else:
2789 tagtype = ""
2789 tagtype = ""
2790 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2790 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2791
2791
2792 def tip(ui, repo, **opts):
2792 def tip(ui, repo, **opts):
2793 """show the tip revision
2793 """show the tip revision
2794
2794
2795 The tip revision (usually just called the tip) is the most
2795 The tip revision (usually just called the tip) is the most
2796 recently added changeset in the repository, the most recently
2796 recently added changeset in the repository, the most recently
2797 changed head.
2797 changed head.
2798
2798
2799 If you have just made a commit, that commit will be the tip. If
2799 If you have just made a commit, that commit will be the tip. If
2800 you have just pulled changes from another repository, the tip of
2800 you have just pulled changes from another repository, the tip of
2801 that repository becomes the current tip. The "tip" tag is special
2801 that repository becomes the current tip. The "tip" tag is special
2802 and cannot be renamed or assigned to a different changeset.
2802 and cannot be renamed or assigned to a different changeset.
2803 """
2803 """
2804 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2804 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2805
2805
2806 def unbundle(ui, repo, fname1, *fnames, **opts):
2806 def unbundle(ui, repo, fname1, *fnames, **opts):
2807 """apply one or more changegroup files
2807 """apply one or more changegroup files
2808
2808
2809 Apply one or more compressed changegroup files generated by the
2809 Apply one or more compressed changegroup files generated by the
2810 bundle command.
2810 bundle command.
2811 """
2811 """
2812 fnames = (fname1,) + fnames
2812 fnames = (fname1,) + fnames
2813
2813
2814 lock = None
2814 lock = None
2815 try:
2815 try:
2816 lock = repo.lock()
2816 lock = repo.lock()
2817 for fname in fnames:
2817 for fname in fnames:
2818 if os.path.exists(fname):
2818 if os.path.exists(fname):
2819 f = open(fname, "rb")
2819 f = open(fname, "rb")
2820 else:
2820 else:
2821 f = urllib.urlopen(fname)
2821 f = urllib.urlopen(fname)
2822 gen = changegroup.readbundle(f, fname)
2822 gen = changegroup.readbundle(f, fname)
2823 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2823 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2824 finally:
2824 finally:
2825 del lock
2825 del lock
2826
2826
2827 return postincoming(ui, repo, modheads, opts['update'], None)
2827 return postincoming(ui, repo, modheads, opts['update'], None)
2828
2828
2829 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2829 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2830 """update working directory
2830 """update working directory
2831
2831
2832 Update the repository's working directory to the specified revision,
2832 Update the repository's working directory to the specified revision,
2833 or the tip of the current branch if none is specified.
2833 or the tip of the current branch if none is specified.
2834
2834
2835 If the requested revision is a descendant of the working
2835 If the requested revision is a descendant of the working
2836 directory, any outstanding changes in the working directory will
2836 directory, any outstanding changes in the working directory will
2837 be merged into the result. If it is not directly descended but is
2837 be merged into the result. If it is not directly descended but is
2838 on the same named branch, update aborts with a suggestion to use
2838 on the same named branch, update aborts with a suggestion to use
2839 merge or update -C instead.
2839 merge or update -C instead.
2840
2840
2841 If the requested revision is on a different named branch and the
2841 If the requested revision is on a different named branch and the
2842 working directory is clean, update quietly switches branches.
2842 working directory is clean, update quietly switches branches.
2843
2843
2844 See 'hg help dates' for a list of formats valid for --date.
2844 See 'hg help dates' for a list of formats valid for --date.
2845 """
2845 """
2846 if rev and node:
2846 if rev and node:
2847 raise util.Abort(_("please specify just one revision"))
2847 raise util.Abort(_("please specify just one revision"))
2848
2848
2849 if not rev:
2849 if not rev:
2850 rev = node
2850 rev = node
2851
2851
2852 if date:
2852 if date:
2853 if rev:
2853 if rev:
2854 raise util.Abort(_("you can't specify a revision and a date"))
2854 raise util.Abort(_("you can't specify a revision and a date"))
2855 rev = cmdutil.finddate(ui, repo, date)
2855 rev = cmdutil.finddate(ui, repo, date)
2856
2856
2857 if clean:
2857 if clean:
2858 return hg.clean(repo, rev)
2858 return hg.clean(repo, rev)
2859 else:
2859 else:
2860 return hg.update(repo, rev)
2860 return hg.update(repo, rev)
2861
2861
2862 def verify(ui, repo):
2862 def verify(ui, repo):
2863 """verify the integrity of the repository
2863 """verify the integrity of the repository
2864
2864
2865 Verify the integrity of the current repository.
2865 Verify the integrity of the current repository.
2866
2866
2867 This will perform an extensive check of the repository's
2867 This will perform an extensive check of the repository's
2868 integrity, validating the hashes and checksums of each entry in
2868 integrity, validating the hashes and checksums of each entry in
2869 the changelog, manifest, and tracked files, as well as the
2869 the changelog, manifest, and tracked files, as well as the
2870 integrity of their crosslinks and indices.
2870 integrity of their crosslinks and indices.
2871 """
2871 """
2872 return hg.verify(repo)
2872 return hg.verify(repo)
2873
2873
2874 def version_(ui):
2874 def version_(ui):
2875 """output version and copyright information"""
2875 """output version and copyright information"""
2876 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2876 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2877 % version.get_version())
2877 % version.get_version())
2878 ui.status(_(
2878 ui.status(_(
2879 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2879 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2880 "This is free software; see the source for copying conditions. "
2880 "This is free software; see the source for copying conditions. "
2881 "There is NO\nwarranty; "
2881 "There is NO\nwarranty; "
2882 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2882 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2883 ))
2883 ))
2884
2884
2885 # Command options and aliases are listed here, alphabetically
2885 # Command options and aliases are listed here, alphabetically
2886
2886
2887 globalopts = [
2887 globalopts = [
2888 ('R', 'repository', '',
2888 ('R', 'repository', '',
2889 _('repository root directory or symbolic path name')),
2889 _('repository root directory or symbolic path name')),
2890 ('', 'cwd', '', _('change working directory')),
2890 ('', 'cwd', '', _('change working directory')),
2891 ('y', 'noninteractive', None,
2891 ('y', 'noninteractive', None,
2892 _('do not prompt, assume \'yes\' for any required answers')),
2892 _('do not prompt, assume \'yes\' for any required answers')),
2893 ('q', 'quiet', None, _('suppress output')),
2893 ('q', 'quiet', None, _('suppress output')),
2894 ('v', 'verbose', None, _('enable additional output')),
2894 ('v', 'verbose', None, _('enable additional output')),
2895 ('', 'config', [], _('set/override config option')),
2895 ('', 'config', [], _('set/override config option')),
2896 ('', 'debug', None, _('enable debugging output')),
2896 ('', 'debug', None, _('enable debugging output')),
2897 ('', 'debugger', None, _('start debugger')),
2897 ('', 'debugger', None, _('start debugger')),
2898 ('', 'encoding', util._encoding, _('set the charset encoding')),
2898 ('', 'encoding', util._encoding, _('set the charset encoding')),
2899 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2899 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2900 ('', 'lsprof', None, _('print improved command execution profile')),
2900 ('', 'lsprof', None, _('print improved command execution profile')),
2901 ('', 'traceback', None, _('print traceback on exception')),
2901 ('', 'traceback', None, _('print traceback on exception')),
2902 ('', 'time', None, _('time how long the command takes')),
2902 ('', 'time', None, _('time how long the command takes')),
2903 ('', 'profile', None, _('print command execution profile')),
2903 ('', 'profile', None, _('print command execution profile')),
2904 ('', 'version', None, _('output version information and exit')),
2904 ('', 'version', None, _('output version information and exit')),
2905 ('h', 'help', None, _('display help and exit')),
2905 ('h', 'help', None, _('display help and exit')),
2906 ]
2906 ]
2907
2907
2908 dryrunopts = [('n', 'dry-run', None,
2908 dryrunopts = [('n', 'dry-run', None,
2909 _('do not perform actions, just print output'))]
2909 _('do not perform actions, just print output'))]
2910
2910
2911 remoteopts = [
2911 remoteopts = [
2912 ('e', 'ssh', '', _('specify ssh command to use')),
2912 ('e', 'ssh', '', _('specify ssh command to use')),
2913 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2913 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2914 ]
2914 ]
2915
2915
2916 walkopts = [
2916 walkopts = [
2917 ('I', 'include', [], _('include names matching the given patterns')),
2917 ('I', 'include', [], _('include names matching the given patterns')),
2918 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2918 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2919 ]
2919 ]
2920
2920
2921 commitopts = [
2921 commitopts = [
2922 ('m', 'message', '', _('use <text> as commit message')),
2922 ('m', 'message', '', _('use <text> as commit message')),
2923 ('l', 'logfile', '', _('read commit message from <file>')),
2923 ('l', 'logfile', '', _('read commit message from <file>')),
2924 ]
2924 ]
2925
2925
2926 commitopts2 = [
2926 commitopts2 = [
2927 ('d', 'date', '', _('record datecode as commit date')),
2927 ('d', 'date', '', _('record datecode as commit date')),
2928 ('u', 'user', '', _('record user as committer')),
2928 ('u', 'user', '', _('record user as committer')),
2929 ]
2929 ]
2930
2930
2931 templateopts = [
2931 templateopts = [
2932 ('', 'style', '', _('display using template map file')),
2932 ('', 'style', '', _('display using template map file')),
2933 ('', 'template', '', _('display with template')),
2933 ('', 'template', '', _('display with template')),
2934 ]
2934 ]
2935
2935
2936 logopts = [
2936 logopts = [
2937 ('p', 'patch', None, _('show patch')),
2937 ('p', 'patch', None, _('show patch')),
2938 ('l', 'limit', '', _('limit number of changes displayed')),
2938 ('l', 'limit', '', _('limit number of changes displayed')),
2939 ('M', 'no-merges', None, _('do not show merges')),
2939 ('M', 'no-merges', None, _('do not show merges')),
2940 ] + templateopts
2940 ] + templateopts
2941
2941
2942 diffopts = [
2942 diffopts = [
2943 ('a', 'text', None, _('treat all files as text')),
2943 ('a', 'text', None, _('treat all files as text')),
2944 ('g', 'git', None, _('use git extended diff format')),
2944 ('g', 'git', None, _('use git extended diff format')),
2945 ('', 'nodates', None, _("don't include dates in diff headers"))
2945 ('', 'nodates', None, _("don't include dates in diff headers"))
2946 ]
2946 ]
2947
2947
2948 diffopts2 = [
2948 diffopts2 = [
2949 ('p', 'show-function', None, _('show which function each change is in')),
2949 ('p', 'show-function', None, _('show which function each change is in')),
2950 ('w', 'ignore-all-space', None,
2950 ('w', 'ignore-all-space', None,
2951 _('ignore white space when comparing lines')),
2951 _('ignore white space when comparing lines')),
2952 ('b', 'ignore-space-change', None,
2952 ('b', 'ignore-space-change', None,
2953 _('ignore changes in the amount of white space')),
2953 _('ignore changes in the amount of white space')),
2954 ('B', 'ignore-blank-lines', None,
2954 ('B', 'ignore-blank-lines', None,
2955 _('ignore changes whose lines are all blank')),
2955 _('ignore changes whose lines are all blank')),
2956 ('U', 'unified', '', _('number of lines of context to show'))
2956 ('U', 'unified', '', _('number of lines of context to show'))
2957 ]
2957 ]
2958
2958
2959 table = {
2959 table = {
2960 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2960 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2961 "addremove":
2961 "addremove":
2962 (addremove,
2962 (addremove,
2963 [('s', 'similarity', '',
2963 [('s', 'similarity', '',
2964 _('guess renamed files by similarity (0<=s<=100)')),
2964 _('guess renamed files by similarity (0<=s<=100)')),
2965 ] + walkopts + dryrunopts,
2965 ] + walkopts + dryrunopts,
2966 _('hg addremove [OPTION]... [FILE]...')),
2966 _('hg addremove [OPTION]... [FILE]...')),
2967 "^annotate|blame":
2967 "^annotate|blame":
2968 (annotate,
2968 (annotate,
2969 [('r', 'rev', '', _('annotate the specified revision')),
2969 [('r', 'rev', '', _('annotate the specified revision')),
2970 ('f', 'follow', None, _('follow file copies and renames')),
2970 ('f', 'follow', None, _('follow file copies and renames')),
2971 ('a', 'text', None, _('treat all files as text')),
2971 ('a', 'text', None, _('treat all files as text')),
2972 ('u', 'user', None, _('list the author (long with -v)')),
2972 ('u', 'user', None, _('list the author (long with -v)')),
2973 ('d', 'date', None, _('list the date (short with -q)')),
2973 ('d', 'date', None, _('list the date (short with -q)')),
2974 ('n', 'number', None, _('list the revision number (default)')),
2974 ('n', 'number', None, _('list the revision number (default)')),
2975 ('c', 'changeset', None, _('list the changeset')),
2975 ('c', 'changeset', None, _('list the changeset')),
2976 ('l', 'line-number', None,
2976 ('l', 'line-number', None,
2977 _('show line number at the first appearance'))
2977 _('show line number at the first appearance'))
2978 ] + walkopts,
2978 ] + walkopts,
2979 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2979 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2980 "archive":
2980 "archive":
2981 (archive,
2981 (archive,
2982 [('', 'no-decode', None, _('do not pass files through decoders')),
2982 [('', 'no-decode', None, _('do not pass files through decoders')),
2983 ('p', 'prefix', '', _('directory prefix for files in archive')),
2983 ('p', 'prefix', '', _('directory prefix for files in archive')),
2984 ('r', 'rev', '', _('revision to distribute')),
2984 ('r', 'rev', '', _('revision to distribute')),
2985 ('t', 'type', '', _('type of distribution to create')),
2985 ('t', 'type', '', _('type of distribution to create')),
2986 ] + walkopts,
2986 ] + walkopts,
2987 _('hg archive [OPTION]... DEST')),
2987 _('hg archive [OPTION]... DEST')),
2988 "backout":
2988 "backout":
2989 (backout,
2989 (backout,
2990 [('', 'merge', None,
2990 [('', 'merge', None,
2991 _('merge with old dirstate parent after backout')),
2991 _('merge with old dirstate parent after backout')),
2992 ('', 'parent', '', _('parent to choose when backing out merge')),
2992 ('', 'parent', '', _('parent to choose when backing out merge')),
2993 ('r', 'rev', '', _('revision to backout')),
2993 ('r', 'rev', '', _('revision to backout')),
2994 ] + walkopts + commitopts + commitopts2,
2994 ] + walkopts + commitopts + commitopts2,
2995 _('hg backout [OPTION]... [-r] REV')),
2995 _('hg backout [OPTION]... [-r] REV')),
2996 "bisect":
2996 "bisect":
2997 (bisect,
2997 (bisect,
2998 [('r', 'reset', False, _('reset bisect state')),
2998 [('r', 'reset', False, _('reset bisect state')),
2999 ('g', 'good', False, _('mark changeset good')),
2999 ('g', 'good', False, _('mark changeset good')),
3000 ('b', 'bad', False, _('mark changeset bad')),
3000 ('b', 'bad', False, _('mark changeset bad')),
3001 ('s', 'skip', False, _('skip testing changeset')),
3001 ('s', 'skip', False, _('skip testing changeset')),
3002 ('U', 'noupdate', False, _('do not update to target'))],
3002 ('U', 'noupdate', False, _('do not update to target'))],
3003 _("hg bisect [-gbsr] [REV]")),
3003 _("hg bisect [-gbsr] [REV]")),
3004 "branch":
3004 "branch":
3005 (branch,
3005 (branch,
3006 [('f', 'force', None,
3006 [('f', 'force', None,
3007 _('set branch name even if it shadows an existing branch'))],
3007 _('set branch name even if it shadows an existing branch'))],
3008 _('hg branch [-f] [NAME]')),
3008 _('hg branch [-f] [NAME]')),
3009 "branches":
3009 "branches":
3010 (branches,
3010 (branches,
3011 [('a', 'active', False,
3011 [('a', 'active', False,
3012 _('show only branches that have unmerged heads'))],
3012 _('show only branches that have unmerged heads'))],
3013 _('hg branches [-a]')),
3013 _('hg branches [-a]')),
3014 "bundle":
3014 "bundle":
3015 (bundle,
3015 (bundle,
3016 [('f', 'force', None,
3016 [('f', 'force', None,
3017 _('run even when remote repository is unrelated')),
3017 _('run even when remote repository is unrelated')),
3018 ('r', 'rev', [],
3018 ('r', 'rev', [],
3019 _('a changeset up to which you would like to bundle')),
3019 _('a changeset up to which you would like to bundle')),
3020 ('', 'base', [],
3020 ('', 'base', [],
3021 _('a base changeset to specify instead of a destination')),
3021 _('a base changeset to specify instead of a destination')),
3022 ('a', 'all', None, _('bundle all changesets in the repository')),
3022 ('a', 'all', None, _('bundle all changesets in the repository')),
3023 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3023 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3024 ] + remoteopts,
3024 ] + remoteopts,
3025 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3025 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3026 "cat":
3026 "cat":
3027 (cat,
3027 (cat,
3028 [('o', 'output', '', _('print output to file with formatted name')),
3028 [('o', 'output', '', _('print output to file with formatted name')),
3029 ('r', 'rev', '', _('print the given revision')),
3029 ('r', 'rev', '', _('print the given revision')),
3030 ('', 'decode', None, _('apply any matching decode filter')),
3030 ('', 'decode', None, _('apply any matching decode filter')),
3031 ] + walkopts,
3031 ] + walkopts,
3032 _('hg cat [OPTION]... FILE...')),
3032 _('hg cat [OPTION]... FILE...')),
3033 "^clone":
3033 "^clone":
3034 (clone,
3034 (clone,
3035 [('U', 'noupdate', None,
3035 [('U', 'noupdate', None,
3036 _('the clone will only contain a repository (no working copy)')),
3036 _('the clone will only contain a repository (no working copy)')),
3037 ('r', 'rev', [],
3037 ('r', 'rev', [],
3038 _('a changeset you would like to have after cloning')),
3038 _('a changeset you would like to have after cloning')),
3039 ('', 'pull', None, _('use pull protocol to copy metadata')),
3039 ('', 'pull', None, _('use pull protocol to copy metadata')),
3040 ('', 'uncompressed', None,
3040 ('', 'uncompressed', None,
3041 _('use uncompressed transfer (fast over LAN)')),
3041 _('use uncompressed transfer (fast over LAN)')),
3042 ] + remoteopts,
3042 ] + remoteopts,
3043 _('hg clone [OPTION]... SOURCE [DEST]')),
3043 _('hg clone [OPTION]... SOURCE [DEST]')),
3044 "^commit|ci":
3044 "^commit|ci":
3045 (commit,
3045 (commit,
3046 [('A', 'addremove', None,
3046 [('A', 'addremove', None,
3047 _('mark new/missing files as added/removed before committing')),
3047 _('mark new/missing files as added/removed before committing')),
3048 ] + walkopts + commitopts + commitopts2,
3048 ] + walkopts + commitopts + commitopts2,
3049 _('hg commit [OPTION]... [FILE]...')),
3049 _('hg commit [OPTION]... [FILE]...')),
3050 "copy|cp":
3050 "copy|cp":
3051 (copy,
3051 (copy,
3052 [('A', 'after', None, _('record a copy that has already occurred')),
3052 [('A', 'after', None, _('record a copy that has already occurred')),
3053 ('f', 'force', None,
3053 ('f', 'force', None,
3054 _('forcibly copy over an existing managed file')),
3054 _('forcibly copy over an existing managed file')),
3055 ] + walkopts + dryrunopts,
3055 ] + walkopts + dryrunopts,
3056 _('hg copy [OPTION]... [SOURCE]... DEST')),
3056 _('hg copy [OPTION]... [SOURCE]... DEST')),
3057 "debugancestor": (debugancestor, [],
3057 "debugancestor": (debugancestor, [],
3058 _('hg debugancestor [INDEX] REV1 REV2')),
3058 _('hg debugancestor [INDEX] REV1 REV2')),
3059 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3059 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3060 "debugcomplete":
3060 "debugcomplete":
3061 (debugcomplete,
3061 (debugcomplete,
3062 [('o', 'options', None, _('show the command options'))],
3062 [('o', 'options', None, _('show the command options'))],
3063 _('hg debugcomplete [-o] CMD')),
3063 _('hg debugcomplete [-o] CMD')),
3064 "debugdate":
3064 "debugdate":
3065 (debugdate,
3065 (debugdate,
3066 [('e', 'extended', None, _('try extended date formats'))],
3066 [('e', 'extended', None, _('try extended date formats'))],
3067 _('hg debugdate [-e] DATE [RANGE]')),
3067 _('hg debugdate [-e] DATE [RANGE]')),
3068 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3068 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3069 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3069 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3070 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3070 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3071 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3071 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3072 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3072 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3073 "debugrawcommit|rawcommit":
3073 "debugrawcommit|rawcommit":
3074 (rawcommit,
3074 (rawcommit,
3075 [('p', 'parent', [], _('parent')),
3075 [('p', 'parent', [], _('parent')),
3076 ('F', 'files', '', _('file list'))
3076 ('F', 'files', '', _('file list'))
3077 ] + commitopts + commitopts2,
3077 ] + commitopts + commitopts2,
3078 _('hg debugrawcommit [OPTION]... [FILE]...')),
3078 _('hg debugrawcommit [OPTION]... [FILE]...')),
3079 "debugrebuildstate":
3079 "debugrebuildstate":
3080 (debugrebuildstate,
3080 (debugrebuildstate,
3081 [('r', 'rev', '', _('revision to rebuild to'))],
3081 [('r', 'rev', '', _('revision to rebuild to'))],
3082 _('hg debugrebuildstate [-r REV] [REV]')),
3082 _('hg debugrebuildstate [-r REV] [REV]')),
3083 "debugrename":
3083 "debugrename":
3084 (debugrename,
3084 (debugrename,
3085 [('r', 'rev', '', _('revision to debug'))],
3085 [('r', 'rev', '', _('revision to debug'))],
3086 _('hg debugrename [-r REV] FILE')),
3086 _('hg debugrename [-r REV] FILE')),
3087 "debugsetparents":
3087 "debugsetparents":
3088 (debugsetparents,
3088 (debugsetparents,
3089 [],
3089 [],
3090 _('hg debugsetparents REV1 [REV2]')),
3090 _('hg debugsetparents REV1 [REV2]')),
3091 "debugstate":
3091 "debugstate":
3092 (debugstate,
3092 (debugstate,
3093 [('', 'nodates', None, _('do not display the saved mtime'))],
3093 [('', 'nodates', None, _('do not display the saved mtime'))],
3094 _('hg debugstate [OPTS]')),
3094 _('hg debugstate [OPTS]')),
3095 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3095 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3096 "^diff":
3096 "^diff":
3097 (diff,
3097 (diff,
3098 [('r', 'rev', [], _('revision'))
3098 [('r', 'rev', [], _('revision'))
3099 ] + diffopts + diffopts2 + walkopts,
3099 ] + diffopts + diffopts2 + walkopts,
3100 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3100 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3101 "^export":
3101 "^export":
3102 (export,
3102 (export,
3103 [('o', 'output', '', _('print output to file with formatted name')),
3103 [('o', 'output', '', _('print output to file with formatted name')),
3104 ('', 'switch-parent', None, _('diff against the second parent'))
3104 ('', 'switch-parent', None, _('diff against the second parent'))
3105 ] + diffopts,
3105 ] + diffopts,
3106 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3106 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3107 "grep":
3107 "grep":
3108 (grep,
3108 (grep,
3109 [('0', 'print0', None, _('end fields with NUL')),
3109 [('0', 'print0', None, _('end fields with NUL')),
3110 ('', 'all', None, _('print all revisions that match')),
3110 ('', 'all', None, _('print all revisions that match')),
3111 ('f', 'follow', None,
3111 ('f', 'follow', None,
3112 _('follow changeset history, or file history across copies and renames')),
3112 _('follow changeset history, or file history across copies and renames')),
3113 ('i', 'ignore-case', None, _('ignore case when matching')),
3113 ('i', 'ignore-case', None, _('ignore case when matching')),
3114 ('l', 'files-with-matches', None,
3114 ('l', 'files-with-matches', None,
3115 _('print only filenames and revs that match')),
3115 _('print only filenames and revs that match')),
3116 ('n', 'line-number', None, _('print matching line numbers')),
3116 ('n', 'line-number', None, _('print matching line numbers')),
3117 ('r', 'rev', [], _('search in given revision range')),
3117 ('r', 'rev', [], _('search in given revision range')),
3118 ('u', 'user', None, _('list the author (long with -v)')),
3118 ('u', 'user', None, _('list the author (long with -v)')),
3119 ('d', 'date', None, _('list the date (short with -q)')),
3119 ('d', 'date', None, _('list the date (short with -q)')),
3120 ] + walkopts,
3120 ] + walkopts,
3121 _('hg grep [OPTION]... PATTERN [FILE]...')),
3121 _('hg grep [OPTION]... PATTERN [FILE]...')),
3122 "heads":
3122 "heads":
3123 (heads,
3123 (heads,
3124 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3124 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3125 ] + templateopts,
3125 ] + templateopts,
3126 _('hg heads [-r REV] [REV]...')),
3126 _('hg heads [-r REV] [REV]...')),
3127 "help": (help_, [], _('hg help [COMMAND]')),
3127 "help": (help_, [], _('hg help [COMMAND]')),
3128 "identify|id":
3128 "identify|id":
3129 (identify,
3129 (identify,
3130 [('r', 'rev', '', _('identify the specified rev')),
3130 [('r', 'rev', '', _('identify the specified rev')),
3131 ('n', 'num', None, _('show local revision number')),
3131 ('n', 'num', None, _('show local revision number')),
3132 ('i', 'id', None, _('show global revision id')),
3132 ('i', 'id', None, _('show global revision id')),
3133 ('b', 'branch', None, _('show branch')),
3133 ('b', 'branch', None, _('show branch')),
3134 ('t', 'tags', None, _('show tags'))],
3134 ('t', 'tags', None, _('show tags'))],
3135 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3135 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3136 "import|patch":
3136 "import|patch":
3137 (import_,
3137 (import_,
3138 [('p', 'strip', 1,
3138 [('p', 'strip', 1,
3139 _('directory strip option for patch. This has the same\n'
3139 _('directory strip option for patch. This has the same\n'
3140 'meaning as the corresponding patch option')),
3140 'meaning as the corresponding patch option')),
3141 ('b', 'base', '', _('base path')),
3141 ('b', 'base', '', _('base path')),
3142 ('f', 'force', None,
3142 ('f', 'force', None,
3143 _('skip check for outstanding uncommitted changes')),
3143 _('skip check for outstanding uncommitted changes')),
3144 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3144 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3145 ('', 'exact', None,
3145 ('', 'exact', None,
3146 _('apply patch to the nodes from which it was generated')),
3146 _('apply patch to the nodes from which it was generated')),
3147 ('', 'import-branch', None,
3147 ('', 'import-branch', None,
3148 _('Use any branch information in patch (implied by --exact)'))] +
3148 _('Use any branch information in patch (implied by --exact)'))] +
3149 commitopts + commitopts2,
3149 commitopts + commitopts2,
3150 _('hg import [OPTION]... PATCH...')),
3150 _('hg import [OPTION]... PATCH...')),
3151 "incoming|in":
3151 "incoming|in":
3152 (incoming,
3152 (incoming,
3153 [('f', 'force', None,
3153 [('f', 'force', None,
3154 _('run even when remote repository is unrelated')),
3154 _('run even when remote repository is unrelated')),
3155 ('n', 'newest-first', None, _('show newest record first')),
3155 ('n', 'newest-first', None, _('show newest record first')),
3156 ('', 'bundle', '', _('file to store the bundles into')),
3156 ('', 'bundle', '', _('file to store the bundles into')),
3157 ('r', 'rev', [],
3157 ('r', 'rev', [],
3158 _('a specific revision up to which you would like to pull')),
3158 _('a specific revision up to which you would like to pull')),
3159 ] + logopts + remoteopts,
3159 ] + logopts + remoteopts,
3160 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3160 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3161 ' [--bundle FILENAME] [SOURCE]')),
3161 ' [--bundle FILENAME] [SOURCE]')),
3162 "^init":
3162 "^init":
3163 (init,
3163 (init,
3164 remoteopts,
3164 remoteopts,
3165 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3165 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3166 "locate":
3166 "locate":
3167 (locate,
3167 (locate,
3168 [('r', 'rev', '', _('search the repository as it stood at rev')),
3168 [('r', 'rev', '', _('search the repository as it stood at rev')),
3169 ('0', 'print0', None,
3169 ('0', 'print0', None,
3170 _('end filenames with NUL, for use with xargs')),
3170 _('end filenames with NUL, for use with xargs')),
3171 ('f', 'fullpath', None,
3171 ('f', 'fullpath', None,
3172 _('print complete paths from the filesystem root')),
3172 _('print complete paths from the filesystem root')),
3173 ] + walkopts,
3173 ] + walkopts,
3174 _('hg locate [OPTION]... [PATTERN]...')),
3174 _('hg locate [OPTION]... [PATTERN]...')),
3175 "^log|history":
3175 "^log|history":
3176 (log,
3176 (log,
3177 [('f', 'follow', None,
3177 [('f', 'follow', None,
3178 _('follow changeset history, or file history across copies and renames')),
3178 _('follow changeset history, or file history across copies and renames')),
3179 ('', 'follow-first', None,
3179 ('', 'follow-first', None,
3180 _('only follow the first parent of merge changesets')),
3180 _('only follow the first parent of merge changesets')),
3181 ('d', 'date', '', _('show revs matching date spec')),
3181 ('d', 'date', '', _('show revs matching date spec')),
3182 ('C', 'copies', None, _('show copied files')),
3182 ('C', 'copies', None, _('show copied files')),
3183 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3183 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3184 ('r', 'rev', [], _('show the specified revision or range')),
3184 ('r', 'rev', [], _('show the specified revision or range')),
3185 ('', 'removed', None, _('include revs where files were removed')),
3185 ('', 'removed', None, _('include revs where files were removed')),
3186 ('m', 'only-merges', None, _('show only merges')),
3186 ('m', 'only-merges', None, _('show only merges')),
3187 ('b', 'only-branch', [],
3187 ('b', 'only-branch', [],
3188 _('show only changesets within the given named branch')),
3188 _('show only changesets within the given named branch')),
3189 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3189 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3190 ] + logopts + walkopts,
3190 ] + logopts + walkopts,
3191 _('hg log [OPTION]... [FILE]')),
3191 _('hg log [OPTION]... [FILE]')),
3192 "manifest":
3192 "manifest":
3193 (manifest,
3193 (manifest,
3194 [('r', 'rev', '', _('revision to display'))],
3194 [('r', 'rev', '', _('revision to display'))],
3195 _('hg manifest [-r REV]')),
3195 _('hg manifest [-r REV]')),
3196 "^merge":
3196 "^merge":
3197 (merge,
3197 (merge,
3198 [('f', 'force', None, _('force a merge with outstanding changes')),
3198 [('f', 'force', None, _('force a merge with outstanding changes')),
3199 ('r', 'rev', '', _('revision to merge')),
3199 ('r', 'rev', '', _('revision to merge')),
3200 ],
3200 ],
3201 _('hg merge [-f] [[-r] REV]')),
3201 _('hg merge [-f] [[-r] REV]')),
3202 "outgoing|out":
3202 "outgoing|out":
3203 (outgoing,
3203 (outgoing,
3204 [('f', 'force', None,
3204 [('f', 'force', None,
3205 _('run even when remote repository is unrelated')),
3205 _('run even when remote repository is unrelated')),
3206 ('r', 'rev', [],
3206 ('r', 'rev', [],
3207 _('a specific revision up to which you would like to push')),
3207 _('a specific revision up to which you would like to push')),
3208 ('n', 'newest-first', None, _('show newest record first')),
3208 ('n', 'newest-first', None, _('show newest record first')),
3209 ] + logopts + remoteopts,
3209 ] + logopts + remoteopts,
3210 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3210 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3211 "^parents":
3211 "^parents":
3212 (parents,
3212 (parents,
3213 [('r', 'rev', '', _('show parents from the specified rev')),
3213 [('r', 'rev', '', _('show parents from the specified rev')),
3214 ] + templateopts,
3214 ] + templateopts,
3215 _('hg parents [-r REV] [FILE]')),
3215 _('hg parents [-r REV] [FILE]')),
3216 "paths": (paths, [], _('hg paths [NAME]')),
3216 "paths": (paths, [], _('hg paths [NAME]')),
3217 "^pull":
3217 "^pull":
3218 (pull,
3218 (pull,
3219 [('u', 'update', None,
3219 [('u', 'update', None,
3220 _('update to new tip if changesets were pulled')),
3220 _('update to new tip if changesets were pulled')),
3221 ('f', 'force', None,
3221 ('f', 'force', None,
3222 _('run even when remote repository is unrelated')),
3222 _('run even when remote repository is unrelated')),
3223 ('r', 'rev', [],
3223 ('r', 'rev', [],
3224 _('a specific revision up to which you would like to pull')),
3224 _('a specific revision up to which you would like to pull')),
3225 ] + remoteopts,
3225 ] + remoteopts,
3226 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3226 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3227 "^push":
3227 "^push":
3228 (push,
3228 (push,
3229 [('f', 'force', None, _('force push')),
3229 [('f', 'force', None, _('force push')),
3230 ('r', 'rev', [],
3230 ('r', 'rev', [],
3231 _('a specific revision up to which you would like to push')),
3231 _('a specific revision up to which you would like to push')),
3232 ] + remoteopts,
3232 ] + remoteopts,
3233 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3233 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3234 "recover": (recover, [], _('hg recover')),
3234 "recover": (recover, [], _('hg recover')),
3235 "^remove|rm":
3235 "^remove|rm":
3236 (remove,
3236 (remove,
3237 [('A', 'after', None, _('record delete for missing files')),
3237 [('A', 'after', None, _('record delete for missing files')),
3238 ('f', 'force', None,
3238 ('f', 'force', None,
3239 _('remove (and delete) file even if added or modified')),
3239 _('remove (and delete) file even if added or modified')),
3240 ] + walkopts,
3240 ] + walkopts,
3241 _('hg remove [OPTION]... FILE...')),
3241 _('hg remove [OPTION]... FILE...')),
3242 "rename|mv":
3242 "rename|mv":
3243 (rename,
3243 (rename,
3244 [('A', 'after', None, _('record a rename that has already occurred')),
3244 [('A', 'after', None, _('record a rename that has already occurred')),
3245 ('f', 'force', None,
3245 ('f', 'force', None,
3246 _('forcibly copy over an existing managed file')),
3246 _('forcibly copy over an existing managed file')),
3247 ] + walkopts + dryrunopts,
3247 ] + walkopts + dryrunopts,
3248 _('hg rename [OPTION]... SOURCE... DEST')),
3248 _('hg rename [OPTION]... SOURCE... DEST')),
3249 "resolve":
3249 "resolve":
3250 (resolve,
3250 (resolve,
3251 [('l', 'list', None, _('list state of files needing merge')),
3251 [('l', 'list', None, _('list state of files needing merge')),
3252 ('m', 'mark', None, _('mark files as resolved')),
3252 ('m', 'mark', None, _('mark files as resolved')),
3253 ('u', 'unmark', None, _('unmark files as resolved'))],
3253 ('u', 'unmark', None, _('unmark files as resolved'))],
3254 ('hg resolve [OPTION] [FILES...]')),
3254 ('hg resolve [OPTION] [FILES...]')),
3255 "revert":
3255 "revert":
3256 (revert,
3256 (revert,
3257 [('a', 'all', None, _('revert all changes when no arguments given')),
3257 [('a', 'all', None, _('revert all changes when no arguments given')),
3258 ('d', 'date', '', _('tipmost revision matching date')),
3258 ('d', 'date', '', _('tipmost revision matching date')),
3259 ('r', 'rev', '', _('revision to revert to')),
3259 ('r', 'rev', '', _('revision to revert to')),
3260 ('', 'no-backup', None, _('do not save backup copies of files')),
3260 ('', 'no-backup', None, _('do not save backup copies of files')),
3261 ] + walkopts + dryrunopts,
3261 ] + walkopts + dryrunopts,
3262 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3262 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3263 "rollback": (rollback, [], _('hg rollback')),
3263 "rollback": (rollback, [], _('hg rollback')),
3264 "root": (root, [], _('hg root')),
3264 "root": (root, [], _('hg root')),
3265 "^serve":
3265 "^serve":
3266 (serve,
3266 (serve,
3267 [('A', 'accesslog', '', _('name of access log file to write to')),
3267 [('A', 'accesslog', '', _('name of access log file to write to')),
3268 ('d', 'daemon', None, _('run server in background')),
3268 ('d', 'daemon', None, _('run server in background')),
3269 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3269 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3270 ('E', 'errorlog', '', _('name of error log file to write to')),
3270 ('E', 'errorlog', '', _('name of error log file to write to')),
3271 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3271 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3272 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3272 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3273 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3273 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3274 ('n', 'name', '',
3274 ('n', 'name', '',
3275 _('name to show in web pages (default: working dir)')),
3275 _('name to show in web pages (default: working dir)')),
3276 ('', 'webdir-conf', '', _('name of the webdir config file'
3276 ('', 'webdir-conf', '', _('name of the webdir config file'
3277 ' (serve more than one repo)')),
3277 ' (serve more than one repo)')),
3278 ('', 'pid-file', '', _('name of file to write process ID to')),
3278 ('', 'pid-file', '', _('name of file to write process ID to')),
3279 ('', 'stdio', None, _('for remote clients')),
3279 ('', 'stdio', None, _('for remote clients')),
3280 ('t', 'templates', '', _('web templates to use')),
3280 ('t', 'templates', '', _('web templates to use')),
3281 ('', 'style', '', _('template style to use')),
3281 ('', 'style', '', _('template style to use')),
3282 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3282 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3283 ('', 'certificate', '', _('SSL certificate file'))],
3283 ('', 'certificate', '', _('SSL certificate file'))],
3284 _('hg serve [OPTION]...')),
3284 _('hg serve [OPTION]...')),
3285 "showconfig|debugconfig":
3285 "showconfig|debugconfig":
3286 (showconfig,
3286 (showconfig,
3287 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3287 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3288 _('hg showconfig [-u] [NAME]...')),
3288 _('hg showconfig [-u] [NAME]...')),
3289 "^status|st":
3289 "^status|st":
3290 (status,
3290 (status,
3291 [('A', 'all', None, _('show status of all files')),
3291 [('A', 'all', None, _('show status of all files')),
3292 ('m', 'modified', None, _('show only modified files')),
3292 ('m', 'modified', None, _('show only modified files')),
3293 ('a', 'added', None, _('show only added files')),
3293 ('a', 'added', None, _('show only added files')),
3294 ('r', 'removed', None, _('show only removed files')),
3294 ('r', 'removed', None, _('show only removed files')),
3295 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3295 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3296 ('c', 'clean', None, _('show only files without changes')),
3296 ('c', 'clean', None, _('show only files without changes')),
3297 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3297 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3298 ('i', 'ignored', None, _('show only ignored files')),
3298 ('i', 'ignored', None, _('show only ignored files')),
3299 ('n', 'no-status', None, _('hide status prefix')),
3299 ('n', 'no-status', None, _('hide status prefix')),
3300 ('C', 'copies', None, _('show source of copied files')),
3300 ('C', 'copies', None, _('show source of copied files')),
3301 ('0', 'print0', None,
3301 ('0', 'print0', None,
3302 _('end filenames with NUL, for use with xargs')),
3302 _('end filenames with NUL, for use with xargs')),
3303 ('', 'rev', [], _('show difference from revision')),
3303 ('', 'rev', [], _('show difference from revision')),
3304 ] + walkopts,
3304 ] + walkopts,
3305 _('hg status [OPTION]... [FILE]...')),
3305 _('hg status [OPTION]... [FILE]...')),
3306 "tag":
3306 "tag":
3307 (tag,
3307 (tag,
3308 [('f', 'force', None, _('replace existing tag')),
3308 [('f', 'force', None, _('replace existing tag')),
3309 ('l', 'local', None, _('make the tag local')),
3309 ('l', 'local', None, _('make the tag local')),
3310 ('r', 'rev', '', _('revision to tag')),
3310 ('r', 'rev', '', _('revision to tag')),
3311 ('', 'remove', None, _('remove a tag')),
3311 ('', 'remove', None, _('remove a tag')),
3312 # -l/--local is already there, commitopts cannot be used
3312 # -l/--local is already there, commitopts cannot be used
3313 ('m', 'message', '', _('use <text> as commit message')),
3313 ('m', 'message', '', _('use <text> as commit message')),
3314 ] + commitopts2,
3314 ] + commitopts2,
3315 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3315 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3316 "tags": (tags, [], _('hg tags')),
3316 "tags": (tags, [], _('hg tags')),
3317 "tip":
3317 "tip":
3318 (tip,
3318 (tip,
3319 [('p', 'patch', None, _('show patch')),
3319 [('p', 'patch', None, _('show patch')),
3320 ] + templateopts,
3320 ] + templateopts,
3321 _('hg tip [-p]')),
3321 _('hg tip [-p]')),
3322 "unbundle":
3322 "unbundle":
3323 (unbundle,
3323 (unbundle,
3324 [('u', 'update', None,
3324 [('u', 'update', None,
3325 _('update to new tip if changesets were unbundled'))],
3325 _('update to new tip if changesets were unbundled'))],
3326 _('hg unbundle [-u] FILE...')),
3326 _('hg unbundle [-u] FILE...')),
3327 "^update|up|checkout|co":
3327 "^update|up|checkout|co":
3328 (update,
3328 (update,
3329 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3329 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3330 ('d', 'date', '', _('tipmost revision matching date')),
3330 ('d', 'date', '', _('tipmost revision matching date')),
3331 ('r', 'rev', '', _('revision'))],
3331 ('r', 'rev', '', _('revision'))],
3332 _('hg update [-C] [-d DATE] [[-r] REV]')),
3332 _('hg update [-C] [-d DATE] [[-r] REV]')),
3333 "verify": (verify, [], _('hg verify')),
3333 "verify": (verify, [], _('hg verify')),
3334 "version": (version_, [], _('hg version')),
3334 "version": (version_, [], _('hg version')),
3335 }
3335 }
3336
3336
3337 norepo = ("clone init version help debugcomplete debugdata"
3337 norepo = ("clone init version help debugcomplete debugdata"
3338 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3338 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3339 optionalrepo = ("identify paths serve showconfig debugancestor")
3339 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,698 +1,698
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import nullid
10 from node import nullid
11 from i18n import _
11 from i18n import _
12 import struct, os, bisect, stat, strutil, util, errno, ignore
12 import struct, os, bisect, stat, strutil, util, errno, ignore
13 import cStringIO, osutil, sys
13 import cStringIO, osutil, sys
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 class dirstate(object):
18 class dirstate(object):
19
19
20 def __init__(self, opener, ui, root):
20 def __init__(self, opener, ui, root):
21 self._opener = opener
21 self._opener = opener
22 self._root = root
22 self._root = root
23 self._dirty = False
23 self._dirty = False
24 self._dirtypl = False
24 self._dirtypl = False
25 self._ui = ui
25 self._ui = ui
26
26
27 def __getattr__(self, name):
27 def __getattr__(self, name):
28 if name == '_map':
28 if name == '_map':
29 self._read()
29 self._read()
30 return self._map
30 return self._map
31 elif name == '_copymap':
31 elif name == '_copymap':
32 self._read()
32 self._read()
33 return self._copymap
33 return self._copymap
34 elif name == '_foldmap':
34 elif name == '_foldmap':
35 _foldmap = {}
35 _foldmap = {}
36 for name in self._map:
36 for name in self._map:
37 norm = os.path.normcase(os.path.normpath(name))
37 norm = os.path.normcase(os.path.normpath(name))
38 _foldmap[norm] = name
38 _foldmap[norm] = name
39 self._foldmap = _foldmap
39 self._foldmap = _foldmap
40 return self._foldmap
40 return self._foldmap
41 elif name == '_branch':
41 elif name == '_branch':
42 try:
42 try:
43 self._branch = (self._opener("branch").read().strip()
43 self._branch = (self._opener("branch").read().strip()
44 or "default")
44 or "default")
45 except IOError:
45 except IOError:
46 self._branch = "default"
46 self._branch = "default"
47 return self._branch
47 return self._branch
48 elif name == '_pl':
48 elif name == '_pl':
49 self._pl = [nullid, nullid]
49 self._pl = [nullid, nullid]
50 try:
50 try:
51 st = self._opener("dirstate").read(40)
51 st = self._opener("dirstate").read(40)
52 if len(st) == 40:
52 if len(st) == 40:
53 self._pl = st[:20], st[20:40]
53 self._pl = st[:20], st[20:40]
54 except IOError, err:
54 except IOError, err:
55 if err.errno != errno.ENOENT: raise
55 if err.errno != errno.ENOENT: raise
56 return self._pl
56 return self._pl
57 elif name == '_dirs':
57 elif name == '_dirs':
58 self._dirs = {}
58 self._dirs = {}
59 for f in self._map:
59 for f in self._map:
60 if self[f] != 'r':
60 if self[f] != 'r':
61 self._incpath(f)
61 self._incpath(f)
62 return self._dirs
62 return self._dirs
63 elif name == '_ignore':
63 elif name == '_ignore':
64 files = [self._join('.hgignore')]
64 files = [self._join('.hgignore')]
65 for name, path in self._ui.configitems("ui"):
65 for name, path in self._ui.configitems("ui"):
66 if name == 'ignore' or name.startswith('ignore.'):
66 if name == 'ignore' or name.startswith('ignore.'):
67 files.append(os.path.expanduser(path))
67 files.append(os.path.expanduser(path))
68 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
68 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
69 return self._ignore
69 return self._ignore
70 elif name == '_slash':
70 elif name == '_slash':
71 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
71 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
72 return self._slash
72 return self._slash
73 elif name == '_checklink':
73 elif name == '_checklink':
74 self._checklink = util.checklink(self._root)
74 self._checklink = util.checklink(self._root)
75 return self._checklink
75 return self._checklink
76 elif name == '_checkexec':
76 elif name == '_checkexec':
77 self._checkexec = util.checkexec(self._root)
77 self._checkexec = util.checkexec(self._root)
78 return self._checkexec
78 return self._checkexec
79 elif name == '_folding':
79 elif name == '_checkcase':
80 self._folding = not util.checkfolding(self._join('.hg'))
80 self._checkcase = not util.checkcase(self._join('.hg'))
81 return self._folding
81 return self._checkcase
82 elif name == 'normalize':
82 elif name == 'normalize':
83 if self._folding:
83 if self._checkcase:
84 self.normalize = self._normalize
84 self.normalize = self._normalize
85 else:
85 else:
86 self.normalize = lambda x: x
86 self.normalize = lambda x: x
87 return self.normalize
87 return self.normalize
88 else:
88 else:
89 raise AttributeError, name
89 raise AttributeError, name
90
90
91 def _join(self, f):
91 def _join(self, f):
92 return os.path.join(self._root, f)
92 return os.path.join(self._root, f)
93
93
94 def flagfunc(self, fallback):
94 def flagfunc(self, fallback):
95 if self._checklink:
95 if self._checklink:
96 if self._checkexec:
96 if self._checkexec:
97 def f(x):
97 def f(x):
98 p = os.path.join(self._root, x)
98 p = os.path.join(self._root, x)
99 if os.path.islink(p):
99 if os.path.islink(p):
100 return 'l'
100 return 'l'
101 if util.is_exec(p):
101 if util.is_exec(p):
102 return 'x'
102 return 'x'
103 return ''
103 return ''
104 return f
104 return f
105 def f(x):
105 def f(x):
106 if os.path.islink(os.path.join(self._root, x)):
106 if os.path.islink(os.path.join(self._root, x)):
107 return 'l'
107 return 'l'
108 if 'x' in fallback(x):
108 if 'x' in fallback(x):
109 return 'x'
109 return 'x'
110 return ''
110 return ''
111 return f
111 return f
112 if self._checkexec:
112 if self._checkexec:
113 def f(x):
113 def f(x):
114 if 'l' in fallback(x):
114 if 'l' in fallback(x):
115 return 'l'
115 return 'l'
116 if util.is_exec(os.path.join(self._root, x)):
116 if util.is_exec(os.path.join(self._root, x)):
117 return 'x'
117 return 'x'
118 return ''
118 return ''
119 return f
119 return f
120 return fallback
120 return fallback
121
121
122 def getcwd(self):
122 def getcwd(self):
123 cwd = os.getcwd()
123 cwd = os.getcwd()
124 if cwd == self._root: return ''
124 if cwd == self._root: return ''
125 # self._root ends with a path separator if self._root is '/' or 'C:\'
125 # self._root ends with a path separator if self._root is '/' or 'C:\'
126 rootsep = self._root
126 rootsep = self._root
127 if not util.endswithsep(rootsep):
127 if not util.endswithsep(rootsep):
128 rootsep += os.sep
128 rootsep += os.sep
129 if cwd.startswith(rootsep):
129 if cwd.startswith(rootsep):
130 return cwd[len(rootsep):]
130 return cwd[len(rootsep):]
131 else:
131 else:
132 # we're outside the repo. return an absolute path.
132 # we're outside the repo. return an absolute path.
133 return cwd
133 return cwd
134
134
135 def pathto(self, f, cwd=None):
135 def pathto(self, f, cwd=None):
136 if cwd is None:
136 if cwd is None:
137 cwd = self.getcwd()
137 cwd = self.getcwd()
138 path = util.pathto(self._root, cwd, f)
138 path = util.pathto(self._root, cwd, f)
139 if self._slash:
139 if self._slash:
140 return util.normpath(path)
140 return util.normpath(path)
141 return path
141 return path
142
142
143 def __getitem__(self, key):
143 def __getitem__(self, key):
144 ''' current states:
144 ''' current states:
145 n normal
145 n normal
146 m needs merging
146 m needs merging
147 r marked for removal
147 r marked for removal
148 a marked for addition
148 a marked for addition
149 ? not tracked'''
149 ? not tracked'''
150 return self._map.get(key, ("?",))[0]
150 return self._map.get(key, ("?",))[0]
151
151
152 def __contains__(self, key):
152 def __contains__(self, key):
153 return key in self._map
153 return key in self._map
154
154
155 def __iter__(self):
155 def __iter__(self):
156 a = self._map.keys()
156 a = self._map.keys()
157 a.sort()
157 a.sort()
158 for x in a:
158 for x in a:
159 yield x
159 yield x
160
160
161 def parents(self):
161 def parents(self):
162 return self._pl
162 return self._pl
163
163
164 def branch(self):
164 def branch(self):
165 return self._branch
165 return self._branch
166
166
167 def setparents(self, p1, p2=nullid):
167 def setparents(self, p1, p2=nullid):
168 self._dirty = self._dirtypl = True
168 self._dirty = self._dirtypl = True
169 self._pl = p1, p2
169 self._pl = p1, p2
170
170
171 def setbranch(self, branch):
171 def setbranch(self, branch):
172 self._branch = branch
172 self._branch = branch
173 self._opener("branch", "w").write(branch + '\n')
173 self._opener("branch", "w").write(branch + '\n')
174
174
175 def _read(self):
175 def _read(self):
176 self._map = {}
176 self._map = {}
177 self._copymap = {}
177 self._copymap = {}
178 if not self._dirtypl:
178 if not self._dirtypl:
179 self._pl = [nullid, nullid]
179 self._pl = [nullid, nullid]
180 try:
180 try:
181 st = self._opener("dirstate").read()
181 st = self._opener("dirstate").read()
182 except IOError, err:
182 except IOError, err:
183 if err.errno != errno.ENOENT: raise
183 if err.errno != errno.ENOENT: raise
184 return
184 return
185 if not st:
185 if not st:
186 return
186 return
187
187
188 if not self._dirtypl:
188 if not self._dirtypl:
189 self._pl = [st[:20], st[20: 40]]
189 self._pl = [st[:20], st[20: 40]]
190
190
191 # deref fields so they will be local in loop
191 # deref fields so they will be local in loop
192 dmap = self._map
192 dmap = self._map
193 copymap = self._copymap
193 copymap = self._copymap
194 unpack = struct.unpack
194 unpack = struct.unpack
195 e_size = struct.calcsize(_format)
195 e_size = struct.calcsize(_format)
196 pos1 = 40
196 pos1 = 40
197 l = len(st)
197 l = len(st)
198
198
199 # the inner loop
199 # the inner loop
200 while pos1 < l:
200 while pos1 < l:
201 pos2 = pos1 + e_size
201 pos2 = pos1 + e_size
202 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
202 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
203 pos1 = pos2 + e[4]
203 pos1 = pos2 + e[4]
204 f = st[pos2:pos1]
204 f = st[pos2:pos1]
205 if '\0' in f:
205 if '\0' in f:
206 f, c = f.split('\0')
206 f, c = f.split('\0')
207 copymap[f] = c
207 copymap[f] = c
208 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
208 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
209
209
210 def invalidate(self):
210 def invalidate(self):
211 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
211 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
212 if a in self.__dict__:
212 if a in self.__dict__:
213 delattr(self, a)
213 delattr(self, a)
214 self._dirty = False
214 self._dirty = False
215
215
216 def copy(self, source, dest):
216 def copy(self, source, dest):
217 if source == dest:
217 if source == dest:
218 return
218 return
219 self._dirty = True
219 self._dirty = True
220 self._copymap[dest] = source
220 self._copymap[dest] = source
221
221
222 def copied(self, file):
222 def copied(self, file):
223 return self._copymap.get(file, None)
223 return self._copymap.get(file, None)
224
224
225 def copies(self):
225 def copies(self):
226 return self._copymap
226 return self._copymap
227
227
228 def _incpath(self, path):
228 def _incpath(self, path):
229 c = path.rfind('/')
229 c = path.rfind('/')
230 if c >= 0:
230 if c >= 0:
231 dirs = self._dirs
231 dirs = self._dirs
232 base = path[:c]
232 base = path[:c]
233 if base not in dirs:
233 if base not in dirs:
234 self._incpath(base)
234 self._incpath(base)
235 dirs[base] = 1
235 dirs[base] = 1
236 else:
236 else:
237 dirs[base] += 1
237 dirs[base] += 1
238
238
239 def _decpath(self, path):
239 def _decpath(self, path):
240 c = path.rfind('/')
240 c = path.rfind('/')
241 if c >= 0:
241 if c >= 0:
242 base = path[:c]
242 base = path[:c]
243 dirs = self._dirs
243 dirs = self._dirs
244 if dirs[base] == 1:
244 if dirs[base] == 1:
245 del dirs[base]
245 del dirs[base]
246 self._decpath(base)
246 self._decpath(base)
247 else:
247 else:
248 dirs[base] -= 1
248 dirs[base] -= 1
249
249
250 def _incpathcheck(self, f):
250 def _incpathcheck(self, f):
251 if '\r' in f or '\n' in f:
251 if '\r' in f or '\n' in f:
252 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
252 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
253 % f)
253 % f)
254 # shadows
254 # shadows
255 if f in self._dirs:
255 if f in self._dirs:
256 raise util.Abort(_('directory %r already in dirstate') % f)
256 raise util.Abort(_('directory %r already in dirstate') % f)
257 for c in strutil.rfindall(f, '/'):
257 for c in strutil.rfindall(f, '/'):
258 d = f[:c]
258 d = f[:c]
259 if d in self._dirs:
259 if d in self._dirs:
260 break
260 break
261 if d in self._map and self[d] != 'r':
261 if d in self._map and self[d] != 'r':
262 raise util.Abort(_('file %r in dirstate clashes with %r') %
262 raise util.Abort(_('file %r in dirstate clashes with %r') %
263 (d, f))
263 (d, f))
264 self._incpath(f)
264 self._incpath(f)
265
265
266 def _changepath(self, f, newstate, relaxed=False):
266 def _changepath(self, f, newstate, relaxed=False):
267 # handle upcoming path changes
267 # handle upcoming path changes
268 oldstate = self[f]
268 oldstate = self[f]
269 if oldstate not in "?r" and newstate in "?r":
269 if oldstate not in "?r" and newstate in "?r":
270 if "_dirs" in self.__dict__:
270 if "_dirs" in self.__dict__:
271 self._decpath(f)
271 self._decpath(f)
272 return
272 return
273 if oldstate in "?r" and newstate not in "?r":
273 if oldstate in "?r" and newstate not in "?r":
274 if relaxed and oldstate == '?':
274 if relaxed and oldstate == '?':
275 # XXX
275 # XXX
276 # in relaxed mode we assume the caller knows
276 # in relaxed mode we assume the caller knows
277 # what it is doing, workaround for updating
277 # what it is doing, workaround for updating
278 # dir-to-file revisions
278 # dir-to-file revisions
279 if "_dirs" in self.__dict__:
279 if "_dirs" in self.__dict__:
280 self._incpath(f)
280 self._incpath(f)
281 return
281 return
282 self._incpathcheck(f)
282 self._incpathcheck(f)
283 return
283 return
284
284
285 def normal(self, f):
285 def normal(self, f):
286 'mark a file normal and clean'
286 'mark a file normal and clean'
287 self._dirty = True
287 self._dirty = True
288 self._changepath(f, 'n', True)
288 self._changepath(f, 'n', True)
289 s = os.lstat(self._join(f))
289 s = os.lstat(self._join(f))
290 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
290 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
291 if f in self._copymap:
291 if f in self._copymap:
292 del self._copymap[f]
292 del self._copymap[f]
293
293
294 def normallookup(self, f):
294 def normallookup(self, f):
295 'mark a file normal, but possibly dirty'
295 'mark a file normal, but possibly dirty'
296 if self._pl[1] != nullid and f in self._map:
296 if self._pl[1] != nullid and f in self._map:
297 # if there is a merge going on and the file was either
297 # if there is a merge going on and the file was either
298 # in state 'm' or dirty before being removed, restore that state.
298 # in state 'm' or dirty before being removed, restore that state.
299 entry = self._map[f]
299 entry = self._map[f]
300 if entry[0] == 'r' and entry[2] in (-1, -2):
300 if entry[0] == 'r' and entry[2] in (-1, -2):
301 source = self._copymap.get(f)
301 source = self._copymap.get(f)
302 if entry[2] == -1:
302 if entry[2] == -1:
303 self.merge(f)
303 self.merge(f)
304 elif entry[2] == -2:
304 elif entry[2] == -2:
305 self.normaldirty(f)
305 self.normaldirty(f)
306 if source:
306 if source:
307 self.copy(source, f)
307 self.copy(source, f)
308 return
308 return
309 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
309 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
310 return
310 return
311 self._dirty = True
311 self._dirty = True
312 self._changepath(f, 'n', True)
312 self._changepath(f, 'n', True)
313 self._map[f] = ('n', 0, -1, -1, 0)
313 self._map[f] = ('n', 0, -1, -1, 0)
314 if f in self._copymap:
314 if f in self._copymap:
315 del self._copymap[f]
315 del self._copymap[f]
316
316
317 def normaldirty(self, f):
317 def normaldirty(self, f):
318 'mark a file normal, but dirty'
318 'mark a file normal, but dirty'
319 self._dirty = True
319 self._dirty = True
320 self._changepath(f, 'n', True)
320 self._changepath(f, 'n', True)
321 self._map[f] = ('n', 0, -2, -1, 0)
321 self._map[f] = ('n', 0, -2, -1, 0)
322 if f in self._copymap:
322 if f in self._copymap:
323 del self._copymap[f]
323 del self._copymap[f]
324
324
325 def add(self, f):
325 def add(self, f):
326 'mark a file added'
326 'mark a file added'
327 self._dirty = True
327 self._dirty = True
328 self._changepath(f, 'a')
328 self._changepath(f, 'a')
329 self._map[f] = ('a', 0, -1, -1, 0)
329 self._map[f] = ('a', 0, -1, -1, 0)
330 if f in self._copymap:
330 if f in self._copymap:
331 del self._copymap[f]
331 del self._copymap[f]
332
332
333 def remove(self, f):
333 def remove(self, f):
334 'mark a file removed'
334 'mark a file removed'
335 self._dirty = True
335 self._dirty = True
336 self._changepath(f, 'r')
336 self._changepath(f, 'r')
337 size = 0
337 size = 0
338 if self._pl[1] != nullid and f in self._map:
338 if self._pl[1] != nullid and f in self._map:
339 entry = self._map[f]
339 entry = self._map[f]
340 if entry[0] == 'm':
340 if entry[0] == 'm':
341 size = -1
341 size = -1
342 elif entry[0] == 'n' and entry[2] == -2:
342 elif entry[0] == 'n' and entry[2] == -2:
343 size = -2
343 size = -2
344 self._map[f] = ('r', 0, size, 0, 0)
344 self._map[f] = ('r', 0, size, 0, 0)
345 if size == 0 and f in self._copymap:
345 if size == 0 and f in self._copymap:
346 del self._copymap[f]
346 del self._copymap[f]
347
347
348 def merge(self, f):
348 def merge(self, f):
349 'mark a file merged'
349 'mark a file merged'
350 self._dirty = True
350 self._dirty = True
351 s = os.lstat(self._join(f))
351 s = os.lstat(self._join(f))
352 self._changepath(f, 'm', True)
352 self._changepath(f, 'm', True)
353 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
353 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
354 if f in self._copymap:
354 if f in self._copymap:
355 del self._copymap[f]
355 del self._copymap[f]
356
356
357 def forget(self, f):
357 def forget(self, f):
358 'forget a file'
358 'forget a file'
359 self._dirty = True
359 self._dirty = True
360 try:
360 try:
361 self._changepath(f, '?')
361 self._changepath(f, '?')
362 del self._map[f]
362 del self._map[f]
363 except KeyError:
363 except KeyError:
364 self._ui.warn(_("not in dirstate: %s\n") % f)
364 self._ui.warn(_("not in dirstate: %s\n") % f)
365
365
366 def _normalize(self, path):
366 def _normalize(self, path):
367 normpath = os.path.normcase(os.path.normpath(path))
367 normpath = os.path.normcase(os.path.normpath(path))
368 if normpath in self._foldmap:
368 if normpath in self._foldmap:
369 return self._foldmap[normpath]
369 return self._foldmap[normpath]
370 elif os.path.exists(path):
370 elif os.path.exists(path):
371 self._foldmap[normpath] = util.fspath(path, self._root)
371 self._foldmap[normpath] = util.fspath(path, self._root)
372 return self._foldmap[normpath]
372 return self._foldmap[normpath]
373 else:
373 else:
374 return path
374 return path
375
375
376 def clear(self):
376 def clear(self):
377 self._map = {}
377 self._map = {}
378 if "_dirs" in self.__dict__:
378 if "_dirs" in self.__dict__:
379 delattr(self, "_dirs");
379 delattr(self, "_dirs");
380 self._copymap = {}
380 self._copymap = {}
381 self._pl = [nullid, nullid]
381 self._pl = [nullid, nullid]
382 self._dirty = True
382 self._dirty = True
383
383
384 def rebuild(self, parent, files):
384 def rebuild(self, parent, files):
385 self.clear()
385 self.clear()
386 for f in files:
386 for f in files:
387 if files.execf(f):
387 if files.execf(f):
388 self._map[f] = ('n', 0777, -1, 0, 0)
388 self._map[f] = ('n', 0777, -1, 0, 0)
389 else:
389 else:
390 self._map[f] = ('n', 0666, -1, 0, 0)
390 self._map[f] = ('n', 0666, -1, 0, 0)
391 self._pl = (parent, nullid)
391 self._pl = (parent, nullid)
392 self._dirty = True
392 self._dirty = True
393
393
394 def write(self):
394 def write(self):
395 if not self._dirty:
395 if not self._dirty:
396 return
396 return
397 st = self._opener("dirstate", "w", atomictemp=True)
397 st = self._opener("dirstate", "w", atomictemp=True)
398
398
399 try:
399 try:
400 gran = int(self._ui.config('dirstate', 'granularity', 1))
400 gran = int(self._ui.config('dirstate', 'granularity', 1))
401 except ValueError:
401 except ValueError:
402 gran = 1
402 gran = 1
403 limit = sys.maxint
403 limit = sys.maxint
404 if gran > 0:
404 if gran > 0:
405 limit = util.fstat(st).st_mtime - gran
405 limit = util.fstat(st).st_mtime - gran
406
406
407 cs = cStringIO.StringIO()
407 cs = cStringIO.StringIO()
408 copymap = self._copymap
408 copymap = self._copymap
409 pack = struct.pack
409 pack = struct.pack
410 write = cs.write
410 write = cs.write
411 write("".join(self._pl))
411 write("".join(self._pl))
412 for f, e in self._map.iteritems():
412 for f, e in self._map.iteritems():
413 if f in copymap:
413 if f in copymap:
414 f = "%s\0%s" % (f, copymap[f])
414 f = "%s\0%s" % (f, copymap[f])
415 if e[3] > limit and e[0] == 'n':
415 if e[3] > limit and e[0] == 'n':
416 e = (e[0], 0, -1, -1, 0)
416 e = (e[0], 0, -1, -1, 0)
417 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
417 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
418 write(e)
418 write(e)
419 write(f)
419 write(f)
420 st.write(cs.getvalue())
420 st.write(cs.getvalue())
421 st.rename()
421 st.rename()
422 self._dirty = self._dirtypl = False
422 self._dirty = self._dirtypl = False
423
423
424 def _filter(self, files):
424 def _filter(self, files):
425 ret = {}
425 ret = {}
426 unknown = []
426 unknown = []
427
427
428 for x in files:
428 for x in files:
429 if x == '.':
429 if x == '.':
430 return self._map.copy()
430 return self._map.copy()
431 if x not in self._map:
431 if x not in self._map:
432 unknown.append(x)
432 unknown.append(x)
433 else:
433 else:
434 ret[x] = self._map[x]
434 ret[x] = self._map[x]
435
435
436 if not unknown:
436 if not unknown:
437 return ret
437 return ret
438
438
439 b = self._map.keys()
439 b = self._map.keys()
440 b.sort()
440 b.sort()
441 blen = len(b)
441 blen = len(b)
442
442
443 for x in unknown:
443 for x in unknown:
444 bs = bisect.bisect(b, "%s%s" % (x, '/'))
444 bs = bisect.bisect(b, "%s%s" % (x, '/'))
445 while bs < blen:
445 while bs < blen:
446 s = b[bs]
446 s = b[bs]
447 if len(s) > len(x) and s.startswith(x):
447 if len(s) > len(x) and s.startswith(x):
448 ret[s] = self._map[s]
448 ret[s] = self._map[s]
449 else:
449 else:
450 break
450 break
451 bs += 1
451 bs += 1
452 return ret
452 return ret
453
453
454 def _supported(self, f, mode, verbose=False):
454 def _supported(self, f, mode, verbose=False):
455 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
455 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
456 return True
456 return True
457 if verbose:
457 if verbose:
458 kind = 'unknown'
458 kind = 'unknown'
459 if stat.S_ISCHR(mode): kind = _('character device')
459 if stat.S_ISCHR(mode): kind = _('character device')
460 elif stat.S_ISBLK(mode): kind = _('block device')
460 elif stat.S_ISBLK(mode): kind = _('block device')
461 elif stat.S_ISFIFO(mode): kind = _('fifo')
461 elif stat.S_ISFIFO(mode): kind = _('fifo')
462 elif stat.S_ISSOCK(mode): kind = _('socket')
462 elif stat.S_ISSOCK(mode): kind = _('socket')
463 elif stat.S_ISDIR(mode): kind = _('directory')
463 elif stat.S_ISDIR(mode): kind = _('directory')
464 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
464 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
465 % (self.pathto(f), kind))
465 % (self.pathto(f), kind))
466 return False
466 return False
467
467
468 def _dirignore(self, f):
468 def _dirignore(self, f):
469 if f == '.':
469 if f == '.':
470 return False
470 return False
471 if self._ignore(f):
471 if self._ignore(f):
472 return True
472 return True
473 for c in strutil.findall(f, '/'):
473 for c in strutil.findall(f, '/'):
474 if self._ignore(f[:c]):
474 if self._ignore(f[:c]):
475 return True
475 return True
476 return False
476 return False
477
477
478 def walk(self, match):
478 def walk(self, match):
479 # filter out the src and stat
479 # filter out the src and stat
480 for src, f, st in self.statwalk(match):
480 for src, f, st in self.statwalk(match):
481 yield f
481 yield f
482
482
483 def statwalk(self, match, unknown=True, ignored=False):
483 def statwalk(self, match, unknown=True, ignored=False):
484 '''
484 '''
485 walk recursively through the directory tree, finding all files
485 walk recursively through the directory tree, finding all files
486 matched by the match function
486 matched by the match function
487
487
488 results are yielded in a tuple (src, filename, st), where src
488 results are yielded in a tuple (src, filename, st), where src
489 is one of:
489 is one of:
490 'f' the file was found in the directory tree
490 'f' the file was found in the directory tree
491 'm' the file was only in the dirstate and not in the tree
491 'm' the file was only in the dirstate and not in the tree
492
492
493 and st is the stat result if the file was found in the directory.
493 and st is the stat result if the file was found in the directory.
494 '''
494 '''
495
495
496 def fwarn(f, msg):
496 def fwarn(f, msg):
497 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
497 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
498 return False
498 return False
499 badfn = fwarn
499 badfn = fwarn
500 if hasattr(match, 'bad'):
500 if hasattr(match, 'bad'):
501 badfn = match.bad
501 badfn = match.bad
502
502
503 # walk all files by default
503 # walk all files by default
504 files = match.files()
504 files = match.files()
505 if not files:
505 if not files:
506 files = ['.']
506 files = ['.']
507 dc = self._map.copy()
507 dc = self._map.copy()
508 else:
508 else:
509 files = util.unique(files)
509 files = util.unique(files)
510 dc = self._filter(files)
510 dc = self._filter(files)
511
511
512 def imatch(file_):
512 def imatch(file_):
513 if file_ not in dc and self._ignore(file_):
513 if file_ not in dc and self._ignore(file_):
514 return False
514 return False
515 return match(file_)
515 return match(file_)
516
516
517 # TODO: don't walk unknown directories if unknown and ignored are False
517 # TODO: don't walk unknown directories if unknown and ignored are False
518 ignore = self._ignore
518 ignore = self._ignore
519 dirignore = self._dirignore
519 dirignore = self._dirignore
520 if ignored:
520 if ignored:
521 imatch = match
521 imatch = match
522 ignore = util.never
522 ignore = util.never
523 dirignore = util.never
523 dirignore = util.never
524
524
525 # self._root may end with a path separator when self._root == '/'
525 # self._root may end with a path separator when self._root == '/'
526 common_prefix_len = len(self._root)
526 common_prefix_len = len(self._root)
527 if not util.endswithsep(self._root):
527 if not util.endswithsep(self._root):
528 common_prefix_len += 1
528 common_prefix_len += 1
529
529
530 normpath = util.normpath
530 normpath = util.normpath
531 listdir = osutil.listdir
531 listdir = osutil.listdir
532 lstat = os.lstat
532 lstat = os.lstat
533 bisect_left = bisect.bisect_left
533 bisect_left = bisect.bisect_left
534 isdir = os.path.isdir
534 isdir = os.path.isdir
535 pconvert = util.pconvert
535 pconvert = util.pconvert
536 join = os.path.join
536 join = os.path.join
537 s_isdir = stat.S_ISDIR
537 s_isdir = stat.S_ISDIR
538 supported = self._supported
538 supported = self._supported
539 _join = self._join
539 _join = self._join
540 known = {'.hg': 1}
540 known = {'.hg': 1}
541
541
542 # recursion free walker, faster than os.walk.
542 # recursion free walker, faster than os.walk.
543 def findfiles(s):
543 def findfiles(s):
544 work = [s]
544 work = [s]
545 wadd = work.append
545 wadd = work.append
546 found = []
546 found = []
547 add = found.append
547 add = found.append
548 if hasattr(match, 'dir'):
548 if hasattr(match, 'dir'):
549 match.dir(normpath(s[common_prefix_len:]))
549 match.dir(normpath(s[common_prefix_len:]))
550 while work:
550 while work:
551 top = work.pop()
551 top = work.pop()
552 entries = listdir(top, stat=True)
552 entries = listdir(top, stat=True)
553 # nd is the top of the repository dir tree
553 # nd is the top of the repository dir tree
554 nd = normpath(top[common_prefix_len:])
554 nd = normpath(top[common_prefix_len:])
555 if nd == '.':
555 if nd == '.':
556 nd = ''
556 nd = ''
557 else:
557 else:
558 # do not recurse into a repo contained in this
558 # do not recurse into a repo contained in this
559 # one. use bisect to find .hg directory so speed
559 # one. use bisect to find .hg directory so speed
560 # is good on big directory.
560 # is good on big directory.
561 names = [e[0] for e in entries]
561 names = [e[0] for e in entries]
562 hg = bisect_left(names, '.hg')
562 hg = bisect_left(names, '.hg')
563 if hg < len(names) and names[hg] == '.hg':
563 if hg < len(names) and names[hg] == '.hg':
564 if isdir(join(top, '.hg')):
564 if isdir(join(top, '.hg')):
565 continue
565 continue
566 for f, kind, st in entries:
566 for f, kind, st in entries:
567 np = pconvert(join(nd, f))
567 np = pconvert(join(nd, f))
568 if np in known:
568 if np in known:
569 continue
569 continue
570 known[np] = 1
570 known[np] = 1
571 p = join(top, f)
571 p = join(top, f)
572 # don't trip over symlinks
572 # don't trip over symlinks
573 if kind == stat.S_IFDIR:
573 if kind == stat.S_IFDIR:
574 if not ignore(np):
574 if not ignore(np):
575 wadd(p)
575 wadd(p)
576 if hasattr(match, 'dir'):
576 if hasattr(match, 'dir'):
577 match.dir(np)
577 match.dir(np)
578 if np in dc and match(np):
578 if np in dc and match(np):
579 add((np, 'm', st))
579 add((np, 'm', st))
580 elif imatch(np):
580 elif imatch(np):
581 if supported(np, st.st_mode):
581 if supported(np, st.st_mode):
582 add((np, 'f', st))
582 add((np, 'f', st))
583 elif np in dc:
583 elif np in dc:
584 add((np, 'm', st))
584 add((np, 'm', st))
585 found.sort()
585 found.sort()
586 return found
586 return found
587
587
588 # step one, find all files that match our criteria
588 # step one, find all files that match our criteria
589 files.sort()
589 files.sort()
590 for ff in files:
590 for ff in files:
591 nf = normpath(ff)
591 nf = normpath(ff)
592 f = _join(ff)
592 f = _join(ff)
593 try:
593 try:
594 st = lstat(f)
594 st = lstat(f)
595 except OSError, inst:
595 except OSError, inst:
596 found = False
596 found = False
597 for fn in dc:
597 for fn in dc:
598 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
598 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
599 found = True
599 found = True
600 break
600 break
601 if not found:
601 if not found:
602 if inst.errno != errno.ENOENT:
602 if inst.errno != errno.ENOENT:
603 fwarn(ff, inst.strerror)
603 fwarn(ff, inst.strerror)
604 elif badfn(ff, inst.strerror) and imatch(nf):
604 elif badfn(ff, inst.strerror) and imatch(nf):
605 yield 'f', ff, None
605 yield 'f', ff, None
606 continue
606 continue
607 if s_isdir(st.st_mode):
607 if s_isdir(st.st_mode):
608 if not dirignore(nf):
608 if not dirignore(nf):
609 for f, src, st in findfiles(f):
609 for f, src, st in findfiles(f):
610 yield src, f, st
610 yield src, f, st
611 else:
611 else:
612 if nf in known:
612 if nf in known:
613 continue
613 continue
614 known[nf] = 1
614 known[nf] = 1
615 if match(nf):
615 if match(nf):
616 if supported(ff, st.st_mode, verbose=True):
616 if supported(ff, st.st_mode, verbose=True):
617 yield 'f', self.normalize(nf), st
617 yield 'f', self.normalize(nf), st
618 elif ff in dc:
618 elif ff in dc:
619 yield 'm', nf, st
619 yield 'm', nf, st
620
620
621 # step two run through anything left in the dc hash and yield
621 # step two run through anything left in the dc hash and yield
622 # if we haven't already seen it
622 # if we haven't already seen it
623 ks = dc.keys()
623 ks = dc.keys()
624 ks.sort()
624 ks.sort()
625 for k in ks:
625 for k in ks:
626 if k in known:
626 if k in known:
627 continue
627 continue
628 known[k] = 1
628 known[k] = 1
629 if imatch(k):
629 if imatch(k):
630 yield 'm', k, None
630 yield 'm', k, None
631
631
632 def status(self, match, list_ignored, list_clean, list_unknown):
632 def status(self, match, list_ignored, list_clean, list_unknown):
633 lookup, modified, added, unknown, ignored = [], [], [], [], []
633 lookup, modified, added, unknown, ignored = [], [], [], [], []
634 removed, deleted, clean = [], [], []
634 removed, deleted, clean = [], [], []
635
635
636 _join = self._join
636 _join = self._join
637 lstat = os.lstat
637 lstat = os.lstat
638 cmap = self._copymap
638 cmap = self._copymap
639 dmap = self._map
639 dmap = self._map
640 ladd = lookup.append
640 ladd = lookup.append
641 madd = modified.append
641 madd = modified.append
642 aadd = added.append
642 aadd = added.append
643 uadd = unknown.append
643 uadd = unknown.append
644 iadd = ignored.append
644 iadd = ignored.append
645 radd = removed.append
645 radd = removed.append
646 dadd = deleted.append
646 dadd = deleted.append
647 cadd = clean.append
647 cadd = clean.append
648
648
649 for src, fn, st in self.statwalk(match, unknown=list_unknown,
649 for src, fn, st in self.statwalk(match, unknown=list_unknown,
650 ignored=list_ignored):
650 ignored=list_ignored):
651 if fn not in dmap:
651 if fn not in dmap:
652 if (list_ignored or match.exact(fn)) and self._dirignore(fn):
652 if (list_ignored or match.exact(fn)) and self._dirignore(fn):
653 if list_ignored:
653 if list_ignored:
654 iadd(fn)
654 iadd(fn)
655 elif list_unknown:
655 elif list_unknown:
656 uadd(fn)
656 uadd(fn)
657 continue
657 continue
658
658
659 state, mode, size, time, foo = dmap[fn]
659 state, mode, size, time, foo = dmap[fn]
660
660
661 if src == 'm':
661 if src == 'm':
662 nonexistent = True
662 nonexistent = True
663 if not st:
663 if not st:
664 try:
664 try:
665 st = lstat(_join(fn))
665 st = lstat(_join(fn))
666 except OSError, inst:
666 except OSError, inst:
667 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
667 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
668 raise
668 raise
669 st = None
669 st = None
670 # We need to re-check that it is a valid file
670 # We need to re-check that it is a valid file
671 if st and self._supported(fn, st.st_mode):
671 if st and self._supported(fn, st.st_mode):
672 nonexistent = False
672 nonexistent = False
673 if nonexistent and state in "nma":
673 if nonexistent and state in "nma":
674 dadd(fn)
674 dadd(fn)
675 continue
675 continue
676 # check the common case first
676 # check the common case first
677 if state == 'n':
677 if state == 'n':
678 if not st:
678 if not st:
679 st = lstat(_join(fn))
679 st = lstat(_join(fn))
680 if (size >= 0 and
680 if (size >= 0 and
681 (size != st.st_size
681 (size != st.st_size
682 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
682 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
683 or size == -2
683 or size == -2
684 or fn in self._copymap):
684 or fn in self._copymap):
685 madd(fn)
685 madd(fn)
686 elif time != int(st.st_mtime):
686 elif time != int(st.st_mtime):
687 ladd(fn)
687 ladd(fn)
688 elif list_clean:
688 elif list_clean:
689 cadd(fn)
689 cadd(fn)
690 elif state == 'm':
690 elif state == 'm':
691 madd(fn)
691 madd(fn)
692 elif state == 'a':
692 elif state == 'a':
693 aadd(fn)
693 aadd(fn)
694 elif state == 'r':
694 elif state == 'r':
695 radd(fn)
695 radd(fn)
696
696
697 return (lookup, modified, added, removed, deleted, unknown, ignored,
697 return (lookup, modified, added, removed, deleted, unknown, ignored,
698 clean)
698 clean)
@@ -1,485 +1,485
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import errno, util, os, filemerge, copies, shutil
10 import errno, util, os, filemerge, copies, shutil
11
11
12 class mergestate(object):
12 class mergestate(object):
13 '''track 3-way merge state of individual files'''
13 '''track 3-way merge state of individual files'''
14 def __init__(self, repo):
14 def __init__(self, repo):
15 self._repo = repo
15 self._repo = repo
16 self._read()
16 self._read()
17 def reset(self, node):
17 def reset(self, node):
18 self._state = {}
18 self._state = {}
19 self._local = node
19 self._local = node
20 shutil.rmtree(self._repo.join("merge"), True)
20 shutil.rmtree(self._repo.join("merge"), True)
21 def _read(self):
21 def _read(self):
22 self._state = {}
22 self._state = {}
23 try:
23 try:
24 localnode = None
24 localnode = None
25 f = self._repo.opener("merge/state")
25 f = self._repo.opener("merge/state")
26 for i, l in enumerate(f):
26 for i, l in enumerate(f):
27 if i == 0:
27 if i == 0:
28 localnode = l[:-1]
28 localnode = l[:-1]
29 else:
29 else:
30 bits = l[:-1].split("\0")
30 bits = l[:-1].split("\0")
31 self._state[bits[0]] = bits[1:]
31 self._state[bits[0]] = bits[1:]
32 self._local = bin(localnode)
32 self._local = bin(localnode)
33 except IOError, err:
33 except IOError, err:
34 if err.errno != errno.ENOENT:
34 if err.errno != errno.ENOENT:
35 raise
35 raise
36 def _write(self):
36 def _write(self):
37 f = self._repo.opener("merge/state", "w")
37 f = self._repo.opener("merge/state", "w")
38 f.write(hex(self._local) + "\n")
38 f.write(hex(self._local) + "\n")
39 for d, v in self._state.items():
39 for d, v in self._state.items():
40 f.write("\0".join([d] + v) + "\n")
40 f.write("\0".join([d] + v) + "\n")
41 def add(self, fcl, fco, fca, fd, flags):
41 def add(self, fcl, fco, fca, fd, flags):
42 hash = util.sha1(fcl.path()).hexdigest()
42 hash = util.sha1(fcl.path()).hexdigest()
43 self._repo.opener("merge/" + hash, "w").write(fcl.data())
43 self._repo.opener("merge/" + hash, "w").write(fcl.data())
44 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
44 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
45 hex(fca.filenode()), fco.path(), flags]
45 hex(fca.filenode()), fco.path(), flags]
46 self._write()
46 self._write()
47 def __contains__(self, dfile):
47 def __contains__(self, dfile):
48 return dfile in self._state
48 return dfile in self._state
49 def __getitem__(self, dfile):
49 def __getitem__(self, dfile):
50 return self._state[dfile][0]
50 return self._state[dfile][0]
51 def __iter__(self):
51 def __iter__(self):
52 l = self._state.keys()
52 l = self._state.keys()
53 l.sort()
53 l.sort()
54 for f in l:
54 for f in l:
55 yield f
55 yield f
56 def mark(self, dfile, state):
56 def mark(self, dfile, state):
57 self._state[dfile][0] = state
57 self._state[dfile][0] = state
58 self._write()
58 self._write()
59 def resolve(self, dfile, wctx, octx):
59 def resolve(self, dfile, wctx, octx):
60 if self[dfile] == 'r':
60 if self[dfile] == 'r':
61 return 0
61 return 0
62 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
62 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
63 f = self._repo.opener("merge/" + hash)
63 f = self._repo.opener("merge/" + hash)
64 self._repo.wwrite(dfile, f.read(), flags)
64 self._repo.wwrite(dfile, f.read(), flags)
65 fcd = wctx[dfile]
65 fcd = wctx[dfile]
66 fco = octx[ofile]
66 fco = octx[ofile]
67 fca = self._repo.filectx(afile, fileid=anode)
67 fca = self._repo.filectx(afile, fileid=anode)
68 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
68 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
69 if not r:
69 if not r:
70 self.mark(dfile, 'r')
70 self.mark(dfile, 'r')
71 return r
71 return r
72
72
73 def _checkunknown(wctx, mctx):
73 def _checkunknown(wctx, mctx):
74 "check for collisions between unknown files and files in mctx"
74 "check for collisions between unknown files and files in mctx"
75 for f in wctx.unknown():
75 for f in wctx.unknown():
76 if f in mctx and mctx[f].cmp(wctx[f].data()):
76 if f in mctx and mctx[f].cmp(wctx[f].data()):
77 raise util.Abort(_("untracked file in working directory differs"
77 raise util.Abort(_("untracked file in working directory differs"
78 " from file in requested revision: '%s'") % f)
78 " from file in requested revision: '%s'") % f)
79
79
80 def _checkcollision(mctx):
80 def _checkcollision(mctx):
81 "check for case folding collisions in the destination context"
81 "check for case folding collisions in the destination context"
82 folded = {}
82 folded = {}
83 for fn in mctx:
83 for fn in mctx:
84 fold = fn.lower()
84 fold = fn.lower()
85 if fold in folded:
85 if fold in folded:
86 raise util.Abort(_("case-folding collision between %s and %s")
86 raise util.Abort(_("case-folding collision between %s and %s")
87 % (fn, folded[fold]))
87 % (fn, folded[fold]))
88 folded[fold] = fn
88 folded[fold] = fn
89
89
90 def _forgetremoved(wctx, mctx, branchmerge):
90 def _forgetremoved(wctx, mctx, branchmerge):
91 """
91 """
92 Forget removed files
92 Forget removed files
93
93
94 If we're jumping between revisions (as opposed to merging), and if
94 If we're jumping between revisions (as opposed to merging), and if
95 neither the working directory nor the target rev has the file,
95 neither the working directory nor the target rev has the file,
96 then we need to remove it from the dirstate, to prevent the
96 then we need to remove it from the dirstate, to prevent the
97 dirstate from listing the file when it is no longer in the
97 dirstate from listing the file when it is no longer in the
98 manifest.
98 manifest.
99
99
100 If we're merging, and the other revision has removed a file
100 If we're merging, and the other revision has removed a file
101 that is not present in the working directory, we need to mark it
101 that is not present in the working directory, we need to mark it
102 as removed.
102 as removed.
103 """
103 """
104
104
105 action = []
105 action = []
106 state = branchmerge and 'r' or 'f'
106 state = branchmerge and 'r' or 'f'
107 for f in wctx.deleted():
107 for f in wctx.deleted():
108 if f not in mctx:
108 if f not in mctx:
109 action.append((f, state))
109 action.append((f, state))
110
110
111 if not branchmerge:
111 if not branchmerge:
112 for f in wctx.removed():
112 for f in wctx.removed():
113 if f not in mctx:
113 if f not in mctx:
114 action.append((f, "f"))
114 action.append((f, "f"))
115
115
116 return action
116 return action
117
117
118 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
118 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
119 """
119 """
120 Merge p1 and p2 with ancestor ma and generate merge action list
120 Merge p1 and p2 with ancestor ma and generate merge action list
121
121
122 overwrite = whether we clobber working files
122 overwrite = whether we clobber working files
123 partial = function to filter file lists
123 partial = function to filter file lists
124 """
124 """
125
125
126 repo.ui.note(_("resolving manifests\n"))
126 repo.ui.note(_("resolving manifests\n"))
127 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
127 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
128 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
128 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
129
129
130 m1 = p1.manifest()
130 m1 = p1.manifest()
131 m2 = p2.manifest()
131 m2 = p2.manifest()
132 ma = pa.manifest()
132 ma = pa.manifest()
133 backwards = (pa == p2)
133 backwards = (pa == p2)
134 action = []
134 action = []
135 copy, copied, diverge = {}, {}, {}
135 copy, copied, diverge = {}, {}, {}
136
136
137 def fmerge(f, f2=None, fa=None):
137 def fmerge(f, f2=None, fa=None):
138 """merge flags"""
138 """merge flags"""
139 if not f2:
139 if not f2:
140 f2 = f
140 f2 = f
141 fa = f
141 fa = f
142 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
142 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
143 if m == n: # flags agree
143 if m == n: # flags agree
144 return m # unchanged
144 return m # unchanged
145 if m and n: # flags are set but don't agree
145 if m and n: # flags are set but don't agree
146 if not a: # both differ from parent
146 if not a: # both differ from parent
147 r = repo.ui.prompt(
147 r = repo.ui.prompt(
148 _(" conflicting flags for %s\n"
148 _(" conflicting flags for %s\n"
149 "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
149 "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
150 return r != "n" and r or ''
150 return r != "n" and r or ''
151 if m == a:
151 if m == a:
152 return n # changed from m to n
152 return n # changed from m to n
153 return m # changed from n to m
153 return m # changed from n to m
154 if m and m != a: # changed from a to m
154 if m and m != a: # changed from a to m
155 return m
155 return m
156 if n and n != a: # changed from a to n
156 if n and n != a: # changed from a to n
157 return n
157 return n
158 return '' # flag was cleared
158 return '' # flag was cleared
159
159
160 def act(msg, m, f, *args):
160 def act(msg, m, f, *args):
161 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
161 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
162 action.append((f, m) + args)
162 action.append((f, m) + args)
163
163
164 if pa and not (backwards or overwrite):
164 if pa and not (backwards or overwrite):
165 if repo.ui.configbool("merge", "followcopies", True):
165 if repo.ui.configbool("merge", "followcopies", True):
166 dirs = repo.ui.configbool("merge", "followdirs", True)
166 dirs = repo.ui.configbool("merge", "followdirs", True)
167 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
167 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
168 copied = dict.fromkeys(copy.values())
168 copied = dict.fromkeys(copy.values())
169 for of, fl in diverge.items():
169 for of, fl in diverge.items():
170 act("divergent renames", "dr", of, fl)
170 act("divergent renames", "dr", of, fl)
171
171
172 # Compare manifests
172 # Compare manifests
173 for f, n in m1.iteritems():
173 for f, n in m1.iteritems():
174 if partial and not partial(f):
174 if partial and not partial(f):
175 continue
175 continue
176 if f in m2:
176 if f in m2:
177 if overwrite or backwards:
177 if overwrite or backwards:
178 rflags = m2.flags(f)
178 rflags = m2.flags(f)
179 else:
179 else:
180 rflags = fmerge(f)
180 rflags = fmerge(f)
181 # are files different?
181 # are files different?
182 if n != m2[f]:
182 if n != m2[f]:
183 a = ma.get(f, nullid)
183 a = ma.get(f, nullid)
184 # are we clobbering?
184 # are we clobbering?
185 if overwrite:
185 if overwrite:
186 act("clobbering", "g", f, rflags)
186 act("clobbering", "g", f, rflags)
187 # or are we going back in time and clean?
187 # or are we going back in time and clean?
188 elif backwards and not n[20:]:
188 elif backwards and not n[20:]:
189 act("reverting", "g", f, rflags)
189 act("reverting", "g", f, rflags)
190 # are both different from the ancestor?
190 # are both different from the ancestor?
191 elif n != a and m2[f] != a:
191 elif n != a and m2[f] != a:
192 act("versions differ", "m", f, f, f, rflags, False)
192 act("versions differ", "m", f, f, f, rflags, False)
193 # is remote's version newer?
193 # is remote's version newer?
194 elif m2[f] != a:
194 elif m2[f] != a:
195 act("remote is newer", "g", f, rflags)
195 act("remote is newer", "g", f, rflags)
196 # local is newer, not overwrite, check mode bits
196 # local is newer, not overwrite, check mode bits
197 elif m1.flags(f) != rflags:
197 elif m1.flags(f) != rflags:
198 act("update permissions", "e", f, rflags)
198 act("update permissions", "e", f, rflags)
199 # contents same, check mode bits
199 # contents same, check mode bits
200 elif m1.flags(f) != rflags:
200 elif m1.flags(f) != rflags:
201 act("update permissions", "e", f, rflags)
201 act("update permissions", "e", f, rflags)
202 elif f in copied:
202 elif f in copied:
203 continue
203 continue
204 elif f in copy:
204 elif f in copy:
205 f2 = copy[f]
205 f2 = copy[f]
206 if f2 not in m2: # directory rename
206 if f2 not in m2: # directory rename
207 act("remote renamed directory to " + f2, "d",
207 act("remote renamed directory to " + f2, "d",
208 f, None, f2, m1.flags(f))
208 f, None, f2, m1.flags(f))
209 elif f2 in m1: # case 2 A,B/B/B
209 elif f2 in m1: # case 2 A,B/B/B
210 act("local copied to " + f2, "m",
210 act("local copied to " + f2, "m",
211 f, f2, f, fmerge(f, f2, f2), False)
211 f, f2, f, fmerge(f, f2, f2), False)
212 else: # case 4,21 A/B/B
212 else: # case 4,21 A/B/B
213 act("local moved to " + f2, "m",
213 act("local moved to " + f2, "m",
214 f, f2, f, fmerge(f, f2, f2), False)
214 f, f2, f, fmerge(f, f2, f2), False)
215 elif f in ma:
215 elif f in ma:
216 if n != ma[f] and not overwrite:
216 if n != ma[f] and not overwrite:
217 if repo.ui.prompt(
217 if repo.ui.prompt(
218 _(" local changed %s which remote deleted\n"
218 _(" local changed %s which remote deleted\n"
219 "use (c)hanged version or (d)elete?") % f,
219 "use (c)hanged version or (d)elete?") % f,
220 _("[cd]"), _("c")) == _("d"):
220 _("[cd]"), _("c")) == _("d"):
221 act("prompt delete", "r", f)
221 act("prompt delete", "r", f)
222 else:
222 else:
223 act("other deleted", "r", f)
223 act("other deleted", "r", f)
224 else:
224 else:
225 # file is created on branch or in working directory
225 # file is created on branch or in working directory
226 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
226 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
227 act("remote deleted", "r", f)
227 act("remote deleted", "r", f)
228
228
229 for f, n in m2.iteritems():
229 for f, n in m2.iteritems():
230 if partial and not partial(f):
230 if partial and not partial(f):
231 continue
231 continue
232 if f in m1:
232 if f in m1:
233 continue
233 continue
234 if f in copied:
234 if f in copied:
235 continue
235 continue
236 if f in copy:
236 if f in copy:
237 f2 = copy[f]
237 f2 = copy[f]
238 if f2 not in m1: # directory rename
238 if f2 not in m1: # directory rename
239 act("local renamed directory to " + f2, "d",
239 act("local renamed directory to " + f2, "d",
240 None, f, f2, m2.flags(f))
240 None, f, f2, m2.flags(f))
241 elif f2 in m2: # rename case 1, A/A,B/A
241 elif f2 in m2: # rename case 1, A/A,B/A
242 act("remote copied to " + f, "m",
242 act("remote copied to " + f, "m",
243 f2, f, f, fmerge(f2, f, f2), False)
243 f2, f, f, fmerge(f2, f, f2), False)
244 else: # case 3,20 A/B/A
244 else: # case 3,20 A/B/A
245 act("remote moved to " + f, "m",
245 act("remote moved to " + f, "m",
246 f2, f, f, fmerge(f2, f, f2), True)
246 f2, f, f, fmerge(f2, f, f2), True)
247 elif f in ma:
247 elif f in ma:
248 if overwrite or backwards:
248 if overwrite or backwards:
249 act("recreating", "g", f, m2.flags(f))
249 act("recreating", "g", f, m2.flags(f))
250 elif n != ma[f]:
250 elif n != ma[f]:
251 if repo.ui.prompt(
251 if repo.ui.prompt(
252 _("remote changed %s which local deleted\n"
252 _("remote changed %s which local deleted\n"
253 "use (c)hanged version or leave (d)eleted?") % f,
253 "use (c)hanged version or leave (d)eleted?") % f,
254 _("[cd]"), _("c")) == _("c"):
254 _("[cd]"), _("c")) == _("c"):
255 act("prompt recreating", "g", f, m2.flags(f))
255 act("prompt recreating", "g", f, m2.flags(f))
256 else:
256 else:
257 act("remote created", "g", f, m2.flags(f))
257 act("remote created", "g", f, m2.flags(f))
258
258
259 return action
259 return action
260
260
261 def applyupdates(repo, action, wctx, mctx):
261 def applyupdates(repo, action, wctx, mctx):
262 "apply the merge action list to the working directory"
262 "apply the merge action list to the working directory"
263
263
264 updated, merged, removed, unresolved = 0, 0, 0, 0
264 updated, merged, removed, unresolved = 0, 0, 0, 0
265 action.sort()
265 action.sort()
266
266
267 ms = mergestate(repo)
267 ms = mergestate(repo)
268 ms.reset(wctx.parents()[0].node())
268 ms.reset(wctx.parents()[0].node())
269 moves = []
269 moves = []
270
270
271 # prescan for merges
271 # prescan for merges
272 for a in action:
272 for a in action:
273 f, m = a[:2]
273 f, m = a[:2]
274 if m == 'm': # merge
274 if m == 'm': # merge
275 f2, fd, flags, move = a[2:]
275 f2, fd, flags, move = a[2:]
276 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
276 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
277 fcl = wctx[f]
277 fcl = wctx[f]
278 fco = mctx[f2]
278 fco = mctx[f2]
279 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
279 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
280 ms.add(fcl, fco, fca, fd, flags)
280 ms.add(fcl, fco, fca, fd, flags)
281 if f != fd and move:
281 if f != fd and move:
282 moves.append(f)
282 moves.append(f)
283
283
284 # remove renamed files after safely stored
284 # remove renamed files after safely stored
285 for f in moves:
285 for f in moves:
286 if util.lexists(repo.wjoin(f)):
286 if util.lexists(repo.wjoin(f)):
287 repo.ui.debug(_("removing %s\n") % f)
287 repo.ui.debug(_("removing %s\n") % f)
288 os.unlink(repo.wjoin(f))
288 os.unlink(repo.wjoin(f))
289
289
290 audit_path = util.path_auditor(repo.root)
290 audit_path = util.path_auditor(repo.root)
291
291
292 for a in action:
292 for a in action:
293 f, m = a[:2]
293 f, m = a[:2]
294 if f and f[0] == "/":
294 if f and f[0] == "/":
295 continue
295 continue
296 if m == "r": # remove
296 if m == "r": # remove
297 repo.ui.note(_("removing %s\n") % f)
297 repo.ui.note(_("removing %s\n") % f)
298 audit_path(f)
298 audit_path(f)
299 try:
299 try:
300 util.unlink(repo.wjoin(f))
300 util.unlink(repo.wjoin(f))
301 except OSError, inst:
301 except OSError, inst:
302 if inst.errno != errno.ENOENT:
302 if inst.errno != errno.ENOENT:
303 repo.ui.warn(_("update failed to remove %s: %s!\n") %
303 repo.ui.warn(_("update failed to remove %s: %s!\n") %
304 (f, inst.strerror))
304 (f, inst.strerror))
305 removed += 1
305 removed += 1
306 elif m == "m": # merge
306 elif m == "m": # merge
307 f2, fd, flags, move = a[2:]
307 f2, fd, flags, move = a[2:]
308 r = ms.resolve(fd, wctx, mctx)
308 r = ms.resolve(fd, wctx, mctx)
309 if r > 0:
309 if r > 0:
310 unresolved += 1
310 unresolved += 1
311 else:
311 else:
312 if r is None:
312 if r is None:
313 updated += 1
313 updated += 1
314 else:
314 else:
315 merged += 1
315 merged += 1
316 elif m == "g": # get
316 elif m == "g": # get
317 flags = a[2]
317 flags = a[2]
318 repo.ui.note(_("getting %s\n") % f)
318 repo.ui.note(_("getting %s\n") % f)
319 t = mctx.filectx(f).data()
319 t = mctx.filectx(f).data()
320 repo.wwrite(f, t, flags)
320 repo.wwrite(f, t, flags)
321 updated += 1
321 updated += 1
322 elif m == "d": # directory rename
322 elif m == "d": # directory rename
323 f2, fd, flags = a[2:]
323 f2, fd, flags = a[2:]
324 if f:
324 if f:
325 repo.ui.note(_("moving %s to %s\n") % (f, fd))
325 repo.ui.note(_("moving %s to %s\n") % (f, fd))
326 t = wctx.filectx(f).data()
326 t = wctx.filectx(f).data()
327 repo.wwrite(fd, t, flags)
327 repo.wwrite(fd, t, flags)
328 util.unlink(repo.wjoin(f))
328 util.unlink(repo.wjoin(f))
329 if f2:
329 if f2:
330 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
330 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
331 t = mctx.filectx(f2).data()
331 t = mctx.filectx(f2).data()
332 repo.wwrite(fd, t, flags)
332 repo.wwrite(fd, t, flags)
333 updated += 1
333 updated += 1
334 elif m == "dr": # divergent renames
334 elif m == "dr": # divergent renames
335 fl = a[2]
335 fl = a[2]
336 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
336 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
337 for nf in fl:
337 for nf in fl:
338 repo.ui.warn(" %s\n" % nf)
338 repo.ui.warn(" %s\n" % nf)
339 elif m == "e": # exec
339 elif m == "e": # exec
340 flags = a[2]
340 flags = a[2]
341 util.set_flags(repo.wjoin(f), flags)
341 util.set_flags(repo.wjoin(f), flags)
342
342
343 return updated, merged, removed, unresolved
343 return updated, merged, removed, unresolved
344
344
345 def recordupdates(repo, action, branchmerge):
345 def recordupdates(repo, action, branchmerge):
346 "record merge actions to the dirstate"
346 "record merge actions to the dirstate"
347
347
348 for a in action:
348 for a in action:
349 f, m = a[:2]
349 f, m = a[:2]
350 if m == "r": # remove
350 if m == "r": # remove
351 if branchmerge:
351 if branchmerge:
352 repo.dirstate.remove(f)
352 repo.dirstate.remove(f)
353 else:
353 else:
354 repo.dirstate.forget(f)
354 repo.dirstate.forget(f)
355 elif m == "f": # forget
355 elif m == "f": # forget
356 repo.dirstate.forget(f)
356 repo.dirstate.forget(f)
357 elif m in "ge": # get or exec change
357 elif m in "ge": # get or exec change
358 if branchmerge:
358 if branchmerge:
359 repo.dirstate.normaldirty(f)
359 repo.dirstate.normaldirty(f)
360 else:
360 else:
361 repo.dirstate.normal(f)
361 repo.dirstate.normal(f)
362 elif m == "m": # merge
362 elif m == "m": # merge
363 f2, fd, flag, move = a[2:]
363 f2, fd, flag, move = a[2:]
364 if branchmerge:
364 if branchmerge:
365 # We've done a branch merge, mark this file as merged
365 # We've done a branch merge, mark this file as merged
366 # so that we properly record the merger later
366 # so that we properly record the merger later
367 repo.dirstate.merge(fd)
367 repo.dirstate.merge(fd)
368 if f != f2: # copy/rename
368 if f != f2: # copy/rename
369 if move:
369 if move:
370 repo.dirstate.remove(f)
370 repo.dirstate.remove(f)
371 if f != fd:
371 if f != fd:
372 repo.dirstate.copy(f, fd)
372 repo.dirstate.copy(f, fd)
373 else:
373 else:
374 repo.dirstate.copy(f2, fd)
374 repo.dirstate.copy(f2, fd)
375 else:
375 else:
376 # We've update-merged a locally modified file, so
376 # We've update-merged a locally modified file, so
377 # we set the dirstate to emulate a normal checkout
377 # we set the dirstate to emulate a normal checkout
378 # of that file some time in the past. Thus our
378 # of that file some time in the past. Thus our
379 # merge will appear as a normal local file
379 # merge will appear as a normal local file
380 # modification.
380 # modification.
381 repo.dirstate.normallookup(fd)
381 repo.dirstate.normallookup(fd)
382 if move:
382 if move:
383 repo.dirstate.forget(f)
383 repo.dirstate.forget(f)
384 elif m == "d": # directory rename
384 elif m == "d": # directory rename
385 f2, fd, flag = a[2:]
385 f2, fd, flag = a[2:]
386 if not f2 and f not in repo.dirstate:
386 if not f2 and f not in repo.dirstate:
387 # untracked file moved
387 # untracked file moved
388 continue
388 continue
389 if branchmerge:
389 if branchmerge:
390 repo.dirstate.add(fd)
390 repo.dirstate.add(fd)
391 if f:
391 if f:
392 repo.dirstate.remove(f)
392 repo.dirstate.remove(f)
393 repo.dirstate.copy(f, fd)
393 repo.dirstate.copy(f, fd)
394 if f2:
394 if f2:
395 repo.dirstate.copy(f2, fd)
395 repo.dirstate.copy(f2, fd)
396 else:
396 else:
397 repo.dirstate.normal(fd)
397 repo.dirstate.normal(fd)
398 if f:
398 if f:
399 repo.dirstate.forget(f)
399 repo.dirstate.forget(f)
400
400
401 def update(repo, node, branchmerge, force, partial):
401 def update(repo, node, branchmerge, force, partial):
402 """
402 """
403 Perform a merge between the working directory and the given node
403 Perform a merge between the working directory and the given node
404
404
405 branchmerge = whether to merge between branches
405 branchmerge = whether to merge between branches
406 force = whether to force branch merging or file overwriting
406 force = whether to force branch merging or file overwriting
407 partial = a function to filter file lists (dirstate not updated)
407 partial = a function to filter file lists (dirstate not updated)
408 """
408 """
409
409
410 wlock = repo.wlock()
410 wlock = repo.wlock()
411 try:
411 try:
412 wc = repo.changectx(None)
412 wc = repo.changectx(None)
413 if node is None:
413 if node is None:
414 # tip of current branch
414 # tip of current branch
415 try:
415 try:
416 node = repo.branchtags()[wc.branch()]
416 node = repo.branchtags()[wc.branch()]
417 except KeyError:
417 except KeyError:
418 if wc.branch() == "default": # no default branch!
418 if wc.branch() == "default": # no default branch!
419 node = repo.lookup("tip") # update to tip
419 node = repo.lookup("tip") # update to tip
420 else:
420 else:
421 raise util.Abort(_("branch %s not found") % wc.branch())
421 raise util.Abort(_("branch %s not found") % wc.branch())
422 overwrite = force and not branchmerge
422 overwrite = force and not branchmerge
423 pl = wc.parents()
423 pl = wc.parents()
424 p1, p2 = pl[0], repo.changectx(node)
424 p1, p2 = pl[0], repo.changectx(node)
425 pa = p1.ancestor(p2)
425 pa = p1.ancestor(p2)
426 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
426 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
427 fastforward = False
427 fastforward = False
428
428
429 ### check phase
429 ### check phase
430 if not overwrite and len(pl) > 1:
430 if not overwrite and len(pl) > 1:
431 raise util.Abort(_("outstanding uncommitted merges"))
431 raise util.Abort(_("outstanding uncommitted merges"))
432 if branchmerge:
432 if branchmerge:
433 if pa == p2:
433 if pa == p2:
434 raise util.Abort(_("can't merge with ancestor"))
434 raise util.Abort(_("can't merge with ancestor"))
435 elif pa == p1:
435 elif pa == p1:
436 if p1.branch() != p2.branch():
436 if p1.branch() != p2.branch():
437 fastforward = True
437 fastforward = True
438 else:
438 else:
439 raise util.Abort(_("nothing to merge (use 'hg update'"
439 raise util.Abort(_("nothing to merge (use 'hg update'"
440 " or check 'hg heads')"))
440 " or check 'hg heads')"))
441 if not force and (wc.files() or wc.deleted()):
441 if not force and (wc.files() or wc.deleted()):
442 raise util.Abort(_("outstanding uncommitted changes"))
442 raise util.Abort(_("outstanding uncommitted changes"))
443 elif not overwrite:
443 elif not overwrite:
444 if pa == p1 or pa == p2: # linear
444 if pa == p1 or pa == p2: # linear
445 pass # all good
445 pass # all good
446 elif p1.branch() == p2.branch():
446 elif p1.branch() == p2.branch():
447 if wc.files() or wc.deleted():
447 if wc.files() or wc.deleted():
448 raise util.Abort(_("crosses branches (use 'hg merge' or "
448 raise util.Abort(_("crosses branches (use 'hg merge' or "
449 "'hg update -C' to discard changes)"))
449 "'hg update -C' to discard changes)"))
450 raise util.Abort(_("crosses branches (use 'hg merge' "
450 raise util.Abort(_("crosses branches (use 'hg merge' "
451 "or 'hg update -C')"))
451 "or 'hg update -C')"))
452 elif wc.files() or wc.deleted():
452 elif wc.files() or wc.deleted():
453 raise util.Abort(_("crosses named branches (use "
453 raise util.Abort(_("crosses named branches (use "
454 "'hg update -C' to discard changes)"))
454 "'hg update -C' to discard changes)"))
455 else:
455 else:
456 # Allow jumping branches if there are no changes
456 # Allow jumping branches if there are no changes
457 overwrite = True
457 overwrite = True
458
458
459 ### calculate phase
459 ### calculate phase
460 action = []
460 action = []
461 if not force:
461 if not force:
462 _checkunknown(wc, p2)
462 _checkunknown(wc, p2)
463 if not util.checkfolding(repo.path):
463 if not util.checkcase(repo.path):
464 _checkcollision(p2)
464 _checkcollision(p2)
465 action += _forgetremoved(wc, p2, branchmerge)
465 action += _forgetremoved(wc, p2, branchmerge)
466 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
466 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
467
467
468 ### apply phase
468 ### apply phase
469 if not branchmerge: # just jump to the new rev
469 if not branchmerge: # just jump to the new rev
470 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
470 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
471 if not partial:
471 if not partial:
472 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
472 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
473
473
474 stats = applyupdates(repo, action, wc, p2)
474 stats = applyupdates(repo, action, wc, p2)
475
475
476 if not partial:
476 if not partial:
477 recordupdates(repo, action, branchmerge)
477 recordupdates(repo, action, branchmerge)
478 repo.dirstate.setparents(fp1, fp2)
478 repo.dirstate.setparents(fp1, fp2)
479 if not branchmerge and not fastforward:
479 if not branchmerge and not fastforward:
480 repo.dirstate.setbranch(p2.branch())
480 repo.dirstate.setbranch(p2.branch())
481 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
481 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
482
482
483 return stats
483 return stats
484 finally:
484 finally:
485 del wlock
485 del wlock
@@ -1,1891 +1,1891
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp, urlparse
18 import imp, urlparse
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 _encoding = os.environ.get("HGENCODING")
53 _encoding = os.environ.get("HGENCODING")
54 if sys.platform == 'darwin' and not _encoding:
54 if sys.platform == 'darwin' and not _encoding:
55 # On darwin, getpreferredencoding ignores the locale environment and
55 # On darwin, getpreferredencoding ignores the locale environment and
56 # always returns mac-roman. We override this if the environment is
56 # always returns mac-roman. We override this if the environment is
57 # not C (has been customized by the user).
57 # not C (has been customized by the user).
58 locale.setlocale(locale.LC_CTYPE, '')
58 locale.setlocale(locale.LC_CTYPE, '')
59 _encoding = locale.getlocale()[1]
59 _encoding = locale.getlocale()[1]
60 if not _encoding:
60 if not _encoding:
61 _encoding = locale.getpreferredencoding() or 'ascii'
61 _encoding = locale.getpreferredencoding() or 'ascii'
62 except locale.Error:
62 except locale.Error:
63 _encoding = 'ascii'
63 _encoding = 'ascii'
64 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
64 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
65 _fallbackencoding = 'ISO-8859-1'
65 _fallbackencoding = 'ISO-8859-1'
66
66
67 def tolocal(s):
67 def tolocal(s):
68 """
68 """
69 Convert a string from internal UTF-8 to local encoding
69 Convert a string from internal UTF-8 to local encoding
70
70
71 All internal strings should be UTF-8 but some repos before the
71 All internal strings should be UTF-8 but some repos before the
72 implementation of locale support may contain latin1 or possibly
72 implementation of locale support may contain latin1 or possibly
73 other character sets. We attempt to decode everything strictly
73 other character sets. We attempt to decode everything strictly
74 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
74 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
75 replace unknown characters.
75 replace unknown characters.
76 """
76 """
77 for e in ('UTF-8', _fallbackencoding):
77 for e in ('UTF-8', _fallbackencoding):
78 try:
78 try:
79 u = s.decode(e) # attempt strict decoding
79 u = s.decode(e) # attempt strict decoding
80 return u.encode(_encoding, "replace")
80 return u.encode(_encoding, "replace")
81 except LookupError, k:
81 except LookupError, k:
82 raise Abort(_("%s, please check your locale settings") % k)
82 raise Abort(_("%s, please check your locale settings") % k)
83 except UnicodeDecodeError:
83 except UnicodeDecodeError:
84 pass
84 pass
85 u = s.decode("utf-8", "replace") # last ditch
85 u = s.decode("utf-8", "replace") # last ditch
86 return u.encode(_encoding, "replace")
86 return u.encode(_encoding, "replace")
87
87
88 def fromlocal(s):
88 def fromlocal(s):
89 """
89 """
90 Convert a string from the local character encoding to UTF-8
90 Convert a string from the local character encoding to UTF-8
91
91
92 We attempt to decode strings using the encoding mode set by
92 We attempt to decode strings using the encoding mode set by
93 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
93 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
94 characters will cause an error message. Other modes include
94 characters will cause an error message. Other modes include
95 'replace', which replaces unknown characters with a special
95 'replace', which replaces unknown characters with a special
96 Unicode character, and 'ignore', which drops the character.
96 Unicode character, and 'ignore', which drops the character.
97 """
97 """
98 try:
98 try:
99 return s.decode(_encoding, _encodingmode).encode("utf-8")
99 return s.decode(_encoding, _encodingmode).encode("utf-8")
100 except UnicodeDecodeError, inst:
100 except UnicodeDecodeError, inst:
101 sub = s[max(0, inst.start-10):inst.start+10]
101 sub = s[max(0, inst.start-10):inst.start+10]
102 raise Abort("decoding near '%s': %s!" % (sub, inst))
102 raise Abort("decoding near '%s': %s!" % (sub, inst))
103 except LookupError, k:
103 except LookupError, k:
104 raise Abort(_("%s, please check your locale settings") % k)
104 raise Abort(_("%s, please check your locale settings") % k)
105
105
106 def locallen(s):
106 def locallen(s):
107 """Find the length in characters of a local string"""
107 """Find the length in characters of a local string"""
108 return len(s.decode(_encoding, "replace"))
108 return len(s.decode(_encoding, "replace"))
109
109
110 # used by parsedate
110 # used by parsedate
111 defaultdateformats = (
111 defaultdateformats = (
112 '%Y-%m-%d %H:%M:%S',
112 '%Y-%m-%d %H:%M:%S',
113 '%Y-%m-%d %I:%M:%S%p',
113 '%Y-%m-%d %I:%M:%S%p',
114 '%Y-%m-%d %H:%M',
114 '%Y-%m-%d %H:%M',
115 '%Y-%m-%d %I:%M%p',
115 '%Y-%m-%d %I:%M%p',
116 '%Y-%m-%d',
116 '%Y-%m-%d',
117 '%m-%d',
117 '%m-%d',
118 '%m/%d',
118 '%m/%d',
119 '%m/%d/%y',
119 '%m/%d/%y',
120 '%m/%d/%Y',
120 '%m/%d/%Y',
121 '%a %b %d %H:%M:%S %Y',
121 '%a %b %d %H:%M:%S %Y',
122 '%a %b %d %I:%M:%S%p %Y',
122 '%a %b %d %I:%M:%S%p %Y',
123 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
123 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
124 '%b %d %H:%M:%S %Y',
124 '%b %d %H:%M:%S %Y',
125 '%b %d %I:%M:%S%p %Y',
125 '%b %d %I:%M:%S%p %Y',
126 '%b %d %H:%M:%S',
126 '%b %d %H:%M:%S',
127 '%b %d %I:%M:%S%p',
127 '%b %d %I:%M:%S%p',
128 '%b %d %H:%M',
128 '%b %d %H:%M',
129 '%b %d %I:%M%p',
129 '%b %d %I:%M%p',
130 '%b %d %Y',
130 '%b %d %Y',
131 '%b %d',
131 '%b %d',
132 '%H:%M:%S',
132 '%H:%M:%S',
133 '%I:%M:%SP',
133 '%I:%M:%SP',
134 '%H:%M',
134 '%H:%M',
135 '%I:%M%p',
135 '%I:%M%p',
136 )
136 )
137
137
138 extendeddateformats = defaultdateformats + (
138 extendeddateformats = defaultdateformats + (
139 "%Y",
139 "%Y",
140 "%Y-%m",
140 "%Y-%m",
141 "%b",
141 "%b",
142 "%b %Y",
142 "%b %Y",
143 )
143 )
144
144
145 class SignalInterrupt(Exception):
145 class SignalInterrupt(Exception):
146 """Exception raised on SIGTERM and SIGHUP."""
146 """Exception raised on SIGTERM and SIGHUP."""
147
147
148 # differences from SafeConfigParser:
148 # differences from SafeConfigParser:
149 # - case-sensitive keys
149 # - case-sensitive keys
150 # - allows values that are not strings (this means that you may not
150 # - allows values that are not strings (this means that you may not
151 # be able to save the configuration to a file)
151 # be able to save the configuration to a file)
152 class configparser(ConfigParser.SafeConfigParser):
152 class configparser(ConfigParser.SafeConfigParser):
153 def optionxform(self, optionstr):
153 def optionxform(self, optionstr):
154 return optionstr
154 return optionstr
155
155
156 def set(self, section, option, value):
156 def set(self, section, option, value):
157 return ConfigParser.ConfigParser.set(self, section, option, value)
157 return ConfigParser.ConfigParser.set(self, section, option, value)
158
158
159 def _interpolate(self, section, option, rawval, vars):
159 def _interpolate(self, section, option, rawval, vars):
160 if not isinstance(rawval, basestring):
160 if not isinstance(rawval, basestring):
161 return rawval
161 return rawval
162 return ConfigParser.SafeConfigParser._interpolate(self, section,
162 return ConfigParser.SafeConfigParser._interpolate(self, section,
163 option, rawval, vars)
163 option, rawval, vars)
164
164
165 def cachefunc(func):
165 def cachefunc(func):
166 '''cache the result of function calls'''
166 '''cache the result of function calls'''
167 # XXX doesn't handle keywords args
167 # XXX doesn't handle keywords args
168 cache = {}
168 cache = {}
169 if func.func_code.co_argcount == 1:
169 if func.func_code.co_argcount == 1:
170 # we gain a small amount of time because
170 # we gain a small amount of time because
171 # we don't need to pack/unpack the list
171 # we don't need to pack/unpack the list
172 def f(arg):
172 def f(arg):
173 if arg not in cache:
173 if arg not in cache:
174 cache[arg] = func(arg)
174 cache[arg] = func(arg)
175 return cache[arg]
175 return cache[arg]
176 else:
176 else:
177 def f(*args):
177 def f(*args):
178 if args not in cache:
178 if args not in cache:
179 cache[args] = func(*args)
179 cache[args] = func(*args)
180 return cache[args]
180 return cache[args]
181
181
182 return f
182 return f
183
183
184 def pipefilter(s, cmd):
184 def pipefilter(s, cmd):
185 '''filter string S through command CMD, returning its output'''
185 '''filter string S through command CMD, returning its output'''
186 (pin, pout) = os.popen2(cmd, 'b')
186 (pin, pout) = os.popen2(cmd, 'b')
187 def writer():
187 def writer():
188 try:
188 try:
189 pin.write(s)
189 pin.write(s)
190 pin.close()
190 pin.close()
191 except IOError, inst:
191 except IOError, inst:
192 if inst.errno != errno.EPIPE:
192 if inst.errno != errno.EPIPE:
193 raise
193 raise
194
194
195 # we should use select instead on UNIX, but this will work on most
195 # we should use select instead on UNIX, but this will work on most
196 # systems, including Windows
196 # systems, including Windows
197 w = threading.Thread(target=writer)
197 w = threading.Thread(target=writer)
198 w.start()
198 w.start()
199 f = pout.read()
199 f = pout.read()
200 pout.close()
200 pout.close()
201 w.join()
201 w.join()
202 return f
202 return f
203
203
204 def tempfilter(s, cmd):
204 def tempfilter(s, cmd):
205 '''filter string S through a pair of temporary files with CMD.
205 '''filter string S through a pair of temporary files with CMD.
206 CMD is used as a template to create the real command to be run,
206 CMD is used as a template to create the real command to be run,
207 with the strings INFILE and OUTFILE replaced by the real names of
207 with the strings INFILE and OUTFILE replaced by the real names of
208 the temporary files generated.'''
208 the temporary files generated.'''
209 inname, outname = None, None
209 inname, outname = None, None
210 try:
210 try:
211 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
211 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
212 fp = os.fdopen(infd, 'wb')
212 fp = os.fdopen(infd, 'wb')
213 fp.write(s)
213 fp.write(s)
214 fp.close()
214 fp.close()
215 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
215 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
216 os.close(outfd)
216 os.close(outfd)
217 cmd = cmd.replace('INFILE', inname)
217 cmd = cmd.replace('INFILE', inname)
218 cmd = cmd.replace('OUTFILE', outname)
218 cmd = cmd.replace('OUTFILE', outname)
219 code = os.system(cmd)
219 code = os.system(cmd)
220 if sys.platform == 'OpenVMS' and code & 1:
220 if sys.platform == 'OpenVMS' and code & 1:
221 code = 0
221 code = 0
222 if code: raise Abort(_("command '%s' failed: %s") %
222 if code: raise Abort(_("command '%s' failed: %s") %
223 (cmd, explain_exit(code)))
223 (cmd, explain_exit(code)))
224 return open(outname, 'rb').read()
224 return open(outname, 'rb').read()
225 finally:
225 finally:
226 try:
226 try:
227 if inname: os.unlink(inname)
227 if inname: os.unlink(inname)
228 except: pass
228 except: pass
229 try:
229 try:
230 if outname: os.unlink(outname)
230 if outname: os.unlink(outname)
231 except: pass
231 except: pass
232
232
233 filtertable = {
233 filtertable = {
234 'tempfile:': tempfilter,
234 'tempfile:': tempfilter,
235 'pipe:': pipefilter,
235 'pipe:': pipefilter,
236 }
236 }
237
237
238 def filter(s, cmd):
238 def filter(s, cmd):
239 "filter a string through a command that transforms its input to its output"
239 "filter a string through a command that transforms its input to its output"
240 for name, fn in filtertable.iteritems():
240 for name, fn in filtertable.iteritems():
241 if cmd.startswith(name):
241 if cmd.startswith(name):
242 return fn(s, cmd[len(name):].lstrip())
242 return fn(s, cmd[len(name):].lstrip())
243 return pipefilter(s, cmd)
243 return pipefilter(s, cmd)
244
244
245 def binary(s):
245 def binary(s):
246 """return true if a string is binary data"""
246 """return true if a string is binary data"""
247 if s and '\0' in s:
247 if s and '\0' in s:
248 return True
248 return True
249 return False
249 return False
250
250
251 def unique(g):
251 def unique(g):
252 """return the uniq elements of iterable g"""
252 """return the uniq elements of iterable g"""
253 return dict.fromkeys(g).keys()
253 return dict.fromkeys(g).keys()
254
254
255 class Abort(Exception):
255 class Abort(Exception):
256 """Raised if a command needs to print an error and exit."""
256 """Raised if a command needs to print an error and exit."""
257
257
258 class UnexpectedOutput(Abort):
258 class UnexpectedOutput(Abort):
259 """Raised to print an error with part of output and exit."""
259 """Raised to print an error with part of output and exit."""
260
260
261 def always(fn): return True
261 def always(fn): return True
262 def never(fn): return False
262 def never(fn): return False
263
263
264 def expand_glob(pats):
264 def expand_glob(pats):
265 '''On Windows, expand the implicit globs in a list of patterns'''
265 '''On Windows, expand the implicit globs in a list of patterns'''
266 if os.name != 'nt':
266 if os.name != 'nt':
267 return list(pats)
267 return list(pats)
268 ret = []
268 ret = []
269 for p in pats:
269 for p in pats:
270 kind, name = patkind(p, None)
270 kind, name = patkind(p, None)
271 if kind is None:
271 if kind is None:
272 globbed = glob.glob(name)
272 globbed = glob.glob(name)
273 if globbed:
273 if globbed:
274 ret.extend(globbed)
274 ret.extend(globbed)
275 continue
275 continue
276 # if we couldn't expand the glob, just keep it around
276 # if we couldn't expand the glob, just keep it around
277 ret.append(p)
277 ret.append(p)
278 return ret
278 return ret
279
279
280 def patkind(name, default):
280 def patkind(name, default):
281 """Split a string into an optional pattern kind prefix and the
281 """Split a string into an optional pattern kind prefix and the
282 actual pattern."""
282 actual pattern."""
283 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
283 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
284 if name.startswith(prefix + ':'): return name.split(':', 1)
284 if name.startswith(prefix + ':'): return name.split(':', 1)
285 return default, name
285 return default, name
286
286
287 def globre(pat, head='^', tail='$'):
287 def globre(pat, head='^', tail='$'):
288 "convert a glob pattern into a regexp"
288 "convert a glob pattern into a regexp"
289 i, n = 0, len(pat)
289 i, n = 0, len(pat)
290 res = ''
290 res = ''
291 group = 0
291 group = 0
292 def peek(): return i < n and pat[i]
292 def peek(): return i < n and pat[i]
293 while i < n:
293 while i < n:
294 c = pat[i]
294 c = pat[i]
295 i = i+1
295 i = i+1
296 if c == '*':
296 if c == '*':
297 if peek() == '*':
297 if peek() == '*':
298 i += 1
298 i += 1
299 res += '.*'
299 res += '.*'
300 else:
300 else:
301 res += '[^/]*'
301 res += '[^/]*'
302 elif c == '?':
302 elif c == '?':
303 res += '.'
303 res += '.'
304 elif c == '[':
304 elif c == '[':
305 j = i
305 j = i
306 if j < n and pat[j] in '!]':
306 if j < n and pat[j] in '!]':
307 j += 1
307 j += 1
308 while j < n and pat[j] != ']':
308 while j < n and pat[j] != ']':
309 j += 1
309 j += 1
310 if j >= n:
310 if j >= n:
311 res += '\\['
311 res += '\\['
312 else:
312 else:
313 stuff = pat[i:j].replace('\\','\\\\')
313 stuff = pat[i:j].replace('\\','\\\\')
314 i = j + 1
314 i = j + 1
315 if stuff[0] == '!':
315 if stuff[0] == '!':
316 stuff = '^' + stuff[1:]
316 stuff = '^' + stuff[1:]
317 elif stuff[0] == '^':
317 elif stuff[0] == '^':
318 stuff = '\\' + stuff
318 stuff = '\\' + stuff
319 res = '%s[%s]' % (res, stuff)
319 res = '%s[%s]' % (res, stuff)
320 elif c == '{':
320 elif c == '{':
321 group += 1
321 group += 1
322 res += '(?:'
322 res += '(?:'
323 elif c == '}' and group:
323 elif c == '}' and group:
324 res += ')'
324 res += ')'
325 group -= 1
325 group -= 1
326 elif c == ',' and group:
326 elif c == ',' and group:
327 res += '|'
327 res += '|'
328 elif c == '\\':
328 elif c == '\\':
329 p = peek()
329 p = peek()
330 if p:
330 if p:
331 i += 1
331 i += 1
332 res += re.escape(p)
332 res += re.escape(p)
333 else:
333 else:
334 res += re.escape(c)
334 res += re.escape(c)
335 else:
335 else:
336 res += re.escape(c)
336 res += re.escape(c)
337 return head + res + tail
337 return head + res + tail
338
338
339 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
339 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
340
340
341 def pathto(root, n1, n2):
341 def pathto(root, n1, n2):
342 '''return the relative path from one place to another.
342 '''return the relative path from one place to another.
343 root should use os.sep to separate directories
343 root should use os.sep to separate directories
344 n1 should use os.sep to separate directories
344 n1 should use os.sep to separate directories
345 n2 should use "/" to separate directories
345 n2 should use "/" to separate directories
346 returns an os.sep-separated path.
346 returns an os.sep-separated path.
347
347
348 If n1 is a relative path, it's assumed it's
348 If n1 is a relative path, it's assumed it's
349 relative to root.
349 relative to root.
350 n2 should always be relative to root.
350 n2 should always be relative to root.
351 '''
351 '''
352 if not n1: return localpath(n2)
352 if not n1: return localpath(n2)
353 if os.path.isabs(n1):
353 if os.path.isabs(n1):
354 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
354 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
355 return os.path.join(root, localpath(n2))
355 return os.path.join(root, localpath(n2))
356 n2 = '/'.join((pconvert(root), n2))
356 n2 = '/'.join((pconvert(root), n2))
357 a, b = splitpath(n1), n2.split('/')
357 a, b = splitpath(n1), n2.split('/')
358 a.reverse()
358 a.reverse()
359 b.reverse()
359 b.reverse()
360 while a and b and a[-1] == b[-1]:
360 while a and b and a[-1] == b[-1]:
361 a.pop()
361 a.pop()
362 b.pop()
362 b.pop()
363 b.reverse()
363 b.reverse()
364 return os.sep.join((['..'] * len(a)) + b) or '.'
364 return os.sep.join((['..'] * len(a)) + b) or '.'
365
365
366 def canonpath(root, cwd, myname):
366 def canonpath(root, cwd, myname):
367 """return the canonical path of myname, given cwd and root"""
367 """return the canonical path of myname, given cwd and root"""
368 if root == os.sep:
368 if root == os.sep:
369 rootsep = os.sep
369 rootsep = os.sep
370 elif endswithsep(root):
370 elif endswithsep(root):
371 rootsep = root
371 rootsep = root
372 else:
372 else:
373 rootsep = root + os.sep
373 rootsep = root + os.sep
374 name = myname
374 name = myname
375 if not os.path.isabs(name):
375 if not os.path.isabs(name):
376 name = os.path.join(root, cwd, name)
376 name = os.path.join(root, cwd, name)
377 name = os.path.normpath(name)
377 name = os.path.normpath(name)
378 audit_path = path_auditor(root)
378 audit_path = path_auditor(root)
379 if name != rootsep and name.startswith(rootsep):
379 if name != rootsep and name.startswith(rootsep):
380 name = name[len(rootsep):]
380 name = name[len(rootsep):]
381 audit_path(name)
381 audit_path(name)
382 return pconvert(name)
382 return pconvert(name)
383 elif name == root:
383 elif name == root:
384 return ''
384 return ''
385 else:
385 else:
386 # Determine whether `name' is in the hierarchy at or beneath `root',
386 # Determine whether `name' is in the hierarchy at or beneath `root',
387 # by iterating name=dirname(name) until that causes no change (can't
387 # by iterating name=dirname(name) until that causes no change (can't
388 # check name == '/', because that doesn't work on windows). For each
388 # check name == '/', because that doesn't work on windows). For each
389 # `name', compare dev/inode numbers. If they match, the list `rel'
389 # `name', compare dev/inode numbers. If they match, the list `rel'
390 # holds the reversed list of components making up the relative file
390 # holds the reversed list of components making up the relative file
391 # name we want.
391 # name we want.
392 root_st = os.stat(root)
392 root_st = os.stat(root)
393 rel = []
393 rel = []
394 while True:
394 while True:
395 try:
395 try:
396 name_st = os.stat(name)
396 name_st = os.stat(name)
397 except OSError:
397 except OSError:
398 break
398 break
399 if samestat(name_st, root_st):
399 if samestat(name_st, root_st):
400 if not rel:
400 if not rel:
401 # name was actually the same as root (maybe a symlink)
401 # name was actually the same as root (maybe a symlink)
402 return ''
402 return ''
403 rel.reverse()
403 rel.reverse()
404 name = os.path.join(*rel)
404 name = os.path.join(*rel)
405 audit_path(name)
405 audit_path(name)
406 return pconvert(name)
406 return pconvert(name)
407 dirname, basename = os.path.split(name)
407 dirname, basename = os.path.split(name)
408 rel.append(basename)
408 rel.append(basename)
409 if dirname == name:
409 if dirname == name:
410 break
410 break
411 name = dirname
411 name = dirname
412
412
413 raise Abort('%s not under root' % myname)
413 raise Abort('%s not under root' % myname)
414
414
415 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
415 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
416 """build a function to match a set of file patterns
416 """build a function to match a set of file patterns
417
417
418 arguments:
418 arguments:
419 canonroot - the canonical root of the tree you're matching against
419 canonroot - the canonical root of the tree you're matching against
420 cwd - the current working directory, if relevant
420 cwd - the current working directory, if relevant
421 names - patterns to find
421 names - patterns to find
422 inc - patterns to include
422 inc - patterns to include
423 exc - patterns to exclude
423 exc - patterns to exclude
424 dflt_pat - if a pattern in names has no explicit type, assume this one
424 dflt_pat - if a pattern in names has no explicit type, assume this one
425 src - where these patterns came from (e.g. .hgignore)
425 src - where these patterns came from (e.g. .hgignore)
426
426
427 a pattern is one of:
427 a pattern is one of:
428 'glob:<glob>' - a glob relative to cwd
428 'glob:<glob>' - a glob relative to cwd
429 're:<regexp>' - a regular expression
429 're:<regexp>' - a regular expression
430 'path:<path>' - a path relative to canonroot
430 'path:<path>' - a path relative to canonroot
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
432 'relpath:<path>' - a path relative to cwd
432 'relpath:<path>' - a path relative to cwd
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
434 '<something>' - one of the cases above, selected by the dflt_pat argument
434 '<something>' - one of the cases above, selected by the dflt_pat argument
435
435
436 returns:
436 returns:
437 a 3-tuple containing
437 a 3-tuple containing
438 - list of roots (places where one should start a recursive walk of the fs);
438 - list of roots (places where one should start a recursive walk of the fs);
439 this often matches the explicit non-pattern names passed in, but also
439 this often matches the explicit non-pattern names passed in, but also
440 includes the initial part of glob: patterns that has no glob characters
440 includes the initial part of glob: patterns that has no glob characters
441 - a bool match(filename) function
441 - a bool match(filename) function
442 - a bool indicating if any patterns were passed in
442 - a bool indicating if any patterns were passed in
443 """
443 """
444
444
445 # a common case: no patterns at all
445 # a common case: no patterns at all
446 if not names and not inc and not exc:
446 if not names and not inc and not exc:
447 return [], always, False
447 return [], always, False
448
448
449 def contains_glob(name):
449 def contains_glob(name):
450 for c in name:
450 for c in name:
451 if c in _globchars: return True
451 if c in _globchars: return True
452 return False
452 return False
453
453
454 def regex(kind, name, tail):
454 def regex(kind, name, tail):
455 '''convert a pattern into a regular expression'''
455 '''convert a pattern into a regular expression'''
456 if not name:
456 if not name:
457 return ''
457 return ''
458 if kind == 're':
458 if kind == 're':
459 return name
459 return name
460 elif kind == 'path':
460 elif kind == 'path':
461 return '^' + re.escape(name) + '(?:/|$)'
461 return '^' + re.escape(name) + '(?:/|$)'
462 elif kind == 'relglob':
462 elif kind == 'relglob':
463 return globre(name, '(?:|.*/)', tail)
463 return globre(name, '(?:|.*/)', tail)
464 elif kind == 'relpath':
464 elif kind == 'relpath':
465 return re.escape(name) + '(?:/|$)'
465 return re.escape(name) + '(?:/|$)'
466 elif kind == 'relre':
466 elif kind == 'relre':
467 if name.startswith('^'):
467 if name.startswith('^'):
468 return name
468 return name
469 return '.*' + name
469 return '.*' + name
470 return globre(name, '', tail)
470 return globre(name, '', tail)
471
471
472 def matchfn(pats, tail):
472 def matchfn(pats, tail):
473 """build a matching function from a set of patterns"""
473 """build a matching function from a set of patterns"""
474 if not pats:
474 if not pats:
475 return
475 return
476 try:
476 try:
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
478 if len(pat) > 20000:
478 if len(pat) > 20000:
479 raise OverflowError()
479 raise OverflowError()
480 return re.compile(pat).match
480 return re.compile(pat).match
481 except OverflowError:
481 except OverflowError:
482 # We're using a Python with a tiny regex engine and we
482 # We're using a Python with a tiny regex engine and we
483 # made it explode, so we'll divide the pattern list in two
483 # made it explode, so we'll divide the pattern list in two
484 # until it works
484 # until it works
485 l = len(pats)
485 l = len(pats)
486 if l < 2:
486 if l < 2:
487 raise
487 raise
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
489 return lambda s: a(s) or b(s)
489 return lambda s: a(s) or b(s)
490 except re.error:
490 except re.error:
491 for k, p in pats:
491 for k, p in pats:
492 try:
492 try:
493 re.compile('(?:%s)' % regex(k, p, tail))
493 re.compile('(?:%s)' % regex(k, p, tail))
494 except re.error:
494 except re.error:
495 if src:
495 if src:
496 raise Abort("%s: invalid pattern (%s): %s" %
496 raise Abort("%s: invalid pattern (%s): %s" %
497 (src, k, p))
497 (src, k, p))
498 else:
498 else:
499 raise Abort("invalid pattern (%s): %s" % (k, p))
499 raise Abort("invalid pattern (%s): %s" % (k, p))
500 raise Abort("invalid pattern")
500 raise Abort("invalid pattern")
501
501
502 def globprefix(pat):
502 def globprefix(pat):
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
504 root = []
504 root = []
505 for p in pat.split('/'):
505 for p in pat.split('/'):
506 if contains_glob(p): break
506 if contains_glob(p): break
507 root.append(p)
507 root.append(p)
508 return '/'.join(root) or '.'
508 return '/'.join(root) or '.'
509
509
510 def normalizepats(names, default):
510 def normalizepats(names, default):
511 pats = []
511 pats = []
512 roots = []
512 roots = []
513 anypats = False
513 anypats = False
514 for kind, name in [patkind(p, default) for p in names]:
514 for kind, name in [patkind(p, default) for p in names]:
515 if kind in ('glob', 'relpath'):
515 if kind in ('glob', 'relpath'):
516 name = canonpath(canonroot, cwd, name)
516 name = canonpath(canonroot, cwd, name)
517 elif kind in ('relglob', 'path'):
517 elif kind in ('relglob', 'path'):
518 name = normpath(name)
518 name = normpath(name)
519
519
520 pats.append((kind, name))
520 pats.append((kind, name))
521
521
522 if kind in ('glob', 're', 'relglob', 'relre'):
522 if kind in ('glob', 're', 'relglob', 'relre'):
523 anypats = True
523 anypats = True
524
524
525 if kind == 'glob':
525 if kind == 'glob':
526 root = globprefix(name)
526 root = globprefix(name)
527 roots.append(root)
527 roots.append(root)
528 elif kind in ('relpath', 'path'):
528 elif kind in ('relpath', 'path'):
529 roots.append(name or '.')
529 roots.append(name or '.')
530 elif kind == 'relglob':
530 elif kind == 'relglob':
531 roots.append('.')
531 roots.append('.')
532 return roots, pats, anypats
532 return roots, pats, anypats
533
533
534 roots, pats, anypats = normalizepats(names, dflt_pat)
534 roots, pats, anypats = normalizepats(names, dflt_pat)
535
535
536 patmatch = matchfn(pats, '$') or always
536 patmatch = matchfn(pats, '$') or always
537 incmatch = always
537 incmatch = always
538 if inc:
538 if inc:
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
540 incmatch = matchfn(inckinds, '(?:/|$)')
540 incmatch = matchfn(inckinds, '(?:/|$)')
541 excmatch = lambda fn: False
541 excmatch = lambda fn: False
542 if exc:
542 if exc:
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
544 excmatch = matchfn(exckinds, '(?:/|$)')
544 excmatch = matchfn(exckinds, '(?:/|$)')
545
545
546 if not names and inc and not exc:
546 if not names and inc and not exc:
547 # common case: hgignore patterns
547 # common case: hgignore patterns
548 match = incmatch
548 match = incmatch
549 else:
549 else:
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
551
551
552 return (roots, match, (inc or exc or anypats) and True)
552 return (roots, match, (inc or exc or anypats) and True)
553
553
554 _hgexecutable = None
554 _hgexecutable = None
555
555
556 def main_is_frozen():
556 def main_is_frozen():
557 """return True if we are a frozen executable.
557 """return True if we are a frozen executable.
558
558
559 The code supports py2exe (most common, Windows only) and tools/freeze
559 The code supports py2exe (most common, Windows only) and tools/freeze
560 (portable, not much used).
560 (portable, not much used).
561 """
561 """
562 return (hasattr(sys, "frozen") or # new py2exe
562 return (hasattr(sys, "frozen") or # new py2exe
563 hasattr(sys, "importers") or # old py2exe
563 hasattr(sys, "importers") or # old py2exe
564 imp.is_frozen("__main__")) # tools/freeze
564 imp.is_frozen("__main__")) # tools/freeze
565
565
566 def hgexecutable():
566 def hgexecutable():
567 """return location of the 'hg' executable.
567 """return location of the 'hg' executable.
568
568
569 Defaults to $HG or 'hg' in the search path.
569 Defaults to $HG or 'hg' in the search path.
570 """
570 """
571 if _hgexecutable is None:
571 if _hgexecutable is None:
572 hg = os.environ.get('HG')
572 hg = os.environ.get('HG')
573 if hg:
573 if hg:
574 set_hgexecutable(hg)
574 set_hgexecutable(hg)
575 elif main_is_frozen():
575 elif main_is_frozen():
576 set_hgexecutable(sys.executable)
576 set_hgexecutable(sys.executable)
577 else:
577 else:
578 set_hgexecutable(find_exe('hg', 'hg'))
578 set_hgexecutable(find_exe('hg', 'hg'))
579 return _hgexecutable
579 return _hgexecutable
580
580
581 def set_hgexecutable(path):
581 def set_hgexecutable(path):
582 """set location of the 'hg' executable"""
582 """set location of the 'hg' executable"""
583 global _hgexecutable
583 global _hgexecutable
584 _hgexecutable = path
584 _hgexecutable = path
585
585
586 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
586 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
587 '''enhanced shell command execution.
587 '''enhanced shell command execution.
588 run with environment maybe modified, maybe in different dir.
588 run with environment maybe modified, maybe in different dir.
589
589
590 if command fails and onerr is None, return status. if ui object,
590 if command fails and onerr is None, return status. if ui object,
591 print error message and return status, else raise onerr object as
591 print error message and return status, else raise onerr object as
592 exception.'''
592 exception.'''
593 def py2shell(val):
593 def py2shell(val):
594 'convert python object into string that is useful to shell'
594 'convert python object into string that is useful to shell'
595 if val in (None, False):
595 if val in (None, False):
596 return '0'
596 return '0'
597 if val == True:
597 if val == True:
598 return '1'
598 return '1'
599 return str(val)
599 return str(val)
600 oldenv = {}
600 oldenv = {}
601 for k in environ:
601 for k in environ:
602 oldenv[k] = os.environ.get(k)
602 oldenv[k] = os.environ.get(k)
603 if cwd is not None:
603 if cwd is not None:
604 oldcwd = os.getcwd()
604 oldcwd = os.getcwd()
605 origcmd = cmd
605 origcmd = cmd
606 if os.name == 'nt':
606 if os.name == 'nt':
607 cmd = '"%s"' % cmd
607 cmd = '"%s"' % cmd
608 try:
608 try:
609 for k, v in environ.iteritems():
609 for k, v in environ.iteritems():
610 os.environ[k] = py2shell(v)
610 os.environ[k] = py2shell(v)
611 os.environ['HG'] = hgexecutable()
611 os.environ['HG'] = hgexecutable()
612 if cwd is not None and oldcwd != cwd:
612 if cwd is not None and oldcwd != cwd:
613 os.chdir(cwd)
613 os.chdir(cwd)
614 rc = os.system(cmd)
614 rc = os.system(cmd)
615 if sys.platform == 'OpenVMS' and rc & 1:
615 if sys.platform == 'OpenVMS' and rc & 1:
616 rc = 0
616 rc = 0
617 if rc and onerr:
617 if rc and onerr:
618 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
618 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
619 explain_exit(rc)[0])
619 explain_exit(rc)[0])
620 if errprefix:
620 if errprefix:
621 errmsg = '%s: %s' % (errprefix, errmsg)
621 errmsg = '%s: %s' % (errprefix, errmsg)
622 try:
622 try:
623 onerr.warn(errmsg + '\n')
623 onerr.warn(errmsg + '\n')
624 except AttributeError:
624 except AttributeError:
625 raise onerr(errmsg)
625 raise onerr(errmsg)
626 return rc
626 return rc
627 finally:
627 finally:
628 for k, v in oldenv.iteritems():
628 for k, v in oldenv.iteritems():
629 if v is None:
629 if v is None:
630 del os.environ[k]
630 del os.environ[k]
631 else:
631 else:
632 os.environ[k] = v
632 os.environ[k] = v
633 if cwd is not None and oldcwd != cwd:
633 if cwd is not None and oldcwd != cwd:
634 os.chdir(oldcwd)
634 os.chdir(oldcwd)
635
635
636 # os.path.lexists is not available on python2.3
636 # os.path.lexists is not available on python2.3
637 def lexists(filename):
637 def lexists(filename):
638 "test whether a file with this name exists. does not follow symlinks"
638 "test whether a file with this name exists. does not follow symlinks"
639 try:
639 try:
640 os.lstat(filename)
640 os.lstat(filename)
641 except:
641 except:
642 return False
642 return False
643 return True
643 return True
644
644
645 def rename(src, dst):
645 def rename(src, dst):
646 """forcibly rename a file"""
646 """forcibly rename a file"""
647 try:
647 try:
648 os.rename(src, dst)
648 os.rename(src, dst)
649 except OSError, err: # FIXME: check err (EEXIST ?)
649 except OSError, err: # FIXME: check err (EEXIST ?)
650 # on windows, rename to existing file is not allowed, so we
650 # on windows, rename to existing file is not allowed, so we
651 # must delete destination first. but if file is open, unlink
651 # must delete destination first. but if file is open, unlink
652 # schedules it for delete but does not delete it. rename
652 # schedules it for delete but does not delete it. rename
653 # happens immediately even for open files, so we create
653 # happens immediately even for open files, so we create
654 # temporary file, delete it, rename destination to that name,
654 # temporary file, delete it, rename destination to that name,
655 # then delete that. then rename is safe to do.
655 # then delete that. then rename is safe to do.
656 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
656 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
657 os.close(fd)
657 os.close(fd)
658 os.unlink(temp)
658 os.unlink(temp)
659 os.rename(dst, temp)
659 os.rename(dst, temp)
660 os.unlink(temp)
660 os.unlink(temp)
661 os.rename(src, dst)
661 os.rename(src, dst)
662
662
663 def unlink(f):
663 def unlink(f):
664 """unlink and remove the directory if it is empty"""
664 """unlink and remove the directory if it is empty"""
665 os.unlink(f)
665 os.unlink(f)
666 # try removing directories that might now be empty
666 # try removing directories that might now be empty
667 try:
667 try:
668 os.removedirs(os.path.dirname(f))
668 os.removedirs(os.path.dirname(f))
669 except OSError:
669 except OSError:
670 pass
670 pass
671
671
672 def copyfile(src, dest):
672 def copyfile(src, dest):
673 "copy a file, preserving mode"
673 "copy a file, preserving mode"
674 if os.path.islink(src):
674 if os.path.islink(src):
675 try:
675 try:
676 os.unlink(dest)
676 os.unlink(dest)
677 except:
677 except:
678 pass
678 pass
679 os.symlink(os.readlink(src), dest)
679 os.symlink(os.readlink(src), dest)
680 else:
680 else:
681 try:
681 try:
682 shutil.copyfile(src, dest)
682 shutil.copyfile(src, dest)
683 shutil.copymode(src, dest)
683 shutil.copymode(src, dest)
684 except shutil.Error, inst:
684 except shutil.Error, inst:
685 raise Abort(str(inst))
685 raise Abort(str(inst))
686
686
687 def copyfiles(src, dst, hardlink=None):
687 def copyfiles(src, dst, hardlink=None):
688 """Copy a directory tree using hardlinks if possible"""
688 """Copy a directory tree using hardlinks if possible"""
689
689
690 if hardlink is None:
690 if hardlink is None:
691 hardlink = (os.stat(src).st_dev ==
691 hardlink = (os.stat(src).st_dev ==
692 os.stat(os.path.dirname(dst)).st_dev)
692 os.stat(os.path.dirname(dst)).st_dev)
693
693
694 if os.path.isdir(src):
694 if os.path.isdir(src):
695 os.mkdir(dst)
695 os.mkdir(dst)
696 for name, kind in osutil.listdir(src):
696 for name, kind in osutil.listdir(src):
697 srcname = os.path.join(src, name)
697 srcname = os.path.join(src, name)
698 dstname = os.path.join(dst, name)
698 dstname = os.path.join(dst, name)
699 copyfiles(srcname, dstname, hardlink)
699 copyfiles(srcname, dstname, hardlink)
700 else:
700 else:
701 if hardlink:
701 if hardlink:
702 try:
702 try:
703 os_link(src, dst)
703 os_link(src, dst)
704 except (IOError, OSError):
704 except (IOError, OSError):
705 hardlink = False
705 hardlink = False
706 shutil.copy(src, dst)
706 shutil.copy(src, dst)
707 else:
707 else:
708 shutil.copy(src, dst)
708 shutil.copy(src, dst)
709
709
710 class path_auditor(object):
710 class path_auditor(object):
711 '''ensure that a filesystem path contains no banned components.
711 '''ensure that a filesystem path contains no banned components.
712 the following properties of a path are checked:
712 the following properties of a path are checked:
713
713
714 - under top-level .hg
714 - under top-level .hg
715 - starts at the root of a windows drive
715 - starts at the root of a windows drive
716 - contains ".."
716 - contains ".."
717 - traverses a symlink (e.g. a/symlink_here/b)
717 - traverses a symlink (e.g. a/symlink_here/b)
718 - inside a nested repository'''
718 - inside a nested repository'''
719
719
720 def __init__(self, root):
720 def __init__(self, root):
721 self.audited = set()
721 self.audited = set()
722 self.auditeddir = set()
722 self.auditeddir = set()
723 self.root = root
723 self.root = root
724
724
725 def __call__(self, path):
725 def __call__(self, path):
726 if path in self.audited:
726 if path in self.audited:
727 return
727 return
728 normpath = os.path.normcase(path)
728 normpath = os.path.normcase(path)
729 parts = splitpath(normpath)
729 parts = splitpath(normpath)
730 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
730 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
731 or os.pardir in parts):
731 or os.pardir in parts):
732 raise Abort(_("path contains illegal component: %s") % path)
732 raise Abort(_("path contains illegal component: %s") % path)
733 def check(prefix):
733 def check(prefix):
734 curpath = os.path.join(self.root, prefix)
734 curpath = os.path.join(self.root, prefix)
735 try:
735 try:
736 st = os.lstat(curpath)
736 st = os.lstat(curpath)
737 except OSError, err:
737 except OSError, err:
738 # EINVAL can be raised as invalid path syntax under win32.
738 # EINVAL can be raised as invalid path syntax under win32.
739 # They must be ignored for patterns can be checked too.
739 # They must be ignored for patterns can be checked too.
740 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
740 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
741 raise
741 raise
742 else:
742 else:
743 if stat.S_ISLNK(st.st_mode):
743 if stat.S_ISLNK(st.st_mode):
744 raise Abort(_('path %r traverses symbolic link %r') %
744 raise Abort(_('path %r traverses symbolic link %r') %
745 (path, prefix))
745 (path, prefix))
746 elif (stat.S_ISDIR(st.st_mode) and
746 elif (stat.S_ISDIR(st.st_mode) and
747 os.path.isdir(os.path.join(curpath, '.hg'))):
747 os.path.isdir(os.path.join(curpath, '.hg'))):
748 raise Abort(_('path %r is inside repo %r') %
748 raise Abort(_('path %r is inside repo %r') %
749 (path, prefix))
749 (path, prefix))
750 parts.pop()
750 parts.pop()
751 prefixes = []
751 prefixes = []
752 for n in range(len(parts)):
752 for n in range(len(parts)):
753 prefix = os.sep.join(parts)
753 prefix = os.sep.join(parts)
754 if prefix in self.auditeddir:
754 if prefix in self.auditeddir:
755 break
755 break
756 check(prefix)
756 check(prefix)
757 prefixes.append(prefix)
757 prefixes.append(prefix)
758 parts.pop()
758 parts.pop()
759
759
760 self.audited.add(path)
760 self.audited.add(path)
761 # only add prefixes to the cache after checking everything: we don't
761 # only add prefixes to the cache after checking everything: we don't
762 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
762 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
763 self.auditeddir.update(prefixes)
763 self.auditeddir.update(prefixes)
764
764
765 def _makelock_file(info, pathname):
765 def _makelock_file(info, pathname):
766 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
766 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
767 os.write(ld, info)
767 os.write(ld, info)
768 os.close(ld)
768 os.close(ld)
769
769
770 def _readlock_file(pathname):
770 def _readlock_file(pathname):
771 return posixfile(pathname).read()
771 return posixfile(pathname).read()
772
772
773 def nlinks(pathname):
773 def nlinks(pathname):
774 """Return number of hardlinks for the given file."""
774 """Return number of hardlinks for the given file."""
775 return os.lstat(pathname).st_nlink
775 return os.lstat(pathname).st_nlink
776
776
777 if hasattr(os, 'link'):
777 if hasattr(os, 'link'):
778 os_link = os.link
778 os_link = os.link
779 else:
779 else:
780 def os_link(src, dst):
780 def os_link(src, dst):
781 raise OSError(0, _("Hardlinks not supported"))
781 raise OSError(0, _("Hardlinks not supported"))
782
782
783 def fstat(fp):
783 def fstat(fp):
784 '''stat file object that may not have fileno method.'''
784 '''stat file object that may not have fileno method.'''
785 try:
785 try:
786 return os.fstat(fp.fileno())
786 return os.fstat(fp.fileno())
787 except AttributeError:
787 except AttributeError:
788 return os.stat(fp.name)
788 return os.stat(fp.name)
789
789
790 posixfile = file
790 posixfile = file
791
791
792 def openhardlinks():
792 def openhardlinks():
793 '''return true if it is safe to hold open file handles to hardlinks'''
793 '''return true if it is safe to hold open file handles to hardlinks'''
794 return True
794 return True
795
795
796 getuser_fallback = None
796 getuser_fallback = None
797
797
798 def getuser():
798 def getuser():
799 '''return name of current user'''
799 '''return name of current user'''
800 try:
800 try:
801 return getpass.getuser()
801 return getpass.getuser()
802 except ImportError:
802 except ImportError:
803 # import of pwd will fail on windows - try fallback
803 # import of pwd will fail on windows - try fallback
804 if getuser_fallback:
804 if getuser_fallback:
805 return getuser_fallback()
805 return getuser_fallback()
806 # raised if win32api not available
806 # raised if win32api not available
807 raise Abort(_('user name not available - set USERNAME '
807 raise Abort(_('user name not available - set USERNAME '
808 'environment variable'))
808 'environment variable'))
809
809
810 def username(uid=None):
810 def username(uid=None):
811 """Return the name of the user with the given uid.
811 """Return the name of the user with the given uid.
812
812
813 If uid is None, return the name of the current user."""
813 If uid is None, return the name of the current user."""
814 try:
814 try:
815 import pwd
815 import pwd
816 if uid is None:
816 if uid is None:
817 uid = os.getuid()
817 uid = os.getuid()
818 try:
818 try:
819 return pwd.getpwuid(uid)[0]
819 return pwd.getpwuid(uid)[0]
820 except KeyError:
820 except KeyError:
821 return str(uid)
821 return str(uid)
822 except ImportError:
822 except ImportError:
823 return None
823 return None
824
824
825 def groupname(gid=None):
825 def groupname(gid=None):
826 """Return the name of the group with the given gid.
826 """Return the name of the group with the given gid.
827
827
828 If gid is None, return the name of the current group."""
828 If gid is None, return the name of the current group."""
829 try:
829 try:
830 import grp
830 import grp
831 if gid is None:
831 if gid is None:
832 gid = os.getgid()
832 gid = os.getgid()
833 try:
833 try:
834 return grp.getgrgid(gid)[0]
834 return grp.getgrgid(gid)[0]
835 except KeyError:
835 except KeyError:
836 return str(gid)
836 return str(gid)
837 except ImportError:
837 except ImportError:
838 return None
838 return None
839
839
840 # File system features
840 # File system features
841
841
842 def checkfolding(path):
842 def checkcase(path):
843 """
843 """
844 Check whether the given path is on a case-sensitive filesystem
844 Check whether the given path is on a case-sensitive filesystem
845
845
846 Requires a path (like /foo/.hg) ending with a foldable final
846 Requires a path (like /foo/.hg) ending with a foldable final
847 directory component.
847 directory component.
848 """
848 """
849 s1 = os.stat(path)
849 s1 = os.stat(path)
850 d, b = os.path.split(path)
850 d, b = os.path.split(path)
851 p2 = os.path.join(d, b.upper())
851 p2 = os.path.join(d, b.upper())
852 if path == p2:
852 if path == p2:
853 p2 = os.path.join(d, b.lower())
853 p2 = os.path.join(d, b.lower())
854 try:
854 try:
855 s2 = os.stat(p2)
855 s2 = os.stat(p2)
856 if s2 == s1:
856 if s2 == s1:
857 return False
857 return False
858 return True
858 return True
859 except:
859 except:
860 return True
860 return True
861
861
862 _fspathcache = {}
862 _fspathcache = {}
863 def fspath(name, root):
863 def fspath(name, root):
864 '''Get name in the case stored in the filesystem
864 '''Get name in the case stored in the filesystem
865
865
866 The name is either relative to root, or it is an absolute path starting
866 The name is either relative to root, or it is an absolute path starting
867 with root. Note that this function is unnecessary, and should not be
867 with root. Note that this function is unnecessary, and should not be
868 called, for case-sensitive filesystems (simply because it's expensive).
868 called, for case-sensitive filesystems (simply because it's expensive).
869 '''
869 '''
870 # If name is absolute, make it relative
870 # If name is absolute, make it relative
871 if name.lower().startswith(root.lower()):
871 if name.lower().startswith(root.lower()):
872 l = len(root)
872 l = len(root)
873 if name[l] == os.sep or name[l] == os.altsep:
873 if name[l] == os.sep or name[l] == os.altsep:
874 l = l + 1
874 l = l + 1
875 name = name[l:]
875 name = name[l:]
876
876
877 if not os.path.exists(os.path.join(root, name)):
877 if not os.path.exists(os.path.join(root, name)):
878 return None
878 return None
879
879
880 seps = os.sep
880 seps = os.sep
881 if os.altsep:
881 if os.altsep:
882 seps = seps + os.altsep
882 seps = seps + os.altsep
883 # Protect backslashes. This gets silly very quickly.
883 # Protect backslashes. This gets silly very quickly.
884 seps.replace('\\','\\\\')
884 seps.replace('\\','\\\\')
885 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
885 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
886 dir = os.path.normcase(os.path.normpath(root))
886 dir = os.path.normcase(os.path.normpath(root))
887 result = []
887 result = []
888 for part, sep in pattern.findall(name):
888 for part, sep in pattern.findall(name):
889 if sep:
889 if sep:
890 result.append(sep)
890 result.append(sep)
891 continue
891 continue
892
892
893 if dir not in _fspathcache:
893 if dir not in _fspathcache:
894 _fspathcache[dir] = os.listdir(dir)
894 _fspathcache[dir] = os.listdir(dir)
895 contents = _fspathcache[dir]
895 contents = _fspathcache[dir]
896
896
897 lpart = part.lower()
897 lpart = part.lower()
898 for n in contents:
898 for n in contents:
899 if n.lower() == lpart:
899 if n.lower() == lpart:
900 result.append(n)
900 result.append(n)
901 break
901 break
902 else:
902 else:
903 # Cannot happen, as the file exists!
903 # Cannot happen, as the file exists!
904 result.append(part)
904 result.append(part)
905 dir = os.path.join(dir, lpart)
905 dir = os.path.join(dir, lpart)
906
906
907 return ''.join(result)
907 return ''.join(result)
908
908
909 def checkexec(path):
909 def checkexec(path):
910 """
910 """
911 Check whether the given path is on a filesystem with UNIX-like exec flags
911 Check whether the given path is on a filesystem with UNIX-like exec flags
912
912
913 Requires a directory (like /foo/.hg)
913 Requires a directory (like /foo/.hg)
914 """
914 """
915
915
916 # VFAT on some Linux versions can flip mode but it doesn't persist
916 # VFAT on some Linux versions can flip mode but it doesn't persist
917 # a FS remount. Frequently we can detect it if files are created
917 # a FS remount. Frequently we can detect it if files are created
918 # with exec bit on.
918 # with exec bit on.
919
919
920 try:
920 try:
921 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
921 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
922 fh, fn = tempfile.mkstemp("", "", path)
922 fh, fn = tempfile.mkstemp("", "", path)
923 try:
923 try:
924 os.close(fh)
924 os.close(fh)
925 m = os.stat(fn).st_mode & 0777
925 m = os.stat(fn).st_mode & 0777
926 new_file_has_exec = m & EXECFLAGS
926 new_file_has_exec = m & EXECFLAGS
927 os.chmod(fn, m ^ EXECFLAGS)
927 os.chmod(fn, m ^ EXECFLAGS)
928 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
928 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
929 finally:
929 finally:
930 os.unlink(fn)
930 os.unlink(fn)
931 except (IOError, OSError):
931 except (IOError, OSError):
932 # we don't care, the user probably won't be able to commit anyway
932 # we don't care, the user probably won't be able to commit anyway
933 return False
933 return False
934 return not (new_file_has_exec or exec_flags_cannot_flip)
934 return not (new_file_has_exec or exec_flags_cannot_flip)
935
935
936 def checklink(path):
936 def checklink(path):
937 """check whether the given path is on a symlink-capable filesystem"""
937 """check whether the given path is on a symlink-capable filesystem"""
938 # mktemp is not racy because symlink creation will fail if the
938 # mktemp is not racy because symlink creation will fail if the
939 # file already exists
939 # file already exists
940 name = tempfile.mktemp(dir=path)
940 name = tempfile.mktemp(dir=path)
941 try:
941 try:
942 os.symlink(".", name)
942 os.symlink(".", name)
943 os.unlink(name)
943 os.unlink(name)
944 return True
944 return True
945 except (OSError, AttributeError):
945 except (OSError, AttributeError):
946 return False
946 return False
947
947
948 _umask = os.umask(0)
948 _umask = os.umask(0)
949 os.umask(_umask)
949 os.umask(_umask)
950
950
951 def needbinarypatch():
951 def needbinarypatch():
952 """return True if patches should be applied in binary mode by default."""
952 """return True if patches should be applied in binary mode by default."""
953 return os.name == 'nt'
953 return os.name == 'nt'
954
954
955 def endswithsep(path):
955 def endswithsep(path):
956 '''Check path ends with os.sep or os.altsep.'''
956 '''Check path ends with os.sep or os.altsep.'''
957 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
957 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
958
958
959 def splitpath(path):
959 def splitpath(path):
960 '''Split path by os.sep.
960 '''Split path by os.sep.
961 Note that this function does not use os.altsep because this is
961 Note that this function does not use os.altsep because this is
962 an alternative of simple "xxx.split(os.sep)".
962 an alternative of simple "xxx.split(os.sep)".
963 It is recommended to use os.path.normpath() before using this
963 It is recommended to use os.path.normpath() before using this
964 function if need.'''
964 function if need.'''
965 return path.split(os.sep)
965 return path.split(os.sep)
966
966
967 def gui():
967 def gui():
968 '''Are we running in a GUI?'''
968 '''Are we running in a GUI?'''
969 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
969 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
970
970
971 def lookup_reg(key, name=None, scope=None):
971 def lookup_reg(key, name=None, scope=None):
972 return None
972 return None
973
973
974 # Platform specific variants
974 # Platform specific variants
975 if os.name == 'nt':
975 if os.name == 'nt':
976 import msvcrt
976 import msvcrt
977 nulldev = 'NUL:'
977 nulldev = 'NUL:'
978
978
979 class winstdout:
979 class winstdout:
980 '''stdout on windows misbehaves if sent through a pipe'''
980 '''stdout on windows misbehaves if sent through a pipe'''
981
981
982 def __init__(self, fp):
982 def __init__(self, fp):
983 self.fp = fp
983 self.fp = fp
984
984
985 def __getattr__(self, key):
985 def __getattr__(self, key):
986 return getattr(self.fp, key)
986 return getattr(self.fp, key)
987
987
988 def close(self):
988 def close(self):
989 try:
989 try:
990 self.fp.close()
990 self.fp.close()
991 except: pass
991 except: pass
992
992
993 def write(self, s):
993 def write(self, s):
994 try:
994 try:
995 # This is workaround for "Not enough space" error on
995 # This is workaround for "Not enough space" error on
996 # writing large size of data to console.
996 # writing large size of data to console.
997 limit = 16000
997 limit = 16000
998 l = len(s)
998 l = len(s)
999 start = 0
999 start = 0
1000 while start < l:
1000 while start < l:
1001 end = start + limit
1001 end = start + limit
1002 self.fp.write(s[start:end])
1002 self.fp.write(s[start:end])
1003 start = end
1003 start = end
1004 except IOError, inst:
1004 except IOError, inst:
1005 if inst.errno != 0: raise
1005 if inst.errno != 0: raise
1006 self.close()
1006 self.close()
1007 raise IOError(errno.EPIPE, 'Broken pipe')
1007 raise IOError(errno.EPIPE, 'Broken pipe')
1008
1008
1009 def flush(self):
1009 def flush(self):
1010 try:
1010 try:
1011 return self.fp.flush()
1011 return self.fp.flush()
1012 except IOError, inst:
1012 except IOError, inst:
1013 if inst.errno != errno.EINVAL: raise
1013 if inst.errno != errno.EINVAL: raise
1014 self.close()
1014 self.close()
1015 raise IOError(errno.EPIPE, 'Broken pipe')
1015 raise IOError(errno.EPIPE, 'Broken pipe')
1016
1016
1017 sys.stdout = winstdout(sys.stdout)
1017 sys.stdout = winstdout(sys.stdout)
1018
1018
1019 def _is_win_9x():
1019 def _is_win_9x():
1020 '''return true if run on windows 95, 98 or me.'''
1020 '''return true if run on windows 95, 98 or me.'''
1021 try:
1021 try:
1022 return sys.getwindowsversion()[3] == 1
1022 return sys.getwindowsversion()[3] == 1
1023 except AttributeError:
1023 except AttributeError:
1024 return 'command' in os.environ.get('comspec', '')
1024 return 'command' in os.environ.get('comspec', '')
1025
1025
1026 def openhardlinks():
1026 def openhardlinks():
1027 return not _is_win_9x and "win32api" in locals()
1027 return not _is_win_9x and "win32api" in locals()
1028
1028
1029 def system_rcpath():
1029 def system_rcpath():
1030 try:
1030 try:
1031 return system_rcpath_win32()
1031 return system_rcpath_win32()
1032 except:
1032 except:
1033 return [r'c:\mercurial\mercurial.ini']
1033 return [r'c:\mercurial\mercurial.ini']
1034
1034
1035 def user_rcpath():
1035 def user_rcpath():
1036 '''return os-specific hgrc search path to the user dir'''
1036 '''return os-specific hgrc search path to the user dir'''
1037 try:
1037 try:
1038 path = user_rcpath_win32()
1038 path = user_rcpath_win32()
1039 except:
1039 except:
1040 home = os.path.expanduser('~')
1040 home = os.path.expanduser('~')
1041 path = [os.path.join(home, 'mercurial.ini'),
1041 path = [os.path.join(home, 'mercurial.ini'),
1042 os.path.join(home, '.hgrc')]
1042 os.path.join(home, '.hgrc')]
1043 userprofile = os.environ.get('USERPROFILE')
1043 userprofile = os.environ.get('USERPROFILE')
1044 if userprofile:
1044 if userprofile:
1045 path.append(os.path.join(userprofile, 'mercurial.ini'))
1045 path.append(os.path.join(userprofile, 'mercurial.ini'))
1046 path.append(os.path.join(userprofile, '.hgrc'))
1046 path.append(os.path.join(userprofile, '.hgrc'))
1047 return path
1047 return path
1048
1048
1049 def parse_patch_output(output_line):
1049 def parse_patch_output(output_line):
1050 """parses the output produced by patch and returns the file name"""
1050 """parses the output produced by patch and returns the file name"""
1051 pf = output_line[14:]
1051 pf = output_line[14:]
1052 if pf[0] == '`':
1052 if pf[0] == '`':
1053 pf = pf[1:-1] # Remove the quotes
1053 pf = pf[1:-1] # Remove the quotes
1054 return pf
1054 return pf
1055
1055
1056 def sshargs(sshcmd, host, user, port):
1056 def sshargs(sshcmd, host, user, port):
1057 '''Build argument list for ssh or Plink'''
1057 '''Build argument list for ssh or Plink'''
1058 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1058 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1059 args = user and ("%s@%s" % (user, host)) or host
1059 args = user and ("%s@%s" % (user, host)) or host
1060 return port and ("%s %s %s" % (args, pflag, port)) or args
1060 return port and ("%s %s %s" % (args, pflag, port)) or args
1061
1061
1062 def testpid(pid):
1062 def testpid(pid):
1063 '''return False if pid dead, True if running or not known'''
1063 '''return False if pid dead, True if running or not known'''
1064 return True
1064 return True
1065
1065
1066 def set_flags(f, flags):
1066 def set_flags(f, flags):
1067 pass
1067 pass
1068
1068
1069 def set_binary(fd):
1069 def set_binary(fd):
1070 # When run without console, pipes may expose invalid
1070 # When run without console, pipes may expose invalid
1071 # fileno(), usually set to -1.
1071 # fileno(), usually set to -1.
1072 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1072 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1073 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1073 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1074
1074
1075 def pconvert(path):
1075 def pconvert(path):
1076 return '/'.join(splitpath(path))
1076 return '/'.join(splitpath(path))
1077
1077
1078 def localpath(path):
1078 def localpath(path):
1079 return path.replace('/', '\\')
1079 return path.replace('/', '\\')
1080
1080
1081 def normpath(path):
1081 def normpath(path):
1082 return pconvert(os.path.normpath(path))
1082 return pconvert(os.path.normpath(path))
1083
1083
1084 makelock = _makelock_file
1084 makelock = _makelock_file
1085 readlock = _readlock_file
1085 readlock = _readlock_file
1086
1086
1087 def samestat(s1, s2):
1087 def samestat(s1, s2):
1088 return False
1088 return False
1089
1089
1090 # A sequence of backslashes is special iff it precedes a double quote:
1090 # A sequence of backslashes is special iff it precedes a double quote:
1091 # - if there's an even number of backslashes, the double quote is not
1091 # - if there's an even number of backslashes, the double quote is not
1092 # quoted (i.e. it ends the quoted region)
1092 # quoted (i.e. it ends the quoted region)
1093 # - if there's an odd number of backslashes, the double quote is quoted
1093 # - if there's an odd number of backslashes, the double quote is quoted
1094 # - in both cases, every pair of backslashes is unquoted into a single
1094 # - in both cases, every pair of backslashes is unquoted into a single
1095 # backslash
1095 # backslash
1096 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1096 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1097 # So, to quote a string, we must surround it in double quotes, double
1097 # So, to quote a string, we must surround it in double quotes, double
1098 # the number of backslashes that preceed double quotes and add another
1098 # the number of backslashes that preceed double quotes and add another
1099 # backslash before every double quote (being careful with the double
1099 # backslash before every double quote (being careful with the double
1100 # quote we've appended to the end)
1100 # quote we've appended to the end)
1101 _quotere = None
1101 _quotere = None
1102 def shellquote(s):
1102 def shellquote(s):
1103 global _quotere
1103 global _quotere
1104 if _quotere is None:
1104 if _quotere is None:
1105 _quotere = re.compile(r'(\\*)("|\\$)')
1105 _quotere = re.compile(r'(\\*)("|\\$)')
1106 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1106 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1107
1107
1108 def quotecommand(cmd):
1108 def quotecommand(cmd):
1109 """Build a command string suitable for os.popen* calls."""
1109 """Build a command string suitable for os.popen* calls."""
1110 # The extra quotes are needed because popen* runs the command
1110 # The extra quotes are needed because popen* runs the command
1111 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1111 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1112 return '"' + cmd + '"'
1112 return '"' + cmd + '"'
1113
1113
1114 def popen(command, mode='r'):
1114 def popen(command, mode='r'):
1115 # Work around "popen spawned process may not write to stdout
1115 # Work around "popen spawned process may not write to stdout
1116 # under windows"
1116 # under windows"
1117 # http://bugs.python.org/issue1366
1117 # http://bugs.python.org/issue1366
1118 command += " 2> %s" % nulldev
1118 command += " 2> %s" % nulldev
1119 return os.popen(quotecommand(command), mode)
1119 return os.popen(quotecommand(command), mode)
1120
1120
1121 def explain_exit(code):
1121 def explain_exit(code):
1122 return _("exited with status %d") % code, code
1122 return _("exited with status %d") % code, code
1123
1123
1124 # if you change this stub into a real check, please try to implement the
1124 # if you change this stub into a real check, please try to implement the
1125 # username and groupname functions above, too.
1125 # username and groupname functions above, too.
1126 def isowner(fp, st=None):
1126 def isowner(fp, st=None):
1127 return True
1127 return True
1128
1128
1129 def find_in_path(name, path, default=None):
1129 def find_in_path(name, path, default=None):
1130 '''find name in search path. path can be string (will be split
1130 '''find name in search path. path can be string (will be split
1131 with os.pathsep), or iterable thing that returns strings. if name
1131 with os.pathsep), or iterable thing that returns strings. if name
1132 found, return path to name. else return default. name is looked up
1132 found, return path to name. else return default. name is looked up
1133 using cmd.exe rules, using PATHEXT.'''
1133 using cmd.exe rules, using PATHEXT.'''
1134 if isinstance(path, str):
1134 if isinstance(path, str):
1135 path = path.split(os.pathsep)
1135 path = path.split(os.pathsep)
1136
1136
1137 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1137 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1138 pathext = pathext.lower().split(os.pathsep)
1138 pathext = pathext.lower().split(os.pathsep)
1139 isexec = os.path.splitext(name)[1].lower() in pathext
1139 isexec = os.path.splitext(name)[1].lower() in pathext
1140
1140
1141 for p in path:
1141 for p in path:
1142 p_name = os.path.join(p, name)
1142 p_name = os.path.join(p, name)
1143
1143
1144 if isexec and os.path.exists(p_name):
1144 if isexec and os.path.exists(p_name):
1145 return p_name
1145 return p_name
1146
1146
1147 for ext in pathext:
1147 for ext in pathext:
1148 p_name_ext = p_name + ext
1148 p_name_ext = p_name + ext
1149 if os.path.exists(p_name_ext):
1149 if os.path.exists(p_name_ext):
1150 return p_name_ext
1150 return p_name_ext
1151 return default
1151 return default
1152
1152
1153 def set_signal_handler():
1153 def set_signal_handler():
1154 try:
1154 try:
1155 set_signal_handler_win32()
1155 set_signal_handler_win32()
1156 except NameError:
1156 except NameError:
1157 pass
1157 pass
1158
1158
1159 try:
1159 try:
1160 # override functions with win32 versions if possible
1160 # override functions with win32 versions if possible
1161 from util_win32 import *
1161 from util_win32 import *
1162 if not _is_win_9x():
1162 if not _is_win_9x():
1163 posixfile = posixfile_nt
1163 posixfile = posixfile_nt
1164 except ImportError:
1164 except ImportError:
1165 pass
1165 pass
1166
1166
1167 else:
1167 else:
1168 nulldev = '/dev/null'
1168 nulldev = '/dev/null'
1169
1169
1170 def rcfiles(path):
1170 def rcfiles(path):
1171 rcs = [os.path.join(path, 'hgrc')]
1171 rcs = [os.path.join(path, 'hgrc')]
1172 rcdir = os.path.join(path, 'hgrc.d')
1172 rcdir = os.path.join(path, 'hgrc.d')
1173 try:
1173 try:
1174 rcs.extend([os.path.join(rcdir, f)
1174 rcs.extend([os.path.join(rcdir, f)
1175 for f, kind in osutil.listdir(rcdir)
1175 for f, kind in osutil.listdir(rcdir)
1176 if f.endswith(".rc")])
1176 if f.endswith(".rc")])
1177 except OSError:
1177 except OSError:
1178 pass
1178 pass
1179 return rcs
1179 return rcs
1180
1180
1181 def system_rcpath():
1181 def system_rcpath():
1182 path = []
1182 path = []
1183 # old mod_python does not set sys.argv
1183 # old mod_python does not set sys.argv
1184 if len(getattr(sys, 'argv', [])) > 0:
1184 if len(getattr(sys, 'argv', [])) > 0:
1185 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1185 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1186 '/../etc/mercurial'))
1186 '/../etc/mercurial'))
1187 path.extend(rcfiles('/etc/mercurial'))
1187 path.extend(rcfiles('/etc/mercurial'))
1188 return path
1188 return path
1189
1189
1190 def user_rcpath():
1190 def user_rcpath():
1191 return [os.path.expanduser('~/.hgrc')]
1191 return [os.path.expanduser('~/.hgrc')]
1192
1192
1193 def parse_patch_output(output_line):
1193 def parse_patch_output(output_line):
1194 """parses the output produced by patch and returns the file name"""
1194 """parses the output produced by patch and returns the file name"""
1195 pf = output_line[14:]
1195 pf = output_line[14:]
1196 if os.sys.platform == 'OpenVMS':
1196 if os.sys.platform == 'OpenVMS':
1197 if pf[0] == '`':
1197 if pf[0] == '`':
1198 pf = pf[1:-1] # Remove the quotes
1198 pf = pf[1:-1] # Remove the quotes
1199 else:
1199 else:
1200 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1200 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1201 pf = pf[1:-1] # Remove the quotes
1201 pf = pf[1:-1] # Remove the quotes
1202 return pf
1202 return pf
1203
1203
1204 def sshargs(sshcmd, host, user, port):
1204 def sshargs(sshcmd, host, user, port):
1205 '''Build argument list for ssh'''
1205 '''Build argument list for ssh'''
1206 args = user and ("%s@%s" % (user, host)) or host
1206 args = user and ("%s@%s" % (user, host)) or host
1207 return port and ("%s -p %s" % (args, port)) or args
1207 return port and ("%s -p %s" % (args, port)) or args
1208
1208
1209 def is_exec(f):
1209 def is_exec(f):
1210 """check whether a file is executable"""
1210 """check whether a file is executable"""
1211 return (os.lstat(f).st_mode & 0100 != 0)
1211 return (os.lstat(f).st_mode & 0100 != 0)
1212
1212
1213 def set_flags(f, flags):
1213 def set_flags(f, flags):
1214 s = os.lstat(f).st_mode
1214 s = os.lstat(f).st_mode
1215 x = "x" in flags
1215 x = "x" in flags
1216 l = "l" in flags
1216 l = "l" in flags
1217 if l:
1217 if l:
1218 if not stat.S_ISLNK(s):
1218 if not stat.S_ISLNK(s):
1219 # switch file to link
1219 # switch file to link
1220 data = file(f).read()
1220 data = file(f).read()
1221 os.unlink(f)
1221 os.unlink(f)
1222 os.symlink(data, f)
1222 os.symlink(data, f)
1223 # no chmod needed at this point
1223 # no chmod needed at this point
1224 return
1224 return
1225 if stat.S_ISLNK(s):
1225 if stat.S_ISLNK(s):
1226 # switch link to file
1226 # switch link to file
1227 data = os.readlink(f)
1227 data = os.readlink(f)
1228 os.unlink(f)
1228 os.unlink(f)
1229 file(f, "w").write(data)
1229 file(f, "w").write(data)
1230 s = 0666 & ~_umask # avoid restatting for chmod
1230 s = 0666 & ~_umask # avoid restatting for chmod
1231
1231
1232 sx = s & 0100
1232 sx = s & 0100
1233 if x and not sx:
1233 if x and not sx:
1234 # Turn on +x for every +r bit when making a file executable
1234 # Turn on +x for every +r bit when making a file executable
1235 # and obey umask.
1235 # and obey umask.
1236 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1236 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1237 elif not x and sx:
1237 elif not x and sx:
1238 # Turn off all +x bits
1238 # Turn off all +x bits
1239 os.chmod(f, s & 0666)
1239 os.chmod(f, s & 0666)
1240
1240
1241 def set_binary(fd):
1241 def set_binary(fd):
1242 pass
1242 pass
1243
1243
1244 def pconvert(path):
1244 def pconvert(path):
1245 return path
1245 return path
1246
1246
1247 def localpath(path):
1247 def localpath(path):
1248 return path
1248 return path
1249
1249
1250 normpath = os.path.normpath
1250 normpath = os.path.normpath
1251 samestat = os.path.samestat
1251 samestat = os.path.samestat
1252
1252
1253 def makelock(info, pathname):
1253 def makelock(info, pathname):
1254 try:
1254 try:
1255 os.symlink(info, pathname)
1255 os.symlink(info, pathname)
1256 except OSError, why:
1256 except OSError, why:
1257 if why.errno == errno.EEXIST:
1257 if why.errno == errno.EEXIST:
1258 raise
1258 raise
1259 else:
1259 else:
1260 _makelock_file(info, pathname)
1260 _makelock_file(info, pathname)
1261
1261
1262 def readlock(pathname):
1262 def readlock(pathname):
1263 try:
1263 try:
1264 return os.readlink(pathname)
1264 return os.readlink(pathname)
1265 except OSError, why:
1265 except OSError, why:
1266 if why.errno in (errno.EINVAL, errno.ENOSYS):
1266 if why.errno in (errno.EINVAL, errno.ENOSYS):
1267 return _readlock_file(pathname)
1267 return _readlock_file(pathname)
1268 else:
1268 else:
1269 raise
1269 raise
1270
1270
1271 def shellquote(s):
1271 def shellquote(s):
1272 if os.sys.platform == 'OpenVMS':
1272 if os.sys.platform == 'OpenVMS':
1273 return '"%s"' % s
1273 return '"%s"' % s
1274 else:
1274 else:
1275 return "'%s'" % s.replace("'", "'\\''")
1275 return "'%s'" % s.replace("'", "'\\''")
1276
1276
1277 def quotecommand(cmd):
1277 def quotecommand(cmd):
1278 return cmd
1278 return cmd
1279
1279
1280 def popen(command, mode='r'):
1280 def popen(command, mode='r'):
1281 return os.popen(command, mode)
1281 return os.popen(command, mode)
1282
1282
1283 def testpid(pid):
1283 def testpid(pid):
1284 '''return False if pid dead, True if running or not sure'''
1284 '''return False if pid dead, True if running or not sure'''
1285 if os.sys.platform == 'OpenVMS':
1285 if os.sys.platform == 'OpenVMS':
1286 return True
1286 return True
1287 try:
1287 try:
1288 os.kill(pid, 0)
1288 os.kill(pid, 0)
1289 return True
1289 return True
1290 except OSError, inst:
1290 except OSError, inst:
1291 return inst.errno != errno.ESRCH
1291 return inst.errno != errno.ESRCH
1292
1292
1293 def explain_exit(code):
1293 def explain_exit(code):
1294 """return a 2-tuple (desc, code) describing a process's status"""
1294 """return a 2-tuple (desc, code) describing a process's status"""
1295 if os.WIFEXITED(code):
1295 if os.WIFEXITED(code):
1296 val = os.WEXITSTATUS(code)
1296 val = os.WEXITSTATUS(code)
1297 return _("exited with status %d") % val, val
1297 return _("exited with status %d") % val, val
1298 elif os.WIFSIGNALED(code):
1298 elif os.WIFSIGNALED(code):
1299 val = os.WTERMSIG(code)
1299 val = os.WTERMSIG(code)
1300 return _("killed by signal %d") % val, val
1300 return _("killed by signal %d") % val, val
1301 elif os.WIFSTOPPED(code):
1301 elif os.WIFSTOPPED(code):
1302 val = os.WSTOPSIG(code)
1302 val = os.WSTOPSIG(code)
1303 return _("stopped by signal %d") % val, val
1303 return _("stopped by signal %d") % val, val
1304 raise ValueError(_("invalid exit code"))
1304 raise ValueError(_("invalid exit code"))
1305
1305
1306 def isowner(fp, st=None):
1306 def isowner(fp, st=None):
1307 """Return True if the file object f belongs to the current user.
1307 """Return True if the file object f belongs to the current user.
1308
1308
1309 The return value of a util.fstat(f) may be passed as the st argument.
1309 The return value of a util.fstat(f) may be passed as the st argument.
1310 """
1310 """
1311 if st is None:
1311 if st is None:
1312 st = fstat(fp)
1312 st = fstat(fp)
1313 return st.st_uid == os.getuid()
1313 return st.st_uid == os.getuid()
1314
1314
1315 def find_in_path(name, path, default=None):
1315 def find_in_path(name, path, default=None):
1316 '''find name in search path. path can be string (will be split
1316 '''find name in search path. path can be string (will be split
1317 with os.pathsep), or iterable thing that returns strings. if name
1317 with os.pathsep), or iterable thing that returns strings. if name
1318 found, return path to name. else return default.'''
1318 found, return path to name. else return default.'''
1319 if isinstance(path, str):
1319 if isinstance(path, str):
1320 path = path.split(os.pathsep)
1320 path = path.split(os.pathsep)
1321 for p in path:
1321 for p in path:
1322 p_name = os.path.join(p, name)
1322 p_name = os.path.join(p, name)
1323 if os.path.exists(p_name):
1323 if os.path.exists(p_name):
1324 return p_name
1324 return p_name
1325 return default
1325 return default
1326
1326
1327 def set_signal_handler():
1327 def set_signal_handler():
1328 pass
1328 pass
1329
1329
1330 def find_exe(name, default=None):
1330 def find_exe(name, default=None):
1331 '''find path of an executable.
1331 '''find path of an executable.
1332 if name contains a path component, return it as is. otherwise,
1332 if name contains a path component, return it as is. otherwise,
1333 use normal executable search path.'''
1333 use normal executable search path.'''
1334
1334
1335 if os.sep in name or sys.platform == 'OpenVMS':
1335 if os.sep in name or sys.platform == 'OpenVMS':
1336 # don't check the executable bit. if the file isn't
1336 # don't check the executable bit. if the file isn't
1337 # executable, whoever tries to actually run it will give a
1337 # executable, whoever tries to actually run it will give a
1338 # much more useful error message.
1338 # much more useful error message.
1339 return name
1339 return name
1340 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1340 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1341
1341
1342 def _buildencodefun():
1342 def _buildencodefun():
1343 e = '_'
1343 e = '_'
1344 win_reserved = [ord(x) for x in '\\:*?"<>|']
1344 win_reserved = [ord(x) for x in '\\:*?"<>|']
1345 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1345 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1346 for x in (range(32) + range(126, 256) + win_reserved):
1346 for x in (range(32) + range(126, 256) + win_reserved):
1347 cmap[chr(x)] = "~%02x" % x
1347 cmap[chr(x)] = "~%02x" % x
1348 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1348 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1349 cmap[chr(x)] = e + chr(x).lower()
1349 cmap[chr(x)] = e + chr(x).lower()
1350 dmap = {}
1350 dmap = {}
1351 for k, v in cmap.iteritems():
1351 for k, v in cmap.iteritems():
1352 dmap[v] = k
1352 dmap[v] = k
1353 def decode(s):
1353 def decode(s):
1354 i = 0
1354 i = 0
1355 while i < len(s):
1355 while i < len(s):
1356 for l in xrange(1, 4):
1356 for l in xrange(1, 4):
1357 try:
1357 try:
1358 yield dmap[s[i:i+l]]
1358 yield dmap[s[i:i+l]]
1359 i += l
1359 i += l
1360 break
1360 break
1361 except KeyError:
1361 except KeyError:
1362 pass
1362 pass
1363 else:
1363 else:
1364 raise KeyError
1364 raise KeyError
1365 return (lambda s: "".join([cmap[c] for c in s]),
1365 return (lambda s: "".join([cmap[c] for c in s]),
1366 lambda s: "".join(list(decode(s))))
1366 lambda s: "".join(list(decode(s))))
1367
1367
1368 encodefilename, decodefilename = _buildencodefun()
1368 encodefilename, decodefilename = _buildencodefun()
1369
1369
1370 def encodedopener(openerfn, fn):
1370 def encodedopener(openerfn, fn):
1371 def o(path, *args, **kw):
1371 def o(path, *args, **kw):
1372 return openerfn(fn(path), *args, **kw)
1372 return openerfn(fn(path), *args, **kw)
1373 return o
1373 return o
1374
1374
1375 def mktempcopy(name, emptyok=False, createmode=None):
1375 def mktempcopy(name, emptyok=False, createmode=None):
1376 """Create a temporary file with the same contents from name
1376 """Create a temporary file with the same contents from name
1377
1377
1378 The permission bits are copied from the original file.
1378 The permission bits are copied from the original file.
1379
1379
1380 If the temporary file is going to be truncated immediately, you
1380 If the temporary file is going to be truncated immediately, you
1381 can use emptyok=True as an optimization.
1381 can use emptyok=True as an optimization.
1382
1382
1383 Returns the name of the temporary file.
1383 Returns the name of the temporary file.
1384 """
1384 """
1385 d, fn = os.path.split(name)
1385 d, fn = os.path.split(name)
1386 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1386 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1387 os.close(fd)
1387 os.close(fd)
1388 # Temporary files are created with mode 0600, which is usually not
1388 # Temporary files are created with mode 0600, which is usually not
1389 # what we want. If the original file already exists, just copy
1389 # what we want. If the original file already exists, just copy
1390 # its mode. Otherwise, manually obey umask.
1390 # its mode. Otherwise, manually obey umask.
1391 try:
1391 try:
1392 st_mode = os.lstat(name).st_mode & 0777
1392 st_mode = os.lstat(name).st_mode & 0777
1393 except OSError, inst:
1393 except OSError, inst:
1394 if inst.errno != errno.ENOENT:
1394 if inst.errno != errno.ENOENT:
1395 raise
1395 raise
1396 st_mode = createmode
1396 st_mode = createmode
1397 if st_mode is None:
1397 if st_mode is None:
1398 st_mode = ~_umask
1398 st_mode = ~_umask
1399 st_mode &= 0666
1399 st_mode &= 0666
1400 os.chmod(temp, st_mode)
1400 os.chmod(temp, st_mode)
1401 if emptyok:
1401 if emptyok:
1402 return temp
1402 return temp
1403 try:
1403 try:
1404 try:
1404 try:
1405 ifp = posixfile(name, "rb")
1405 ifp = posixfile(name, "rb")
1406 except IOError, inst:
1406 except IOError, inst:
1407 if inst.errno == errno.ENOENT:
1407 if inst.errno == errno.ENOENT:
1408 return temp
1408 return temp
1409 if not getattr(inst, 'filename', None):
1409 if not getattr(inst, 'filename', None):
1410 inst.filename = name
1410 inst.filename = name
1411 raise
1411 raise
1412 ofp = posixfile(temp, "wb")
1412 ofp = posixfile(temp, "wb")
1413 for chunk in filechunkiter(ifp):
1413 for chunk in filechunkiter(ifp):
1414 ofp.write(chunk)
1414 ofp.write(chunk)
1415 ifp.close()
1415 ifp.close()
1416 ofp.close()
1416 ofp.close()
1417 except:
1417 except:
1418 try: os.unlink(temp)
1418 try: os.unlink(temp)
1419 except: pass
1419 except: pass
1420 raise
1420 raise
1421 return temp
1421 return temp
1422
1422
1423 class atomictempfile(posixfile):
1423 class atomictempfile(posixfile):
1424 """file-like object that atomically updates a file
1424 """file-like object that atomically updates a file
1425
1425
1426 All writes will be redirected to a temporary copy of the original
1426 All writes will be redirected to a temporary copy of the original
1427 file. When rename is called, the copy is renamed to the original
1427 file. When rename is called, the copy is renamed to the original
1428 name, making the changes visible.
1428 name, making the changes visible.
1429 """
1429 """
1430 def __init__(self, name, mode, createmode):
1430 def __init__(self, name, mode, createmode):
1431 self.__name = name
1431 self.__name = name
1432 self.temp = mktempcopy(name, emptyok=('w' in mode),
1432 self.temp = mktempcopy(name, emptyok=('w' in mode),
1433 createmode=createmode)
1433 createmode=createmode)
1434 posixfile.__init__(self, self.temp, mode)
1434 posixfile.__init__(self, self.temp, mode)
1435
1435
1436 def rename(self):
1436 def rename(self):
1437 if not self.closed:
1437 if not self.closed:
1438 posixfile.close(self)
1438 posixfile.close(self)
1439 rename(self.temp, localpath(self.__name))
1439 rename(self.temp, localpath(self.__name))
1440
1440
1441 def __del__(self):
1441 def __del__(self):
1442 if not self.closed:
1442 if not self.closed:
1443 try:
1443 try:
1444 os.unlink(self.temp)
1444 os.unlink(self.temp)
1445 except: pass
1445 except: pass
1446 posixfile.close(self)
1446 posixfile.close(self)
1447
1447
1448 def makedirs(name, mode=None):
1448 def makedirs(name, mode=None):
1449 """recursive directory creation with parent mode inheritance"""
1449 """recursive directory creation with parent mode inheritance"""
1450 try:
1450 try:
1451 os.mkdir(name)
1451 os.mkdir(name)
1452 if mode is not None:
1452 if mode is not None:
1453 os.chmod(name, mode)
1453 os.chmod(name, mode)
1454 return
1454 return
1455 except OSError, err:
1455 except OSError, err:
1456 if err.errno == errno.EEXIST:
1456 if err.errno == errno.EEXIST:
1457 return
1457 return
1458 if err.errno != errno.ENOENT:
1458 if err.errno != errno.ENOENT:
1459 raise
1459 raise
1460 parent = os.path.abspath(os.path.dirname(name))
1460 parent = os.path.abspath(os.path.dirname(name))
1461 makedirs(parent, mode)
1461 makedirs(parent, mode)
1462 makedirs(name, mode)
1462 makedirs(name, mode)
1463
1463
1464 class opener(object):
1464 class opener(object):
1465 """Open files relative to a base directory
1465 """Open files relative to a base directory
1466
1466
1467 This class is used to hide the details of COW semantics and
1467 This class is used to hide the details of COW semantics and
1468 remote file access from higher level code.
1468 remote file access from higher level code.
1469 """
1469 """
1470 def __init__(self, base, audit=True):
1470 def __init__(self, base, audit=True):
1471 self.base = base
1471 self.base = base
1472 if audit:
1472 if audit:
1473 self.audit_path = path_auditor(base)
1473 self.audit_path = path_auditor(base)
1474 else:
1474 else:
1475 self.audit_path = always
1475 self.audit_path = always
1476 self.createmode = None
1476 self.createmode = None
1477
1477
1478 def __getattr__(self, name):
1478 def __getattr__(self, name):
1479 if name == '_can_symlink':
1479 if name == '_can_symlink':
1480 self._can_symlink = checklink(self.base)
1480 self._can_symlink = checklink(self.base)
1481 return self._can_symlink
1481 return self._can_symlink
1482 raise AttributeError(name)
1482 raise AttributeError(name)
1483
1483
1484 def _fixfilemode(self, name):
1484 def _fixfilemode(self, name):
1485 if self.createmode is None:
1485 if self.createmode is None:
1486 return
1486 return
1487 os.chmod(name, self.createmode & 0666)
1487 os.chmod(name, self.createmode & 0666)
1488
1488
1489 def __call__(self, path, mode="r", text=False, atomictemp=False):
1489 def __call__(self, path, mode="r", text=False, atomictemp=False):
1490 self.audit_path(path)
1490 self.audit_path(path)
1491 f = os.path.join(self.base, path)
1491 f = os.path.join(self.base, path)
1492
1492
1493 if not text and "b" not in mode:
1493 if not text and "b" not in mode:
1494 mode += "b" # for that other OS
1494 mode += "b" # for that other OS
1495
1495
1496 nlink = -1
1496 nlink = -1
1497 if mode[0] != "r":
1497 if mode[0] != "r":
1498 try:
1498 try:
1499 nlink = nlinks(f)
1499 nlink = nlinks(f)
1500 except OSError:
1500 except OSError:
1501 nlink = 0
1501 nlink = 0
1502 d = os.path.dirname(f)
1502 d = os.path.dirname(f)
1503 if not os.path.isdir(d):
1503 if not os.path.isdir(d):
1504 makedirs(d, self.createmode)
1504 makedirs(d, self.createmode)
1505 if atomictemp:
1505 if atomictemp:
1506 return atomictempfile(f, mode, self.createmode)
1506 return atomictempfile(f, mode, self.createmode)
1507 if nlink > 1:
1507 if nlink > 1:
1508 rename(mktempcopy(f), f)
1508 rename(mktempcopy(f), f)
1509 fp = posixfile(f, mode)
1509 fp = posixfile(f, mode)
1510 if nlink == 0:
1510 if nlink == 0:
1511 self._fixfilemode(f)
1511 self._fixfilemode(f)
1512 return fp
1512 return fp
1513
1513
1514 def symlink(self, src, dst):
1514 def symlink(self, src, dst):
1515 self.audit_path(dst)
1515 self.audit_path(dst)
1516 linkname = os.path.join(self.base, dst)
1516 linkname = os.path.join(self.base, dst)
1517 try:
1517 try:
1518 os.unlink(linkname)
1518 os.unlink(linkname)
1519 except OSError:
1519 except OSError:
1520 pass
1520 pass
1521
1521
1522 dirname = os.path.dirname(linkname)
1522 dirname = os.path.dirname(linkname)
1523 if not os.path.exists(dirname):
1523 if not os.path.exists(dirname):
1524 makedirs(dirname, self.createmode)
1524 makedirs(dirname, self.createmode)
1525
1525
1526 if self._can_symlink:
1526 if self._can_symlink:
1527 try:
1527 try:
1528 os.symlink(src, linkname)
1528 os.symlink(src, linkname)
1529 except OSError, err:
1529 except OSError, err:
1530 raise OSError(err.errno, _('could not symlink to %r: %s') %
1530 raise OSError(err.errno, _('could not symlink to %r: %s') %
1531 (src, err.strerror), linkname)
1531 (src, err.strerror), linkname)
1532 else:
1532 else:
1533 f = self(dst, "w")
1533 f = self(dst, "w")
1534 f.write(src)
1534 f.write(src)
1535 f.close()
1535 f.close()
1536 self._fixfilemode(dst)
1536 self._fixfilemode(dst)
1537
1537
1538 class chunkbuffer(object):
1538 class chunkbuffer(object):
1539 """Allow arbitrary sized chunks of data to be efficiently read from an
1539 """Allow arbitrary sized chunks of data to be efficiently read from an
1540 iterator over chunks of arbitrary size."""
1540 iterator over chunks of arbitrary size."""
1541
1541
1542 def __init__(self, in_iter):
1542 def __init__(self, in_iter):
1543 """in_iter is the iterator that's iterating over the input chunks.
1543 """in_iter is the iterator that's iterating over the input chunks.
1544 targetsize is how big a buffer to try to maintain."""
1544 targetsize is how big a buffer to try to maintain."""
1545 self.iter = iter(in_iter)
1545 self.iter = iter(in_iter)
1546 self.buf = ''
1546 self.buf = ''
1547 self.targetsize = 2**16
1547 self.targetsize = 2**16
1548
1548
1549 def read(self, l):
1549 def read(self, l):
1550 """Read L bytes of data from the iterator of chunks of data.
1550 """Read L bytes of data from the iterator of chunks of data.
1551 Returns less than L bytes if the iterator runs dry."""
1551 Returns less than L bytes if the iterator runs dry."""
1552 if l > len(self.buf) and self.iter:
1552 if l > len(self.buf) and self.iter:
1553 # Clamp to a multiple of self.targetsize
1553 # Clamp to a multiple of self.targetsize
1554 targetsize = max(l, self.targetsize)
1554 targetsize = max(l, self.targetsize)
1555 collector = cStringIO.StringIO()
1555 collector = cStringIO.StringIO()
1556 collector.write(self.buf)
1556 collector.write(self.buf)
1557 collected = len(self.buf)
1557 collected = len(self.buf)
1558 for chunk in self.iter:
1558 for chunk in self.iter:
1559 collector.write(chunk)
1559 collector.write(chunk)
1560 collected += len(chunk)
1560 collected += len(chunk)
1561 if collected >= targetsize:
1561 if collected >= targetsize:
1562 break
1562 break
1563 if collected < targetsize:
1563 if collected < targetsize:
1564 self.iter = False
1564 self.iter = False
1565 self.buf = collector.getvalue()
1565 self.buf = collector.getvalue()
1566 if len(self.buf) == l:
1566 if len(self.buf) == l:
1567 s, self.buf = str(self.buf), ''
1567 s, self.buf = str(self.buf), ''
1568 else:
1568 else:
1569 s, self.buf = self.buf[:l], buffer(self.buf, l)
1569 s, self.buf = self.buf[:l], buffer(self.buf, l)
1570 return s
1570 return s
1571
1571
1572 def filechunkiter(f, size=65536, limit=None):
1572 def filechunkiter(f, size=65536, limit=None):
1573 """Create a generator that produces the data in the file size
1573 """Create a generator that produces the data in the file size
1574 (default 65536) bytes at a time, up to optional limit (default is
1574 (default 65536) bytes at a time, up to optional limit (default is
1575 to read all data). Chunks may be less than size bytes if the
1575 to read all data). Chunks may be less than size bytes if the
1576 chunk is the last chunk in the file, or the file is a socket or
1576 chunk is the last chunk in the file, or the file is a socket or
1577 some other type of file that sometimes reads less data than is
1577 some other type of file that sometimes reads less data than is
1578 requested."""
1578 requested."""
1579 assert size >= 0
1579 assert size >= 0
1580 assert limit is None or limit >= 0
1580 assert limit is None or limit >= 0
1581 while True:
1581 while True:
1582 if limit is None: nbytes = size
1582 if limit is None: nbytes = size
1583 else: nbytes = min(limit, size)
1583 else: nbytes = min(limit, size)
1584 s = nbytes and f.read(nbytes)
1584 s = nbytes and f.read(nbytes)
1585 if not s: break
1585 if not s: break
1586 if limit: limit -= len(s)
1586 if limit: limit -= len(s)
1587 yield s
1587 yield s
1588
1588
1589 def makedate():
1589 def makedate():
1590 lt = time.localtime()
1590 lt = time.localtime()
1591 if lt[8] == 1 and time.daylight:
1591 if lt[8] == 1 and time.daylight:
1592 tz = time.altzone
1592 tz = time.altzone
1593 else:
1593 else:
1594 tz = time.timezone
1594 tz = time.timezone
1595 return time.mktime(lt), tz
1595 return time.mktime(lt), tz
1596
1596
1597 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1597 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1598 """represent a (unixtime, offset) tuple as a localized time.
1598 """represent a (unixtime, offset) tuple as a localized time.
1599 unixtime is seconds since the epoch, and offset is the time zone's
1599 unixtime is seconds since the epoch, and offset is the time zone's
1600 number of seconds away from UTC. if timezone is false, do not
1600 number of seconds away from UTC. if timezone is false, do not
1601 append time zone to string."""
1601 append time zone to string."""
1602 t, tz = date or makedate()
1602 t, tz = date or makedate()
1603 if "%1" in format or "%2" in format:
1603 if "%1" in format or "%2" in format:
1604 sign = (tz > 0) and "-" or "+"
1604 sign = (tz > 0) and "-" or "+"
1605 minutes = abs(tz) / 60
1605 minutes = abs(tz) / 60
1606 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1606 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1607 format = format.replace("%2", "%02d" % (minutes % 60))
1607 format = format.replace("%2", "%02d" % (minutes % 60))
1608 s = time.strftime(format, time.gmtime(float(t) - tz))
1608 s = time.strftime(format, time.gmtime(float(t) - tz))
1609 return s
1609 return s
1610
1610
1611 def shortdate(date=None):
1611 def shortdate(date=None):
1612 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1612 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1613 return datestr(date, format='%Y-%m-%d')
1613 return datestr(date, format='%Y-%m-%d')
1614
1614
1615 def strdate(string, format, defaults=[]):
1615 def strdate(string, format, defaults=[]):
1616 """parse a localized time string and return a (unixtime, offset) tuple.
1616 """parse a localized time string and return a (unixtime, offset) tuple.
1617 if the string cannot be parsed, ValueError is raised."""
1617 if the string cannot be parsed, ValueError is raised."""
1618 def timezone(string):
1618 def timezone(string):
1619 tz = string.split()[-1]
1619 tz = string.split()[-1]
1620 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1620 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1621 sign = (tz[0] == "+") and 1 or -1
1621 sign = (tz[0] == "+") and 1 or -1
1622 hours = int(tz[1:3])
1622 hours = int(tz[1:3])
1623 minutes = int(tz[3:5])
1623 minutes = int(tz[3:5])
1624 return -sign * (hours * 60 + minutes) * 60
1624 return -sign * (hours * 60 + minutes) * 60
1625 if tz == "GMT" or tz == "UTC":
1625 if tz == "GMT" or tz == "UTC":
1626 return 0
1626 return 0
1627 return None
1627 return None
1628
1628
1629 # NOTE: unixtime = localunixtime + offset
1629 # NOTE: unixtime = localunixtime + offset
1630 offset, date = timezone(string), string
1630 offset, date = timezone(string), string
1631 if offset != None:
1631 if offset != None:
1632 date = " ".join(string.split()[:-1])
1632 date = " ".join(string.split()[:-1])
1633
1633
1634 # add missing elements from defaults
1634 # add missing elements from defaults
1635 for part in defaults:
1635 for part in defaults:
1636 found = [True for p in part if ("%"+p) in format]
1636 found = [True for p in part if ("%"+p) in format]
1637 if not found:
1637 if not found:
1638 date += "@" + defaults[part]
1638 date += "@" + defaults[part]
1639 format += "@%" + part[0]
1639 format += "@%" + part[0]
1640
1640
1641 timetuple = time.strptime(date, format)
1641 timetuple = time.strptime(date, format)
1642 localunixtime = int(calendar.timegm(timetuple))
1642 localunixtime = int(calendar.timegm(timetuple))
1643 if offset is None:
1643 if offset is None:
1644 # local timezone
1644 # local timezone
1645 unixtime = int(time.mktime(timetuple))
1645 unixtime = int(time.mktime(timetuple))
1646 offset = unixtime - localunixtime
1646 offset = unixtime - localunixtime
1647 else:
1647 else:
1648 unixtime = localunixtime + offset
1648 unixtime = localunixtime + offset
1649 return unixtime, offset
1649 return unixtime, offset
1650
1650
1651 def parsedate(date, formats=None, defaults=None):
1651 def parsedate(date, formats=None, defaults=None):
1652 """parse a localized date/time string and return a (unixtime, offset) tuple.
1652 """parse a localized date/time string and return a (unixtime, offset) tuple.
1653
1653
1654 The date may be a "unixtime offset" string or in one of the specified
1654 The date may be a "unixtime offset" string or in one of the specified
1655 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1655 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1656 """
1656 """
1657 if not date:
1657 if not date:
1658 return 0, 0
1658 return 0, 0
1659 if isinstance(date, tuple) and len(date) == 2:
1659 if isinstance(date, tuple) and len(date) == 2:
1660 return date
1660 return date
1661 if not formats:
1661 if not formats:
1662 formats = defaultdateformats
1662 formats = defaultdateformats
1663 date = date.strip()
1663 date = date.strip()
1664 try:
1664 try:
1665 when, offset = map(int, date.split(' '))
1665 when, offset = map(int, date.split(' '))
1666 except ValueError:
1666 except ValueError:
1667 # fill out defaults
1667 # fill out defaults
1668 if not defaults:
1668 if not defaults:
1669 defaults = {}
1669 defaults = {}
1670 now = makedate()
1670 now = makedate()
1671 for part in "d mb yY HI M S".split():
1671 for part in "d mb yY HI M S".split():
1672 if part not in defaults:
1672 if part not in defaults:
1673 if part[0] in "HMS":
1673 if part[0] in "HMS":
1674 defaults[part] = "00"
1674 defaults[part] = "00"
1675 else:
1675 else:
1676 defaults[part] = datestr(now, "%" + part[0])
1676 defaults[part] = datestr(now, "%" + part[0])
1677
1677
1678 for format in formats:
1678 for format in formats:
1679 try:
1679 try:
1680 when, offset = strdate(date, format, defaults)
1680 when, offset = strdate(date, format, defaults)
1681 except (ValueError, OverflowError):
1681 except (ValueError, OverflowError):
1682 pass
1682 pass
1683 else:
1683 else:
1684 break
1684 break
1685 else:
1685 else:
1686 raise Abort(_('invalid date: %r ') % date)
1686 raise Abort(_('invalid date: %r ') % date)
1687 # validate explicit (probably user-specified) date and
1687 # validate explicit (probably user-specified) date and
1688 # time zone offset. values must fit in signed 32 bits for
1688 # time zone offset. values must fit in signed 32 bits for
1689 # current 32-bit linux runtimes. timezones go from UTC-12
1689 # current 32-bit linux runtimes. timezones go from UTC-12
1690 # to UTC+14
1690 # to UTC+14
1691 if abs(when) > 0x7fffffff:
1691 if abs(when) > 0x7fffffff:
1692 raise Abort(_('date exceeds 32 bits: %d') % when)
1692 raise Abort(_('date exceeds 32 bits: %d') % when)
1693 if offset < -50400 or offset > 43200:
1693 if offset < -50400 or offset > 43200:
1694 raise Abort(_('impossible time zone offset: %d') % offset)
1694 raise Abort(_('impossible time zone offset: %d') % offset)
1695 return when, offset
1695 return when, offset
1696
1696
1697 def matchdate(date):
1697 def matchdate(date):
1698 """Return a function that matches a given date match specifier
1698 """Return a function that matches a given date match specifier
1699
1699
1700 Formats include:
1700 Formats include:
1701
1701
1702 '{date}' match a given date to the accuracy provided
1702 '{date}' match a given date to the accuracy provided
1703
1703
1704 '<{date}' on or before a given date
1704 '<{date}' on or before a given date
1705
1705
1706 '>{date}' on or after a given date
1706 '>{date}' on or after a given date
1707
1707
1708 """
1708 """
1709
1709
1710 def lower(date):
1710 def lower(date):
1711 d = dict(mb="1", d="1")
1711 d = dict(mb="1", d="1")
1712 return parsedate(date, extendeddateformats, d)[0]
1712 return parsedate(date, extendeddateformats, d)[0]
1713
1713
1714 def upper(date):
1714 def upper(date):
1715 d = dict(mb="12", HI="23", M="59", S="59")
1715 d = dict(mb="12", HI="23", M="59", S="59")
1716 for days in "31 30 29".split():
1716 for days in "31 30 29".split():
1717 try:
1717 try:
1718 d["d"] = days
1718 d["d"] = days
1719 return parsedate(date, extendeddateformats, d)[0]
1719 return parsedate(date, extendeddateformats, d)[0]
1720 except:
1720 except:
1721 pass
1721 pass
1722 d["d"] = "28"
1722 d["d"] = "28"
1723 return parsedate(date, extendeddateformats, d)[0]
1723 return parsedate(date, extendeddateformats, d)[0]
1724
1724
1725 if date[0] == "<":
1725 if date[0] == "<":
1726 when = upper(date[1:])
1726 when = upper(date[1:])
1727 return lambda x: x <= when
1727 return lambda x: x <= when
1728 elif date[0] == ">":
1728 elif date[0] == ">":
1729 when = lower(date[1:])
1729 when = lower(date[1:])
1730 return lambda x: x >= when
1730 return lambda x: x >= when
1731 elif date[0] == "-":
1731 elif date[0] == "-":
1732 try:
1732 try:
1733 days = int(date[1:])
1733 days = int(date[1:])
1734 except ValueError:
1734 except ValueError:
1735 raise Abort(_("invalid day spec: %s") % date[1:])
1735 raise Abort(_("invalid day spec: %s") % date[1:])
1736 when = makedate()[0] - days * 3600 * 24
1736 when = makedate()[0] - days * 3600 * 24
1737 return lambda x: x >= when
1737 return lambda x: x >= when
1738 elif " to " in date:
1738 elif " to " in date:
1739 a, b = date.split(" to ")
1739 a, b = date.split(" to ")
1740 start, stop = lower(a), upper(b)
1740 start, stop = lower(a), upper(b)
1741 return lambda x: x >= start and x <= stop
1741 return lambda x: x >= start and x <= stop
1742 else:
1742 else:
1743 start, stop = lower(date), upper(date)
1743 start, stop = lower(date), upper(date)
1744 return lambda x: x >= start and x <= stop
1744 return lambda x: x >= start and x <= stop
1745
1745
1746 def shortuser(user):
1746 def shortuser(user):
1747 """Return a short representation of a user name or email address."""
1747 """Return a short representation of a user name or email address."""
1748 f = user.find('@')
1748 f = user.find('@')
1749 if f >= 0:
1749 if f >= 0:
1750 user = user[:f]
1750 user = user[:f]
1751 f = user.find('<')
1751 f = user.find('<')
1752 if f >= 0:
1752 if f >= 0:
1753 user = user[f+1:]
1753 user = user[f+1:]
1754 f = user.find(' ')
1754 f = user.find(' ')
1755 if f >= 0:
1755 if f >= 0:
1756 user = user[:f]
1756 user = user[:f]
1757 f = user.find('.')
1757 f = user.find('.')
1758 if f >= 0:
1758 if f >= 0:
1759 user = user[:f]
1759 user = user[:f]
1760 return user
1760 return user
1761
1761
1762 def email(author):
1762 def email(author):
1763 '''get email of author.'''
1763 '''get email of author.'''
1764 r = author.find('>')
1764 r = author.find('>')
1765 if r == -1: r = None
1765 if r == -1: r = None
1766 return author[author.find('<')+1:r]
1766 return author[author.find('<')+1:r]
1767
1767
1768 def ellipsis(text, maxlength=400):
1768 def ellipsis(text, maxlength=400):
1769 """Trim string to at most maxlength (default: 400) characters."""
1769 """Trim string to at most maxlength (default: 400) characters."""
1770 if len(text) <= maxlength:
1770 if len(text) <= maxlength:
1771 return text
1771 return text
1772 else:
1772 else:
1773 return "%s..." % (text[:maxlength-3])
1773 return "%s..." % (text[:maxlength-3])
1774
1774
1775 def walkrepos(path, followsym=False, seen_dirs=None):
1775 def walkrepos(path, followsym=False, seen_dirs=None):
1776 '''yield every hg repository under path, recursively.'''
1776 '''yield every hg repository under path, recursively.'''
1777 def errhandler(err):
1777 def errhandler(err):
1778 if err.filename == path:
1778 if err.filename == path:
1779 raise err
1779 raise err
1780 if followsym and hasattr(os.path, 'samestat'):
1780 if followsym and hasattr(os.path, 'samestat'):
1781 def _add_dir_if_not_there(dirlst, dirname):
1781 def _add_dir_if_not_there(dirlst, dirname):
1782 match = False
1782 match = False
1783 samestat = os.path.samestat
1783 samestat = os.path.samestat
1784 dirstat = os.stat(dirname)
1784 dirstat = os.stat(dirname)
1785 for lstdirstat in dirlst:
1785 for lstdirstat in dirlst:
1786 if samestat(dirstat, lstdirstat):
1786 if samestat(dirstat, lstdirstat):
1787 match = True
1787 match = True
1788 break
1788 break
1789 if not match:
1789 if not match:
1790 dirlst.append(dirstat)
1790 dirlst.append(dirstat)
1791 return not match
1791 return not match
1792 else:
1792 else:
1793 followsym = False
1793 followsym = False
1794
1794
1795 if (seen_dirs is None) and followsym:
1795 if (seen_dirs is None) and followsym:
1796 seen_dirs = []
1796 seen_dirs = []
1797 _add_dir_if_not_there(seen_dirs, path)
1797 _add_dir_if_not_there(seen_dirs, path)
1798 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1798 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1799 if '.hg' in dirs:
1799 if '.hg' in dirs:
1800 dirs[:] = [] # don't descend further
1800 dirs[:] = [] # don't descend further
1801 yield root # found a repository
1801 yield root # found a repository
1802 qroot = os.path.join(root, '.hg', 'patches')
1802 qroot = os.path.join(root, '.hg', 'patches')
1803 if os.path.isdir(os.path.join(qroot, '.hg')):
1803 if os.path.isdir(os.path.join(qroot, '.hg')):
1804 yield qroot # we have a patch queue repo here
1804 yield qroot # we have a patch queue repo here
1805 elif followsym:
1805 elif followsym:
1806 newdirs = []
1806 newdirs = []
1807 for d in dirs:
1807 for d in dirs:
1808 fname = os.path.join(root, d)
1808 fname = os.path.join(root, d)
1809 if _add_dir_if_not_there(seen_dirs, fname):
1809 if _add_dir_if_not_there(seen_dirs, fname):
1810 if os.path.islink(fname):
1810 if os.path.islink(fname):
1811 for hgname in walkrepos(fname, True, seen_dirs):
1811 for hgname in walkrepos(fname, True, seen_dirs):
1812 yield hgname
1812 yield hgname
1813 else:
1813 else:
1814 newdirs.append(d)
1814 newdirs.append(d)
1815 dirs[:] = newdirs
1815 dirs[:] = newdirs
1816
1816
1817 _rcpath = None
1817 _rcpath = None
1818
1818
1819 def os_rcpath():
1819 def os_rcpath():
1820 '''return default os-specific hgrc search path'''
1820 '''return default os-specific hgrc search path'''
1821 path = system_rcpath()
1821 path = system_rcpath()
1822 path.extend(user_rcpath())
1822 path.extend(user_rcpath())
1823 path = [os.path.normpath(f) for f in path]
1823 path = [os.path.normpath(f) for f in path]
1824 return path
1824 return path
1825
1825
1826 def rcpath():
1826 def rcpath():
1827 '''return hgrc search path. if env var HGRCPATH is set, use it.
1827 '''return hgrc search path. if env var HGRCPATH is set, use it.
1828 for each item in path, if directory, use files ending in .rc,
1828 for each item in path, if directory, use files ending in .rc,
1829 else use item.
1829 else use item.
1830 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1830 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1831 if no HGRCPATH, use default os-specific path.'''
1831 if no HGRCPATH, use default os-specific path.'''
1832 global _rcpath
1832 global _rcpath
1833 if _rcpath is None:
1833 if _rcpath is None:
1834 if 'HGRCPATH' in os.environ:
1834 if 'HGRCPATH' in os.environ:
1835 _rcpath = []
1835 _rcpath = []
1836 for p in os.environ['HGRCPATH'].split(os.pathsep):
1836 for p in os.environ['HGRCPATH'].split(os.pathsep):
1837 if not p: continue
1837 if not p: continue
1838 if os.path.isdir(p):
1838 if os.path.isdir(p):
1839 for f, kind in osutil.listdir(p):
1839 for f, kind in osutil.listdir(p):
1840 if f.endswith('.rc'):
1840 if f.endswith('.rc'):
1841 _rcpath.append(os.path.join(p, f))
1841 _rcpath.append(os.path.join(p, f))
1842 else:
1842 else:
1843 _rcpath.append(p)
1843 _rcpath.append(p)
1844 else:
1844 else:
1845 _rcpath = os_rcpath()
1845 _rcpath = os_rcpath()
1846 return _rcpath
1846 return _rcpath
1847
1847
1848 def bytecount(nbytes):
1848 def bytecount(nbytes):
1849 '''return byte count formatted as readable string, with units'''
1849 '''return byte count formatted as readable string, with units'''
1850
1850
1851 units = (
1851 units = (
1852 (100, 1<<30, _('%.0f GB')),
1852 (100, 1<<30, _('%.0f GB')),
1853 (10, 1<<30, _('%.1f GB')),
1853 (10, 1<<30, _('%.1f GB')),
1854 (1, 1<<30, _('%.2f GB')),
1854 (1, 1<<30, _('%.2f GB')),
1855 (100, 1<<20, _('%.0f MB')),
1855 (100, 1<<20, _('%.0f MB')),
1856 (10, 1<<20, _('%.1f MB')),
1856 (10, 1<<20, _('%.1f MB')),
1857 (1, 1<<20, _('%.2f MB')),
1857 (1, 1<<20, _('%.2f MB')),
1858 (100, 1<<10, _('%.0f KB')),
1858 (100, 1<<10, _('%.0f KB')),
1859 (10, 1<<10, _('%.1f KB')),
1859 (10, 1<<10, _('%.1f KB')),
1860 (1, 1<<10, _('%.2f KB')),
1860 (1, 1<<10, _('%.2f KB')),
1861 (1, 1, _('%.0f bytes')),
1861 (1, 1, _('%.0f bytes')),
1862 )
1862 )
1863
1863
1864 for multiplier, divisor, format in units:
1864 for multiplier, divisor, format in units:
1865 if nbytes >= divisor * multiplier:
1865 if nbytes >= divisor * multiplier:
1866 return format % (nbytes / float(divisor))
1866 return format % (nbytes / float(divisor))
1867 return units[-1][2] % nbytes
1867 return units[-1][2] % nbytes
1868
1868
1869 def drop_scheme(scheme, path):
1869 def drop_scheme(scheme, path):
1870 sc = scheme + ':'
1870 sc = scheme + ':'
1871 if path.startswith(sc):
1871 if path.startswith(sc):
1872 path = path[len(sc):]
1872 path = path[len(sc):]
1873 if path.startswith('//'):
1873 if path.startswith('//'):
1874 path = path[2:]
1874 path = path[2:]
1875 return path
1875 return path
1876
1876
1877 def uirepr(s):
1877 def uirepr(s):
1878 # Avoid double backslash in Windows path repr()
1878 # Avoid double backslash in Windows path repr()
1879 return repr(s).replace('\\\\', '\\')
1879 return repr(s).replace('\\\\', '\\')
1880
1880
1881 def hidepassword(url):
1881 def hidepassword(url):
1882 '''hide user credential in a url string'''
1882 '''hide user credential in a url string'''
1883 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1883 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1884 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1884 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1885 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1885 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1886
1886
1887 def removeauth(url):
1887 def removeauth(url):
1888 '''remove all authentication information from a url string'''
1888 '''remove all authentication information from a url string'''
1889 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1889 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1890 netloc = netloc[netloc.find('@')+1:]
1890 netloc = netloc[netloc.find('@')+1:]
1891 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1891 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
General Comments 0
You need to be logged in to leave comments. Login now