##// END OF EJS Templates
fix errors spotted by pychecker
Benoit Boissinot -
r3859:8c24b6fd default
parent child Browse files
Show More
@@ -1,120 +1,120 b''
1 """
1 """
2 changegroup.py - Mercurial changegroup manipulation functions
2 changegroup.py - Mercurial changegroup manipulation functions
3
3
4 Copyright 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2006 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 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "struct os bz2 zlib util tempfile")
11 demandload(globals(), "struct os bz2 zlib util tempfile")
12
12
13 def getchunk(source):
13 def getchunk(source):
14 """get a chunk from a changegroup"""
14 """get a chunk from a changegroup"""
15 d = source.read(4)
15 d = source.read(4)
16 if not d:
16 if not d:
17 return ""
17 return ""
18 l = struct.unpack(">l", d)[0]
18 l = struct.unpack(">l", d)[0]
19 if l <= 4:
19 if l <= 4:
20 return ""
20 return ""
21 d = source.read(l - 4)
21 d = source.read(l - 4)
22 if len(d) < l - 4:
22 if len(d) < l - 4:
23 raise util.Abort(_("premature EOF reading chunk"
23 raise util.Abort(_("premature EOF reading chunk"
24 " (got %d bytes, expected %d)")
24 " (got %d bytes, expected %d)")
25 % (len(d), l - 4))
25 % (len(d), l - 4))
26 return d
26 return d
27
27
28 def chunkiter(source):
28 def chunkiter(source):
29 """iterate through the chunks in source"""
29 """iterate through the chunks in source"""
30 while 1:
30 while 1:
31 c = getchunk(source)
31 c = getchunk(source)
32 if not c:
32 if not c:
33 break
33 break
34 yield c
34 yield c
35
35
36 def genchunk(data):
36 def genchunk(data):
37 """build a changegroup chunk"""
37 """build a changegroup chunk"""
38 header = struct.pack(">l", len(data)+ 4)
38 header = struct.pack(">l", len(data)+ 4)
39 return "%s%s" % (header, data)
39 return "%s%s" % (header, data)
40
40
41 def closechunk():
41 def closechunk():
42 return struct.pack(">l", 0)
42 return struct.pack(">l", 0)
43
43
44 class nocompress(object):
44 class nocompress(object):
45 def compress(self, x):
45 def compress(self, x):
46 return x
46 return x
47 def flush(self):
47 def flush(self):
48 return ""
48 return ""
49
49
50 bundletypes = {
50 bundletypes = {
51 "": ("", nocompress),
51 "": ("", nocompress),
52 "HG10UN": ("HG10UN", nocompress),
52 "HG10UN": ("HG10UN", nocompress),
53 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
53 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
54 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
54 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
55 }
55 }
56
56
57 def writebundle(cg, filename, bundletype):
57 def writebundle(cg, filename, bundletype):
58 """Write a bundle file and return its filename.
58 """Write a bundle file and return its filename.
59
59
60 Existing files will not be overwritten.
60 Existing files will not be overwritten.
61 If no filename is specified, a temporary file is created.
61 If no filename is specified, a temporary file is created.
62 bz2 compression can be turned off.
62 bz2 compression can be turned off.
63 The bundle file will be deleted in case of errors.
63 The bundle file will be deleted in case of errors.
64 """
64 """
65
65
66 fh = None
66 fh = None
67 cleanup = None
67 cleanup = None
68 try:
68 try:
69 if filename:
69 if filename:
70 if os.path.exists(filename):
70 if os.path.exists(filename):
71 raise util.Abort(_("file '%s' already exists") % filename)
71 raise util.Abort(_("file '%s' already exists") % filename)
72 fh = open(filename, "wb")
72 fh = open(filename, "wb")
73 else:
73 else:
74 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
74 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
75 fh = os.fdopen(fd, "wb")
75 fh = os.fdopen(fd, "wb")
76 cleanup = filename
76 cleanup = filename
77
77
78 header, compressor = bundletypes[bundletype]
78 header, compressor = bundletypes[bundletype]
79 fh.write(header)
79 fh.write(header)
80 z = compressor()
80 z = compressor()
81
81
82 # parse the changegroup data, otherwise we will block
82 # parse the changegroup data, otherwise we will block
83 # in case of sshrepo because we don't know the end of the stream
83 # in case of sshrepo because we don't know the end of the stream
84
84
85 # an empty chunkiter is the end of the changegroup
85 # an empty chunkiter is the end of the changegroup
86 empty = False
86 empty = False
87 while not empty:
87 while not empty:
88 empty = True
88 empty = True
89 for chunk in chunkiter(cg):
89 for chunk in chunkiter(cg):
90 empty = False
90 empty = False
91 fh.write(z.compress(genchunk(chunk)))
91 fh.write(z.compress(genchunk(chunk)))
92 fh.write(z.compress(closechunk()))
92 fh.write(z.compress(closechunk()))
93 fh.write(z.flush())
93 fh.write(z.flush())
94 cleanup = None
94 cleanup = None
95 return filename
95 return filename
96 finally:
96 finally:
97 if fh is not None:
97 if fh is not None:
98 fh.close()
98 fh.close()
99 if cleanup is not None:
99 if cleanup is not None:
100 os.unlink(cleanup)
100 os.unlink(cleanup)
101
101
102 def readbundle(fh):
102 def readbundle(fh, fname):
103 header = fh.read(6)
103 header = fh.read(6)
104 if not header.startswith("HG"):
104 if not header.startswith("HG"):
105 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
105 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
106 elif not header.startswith("HG10"):
106 elif not header.startswith("HG10"):
107 raise util.Abort(_("%s: unknown bundle version") % fname)
107 raise util.Abort(_("%s: unknown bundle version") % fname)
108
108
109 if header == "HG10BZ":
109 if header == "HG10BZ":
110 def generator(f):
110 def generator(f):
111 zd = bz2.BZ2Decompressor()
111 zd = bz2.BZ2Decompressor()
112 zd.decompress("BZ")
112 zd.decompress("BZ")
113 for chunk in util.filechunkiter(f, 4096):
113 for chunk in util.filechunkiter(f, 4096):
114 yield zd.decompress(chunk)
114 yield zd.decompress(chunk)
115 return util.chunkbuffer(generator(fh))
115 return util.chunkbuffer(generator(fh))
116 elif header == "HG10UN":
116 elif header == "HG10UN":
117 return fh
117 return fh
118
118
119 raise util.Abort(_("%s: unknown bundle compression type")
119 raise util.Abort(_("%s: unknown bundle compression type")
120 % fname)
120 % fname)
@@ -1,3314 +1,3314 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
14 demandload(globals(), "traceback errno version atexit")
14 demandload(globals(), "traceback errno version atexit")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 n = _lookup(repo, hex(op1))
243 n = _lookup(repo, hex(op1))
244 hg.merge(repo, n)
244 hg.merge(repo, n)
245 else:
245 else:
246 ui.status(_('the backout changeset is a new head - '
246 ui.status(_('the backout changeset is a new head - '
247 'do not forget to merge\n'))
247 'do not forget to merge\n'))
248 ui.status(_('(use "backout --merge" '
248 ui.status(_('(use "backout --merge" '
249 'if you want to auto-merge)\n'))
249 'if you want to auto-merge)\n'))
250
250
251 def branch(ui, repo, label=None):
251 def branch(ui, repo, label=None):
252 """set or show the current branch name
252 """set or show the current branch name
253
253
254 With <name>, set the current branch name. Otherwise, show the
254 With <name>, set the current branch name. Otherwise, show the
255 current branch name.
255 current branch name.
256 """
256 """
257
257
258 if label is not None:
258 if label is not None:
259 repo.opener("branch", "w").write(label)
259 repo.opener("branch", "w").write(label)
260 else:
260 else:
261 b = repo.workingctx().branch()
261 b = repo.workingctx().branch()
262 if b:
262 if b:
263 ui.write("%s\n" % b)
263 ui.write("%s\n" % b)
264
264
265 def branches(ui, repo):
265 def branches(ui, repo):
266 """list repository named branches
266 """list repository named branches
267
267
268 List the repository's named branches.
268 List the repository's named branches.
269 """
269 """
270 b = repo.branchtags()
270 b = repo.branchtags()
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
272 l.sort()
272 l.sort()
273 for r, n, t in l:
273 for r, n, t in l:
274 hexfunc = ui.debugflag and hex or short
274 hexfunc = ui.debugflag and hex or short
275 if ui.quiet:
275 if ui.quiet:
276 ui.write("%s\n" % t)
276 ui.write("%s\n" % t)
277 else:
277 else:
278 t = util.localsub(t, 30)
278 t = util.localsub(t, 30)
279 t += " " * (30 - util.locallen(t))
279 t += " " * (30 - util.locallen(t))
280 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
280 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
281
281
282 def bundle(ui, repo, fname, dest=None, **opts):
282 def bundle(ui, repo, fname, dest=None, **opts):
283 """create a changegroup file
283 """create a changegroup file
284
284
285 Generate a compressed changegroup file collecting changesets not
285 Generate a compressed changegroup file collecting changesets not
286 found in the other repository.
286 found in the other repository.
287
287
288 If no destination repository is specified the destination is assumed
288 If no destination repository is specified the destination is assumed
289 to have all the nodes specified by one or more --base parameters.
289 to have all the nodes specified by one or more --base parameters.
290
290
291 The bundle file can then be transferred using conventional means and
291 The bundle file can then be transferred using conventional means and
292 applied to another repository with the unbundle or pull command.
292 applied to another repository with the unbundle or pull command.
293 This is useful when direct push and pull are not available or when
293 This is useful when direct push and pull are not available or when
294 exporting an entire repository is undesirable.
294 exporting an entire repository is undesirable.
295
295
296 Applying bundles preserves all changeset contents including
296 Applying bundles preserves all changeset contents including
297 permissions, copy/rename information, and revision history.
297 permissions, copy/rename information, and revision history.
298 """
298 """
299 revs = opts.get('rev') or None
299 revs = opts.get('rev') or None
300 if revs:
300 if revs:
301 revs = [repo.lookup(rev) for rev in revs]
301 revs = [repo.lookup(rev) for rev in revs]
302 base = opts.get('base')
302 base = opts.get('base')
303 if base:
303 if base:
304 if dest:
304 if dest:
305 raise util.Abort(_("--base is incompatible with specifiying "
305 raise util.Abort(_("--base is incompatible with specifiying "
306 "a destination"))
306 "a destination"))
307 base = [repo.lookup(rev) for rev in base]
307 base = [repo.lookup(rev) for rev in base]
308 # create the right base
308 # create the right base
309 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 # XXX: nodesbetween / changegroup* should be "fixed" instead
310 o = []
310 o = []
311 has = {nullid: None}
311 has = {nullid: None}
312 for n in base:
312 for n in base:
313 has.update(repo.changelog.reachable(n))
313 has.update(repo.changelog.reachable(n))
314 if revs:
314 if revs:
315 visit = list(revs)
315 visit = list(revs)
316 else:
316 else:
317 visit = repo.changelog.heads()
317 visit = repo.changelog.heads()
318 seen = {}
318 seen = {}
319 while visit:
319 while visit:
320 n = visit.pop(0)
320 n = visit.pop(0)
321 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 parents = [p for p in repo.changelog.parents(n) if p not in has]
322 if len(parents) == 0:
322 if len(parents) == 0:
323 o.insert(0, n)
323 o.insert(0, n)
324 else:
324 else:
325 for p in parents:
325 for p in parents:
326 if p not in seen:
326 if p not in seen:
327 seen[p] = 1
327 seen[p] = 1
328 visit.append(p)
328 visit.append(p)
329 else:
329 else:
330 setremoteconfig(ui, opts)
330 setremoteconfig(ui, opts)
331 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 dest = ui.expandpath(dest or 'default-push', dest or 'default')
332 other = hg.repository(ui, dest)
332 other = hg.repository(ui, dest)
333 o = repo.findoutgoing(other, force=opts['force'])
333 o = repo.findoutgoing(other, force=opts['force'])
334
334
335 if revs:
335 if revs:
336 cg = repo.changegroupsubset(o, revs, 'bundle')
336 cg = repo.changegroupsubset(o, revs, 'bundle')
337 else:
337 else:
338 cg = repo.changegroup(o, 'bundle')
338 cg = repo.changegroup(o, 'bundle')
339 changegroup.writebundle(cg, fname, "HG10BZ")
339 changegroup.writebundle(cg, fname, "HG10BZ")
340
340
341 def cat(ui, repo, file1, *pats, **opts):
341 def cat(ui, repo, file1, *pats, **opts):
342 """output the latest or given revisions of files
342 """output the latest or given revisions of files
343
343
344 Print the specified files as they were at the given revision.
344 Print the specified files as they were at the given revision.
345 If no revision is given then working dir parent is used, or tip
345 If no revision is given then working dir parent is used, or tip
346 if no revision is checked out.
346 if no revision is checked out.
347
347
348 Output may be to a file, in which case the name of the file is
348 Output may be to a file, in which case the name of the file is
349 given using a format string. The formatting rules are the same as
349 given using a format string. The formatting rules are the same as
350 for the export command, with the following additions:
350 for the export command, with the following additions:
351
351
352 %s basename of file being printed
352 %s basename of file being printed
353 %d dirname of file being printed, or '.' if in repo root
353 %d dirname of file being printed, or '.' if in repo root
354 %p root-relative path name of file being printed
354 %p root-relative path name of file being printed
355 """
355 """
356 ctx = repo.changectx(opts['rev'])
356 ctx = repo.changectx(opts['rev'])
357 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
358 ctx.node()):
358 ctx.node()):
359 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
360 fp.write(ctx.filectx(abs).data())
360 fp.write(ctx.filectx(abs).data())
361
361
362 def clone(ui, source, dest=None, **opts):
362 def clone(ui, source, dest=None, **opts):
363 """make a copy of an existing repository
363 """make a copy of an existing repository
364
364
365 Create a copy of an existing repository in a new directory.
365 Create a copy of an existing repository in a new directory.
366
366
367 If no destination directory name is specified, it defaults to the
367 If no destination directory name is specified, it defaults to the
368 basename of the source.
368 basename of the source.
369
369
370 The location of the source is added to the new repository's
370 The location of the source is added to the new repository's
371 .hg/hgrc file, as the default to be used for future pulls.
371 .hg/hgrc file, as the default to be used for future pulls.
372
372
373 For efficiency, hardlinks are used for cloning whenever the source
373 For efficiency, hardlinks are used for cloning whenever the source
374 and destination are on the same filesystem (note this applies only
374 and destination are on the same filesystem (note this applies only
375 to the repository data, not to the checked out files). Some
375 to the repository data, not to the checked out files). Some
376 filesystems, such as AFS, implement hardlinking incorrectly, but
376 filesystems, such as AFS, implement hardlinking incorrectly, but
377 do not report errors. In these cases, use the --pull option to
377 do not report errors. In these cases, use the --pull option to
378 avoid hardlinking.
378 avoid hardlinking.
379
379
380 You can safely clone repositories and checked out files using full
380 You can safely clone repositories and checked out files using full
381 hardlinks with
381 hardlinks with
382
382
383 $ cp -al REPO REPOCLONE
383 $ cp -al REPO REPOCLONE
384
384
385 which is the fastest way to clone. However, the operation is not
385 which is the fastest way to clone. However, the operation is not
386 atomic (making sure REPO is not modified during the operation is
386 atomic (making sure REPO is not modified during the operation is
387 up to you) and you have to make sure your editor breaks hardlinks
387 up to you) and you have to make sure your editor breaks hardlinks
388 (Emacs and most Linux Kernel tools do so).
388 (Emacs and most Linux Kernel tools do so).
389
389
390 If you use the -r option to clone up to a specific revision, no
390 If you use the -r option to clone up to a specific revision, no
391 subsequent revisions will be present in the cloned repository.
391 subsequent revisions will be present in the cloned repository.
392 This option implies --pull, even on local repositories.
392 This option implies --pull, even on local repositories.
393
393
394 See pull for valid source format details.
394 See pull for valid source format details.
395
395
396 It is possible to specify an ssh:// URL as the destination, but no
396 It is possible to specify an ssh:// URL as the destination, but no
397 .hg/hgrc and working directory will be created on the remote side.
397 .hg/hgrc and working directory will be created on the remote side.
398 Look at the help text for the pull command for important details
398 Look at the help text for the pull command for important details
399 about ssh:// URLs.
399 about ssh:// URLs.
400 """
400 """
401 setremoteconfig(ui, opts)
401 setremoteconfig(ui, opts)
402 hg.clone(ui, ui.expandpath(source), dest,
402 hg.clone(ui, ui.expandpath(source), dest,
403 pull=opts['pull'],
403 pull=opts['pull'],
404 stream=opts['uncompressed'],
404 stream=opts['uncompressed'],
405 rev=opts['rev'],
405 rev=opts['rev'],
406 update=not opts['noupdate'])
406 update=not opts['noupdate'])
407
407
408 def commit(ui, repo, *pats, **opts):
408 def commit(ui, repo, *pats, **opts):
409 """commit the specified files or all outstanding changes
409 """commit the specified files or all outstanding changes
410
410
411 Commit changes to the given files into the repository.
411 Commit changes to the given files into the repository.
412
412
413 If a list of files is omitted, all changes reported by "hg status"
413 If a list of files is omitted, all changes reported by "hg status"
414 will be committed.
414 will be committed.
415
415
416 If no commit message is specified, the editor configured in your hgrc
416 If no commit message is specified, the editor configured in your hgrc
417 or in the EDITOR environment variable is started to enter a message.
417 or in the EDITOR environment variable is started to enter a message.
418 """
418 """
419 message = logmessage(opts)
419 message = logmessage(opts)
420
420
421 if opts['addremove']:
421 if opts['addremove']:
422 cmdutil.addremove(repo, pats, opts)
422 cmdutil.addremove(repo, pats, opts)
423 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
424 if pats:
424 if pats:
425 status = repo.status(files=fns, match=match)
425 status = repo.status(files=fns, match=match)
426 modified, added, removed, deleted, unknown = status[:5]
426 modified, added, removed, deleted, unknown = status[:5]
427 files = modified + added + removed
427 files = modified + added + removed
428 slist = None
428 slist = None
429 for f in fns:
429 for f in fns:
430 if f not in files:
430 if f not in files:
431 rf = repo.wjoin(f)
431 rf = repo.wjoin(f)
432 if f in unknown:
432 if f in unknown:
433 raise util.Abort(_("file %s not tracked!") % rf)
433 raise util.Abort(_("file %s not tracked!") % rf)
434 try:
434 try:
435 mode = os.lstat(rf)[stat.ST_MODE]
435 mode = os.lstat(rf)[stat.ST_MODE]
436 except OSError:
436 except OSError:
437 raise util.Abort(_("file %s not found!") % rf)
437 raise util.Abort(_("file %s not found!") % rf)
438 if stat.S_ISDIR(mode):
438 if stat.S_ISDIR(mode):
439 name = f + '/'
439 name = f + '/'
440 if slist is None:
440 if slist is None:
441 slist = list(files)
441 slist = list(files)
442 slist.sort()
442 slist.sort()
443 i = bisect.bisect(slist, name)
443 i = bisect.bisect(slist, name)
444 if i >= len(slist) or not slist[i].startswith(name):
444 if i >= len(slist) or not slist[i].startswith(name):
445 raise util.Abort(_("no match under directory %s!")
445 raise util.Abort(_("no match under directory %s!")
446 % rf)
446 % rf)
447 elif not stat.S_ISREG(mode):
447 elif not stat.S_ISREG(mode):
448 raise util.Abort(_("can't commit %s: "
448 raise util.Abort(_("can't commit %s: "
449 "unsupported file type!") % rf)
449 "unsupported file type!") % rf)
450 else:
450 else:
451 files = []
451 files = []
452 try:
452 try:
453 repo.commit(files, message, opts['user'], opts['date'], match,
453 repo.commit(files, message, opts['user'], opts['date'], match,
454 force_editor=opts.get('force_editor'))
454 force_editor=opts.get('force_editor'))
455 except ValueError, inst:
455 except ValueError, inst:
456 raise util.Abort(str(inst))
456 raise util.Abort(str(inst))
457
457
458 def docopy(ui, repo, pats, opts, wlock):
458 def docopy(ui, repo, pats, opts, wlock):
459 # called with the repo lock held
459 # called with the repo lock held
460 #
460 #
461 # hgsep => pathname that uses "/" to separate directories
461 # hgsep => pathname that uses "/" to separate directories
462 # ossep => pathname that uses os.sep to separate directories
462 # ossep => pathname that uses os.sep to separate directories
463 cwd = repo.getcwd()
463 cwd = repo.getcwd()
464 errors = 0
464 errors = 0
465 copied = []
465 copied = []
466 targets = {}
466 targets = {}
467
467
468 # abs: hgsep
468 # abs: hgsep
469 # rel: ossep
469 # rel: ossep
470 # return: hgsep
470 # return: hgsep
471 def okaytocopy(abs, rel, exact):
471 def okaytocopy(abs, rel, exact):
472 reasons = {'?': _('is not managed'),
472 reasons = {'?': _('is not managed'),
473 'a': _('has been marked for add'),
473 'a': _('has been marked for add'),
474 'r': _('has been marked for remove')}
474 'r': _('has been marked for remove')}
475 state = repo.dirstate.state(abs)
475 state = repo.dirstate.state(abs)
476 reason = reasons.get(state)
476 reason = reasons.get(state)
477 if reason:
477 if reason:
478 if state == 'a':
478 if state == 'a':
479 origsrc = repo.dirstate.copied(abs)
479 origsrc = repo.dirstate.copied(abs)
480 if origsrc is not None:
480 if origsrc is not None:
481 return origsrc
481 return origsrc
482 if exact:
482 if exact:
483 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
484 else:
484 else:
485 return abs
485 return abs
486
486
487 # origsrc: hgsep
487 # origsrc: hgsep
488 # abssrc: hgsep
488 # abssrc: hgsep
489 # relsrc: ossep
489 # relsrc: ossep
490 # target: ossep
490 # target: ossep
491 def copy(origsrc, abssrc, relsrc, target, exact):
491 def copy(origsrc, abssrc, relsrc, target, exact):
492 abstarget = util.canonpath(repo.root, cwd, target)
492 abstarget = util.canonpath(repo.root, cwd, target)
493 reltarget = util.pathto(cwd, abstarget)
493 reltarget = util.pathto(cwd, abstarget)
494 prevsrc = targets.get(abstarget)
494 prevsrc = targets.get(abstarget)
495 if prevsrc is not None:
495 if prevsrc is not None:
496 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
497 (reltarget, util.localpath(abssrc),
497 (reltarget, util.localpath(abssrc),
498 util.localpath(prevsrc)))
498 util.localpath(prevsrc)))
499 return
499 return
500 if (not opts['after'] and os.path.exists(reltarget) or
500 if (not opts['after'] and os.path.exists(reltarget) or
501 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
502 if not opts['force']:
502 if not opts['force']:
503 ui.warn(_('%s: not overwriting - file exists\n') %
503 ui.warn(_('%s: not overwriting - file exists\n') %
504 reltarget)
504 reltarget)
505 return
505 return
506 if not opts['after'] and not opts.get('dry_run'):
506 if not opts['after'] and not opts.get('dry_run'):
507 os.unlink(reltarget)
507 os.unlink(reltarget)
508 if opts['after']:
508 if opts['after']:
509 if not os.path.exists(reltarget):
509 if not os.path.exists(reltarget):
510 return
510 return
511 else:
511 else:
512 targetdir = os.path.dirname(reltarget) or '.'
512 targetdir = os.path.dirname(reltarget) or '.'
513 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
514 os.makedirs(targetdir)
514 os.makedirs(targetdir)
515 try:
515 try:
516 restore = repo.dirstate.state(abstarget) == 'r'
516 restore = repo.dirstate.state(abstarget) == 'r'
517 if restore and not opts.get('dry_run'):
517 if restore and not opts.get('dry_run'):
518 repo.undelete([abstarget], wlock)
518 repo.undelete([abstarget], wlock)
519 try:
519 try:
520 if not opts.get('dry_run'):
520 if not opts.get('dry_run'):
521 util.copyfile(relsrc, reltarget)
521 util.copyfile(relsrc, reltarget)
522 restore = False
522 restore = False
523 finally:
523 finally:
524 if restore:
524 if restore:
525 repo.remove([abstarget], wlock)
525 repo.remove([abstarget], wlock)
526 except IOError, inst:
526 except IOError, inst:
527 if inst.errno == errno.ENOENT:
527 if inst.errno == errno.ENOENT:
528 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 ui.warn(_('%s: deleted in working copy\n') % relsrc)
529 else:
529 else:
530 ui.warn(_('%s: cannot copy - %s\n') %
530 ui.warn(_('%s: cannot copy - %s\n') %
531 (relsrc, inst.strerror))
531 (relsrc, inst.strerror))
532 errors += 1
532 errors += 1
533 return
533 return
534 if ui.verbose or not exact:
534 if ui.verbose or not exact:
535 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
536 targets[abstarget] = abssrc
536 targets[abstarget] = abssrc
537 if abstarget != origsrc and not opts.get('dry_run'):
537 if abstarget != origsrc and not opts.get('dry_run'):
538 repo.copy(origsrc, abstarget, wlock)
538 repo.copy(origsrc, abstarget, wlock)
539 copied.append((abssrc, relsrc, exact))
539 copied.append((abssrc, relsrc, exact))
540
540
541 # pat: ossep
541 # pat: ossep
542 # dest ossep
542 # dest ossep
543 # srcs: list of (hgsep, hgsep, ossep, bool)
543 # srcs: list of (hgsep, hgsep, ossep, bool)
544 # return: function that takes hgsep and returns ossep
544 # return: function that takes hgsep and returns ossep
545 def targetpathfn(pat, dest, srcs):
545 def targetpathfn(pat, dest, srcs):
546 if os.path.isdir(pat):
546 if os.path.isdir(pat):
547 abspfx = util.canonpath(repo.root, cwd, pat)
547 abspfx = util.canonpath(repo.root, cwd, pat)
548 abspfx = util.localpath(abspfx)
548 abspfx = util.localpath(abspfx)
549 if destdirexists:
549 if destdirexists:
550 striplen = len(os.path.split(abspfx)[0])
550 striplen = len(os.path.split(abspfx)[0])
551 else:
551 else:
552 striplen = len(abspfx)
552 striplen = len(abspfx)
553 if striplen:
553 if striplen:
554 striplen += len(os.sep)
554 striplen += len(os.sep)
555 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
556 elif destdirexists:
556 elif destdirexists:
557 res = lambda p: os.path.join(dest,
557 res = lambda p: os.path.join(dest,
558 os.path.basename(util.localpath(p)))
558 os.path.basename(util.localpath(p)))
559 else:
559 else:
560 res = lambda p: dest
560 res = lambda p: dest
561 return res
561 return res
562
562
563 # pat: ossep
563 # pat: ossep
564 # dest ossep
564 # dest ossep
565 # srcs: list of (hgsep, hgsep, ossep, bool)
565 # srcs: list of (hgsep, hgsep, ossep, bool)
566 # return: function that takes hgsep and returns ossep
566 # return: function that takes hgsep and returns ossep
567 def targetpathafterfn(pat, dest, srcs):
567 def targetpathafterfn(pat, dest, srcs):
568 if util.patkind(pat, None)[0]:
568 if util.patkind(pat, None)[0]:
569 # a mercurial pattern
569 # a mercurial pattern
570 res = lambda p: os.path.join(dest,
570 res = lambda p: os.path.join(dest,
571 os.path.basename(util.localpath(p)))
571 os.path.basename(util.localpath(p)))
572 else:
572 else:
573 abspfx = util.canonpath(repo.root, cwd, pat)
573 abspfx = util.canonpath(repo.root, cwd, pat)
574 if len(abspfx) < len(srcs[0][0]):
574 if len(abspfx) < len(srcs[0][0]):
575 # A directory. Either the target path contains the last
575 # A directory. Either the target path contains the last
576 # component of the source path or it does not.
576 # component of the source path or it does not.
577 def evalpath(striplen):
577 def evalpath(striplen):
578 score = 0
578 score = 0
579 for s in srcs:
579 for s in srcs:
580 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 t = os.path.join(dest, util.localpath(s[0])[striplen:])
581 if os.path.exists(t):
581 if os.path.exists(t):
582 score += 1
582 score += 1
583 return score
583 return score
584
584
585 abspfx = util.localpath(abspfx)
585 abspfx = util.localpath(abspfx)
586 striplen = len(abspfx)
586 striplen = len(abspfx)
587 if striplen:
587 if striplen:
588 striplen += len(os.sep)
588 striplen += len(os.sep)
589 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
590 score = evalpath(striplen)
590 score = evalpath(striplen)
591 striplen1 = len(os.path.split(abspfx)[0])
591 striplen1 = len(os.path.split(abspfx)[0])
592 if striplen1:
592 if striplen1:
593 striplen1 += len(os.sep)
593 striplen1 += len(os.sep)
594 if evalpath(striplen1) > score:
594 if evalpath(striplen1) > score:
595 striplen = striplen1
595 striplen = striplen1
596 res = lambda p: os.path.join(dest,
596 res = lambda p: os.path.join(dest,
597 util.localpath(p)[striplen:])
597 util.localpath(p)[striplen:])
598 else:
598 else:
599 # a file
599 # a file
600 if destdirexists:
600 if destdirexists:
601 res = lambda p: os.path.join(dest,
601 res = lambda p: os.path.join(dest,
602 os.path.basename(util.localpath(p)))
602 os.path.basename(util.localpath(p)))
603 else:
603 else:
604 res = lambda p: dest
604 res = lambda p: dest
605 return res
605 return res
606
606
607
607
608 pats = list(pats)
608 pats = list(pats)
609 if not pats:
609 if not pats:
610 raise util.Abort(_('no source or destination specified'))
610 raise util.Abort(_('no source or destination specified'))
611 if len(pats) == 1:
611 if len(pats) == 1:
612 raise util.Abort(_('no destination specified'))
612 raise util.Abort(_('no destination specified'))
613 dest = pats.pop()
613 dest = pats.pop()
614 destdirexists = os.path.isdir(dest)
614 destdirexists = os.path.isdir(dest)
615 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
616 raise util.Abort(_('with multiple sources, destination must be an '
616 raise util.Abort(_('with multiple sources, destination must be an '
617 'existing directory'))
617 'existing directory'))
618 if opts['after']:
618 if opts['after']:
619 tfn = targetpathafterfn
619 tfn = targetpathafterfn
620 else:
620 else:
621 tfn = targetpathfn
621 tfn = targetpathfn
622 copylist = []
622 copylist = []
623 for pat in pats:
623 for pat in pats:
624 srcs = []
624 srcs = []
625 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
625 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
626 origsrc = okaytocopy(abssrc, relsrc, exact)
626 origsrc = okaytocopy(abssrc, relsrc, exact)
627 if origsrc:
627 if origsrc:
628 srcs.append((origsrc, abssrc, relsrc, exact))
628 srcs.append((origsrc, abssrc, relsrc, exact))
629 if not srcs:
629 if not srcs:
630 continue
630 continue
631 copylist.append((tfn(pat, dest, srcs), srcs))
631 copylist.append((tfn(pat, dest, srcs), srcs))
632 if not copylist:
632 if not copylist:
633 raise util.Abort(_('no files to copy'))
633 raise util.Abort(_('no files to copy'))
634
634
635 for targetpath, srcs in copylist:
635 for targetpath, srcs in copylist:
636 for origsrc, abssrc, relsrc, exact in srcs:
636 for origsrc, abssrc, relsrc, exact in srcs:
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
638
638
639 if errors:
639 if errors:
640 ui.warn(_('(consider using --after)\n'))
640 ui.warn(_('(consider using --after)\n'))
641 return errors, copied
641 return errors, copied
642
642
643 def copy(ui, repo, *pats, **opts):
643 def copy(ui, repo, *pats, **opts):
644 """mark files as copied for the next commit
644 """mark files as copied for the next commit
645
645
646 Mark dest as having copies of source files. If dest is a
646 Mark dest as having copies of source files. If dest is a
647 directory, copies are put in that directory. If dest is a file,
647 directory, copies are put in that directory. If dest is a file,
648 there can only be one source.
648 there can only be one source.
649
649
650 By default, this command copies the contents of files as they
650 By default, this command copies the contents of files as they
651 stand in the working directory. If invoked with --after, the
651 stand in the working directory. If invoked with --after, the
652 operation is recorded, but no copying is performed.
652 operation is recorded, but no copying is performed.
653
653
654 This command takes effect in the next commit. To undo a copy
654 This command takes effect in the next commit. To undo a copy
655 before that, see hg revert.
655 before that, see hg revert.
656 """
656 """
657 wlock = repo.wlock(0)
657 wlock = repo.wlock(0)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
659 return errs
659 return errs
660
660
661 def debugancestor(ui, index, rev1, rev2):
661 def debugancestor(ui, index, rev1, rev2):
662 """find the ancestor revision of two revisions in a given index"""
662 """find the ancestor revision of two revisions in a given index"""
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
666
666
667 def debugcomplete(ui, cmd='', **opts):
667 def debugcomplete(ui, cmd='', **opts):
668 """returns the completion list associated with the given command"""
668 """returns the completion list associated with the given command"""
669
669
670 if opts['options']:
670 if opts['options']:
671 options = []
671 options = []
672 otables = [globalopts]
672 otables = [globalopts]
673 if cmd:
673 if cmd:
674 aliases, entry = findcmd(ui, cmd)
674 aliases, entry = findcmd(ui, cmd)
675 otables.append(entry[1])
675 otables.append(entry[1])
676 for t in otables:
676 for t in otables:
677 for o in t:
677 for o in t:
678 if o[0]:
678 if o[0]:
679 options.append('-%s' % o[0])
679 options.append('-%s' % o[0])
680 options.append('--%s' % o[1])
680 options.append('--%s' % o[1])
681 ui.write("%s\n" % "\n".join(options))
681 ui.write("%s\n" % "\n".join(options))
682 return
682 return
683
683
684 clist = findpossible(ui, cmd).keys()
684 clist = findpossible(ui, cmd).keys()
685 clist.sort()
685 clist.sort()
686 ui.write("%s\n" % "\n".join(clist))
686 ui.write("%s\n" % "\n".join(clist))
687
687
688 def debugrebuildstate(ui, repo, rev=None):
688 def debugrebuildstate(ui, repo, rev=None):
689 """rebuild the dirstate as it would look like for the given revision"""
689 """rebuild the dirstate as it would look like for the given revision"""
690 if not rev:
690 if not rev:
691 rev = repo.changelog.tip()
691 rev = repo.changelog.tip()
692 else:
692 else:
693 rev = repo.lookup(rev)
693 rev = repo.lookup(rev)
694 change = repo.changelog.read(rev)
694 change = repo.changelog.read(rev)
695 n = change[0]
695 n = change[0]
696 files = repo.manifest.read(n)
696 files = repo.manifest.read(n)
697 wlock = repo.wlock()
697 wlock = repo.wlock()
698 repo.dirstate.rebuild(rev, files)
698 repo.dirstate.rebuild(rev, files)
699
699
700 def debugcheckstate(ui, repo):
700 def debugcheckstate(ui, repo):
701 """validate the correctness of the current dirstate"""
701 """validate the correctness of the current dirstate"""
702 parent1, parent2 = repo.dirstate.parents()
702 parent1, parent2 = repo.dirstate.parents()
703 repo.dirstate.read()
703 repo.dirstate.read()
704 dc = repo.dirstate.map
704 dc = repo.dirstate.map
705 keys = dc.keys()
705 keys = dc.keys()
706 keys.sort()
706 keys.sort()
707 m1n = repo.changelog.read(parent1)[0]
707 m1n = repo.changelog.read(parent1)[0]
708 m2n = repo.changelog.read(parent2)[0]
708 m2n = repo.changelog.read(parent2)[0]
709 m1 = repo.manifest.read(m1n)
709 m1 = repo.manifest.read(m1n)
710 m2 = repo.manifest.read(m2n)
710 m2 = repo.manifest.read(m2n)
711 errors = 0
711 errors = 0
712 for f in dc:
712 for f in dc:
713 state = repo.dirstate.state(f)
713 state = repo.dirstate.state(f)
714 if state in "nr" and f not in m1:
714 if state in "nr" and f not in m1:
715 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
715 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
716 errors += 1
716 errors += 1
717 if state in "a" and f in m1:
717 if state in "a" and f in m1:
718 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
718 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
719 errors += 1
719 errors += 1
720 if state in "m" and f not in m1 and f not in m2:
720 if state in "m" and f not in m1 and f not in m2:
721 ui.warn(_("%s in state %s, but not in either manifest\n") %
721 ui.warn(_("%s in state %s, but not in either manifest\n") %
722 (f, state))
722 (f, state))
723 errors += 1
723 errors += 1
724 for f in m1:
724 for f in m1:
725 state = repo.dirstate.state(f)
725 state = repo.dirstate.state(f)
726 if state not in "nrm":
726 if state not in "nrm":
727 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
727 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
728 errors += 1
728 errors += 1
729 if errors:
729 if errors:
730 error = _(".hg/dirstate inconsistent with current parent's manifest")
730 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 raise util.Abort(error)
731 raise util.Abort(error)
732
732
733 def showconfig(ui, repo, *values, **opts):
733 def showconfig(ui, repo, *values, **opts):
734 """show combined config settings from all hgrc files
734 """show combined config settings from all hgrc files
735
735
736 With no args, print names and values of all config items.
736 With no args, print names and values of all config items.
737
737
738 With one arg of the form section.name, print just the value of
738 With one arg of the form section.name, print just the value of
739 that config item.
739 that config item.
740
740
741 With multiple args, print names and values of all config items
741 With multiple args, print names and values of all config items
742 with matching section names."""
742 with matching section names."""
743
743
744 untrusted = bool(opts.get('untrusted'))
744 untrusted = bool(opts.get('untrusted'))
745 if values:
745 if values:
746 if len([v for v in values if '.' in v]) > 1:
746 if len([v for v in values if '.' in v]) > 1:
747 raise util.Abort(_('only one config item permitted'))
747 raise util.Abort(_('only one config item permitted'))
748 for section, name, value in ui.walkconfig(untrusted=untrusted):
748 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 sectname = section + '.' + name
749 sectname = section + '.' + name
750 if values:
750 if values:
751 for v in values:
751 for v in values:
752 if v == section:
752 if v == section:
753 ui.write('%s=%s\n' % (sectname, value))
753 ui.write('%s=%s\n' % (sectname, value))
754 elif v == sectname:
754 elif v == sectname:
755 ui.write(value, '\n')
755 ui.write(value, '\n')
756 else:
756 else:
757 ui.write('%s=%s\n' % (sectname, value))
757 ui.write('%s=%s\n' % (sectname, value))
758
758
759 def debugsetparents(ui, repo, rev1, rev2=None):
759 def debugsetparents(ui, repo, rev1, rev2=None):
760 """manually set the parents of the current working directory
760 """manually set the parents of the current working directory
761
761
762 This is useful for writing repository conversion tools, but should
762 This is useful for writing repository conversion tools, but should
763 be used with care.
763 be used with care.
764 """
764 """
765
765
766 if not rev2:
766 if not rev2:
767 rev2 = hex(nullid)
767 rev2 = hex(nullid)
768
768
769 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
769 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
770
770
771 def debugstate(ui, repo):
771 def debugstate(ui, repo):
772 """show the contents of the current dirstate"""
772 """show the contents of the current dirstate"""
773 repo.dirstate.read()
773 repo.dirstate.read()
774 dc = repo.dirstate.map
774 dc = repo.dirstate.map
775 keys = dc.keys()
775 keys = dc.keys()
776 keys.sort()
776 keys.sort()
777 for file_ in keys:
777 for file_ in keys:
778 ui.write("%c %3o %10d %s %s\n"
778 ui.write("%c %3o %10d %s %s\n"
779 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
779 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
780 time.strftime("%x %X",
780 time.strftime("%x %X",
781 time.localtime(dc[file_][3])), file_))
781 time.localtime(dc[file_][3])), file_))
782 for f in repo.dirstate.copies():
782 for f in repo.dirstate.copies():
783 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
783 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
784
784
785 def debugdata(ui, file_, rev):
785 def debugdata(ui, file_, rev):
786 """dump the contents of an data file revision"""
786 """dump the contents of an data file revision"""
787 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
787 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
788 file_[:-2] + ".i", file_, 0)
788 file_[:-2] + ".i", file_, 0)
789 try:
789 try:
790 ui.write(r.revision(r.lookup(rev)))
790 ui.write(r.revision(r.lookup(rev)))
791 except KeyError:
791 except KeyError:
792 raise util.Abort(_('invalid revision identifier %s') % rev)
792 raise util.Abort(_('invalid revision identifier %s') % rev)
793
793
794 def debugdate(ui, date, range=None, **opts):
794 def debugdate(ui, date, range=None, **opts):
795 """parse and display a date"""
795 """parse and display a date"""
796 if opts["extended"]:
796 if opts["extended"]:
797 d = util.parsedate(date, util.extendeddateformats)
797 d = util.parsedate(date, util.extendeddateformats)
798 else:
798 else:
799 d = util.parsedate(date)
799 d = util.parsedate(date)
800 ui.write("internal: %s %s\n" % d)
800 ui.write("internal: %s %s\n" % d)
801 ui.write("standard: %s\n" % util.datestr(d))
801 ui.write("standard: %s\n" % util.datestr(d))
802 if range:
802 if range:
803 m = util.matchdate(range)
803 m = util.matchdate(range)
804 ui.write("match: %s\n" % m(d[0]))
804 ui.write("match: %s\n" % m(d[0]))
805
805
806 def debugindex(ui, file_):
806 def debugindex(ui, file_):
807 """dump the contents of an index file"""
807 """dump the contents of an index file"""
808 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
808 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
809 ui.write(" rev offset length base linkrev" +
809 ui.write(" rev offset length base linkrev" +
810 " nodeid p1 p2\n")
810 " nodeid p1 p2\n")
811 for i in xrange(r.count()):
811 for i in xrange(r.count()):
812 node = r.node(i)
812 node = r.node(i)
813 pp = r.parents(node)
813 pp = r.parents(node)
814 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
814 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
815 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
815 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
816 short(node), short(pp[0]), short(pp[1])))
816 short(node), short(pp[0]), short(pp[1])))
817
817
818 def debugindexdot(ui, file_):
818 def debugindexdot(ui, file_):
819 """dump an index DAG as a .dot file"""
819 """dump an index DAG as a .dot file"""
820 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
820 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
821 ui.write("digraph G {\n")
821 ui.write("digraph G {\n")
822 for i in xrange(r.count()):
822 for i in xrange(r.count()):
823 node = r.node(i)
823 node = r.node(i)
824 pp = r.parents(node)
824 pp = r.parents(node)
825 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
825 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
826 if pp[1] != nullid:
826 if pp[1] != nullid:
827 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
827 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
828 ui.write("}\n")
828 ui.write("}\n")
829
829
830 def debuginstall(ui):
830 def debuginstall(ui):
831 '''test Mercurial installation'''
831 '''test Mercurial installation'''
832
832
833 def writetemp(contents):
833 def writetemp(contents):
834 (fd, name) = tempfile.mkstemp()
834 (fd, name) = tempfile.mkstemp()
835 f = os.fdopen(fd, "wb")
835 f = os.fdopen(fd, "wb")
836 f.write(contents)
836 f.write(contents)
837 f.close()
837 f.close()
838 return name
838 return name
839
839
840 problems = 0
840 problems = 0
841
841
842 # encoding
842 # encoding
843 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
843 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
844 try:
844 try:
845 util.fromlocal("test")
845 util.fromlocal("test")
846 except util.Abort, inst:
846 except util.Abort, inst:
847 ui.write(" %s\n" % inst)
847 ui.write(" %s\n" % inst)
848 ui.write(_(" (check that your locale is properly set)\n"))
848 ui.write(_(" (check that your locale is properly set)\n"))
849 problems += 1
849 problems += 1
850
850
851 # compiled modules
851 # compiled modules
852 ui.status(_("Checking extensions...\n"))
852 ui.status(_("Checking extensions...\n"))
853 try:
853 try:
854 import bdiff, mpatch, base85
854 import bdiff, mpatch, base85
855 except Exception, inst:
855 except Exception, inst:
856 ui.write(" %s\n" % inst)
856 ui.write(" %s\n" % inst)
857 ui.write(_(" One or more extensions could not be found"))
857 ui.write(_(" One or more extensions could not be found"))
858 ui.write(_(" (check that you compiled the extensions)\n"))
858 ui.write(_(" (check that you compiled the extensions)\n"))
859 problems += 1
859 problems += 1
860
860
861 # templates
861 # templates
862 ui.status(_("Checking templates...\n"))
862 ui.status(_("Checking templates...\n"))
863 try:
863 try:
864 import templater
864 import templater
865 t = templater.templater(templater.templatepath("map-cmdline.default"))
865 t = templater.templater(templater.templatepath("map-cmdline.default"))
866 except Exception, inst:
866 except Exception, inst:
867 ui.write(" %s\n" % inst)
867 ui.write(" %s\n" % inst)
868 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
868 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
869 problems += 1
869 problems += 1
870
870
871 # patch
871 # patch
872 ui.status(_("Checking patch...\n"))
872 ui.status(_("Checking patch...\n"))
873 path = os.environ.get('PATH', '')
873 path = os.environ.get('PATH', '')
874 patcher = util.find_in_path('gpatch', path,
874 patcher = util.find_in_path('gpatch', path,
875 util.find_in_path('patch', path, None))
875 util.find_in_path('patch', path, None))
876 if not patcher:
876 if not patcher:
877 ui.write(_(" Can't find patch or gpatch in PATH\n"))
877 ui.write(_(" Can't find patch or gpatch in PATH\n"))
878 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
878 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
879 problems += 1
879 problems += 1
880 else:
880 else:
881 # actually attempt a patch here
881 # actually attempt a patch here
882 a = "1\n2\n3\n4\n"
882 a = "1\n2\n3\n4\n"
883 b = "1\n2\n3\ninsert\n4\n"
883 b = "1\n2\n3\ninsert\n4\n"
884 d = mdiff.unidiff(a, None, b, None, "a")
884 d = mdiff.unidiff(a, None, b, None, "a")
885 fa = writetemp(a)
885 fa = writetemp(a)
886 fd = writetemp(d)
886 fd = writetemp(d)
887 fp = os.popen('%s %s %s' % (patcher, fa, fd))
887 fp = os.popen('%s %s %s' % (patcher, fa, fd))
888 files = []
888 files = []
889 output = ""
889 output = ""
890 for line in fp:
890 for line in fp:
891 output += line
891 output += line
892 if line.startswith('patching file '):
892 if line.startswith('patching file '):
893 pf = util.parse_patch_output(line.rstrip())
893 pf = util.parse_patch_output(line.rstrip())
894 files.append(pf)
894 files.append(pf)
895 if files != [fa]:
895 if files != [fa]:
896 ui.write(_(" unexpected patch output!"))
896 ui.write(_(" unexpected patch output!"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
898 ui.write(output)
898 ui.write(output)
899 problems += 1
899 problems += 1
900 a = file(fa).read()
900 a = file(fa).read()
901 if a != b:
901 if a != b:
902 ui.write(_(" patch test failed!"))
902 ui.write(_(" patch test failed!"))
903 ui.write(_(" (you may have an incompatible version of patch)\n"))
903 ui.write(_(" (you may have an incompatible version of patch)\n"))
904 problems += 1
904 problems += 1
905 os.unlink(fa)
905 os.unlink(fa)
906 os.unlink(fd)
906 os.unlink(fd)
907
907
908 # merge helper
908 # merge helper
909 ui.status(_("Checking merge helper...\n"))
909 ui.status(_("Checking merge helper...\n"))
910 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
910 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
911 or "hgmerge")
911 or "hgmerge")
912 cmdpath = util.find_in_path(cmd, path)
912 cmdpath = util.find_in_path(cmd, path)
913 if not cmdpath:
913 if not cmdpath:
914 cmdpath = util.find_in_path(cmd.split()[0], path)
914 cmdpath = util.find_in_path(cmd.split()[0], path)
915 if not cmdpath:
915 if not cmdpath:
916 if cmd == 'hgmerge':
916 if cmd == 'hgmerge':
917 ui.write(_(" No merge helper set and can't find default"
917 ui.write(_(" No merge helper set and can't find default"
918 " hgmerge script in PATH\n"))
918 " hgmerge script in PATH\n"))
919 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
919 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
920 else:
920 else:
921 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
921 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
922 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
922 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
923 problems += 1
923 problems += 1
924 else:
924 else:
925 # actually attempt a patch here
925 # actually attempt a patch here
926 fa = writetemp("1\n2\n3\n4\n")
926 fa = writetemp("1\n2\n3\n4\n")
927 fl = writetemp("1\n2\n3\ninsert\n4\n")
927 fl = writetemp("1\n2\n3\ninsert\n4\n")
928 fr = writetemp("begin\n1\n2\n3\n4\n")
928 fr = writetemp("begin\n1\n2\n3\n4\n")
929 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
929 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
930 if r:
930 if r:
931 ui.write(_(" got unexpected merge error %d!") % r)
931 ui.write(_(" got unexpected merge error %d!") % r)
932 problems += 1
932 problems += 1
933 m = file(fl).read()
933 m = file(fl).read()
934 if m != "begin\n1\n2\n3\ninsert\n4\n":
934 if m != "begin\n1\n2\n3\ninsert\n4\n":
935 ui.write(_(" got unexpected merge results!") % r)
935 ui.write(_(" got unexpected merge results!") % r)
936 ui.write(_(" (your merge helper may have the"
936 ui.write(_(" (your merge helper may have the"
937 " wrong argument order)\n"))
937 " wrong argument order)\n"))
938 ui.write(m)
938 ui.write(m)
939 os.unlink(fa)
939 os.unlink(fa)
940 os.unlink(fl)
940 os.unlink(fl)
941 os.unlink(fr)
941 os.unlink(fr)
942
942
943 # editor
943 # editor
944 ui.status(_("Checking commit editor...\n"))
944 ui.status(_("Checking commit editor...\n"))
945 editor = (os.environ.get("HGEDITOR") or
945 editor = (os.environ.get("HGEDITOR") or
946 ui.config("ui", "editor") or
946 ui.config("ui", "editor") or
947 os.environ.get("EDITOR", "vi"))
947 os.environ.get("EDITOR", "vi"))
948 cmdpath = util.find_in_path(editor, path)
948 cmdpath = util.find_in_path(editor, path)
949 if not cmdpath:
949 if not cmdpath:
950 cmdpath = util.find_in_path(editor.split()[0], path)
950 cmdpath = util.find_in_path(editor.split()[0], path)
951 if not cmdpath:
951 if not cmdpath:
952 if editor == 'vi':
952 if editor == 'vi':
953 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
953 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
954 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
954 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
955 else:
955 else:
956 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
956 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
957 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
957 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
958 problems += 1
958 problems += 1
959
959
960 # check username
960 # check username
961 ui.status(_("Checking username...\n"))
961 ui.status(_("Checking username...\n"))
962 user = os.environ.get("HGUSER")
962 user = os.environ.get("HGUSER")
963 if user is None:
963 if user is None:
964 user = ui.config("ui", "username")
964 user = ui.config("ui", "username")
965 if user is None:
965 if user is None:
966 user = os.environ.get("EMAIL")
966 user = os.environ.get("EMAIL")
967 if not user:
967 if not user:
968 ui.warn(" ")
968 ui.warn(" ")
969 ui.username()
969 ui.username()
970 ui.write(_(" (specify a username in your .hgrc file)\n"))
970 ui.write(_(" (specify a username in your .hgrc file)\n"))
971
971
972 if not problems:
972 if not problems:
973 ui.status(_("No problems detected\n"))
973 ui.status(_("No problems detected\n"))
974 else:
974 else:
975 ui.write(_("%s problems detected,"
975 ui.write(_("%s problems detected,"
976 " please check your install!\n") % problems)
976 " please check your install!\n") % problems)
977
977
978 return problems
978 return problems
979
979
980 def debugrename(ui, repo, file1, *pats, **opts):
980 def debugrename(ui, repo, file1, *pats, **opts):
981 """dump rename information"""
981 """dump rename information"""
982
982
983 ctx = repo.changectx(opts.get('rev', 'tip'))
983 ctx = repo.changectx(opts.get('rev', 'tip'))
984 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
984 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
985 ctx.node()):
985 ctx.node()):
986 m = ctx.filectx(abs).renamed()
986 m = ctx.filectx(abs).renamed()
987 if m:
987 if m:
988 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
988 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
989 else:
989 else:
990 ui.write(_("%s not renamed\n") % rel)
990 ui.write(_("%s not renamed\n") % rel)
991
991
992 def debugwalk(ui, repo, *pats, **opts):
992 def debugwalk(ui, repo, *pats, **opts):
993 """show how files match on given patterns"""
993 """show how files match on given patterns"""
994 items = list(cmdutil.walk(repo, pats, opts))
994 items = list(cmdutil.walk(repo, pats, opts))
995 if not items:
995 if not items:
996 return
996 return
997 fmt = '%%s %%-%ds %%-%ds %%s' % (
997 fmt = '%%s %%-%ds %%-%ds %%s' % (
998 max([len(abs) for (src, abs, rel, exact) in items]),
998 max([len(abs) for (src, abs, rel, exact) in items]),
999 max([len(rel) for (src, abs, rel, exact) in items]))
999 max([len(rel) for (src, abs, rel, exact) in items]))
1000 for src, abs, rel, exact in items:
1000 for src, abs, rel, exact in items:
1001 line = fmt % (src, abs, rel, exact and 'exact' or '')
1001 line = fmt % (src, abs, rel, exact and 'exact' or '')
1002 ui.write("%s\n" % line.rstrip())
1002 ui.write("%s\n" % line.rstrip())
1003
1003
1004 def diff(ui, repo, *pats, **opts):
1004 def diff(ui, repo, *pats, **opts):
1005 """diff repository (or selected files)
1005 """diff repository (or selected files)
1006
1006
1007 Show differences between revisions for the specified files.
1007 Show differences between revisions for the specified files.
1008
1008
1009 Differences between files are shown using the unified diff format.
1009 Differences between files are shown using the unified diff format.
1010
1010
1011 NOTE: diff may generate unexpected results for merges, as it will
1011 NOTE: diff may generate unexpected results for merges, as it will
1012 default to comparing against the working directory's first parent
1012 default to comparing against the working directory's first parent
1013 changeset if no revisions are specified.
1013 changeset if no revisions are specified.
1014
1014
1015 When two revision arguments are given, then changes are shown
1015 When two revision arguments are given, then changes are shown
1016 between those revisions. If only one revision is specified then
1016 between those revisions. If only one revision is specified then
1017 that revision is compared to the working directory, and, when no
1017 that revision is compared to the working directory, and, when no
1018 revisions are specified, the working directory files are compared
1018 revisions are specified, the working directory files are compared
1019 to its parent.
1019 to its parent.
1020
1020
1021 Without the -a option, diff will avoid generating diffs of files
1021 Without the -a option, diff will avoid generating diffs of files
1022 it detects as binary. With -a, diff will generate a diff anyway,
1022 it detects as binary. With -a, diff will generate a diff anyway,
1023 probably with undesirable results.
1023 probably with undesirable results.
1024 """
1024 """
1025 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1025 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1026
1026
1027 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1027 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1028
1028
1029 patch.diff(repo, node1, node2, fns, match=matchfn,
1029 patch.diff(repo, node1, node2, fns, match=matchfn,
1030 opts=patch.diffopts(ui, opts))
1030 opts=patch.diffopts(ui, opts))
1031
1031
1032 def export(ui, repo, *changesets, **opts):
1032 def export(ui, repo, *changesets, **opts):
1033 """dump the header and diffs for one or more changesets
1033 """dump the header and diffs for one or more changesets
1034
1034
1035 Print the changeset header and diffs for one or more revisions.
1035 Print the changeset header and diffs for one or more revisions.
1036
1036
1037 The information shown in the changeset header is: author,
1037 The information shown in the changeset header is: author,
1038 changeset hash, parent(s) and commit comment.
1038 changeset hash, parent(s) and commit comment.
1039
1039
1040 NOTE: export may generate unexpected diff output for merge changesets,
1040 NOTE: export may generate unexpected diff output for merge changesets,
1041 as it will compare the merge changeset against its first parent only.
1041 as it will compare the merge changeset against its first parent only.
1042
1042
1043 Output may be to a file, in which case the name of the file is
1043 Output may be to a file, in which case the name of the file is
1044 given using a format string. The formatting rules are as follows:
1044 given using a format string. The formatting rules are as follows:
1045
1045
1046 %% literal "%" character
1046 %% literal "%" character
1047 %H changeset hash (40 bytes of hexadecimal)
1047 %H changeset hash (40 bytes of hexadecimal)
1048 %N number of patches being generated
1048 %N number of patches being generated
1049 %R changeset revision number
1049 %R changeset revision number
1050 %b basename of the exporting repository
1050 %b basename of the exporting repository
1051 %h short-form changeset hash (12 bytes of hexadecimal)
1051 %h short-form changeset hash (12 bytes of hexadecimal)
1052 %n zero-padded sequence number, starting at 1
1052 %n zero-padded sequence number, starting at 1
1053 %r zero-padded changeset revision number
1053 %r zero-padded changeset revision number
1054
1054
1055 Without the -a option, export will avoid generating diffs of files
1055 Without the -a option, export will avoid generating diffs of files
1056 it detects as binary. With -a, export will generate a diff anyway,
1056 it detects as binary. With -a, export will generate a diff anyway,
1057 probably with undesirable results.
1057 probably with undesirable results.
1058
1058
1059 With the --switch-parent option, the diff will be against the second
1059 With the --switch-parent option, the diff will be against the second
1060 parent. It can be useful to review a merge.
1060 parent. It can be useful to review a merge.
1061 """
1061 """
1062 if not changesets:
1062 if not changesets:
1063 raise util.Abort(_("export requires at least one changeset"))
1063 raise util.Abort(_("export requires at least one changeset"))
1064 revs = cmdutil.revrange(repo, changesets)
1064 revs = cmdutil.revrange(repo, changesets)
1065 if len(revs) > 1:
1065 if len(revs) > 1:
1066 ui.note(_('exporting patches:\n'))
1066 ui.note(_('exporting patches:\n'))
1067 else:
1067 else:
1068 ui.note(_('exporting patch:\n'))
1068 ui.note(_('exporting patch:\n'))
1069 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1069 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1070 switch_parent=opts['switch_parent'],
1070 switch_parent=opts['switch_parent'],
1071 opts=patch.diffopts(ui, opts))
1071 opts=patch.diffopts(ui, opts))
1072
1072
1073 def grep(ui, repo, pattern, *pats, **opts):
1073 def grep(ui, repo, pattern, *pats, **opts):
1074 """search for a pattern in specified files and revisions
1074 """search for a pattern in specified files and revisions
1075
1075
1076 Search revisions of files for a regular expression.
1076 Search revisions of files for a regular expression.
1077
1077
1078 This command behaves differently than Unix grep. It only accepts
1078 This command behaves differently than Unix grep. It only accepts
1079 Python/Perl regexps. It searches repository history, not the
1079 Python/Perl regexps. It searches repository history, not the
1080 working directory. It always prints the revision number in which
1080 working directory. It always prints the revision number in which
1081 a match appears.
1081 a match appears.
1082
1082
1083 By default, grep only prints output for the first revision of a
1083 By default, grep only prints output for the first revision of a
1084 file in which it finds a match. To get it to print every revision
1084 file in which it finds a match. To get it to print every revision
1085 that contains a change in match status ("-" for a match that
1085 that contains a change in match status ("-" for a match that
1086 becomes a non-match, or "+" for a non-match that becomes a match),
1086 becomes a non-match, or "+" for a non-match that becomes a match),
1087 use the --all flag.
1087 use the --all flag.
1088 """
1088 """
1089 reflags = 0
1089 reflags = 0
1090 if opts['ignore_case']:
1090 if opts['ignore_case']:
1091 reflags |= re.I
1091 reflags |= re.I
1092 regexp = re.compile(pattern, reflags)
1092 regexp = re.compile(pattern, reflags)
1093 sep, eol = ':', '\n'
1093 sep, eol = ':', '\n'
1094 if opts['print0']:
1094 if opts['print0']:
1095 sep = eol = '\0'
1095 sep = eol = '\0'
1096
1096
1097 fcache = {}
1097 fcache = {}
1098 def getfile(fn):
1098 def getfile(fn):
1099 if fn not in fcache:
1099 if fn not in fcache:
1100 fcache[fn] = repo.file(fn)
1100 fcache[fn] = repo.file(fn)
1101 return fcache[fn]
1101 return fcache[fn]
1102
1102
1103 def matchlines(body):
1103 def matchlines(body):
1104 begin = 0
1104 begin = 0
1105 linenum = 0
1105 linenum = 0
1106 while True:
1106 while True:
1107 match = regexp.search(body, begin)
1107 match = regexp.search(body, begin)
1108 if not match:
1108 if not match:
1109 break
1109 break
1110 mstart, mend = match.span()
1110 mstart, mend = match.span()
1111 linenum += body.count('\n', begin, mstart) + 1
1111 linenum += body.count('\n', begin, mstart) + 1
1112 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1112 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1113 lend = body.find('\n', mend)
1113 lend = body.find('\n', mend)
1114 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1114 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1115 begin = lend + 1
1115 begin = lend + 1
1116
1116
1117 class linestate(object):
1117 class linestate(object):
1118 def __init__(self, line, linenum, colstart, colend):
1118 def __init__(self, line, linenum, colstart, colend):
1119 self.line = line
1119 self.line = line
1120 self.linenum = linenum
1120 self.linenum = linenum
1121 self.colstart = colstart
1121 self.colstart = colstart
1122 self.colend = colend
1122 self.colend = colend
1123
1123
1124 def __eq__(self, other):
1124 def __eq__(self, other):
1125 return self.line == other.line
1125 return self.line == other.line
1126
1126
1127 matches = {}
1127 matches = {}
1128 copies = {}
1128 copies = {}
1129 def grepbody(fn, rev, body):
1129 def grepbody(fn, rev, body):
1130 matches[rev].setdefault(fn, [])
1130 matches[rev].setdefault(fn, [])
1131 m = matches[rev][fn]
1131 m = matches[rev][fn]
1132 for lnum, cstart, cend, line in matchlines(body):
1132 for lnum, cstart, cend, line in matchlines(body):
1133 s = linestate(line, lnum, cstart, cend)
1133 s = linestate(line, lnum, cstart, cend)
1134 m.append(s)
1134 m.append(s)
1135
1135
1136 def difflinestates(a, b):
1136 def difflinestates(a, b):
1137 sm = difflib.SequenceMatcher(None, a, b)
1137 sm = difflib.SequenceMatcher(None, a, b)
1138 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1138 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1139 if tag == 'insert':
1139 if tag == 'insert':
1140 for i in xrange(blo, bhi):
1140 for i in xrange(blo, bhi):
1141 yield ('+', b[i])
1141 yield ('+', b[i])
1142 elif tag == 'delete':
1142 elif tag == 'delete':
1143 for i in xrange(alo, ahi):
1143 for i in xrange(alo, ahi):
1144 yield ('-', a[i])
1144 yield ('-', a[i])
1145 elif tag == 'replace':
1145 elif tag == 'replace':
1146 for i in xrange(alo, ahi):
1146 for i in xrange(alo, ahi):
1147 yield ('-', a[i])
1147 yield ('-', a[i])
1148 for i in xrange(blo, bhi):
1148 for i in xrange(blo, bhi):
1149 yield ('+', b[i])
1149 yield ('+', b[i])
1150
1150
1151 prev = {}
1151 prev = {}
1152 def display(fn, rev, states, prevstates):
1152 def display(fn, rev, states, prevstates):
1153 counts = {'-': 0, '+': 0}
1153 counts = {'-': 0, '+': 0}
1154 filerevmatches = {}
1154 filerevmatches = {}
1155 if incrementing or not opts['all']:
1155 if incrementing or not opts['all']:
1156 a, b, r = prevstates, states, rev
1156 a, b, r = prevstates, states, rev
1157 else:
1157 else:
1158 a, b, r = states, prevstates, prev.get(fn, -1)
1158 a, b, r = states, prevstates, prev.get(fn, -1)
1159 for change, l in difflinestates(a, b):
1159 for change, l in difflinestates(a, b):
1160 cols = [fn, str(r)]
1160 cols = [fn, str(r)]
1161 if opts['line_number']:
1161 if opts['line_number']:
1162 cols.append(str(l.linenum))
1162 cols.append(str(l.linenum))
1163 if opts['all']:
1163 if opts['all']:
1164 cols.append(change)
1164 cols.append(change)
1165 if opts['user']:
1165 if opts['user']:
1166 cols.append(ui.shortuser(get(r)[1]))
1166 cols.append(ui.shortuser(get(r)[1]))
1167 if opts['files_with_matches']:
1167 if opts['files_with_matches']:
1168 c = (fn, r)
1168 c = (fn, r)
1169 if c in filerevmatches:
1169 if c in filerevmatches:
1170 continue
1170 continue
1171 filerevmatches[c] = 1
1171 filerevmatches[c] = 1
1172 else:
1172 else:
1173 cols.append(l.line)
1173 cols.append(l.line)
1174 ui.write(sep.join(cols), eol)
1174 ui.write(sep.join(cols), eol)
1175 counts[change] += 1
1175 counts[change] += 1
1176 return counts['+'], counts['-']
1176 return counts['+'], counts['-']
1177
1177
1178 fstate = {}
1178 fstate = {}
1179 skip = {}
1179 skip = {}
1180 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1180 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1181 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1181 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1182 count = 0
1182 count = 0
1183 incrementing = False
1183 incrementing = False
1184 follow = opts.get('follow')
1184 follow = opts.get('follow')
1185 for st, rev, fns in changeiter:
1185 for st, rev, fns in changeiter:
1186 if st == 'window':
1186 if st == 'window':
1187 incrementing = rev
1187 incrementing = rev
1188 matches.clear()
1188 matches.clear()
1189 elif st == 'add':
1189 elif st == 'add':
1190 mf = repo.changectx(rev).manifest()
1190 mf = repo.changectx(rev).manifest()
1191 matches[rev] = {}
1191 matches[rev] = {}
1192 for fn in fns:
1192 for fn in fns:
1193 if fn in skip:
1193 if fn in skip:
1194 continue
1194 continue
1195 fstate.setdefault(fn, {})
1195 fstate.setdefault(fn, {})
1196 try:
1196 try:
1197 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1197 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1198 if follow:
1198 if follow:
1199 copied = getfile(fn).renamed(mf[fn])
1199 copied = getfile(fn).renamed(mf[fn])
1200 if copied:
1200 if copied:
1201 copies.setdefault(rev, {})[fn] = copied[0]
1201 copies.setdefault(rev, {})[fn] = copied[0]
1202 except KeyError:
1202 except KeyError:
1203 pass
1203 pass
1204 elif st == 'iter':
1204 elif st == 'iter':
1205 states = matches[rev].items()
1205 states = matches[rev].items()
1206 states.sort()
1206 states.sort()
1207 for fn, m in states:
1207 for fn, m in states:
1208 copy = copies.get(rev, {}).get(fn)
1208 copy = copies.get(rev, {}).get(fn)
1209 if fn in skip:
1209 if fn in skip:
1210 if copy:
1210 if copy:
1211 skip[copy] = True
1211 skip[copy] = True
1212 continue
1212 continue
1213 if incrementing or not opts['all'] or fstate[fn]:
1213 if incrementing or not opts['all'] or fstate[fn]:
1214 pos, neg = display(fn, rev, m, fstate[fn])
1214 pos, neg = display(fn, rev, m, fstate[fn])
1215 count += pos + neg
1215 count += pos + neg
1216 if pos and not opts['all']:
1216 if pos and not opts['all']:
1217 skip[fn] = True
1217 skip[fn] = True
1218 if copy:
1218 if copy:
1219 skip[copy] = True
1219 skip[copy] = True
1220 fstate[fn] = m
1220 fstate[fn] = m
1221 if copy:
1221 if copy:
1222 fstate[copy] = m
1222 fstate[copy] = m
1223 prev[fn] = rev
1223 prev[fn] = rev
1224
1224
1225 if not incrementing:
1225 if not incrementing:
1226 fstate = fstate.items()
1226 fstate = fstate.items()
1227 fstate.sort()
1227 fstate.sort()
1228 for fn, state in fstate:
1228 for fn, state in fstate:
1229 if fn in skip:
1229 if fn in skip:
1230 continue
1230 continue
1231 if fn not in copies.get(prev[fn], {}):
1231 if fn not in copies.get(prev[fn], {}):
1232 display(fn, rev, {}, state)
1232 display(fn, rev, {}, state)
1233 return (count == 0 and 1) or 0
1233 return (count == 0 and 1) or 0
1234
1234
1235 def heads(ui, repo, **opts):
1235 def heads(ui, repo, **opts):
1236 """show current repository heads
1236 """show current repository heads
1237
1237
1238 Show all repository head changesets.
1238 Show all repository head changesets.
1239
1239
1240 Repository "heads" are changesets that don't have children
1240 Repository "heads" are changesets that don't have children
1241 changesets. They are where development generally takes place and
1241 changesets. They are where development generally takes place and
1242 are the usual targets for update and merge operations.
1242 are the usual targets for update and merge operations.
1243 """
1243 """
1244 if opts['rev']:
1244 if opts['rev']:
1245 heads = repo.heads(repo.lookup(opts['rev']))
1245 heads = repo.heads(repo.lookup(opts['rev']))
1246 else:
1246 else:
1247 heads = repo.heads()
1247 heads = repo.heads()
1248 displayer = cmdutil.show_changeset(ui, repo, opts)
1248 displayer = cmdutil.show_changeset(ui, repo, opts)
1249 for n in heads:
1249 for n in heads:
1250 displayer.show(changenode=n)
1250 displayer.show(changenode=n)
1251
1251
1252 def help_(ui, name=None, with_version=False):
1252 def help_(ui, name=None, with_version=False):
1253 """show help for a command, extension, or list of commands
1253 """show help for a command, extension, or list of commands
1254
1254
1255 With no arguments, print a list of commands and short help.
1255 With no arguments, print a list of commands and short help.
1256
1256
1257 Given a command name, print help for that command.
1257 Given a command name, print help for that command.
1258
1258
1259 Given an extension name, print help for that extension, and the
1259 Given an extension name, print help for that extension, and the
1260 commands it provides."""
1260 commands it provides."""
1261 option_lists = []
1261 option_lists = []
1262
1262
1263 def helpcmd(name):
1263 def helpcmd(name):
1264 if with_version:
1264 if with_version:
1265 version_(ui)
1265 version_(ui)
1266 ui.write('\n')
1266 ui.write('\n')
1267 aliases, i = findcmd(ui, name)
1267 aliases, i = findcmd(ui, name)
1268 # synopsis
1268 # synopsis
1269 ui.write("%s\n\n" % i[2])
1269 ui.write("%s\n\n" % i[2])
1270
1270
1271 # description
1271 # description
1272 doc = i[0].__doc__
1272 doc = i[0].__doc__
1273 if not doc:
1273 if not doc:
1274 doc = _("(No help text available)")
1274 doc = _("(No help text available)")
1275 if ui.quiet:
1275 if ui.quiet:
1276 doc = doc.splitlines(0)[0]
1276 doc = doc.splitlines(0)[0]
1277 ui.write("%s\n" % doc.rstrip())
1277 ui.write("%s\n" % doc.rstrip())
1278
1278
1279 if not ui.quiet:
1279 if not ui.quiet:
1280 # aliases
1280 # aliases
1281 if len(aliases) > 1:
1281 if len(aliases) > 1:
1282 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1282 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1283
1283
1284 # options
1284 # options
1285 if i[1]:
1285 if i[1]:
1286 option_lists.append(("options", i[1]))
1286 option_lists.append(("options", i[1]))
1287
1287
1288 def helplist(select=None):
1288 def helplist(select=None):
1289 h = {}
1289 h = {}
1290 cmds = {}
1290 cmds = {}
1291 for c, e in table.items():
1291 for c, e in table.items():
1292 f = c.split("|", 1)[0]
1292 f = c.split("|", 1)[0]
1293 if select and not select(f):
1293 if select and not select(f):
1294 continue
1294 continue
1295 if name == "shortlist" and not f.startswith("^"):
1295 if name == "shortlist" and not f.startswith("^"):
1296 continue
1296 continue
1297 f = f.lstrip("^")
1297 f = f.lstrip("^")
1298 if not ui.debugflag and f.startswith("debug"):
1298 if not ui.debugflag and f.startswith("debug"):
1299 continue
1299 continue
1300 doc = e[0].__doc__
1300 doc = e[0].__doc__
1301 if not doc:
1301 if not doc:
1302 doc = _("(No help text available)")
1302 doc = _("(No help text available)")
1303 h[f] = doc.splitlines(0)[0].rstrip()
1303 h[f] = doc.splitlines(0)[0].rstrip()
1304 cmds[f] = c.lstrip("^")
1304 cmds[f] = c.lstrip("^")
1305
1305
1306 fns = h.keys()
1306 fns = h.keys()
1307 fns.sort()
1307 fns.sort()
1308 m = max(map(len, fns))
1308 m = max(map(len, fns))
1309 for f in fns:
1309 for f in fns:
1310 if ui.verbose:
1310 if ui.verbose:
1311 commands = cmds[f].replace("|",", ")
1311 commands = cmds[f].replace("|",", ")
1312 ui.write(" %s:\n %s\n"%(commands, h[f]))
1312 ui.write(" %s:\n %s\n"%(commands, h[f]))
1313 else:
1313 else:
1314 ui.write(' %-*s %s\n' % (m, f, h[f]))
1314 ui.write(' %-*s %s\n' % (m, f, h[f]))
1315
1315
1316 def helptopic(name):
1316 def helptopic(name):
1317 v = None
1317 v = None
1318 for i in help.helptable:
1318 for i in help.helptable:
1319 l = i.split('|')
1319 l = i.split('|')
1320 if name in l:
1320 if name in l:
1321 v = i
1321 v = i
1322 header = l[-1]
1322 header = l[-1]
1323 if not v:
1323 if not v:
1324 raise UnknownCommand(name)
1324 raise UnknownCommand(name)
1325
1325
1326 # description
1326 # description
1327 doc = help.helptable[v]
1327 doc = help.helptable[v]
1328 if not doc:
1328 if not doc:
1329 doc = _("(No help text available)")
1329 doc = _("(No help text available)")
1330 if callable(doc):
1330 if callable(doc):
1331 doc = doc()
1331 doc = doc()
1332
1332
1333 ui.write("%s\n" % header)
1333 ui.write("%s\n" % header)
1334 ui.write("%s\n" % doc.rstrip())
1334 ui.write("%s\n" % doc.rstrip())
1335
1335
1336 def helpext(name):
1336 def helpext(name):
1337 try:
1337 try:
1338 mod = findext(name)
1338 mod = findext(name)
1339 except KeyError:
1339 except KeyError:
1340 raise UnknownCommand(name)
1340 raise UnknownCommand(name)
1341
1341
1342 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1342 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1343 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1343 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1344 for d in doc[1:]:
1344 for d in doc[1:]:
1345 ui.write(d, '\n')
1345 ui.write(d, '\n')
1346
1346
1347 ui.status('\n')
1347 ui.status('\n')
1348 if ui.verbose:
1348 if ui.verbose:
1349 ui.status(_('list of commands:\n\n'))
1349 ui.status(_('list of commands:\n\n'))
1350 else:
1350 else:
1351 ui.status(_('list of commands (use "hg help -v %s" '
1351 ui.status(_('list of commands (use "hg help -v %s" '
1352 'to show aliases and global options):\n\n') % name)
1352 'to show aliases and global options):\n\n') % name)
1353
1353
1354 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1354 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1355 helplist(modcmds.has_key)
1355 helplist(modcmds.has_key)
1356
1356
1357 if name and name != 'shortlist':
1357 if name and name != 'shortlist':
1358 i = None
1358 i = None
1359 for f in (helpcmd, helptopic, helpext):
1359 for f in (helpcmd, helptopic, helpext):
1360 try:
1360 try:
1361 f(name)
1361 f(name)
1362 i = None
1362 i = None
1363 break
1363 break
1364 except UnknownCommand, inst:
1364 except UnknownCommand, inst:
1365 i = inst
1365 i = inst
1366 if i:
1366 if i:
1367 raise i
1367 raise i
1368
1368
1369 else:
1369 else:
1370 # program name
1370 # program name
1371 if ui.verbose or with_version:
1371 if ui.verbose or with_version:
1372 version_(ui)
1372 version_(ui)
1373 else:
1373 else:
1374 ui.status(_("Mercurial Distributed SCM\n"))
1374 ui.status(_("Mercurial Distributed SCM\n"))
1375 ui.status('\n')
1375 ui.status('\n')
1376
1376
1377 # list of commands
1377 # list of commands
1378 if name == "shortlist":
1378 if name == "shortlist":
1379 ui.status(_('basic commands (use "hg help" '
1379 ui.status(_('basic commands (use "hg help" '
1380 'for the full list or option "-v" for details):\n\n'))
1380 'for the full list or option "-v" for details):\n\n'))
1381 elif ui.verbose:
1381 elif ui.verbose:
1382 ui.status(_('list of commands:\n\n'))
1382 ui.status(_('list of commands:\n\n'))
1383 else:
1383 else:
1384 ui.status(_('list of commands (use "hg help -v" '
1384 ui.status(_('list of commands (use "hg help -v" '
1385 'to show aliases and global options):\n\n'))
1385 'to show aliases and global options):\n\n'))
1386
1386
1387 helplist()
1387 helplist()
1388
1388
1389 # global options
1389 # global options
1390 if ui.verbose:
1390 if ui.verbose:
1391 option_lists.append(("global options", globalopts))
1391 option_lists.append(("global options", globalopts))
1392
1392
1393 # list all option lists
1393 # list all option lists
1394 opt_output = []
1394 opt_output = []
1395 for title, options in option_lists:
1395 for title, options in option_lists:
1396 opt_output.append(("\n%s:\n" % title, None))
1396 opt_output.append(("\n%s:\n" % title, None))
1397 for shortopt, longopt, default, desc in options:
1397 for shortopt, longopt, default, desc in options:
1398 if "DEPRECATED" in desc and not ui.verbose: continue
1398 if "DEPRECATED" in desc and not ui.verbose: continue
1399 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1399 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1400 longopt and " --%s" % longopt),
1400 longopt and " --%s" % longopt),
1401 "%s%s" % (desc,
1401 "%s%s" % (desc,
1402 default
1402 default
1403 and _(" (default: %s)") % default
1403 and _(" (default: %s)") % default
1404 or "")))
1404 or "")))
1405
1405
1406 if opt_output:
1406 if opt_output:
1407 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1407 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1408 for first, second in opt_output:
1408 for first, second in opt_output:
1409 if second:
1409 if second:
1410 ui.write(" %-*s %s\n" % (opts_len, first, second))
1410 ui.write(" %-*s %s\n" % (opts_len, first, second))
1411 else:
1411 else:
1412 ui.write("%s\n" % first)
1412 ui.write("%s\n" % first)
1413
1413
1414 def identify(ui, repo):
1414 def identify(ui, repo):
1415 """print information about the working copy
1415 """print information about the working copy
1416
1416
1417 Print a short summary of the current state of the repo.
1417 Print a short summary of the current state of the repo.
1418
1418
1419 This summary identifies the repository state using one or two parent
1419 This summary identifies the repository state using one or two parent
1420 hash identifiers, followed by a "+" if there are uncommitted changes
1420 hash identifiers, followed by a "+" if there are uncommitted changes
1421 in the working directory, followed by a list of tags for this revision.
1421 in the working directory, followed by a list of tags for this revision.
1422 """
1422 """
1423 parents = [p for p in repo.dirstate.parents() if p != nullid]
1423 parents = [p for p in repo.dirstate.parents() if p != nullid]
1424 if not parents:
1424 if not parents:
1425 ui.write(_("unknown\n"))
1425 ui.write(_("unknown\n"))
1426 return
1426 return
1427
1427
1428 hexfunc = ui.debugflag and hex or short
1428 hexfunc = ui.debugflag and hex or short
1429 modified, added, removed, deleted = repo.status()[:4]
1429 modified, added, removed, deleted = repo.status()[:4]
1430 output = ["%s%s" %
1430 output = ["%s%s" %
1431 ('+'.join([hexfunc(parent) for parent in parents]),
1431 ('+'.join([hexfunc(parent) for parent in parents]),
1432 (modified or added or removed or deleted) and "+" or "")]
1432 (modified or added or removed or deleted) and "+" or "")]
1433
1433
1434 if not ui.quiet:
1434 if not ui.quiet:
1435
1435
1436 branch = repo.workingctx().branch()
1436 branch = repo.workingctx().branch()
1437 if branch:
1437 if branch:
1438 output.append("(%s)" % branch)
1438 output.append("(%s)" % branch)
1439
1439
1440 # multiple tags for a single parent separated by '/'
1440 # multiple tags for a single parent separated by '/'
1441 parenttags = ['/'.join(tags)
1441 parenttags = ['/'.join(tags)
1442 for tags in map(repo.nodetags, parents) if tags]
1442 for tags in map(repo.nodetags, parents) if tags]
1443 # tags for multiple parents separated by ' + '
1443 # tags for multiple parents separated by ' + '
1444 if parenttags:
1444 if parenttags:
1445 output.append(' + '.join(parenttags))
1445 output.append(' + '.join(parenttags))
1446
1446
1447 ui.write("%s\n" % ' '.join(output))
1447 ui.write("%s\n" % ' '.join(output))
1448
1448
1449 def import_(ui, repo, patch1, *patches, **opts):
1449 def import_(ui, repo, patch1, *patches, **opts):
1450 """import an ordered set of patches
1450 """import an ordered set of patches
1451
1451
1452 Import a list of patches and commit them individually.
1452 Import a list of patches and commit them individually.
1453
1453
1454 If there are outstanding changes in the working directory, import
1454 If there are outstanding changes in the working directory, import
1455 will abort unless given the -f flag.
1455 will abort unless given the -f flag.
1456
1456
1457 You can import a patch straight from a mail message. Even patches
1457 You can import a patch straight from a mail message. Even patches
1458 as attachments work (body part must be type text/plain or
1458 as attachments work (body part must be type text/plain or
1459 text/x-patch to be used). From and Subject headers of email
1459 text/x-patch to be used). From and Subject headers of email
1460 message are used as default committer and commit message. All
1460 message are used as default committer and commit message. All
1461 text/plain body parts before first diff are added to commit
1461 text/plain body parts before first diff are added to commit
1462 message.
1462 message.
1463
1463
1464 If imported patch was generated by hg export, user and description
1464 If imported patch was generated by hg export, user and description
1465 from patch override values from message headers and body. Values
1465 from patch override values from message headers and body. Values
1466 given on command line with -m and -u override these.
1466 given on command line with -m and -u override these.
1467
1467
1468 To read a patch from standard input, use patch name "-".
1468 To read a patch from standard input, use patch name "-".
1469 """
1469 """
1470 patches = (patch1,) + patches
1470 patches = (patch1,) + patches
1471
1471
1472 if not opts['force']:
1472 if not opts['force']:
1473 bail_if_changed(repo)
1473 bail_if_changed(repo)
1474
1474
1475 d = opts["base"]
1475 d = opts["base"]
1476 strip = opts["strip"]
1476 strip = opts["strip"]
1477
1477
1478 wlock = repo.wlock()
1478 wlock = repo.wlock()
1479 lock = repo.lock()
1479 lock = repo.lock()
1480
1480
1481 for p in patches:
1481 for p in patches:
1482 pf = os.path.join(d, p)
1482 pf = os.path.join(d, p)
1483
1483
1484 if pf == '-':
1484 if pf == '-':
1485 ui.status(_("applying patch from stdin\n"))
1485 ui.status(_("applying patch from stdin\n"))
1486 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1486 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1487 else:
1487 else:
1488 ui.status(_("applying %s\n") % p)
1488 ui.status(_("applying %s\n") % p)
1489 tmpname, message, user, date = patch.extract(ui, file(pf))
1489 tmpname, message, user, date = patch.extract(ui, file(pf))
1490
1490
1491 if tmpname is None:
1491 if tmpname is None:
1492 raise util.Abort(_('no diffs found'))
1492 raise util.Abort(_('no diffs found'))
1493
1493
1494 try:
1494 try:
1495 cmdline_message = logmessage(opts)
1495 cmdline_message = logmessage(opts)
1496 if cmdline_message:
1496 if cmdline_message:
1497 # pickup the cmdline msg
1497 # pickup the cmdline msg
1498 message = cmdline_message
1498 message = cmdline_message
1499 elif message:
1499 elif message:
1500 # pickup the patch msg
1500 # pickup the patch msg
1501 message = message.strip()
1501 message = message.strip()
1502 else:
1502 else:
1503 # launch the editor
1503 # launch the editor
1504 message = None
1504 message = None
1505 ui.debug(_('message:\n%s\n') % message)
1505 ui.debug(_('message:\n%s\n') % message)
1506
1506
1507 files = {}
1507 files = {}
1508 try:
1508 try:
1509 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1509 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1510 files=files)
1510 files=files)
1511 finally:
1511 finally:
1512 files = patch.updatedir(ui, repo, files, wlock=wlock)
1512 files = patch.updatedir(ui, repo, files, wlock=wlock)
1513 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1513 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1514 finally:
1514 finally:
1515 os.unlink(tmpname)
1515 os.unlink(tmpname)
1516
1516
1517 def incoming(ui, repo, source="default", **opts):
1517 def incoming(ui, repo, source="default", **opts):
1518 """show new changesets found in source
1518 """show new changesets found in source
1519
1519
1520 Show new changesets found in the specified path/URL or the default
1520 Show new changesets found in the specified path/URL or the default
1521 pull location. These are the changesets that would be pulled if a pull
1521 pull location. These are the changesets that would be pulled if a pull
1522 was requested.
1522 was requested.
1523
1523
1524 For remote repository, using --bundle avoids downloading the changesets
1524 For remote repository, using --bundle avoids downloading the changesets
1525 twice if the incoming is followed by a pull.
1525 twice if the incoming is followed by a pull.
1526
1526
1527 See pull for valid source format details.
1527 See pull for valid source format details.
1528 """
1528 """
1529 source = ui.expandpath(source)
1529 source = ui.expandpath(source)
1530 setremoteconfig(ui, opts)
1530 setremoteconfig(ui, opts)
1531
1531
1532 other = hg.repository(ui, source)
1532 other = hg.repository(ui, source)
1533 incoming = repo.findincoming(other, force=opts["force"])
1533 incoming = repo.findincoming(other, force=opts["force"])
1534 if not incoming:
1534 if not incoming:
1535 ui.status(_("no changes found\n"))
1535 ui.status(_("no changes found\n"))
1536 return
1536 return
1537
1537
1538 cleanup = None
1538 cleanup = None
1539 try:
1539 try:
1540 fname = opts["bundle"]
1540 fname = opts["bundle"]
1541 if fname or not other.local():
1541 if fname or not other.local():
1542 # create a bundle (uncompressed if other repo is not local)
1542 # create a bundle (uncompressed if other repo is not local)
1543 cg = other.changegroup(incoming, "incoming")
1543 cg = other.changegroup(incoming, "incoming")
1544 bundletype = other.local() and "HG10BZ" or "HG10UN"
1544 bundletype = other.local() and "HG10BZ" or "HG10UN"
1545 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1545 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1546 # keep written bundle?
1546 # keep written bundle?
1547 if opts["bundle"]:
1547 if opts["bundle"]:
1548 cleanup = None
1548 cleanup = None
1549 if not other.local():
1549 if not other.local():
1550 # use the created uncompressed bundlerepo
1550 # use the created uncompressed bundlerepo
1551 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1551 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1552
1552
1553 revs = None
1553 revs = None
1554 if opts['rev']:
1554 if opts['rev']:
1555 revs = [other.lookup(rev) for rev in opts['rev']]
1555 revs = [other.lookup(rev) for rev in opts['rev']]
1556 o = other.changelog.nodesbetween(incoming, revs)[0]
1556 o = other.changelog.nodesbetween(incoming, revs)[0]
1557 if opts['newest_first']:
1557 if opts['newest_first']:
1558 o.reverse()
1558 o.reverse()
1559 displayer = cmdutil.show_changeset(ui, other, opts)
1559 displayer = cmdutil.show_changeset(ui, other, opts)
1560 for n in o:
1560 for n in o:
1561 parents = [p for p in other.changelog.parents(n) if p != nullid]
1561 parents = [p for p in other.changelog.parents(n) if p != nullid]
1562 if opts['no_merges'] and len(parents) == 2:
1562 if opts['no_merges'] and len(parents) == 2:
1563 continue
1563 continue
1564 displayer.show(changenode=n)
1564 displayer.show(changenode=n)
1565 finally:
1565 finally:
1566 if hasattr(other, 'close'):
1566 if hasattr(other, 'close'):
1567 other.close()
1567 other.close()
1568 if cleanup:
1568 if cleanup:
1569 os.unlink(cleanup)
1569 os.unlink(cleanup)
1570
1570
1571 def init(ui, dest=".", **opts):
1571 def init(ui, dest=".", **opts):
1572 """create a new repository in the given directory
1572 """create a new repository in the given directory
1573
1573
1574 Initialize a new repository in the given directory. If the given
1574 Initialize a new repository in the given directory. If the given
1575 directory does not exist, it is created.
1575 directory does not exist, it is created.
1576
1576
1577 If no directory is given, the current directory is used.
1577 If no directory is given, the current directory is used.
1578
1578
1579 It is possible to specify an ssh:// URL as the destination.
1579 It is possible to specify an ssh:// URL as the destination.
1580 Look at the help text for the pull command for important details
1580 Look at the help text for the pull command for important details
1581 about ssh:// URLs.
1581 about ssh:// URLs.
1582 """
1582 """
1583 setremoteconfig(ui, opts)
1583 setremoteconfig(ui, opts)
1584 hg.repository(ui, dest, create=1)
1584 hg.repository(ui, dest, create=1)
1585
1585
1586 def locate(ui, repo, *pats, **opts):
1586 def locate(ui, repo, *pats, **opts):
1587 """locate files matching specific patterns
1587 """locate files matching specific patterns
1588
1588
1589 Print all files under Mercurial control whose names match the
1589 Print all files under Mercurial control whose names match the
1590 given patterns.
1590 given patterns.
1591
1591
1592 This command searches the current directory and its
1592 This command searches the current directory and its
1593 subdirectories. To search an entire repository, move to the root
1593 subdirectories. To search an entire repository, move to the root
1594 of the repository.
1594 of the repository.
1595
1595
1596 If no patterns are given to match, this command prints all file
1596 If no patterns are given to match, this command prints all file
1597 names.
1597 names.
1598
1598
1599 If you want to feed the output of this command into the "xargs"
1599 If you want to feed the output of this command into the "xargs"
1600 command, use the "-0" option to both this command and "xargs".
1600 command, use the "-0" option to both this command and "xargs".
1601 This will avoid the problem of "xargs" treating single filenames
1601 This will avoid the problem of "xargs" treating single filenames
1602 that contain white space as multiple filenames.
1602 that contain white space as multiple filenames.
1603 """
1603 """
1604 end = opts['print0'] and '\0' or '\n'
1604 end = opts['print0'] and '\0' or '\n'
1605 rev = opts['rev']
1605 rev = opts['rev']
1606 if rev:
1606 if rev:
1607 node = repo.lookup(rev)
1607 node = repo.lookup(rev)
1608 else:
1608 else:
1609 node = None
1609 node = None
1610
1610
1611 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1611 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1612 head='(?:.*/|)'):
1612 head='(?:.*/|)'):
1613 if not node and repo.dirstate.state(abs) == '?':
1613 if not node and repo.dirstate.state(abs) == '?':
1614 continue
1614 continue
1615 if opts['fullpath']:
1615 if opts['fullpath']:
1616 ui.write(os.path.join(repo.root, abs), end)
1616 ui.write(os.path.join(repo.root, abs), end)
1617 else:
1617 else:
1618 ui.write(((pats and rel) or abs), end)
1618 ui.write(((pats and rel) or abs), end)
1619
1619
1620 def log(ui, repo, *pats, **opts):
1620 def log(ui, repo, *pats, **opts):
1621 """show revision history of entire repository or files
1621 """show revision history of entire repository or files
1622
1622
1623 Print the revision history of the specified files or the entire
1623 Print the revision history of the specified files or the entire
1624 project.
1624 project.
1625
1625
1626 File history is shown without following rename or copy history of
1626 File history is shown without following rename or copy history of
1627 files. Use -f/--follow with a file name to follow history across
1627 files. Use -f/--follow with a file name to follow history across
1628 renames and copies. --follow without a file name will only show
1628 renames and copies. --follow without a file name will only show
1629 ancestors or descendants of the starting revision. --follow-first
1629 ancestors or descendants of the starting revision. --follow-first
1630 only follows the first parent of merge revisions.
1630 only follows the first parent of merge revisions.
1631
1631
1632 If no revision range is specified, the default is tip:0 unless
1632 If no revision range is specified, the default is tip:0 unless
1633 --follow is set, in which case the working directory parent is
1633 --follow is set, in which case the working directory parent is
1634 used as the starting revision.
1634 used as the starting revision.
1635
1635
1636 By default this command outputs: changeset id and hash, tags,
1636 By default this command outputs: changeset id and hash, tags,
1637 non-trivial parents, user, date and time, and a summary for each
1637 non-trivial parents, user, date and time, and a summary for each
1638 commit. When the -v/--verbose switch is used, the list of changed
1638 commit. When the -v/--verbose switch is used, the list of changed
1639 files and full commit message is shown.
1639 files and full commit message is shown.
1640
1640
1641 NOTE: log -p may generate unexpected diff output for merge
1641 NOTE: log -p may generate unexpected diff output for merge
1642 changesets, as it will compare the merge changeset against its
1642 changesets, as it will compare the merge changeset against its
1643 first parent only. Also, the files: list will only reflect files
1643 first parent only. Also, the files: list will only reflect files
1644 that are different from BOTH parents.
1644 that are different from BOTH parents.
1645
1645
1646 """
1646 """
1647
1647
1648 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1648 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1649 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1649 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1650
1650
1651 if opts['limit']:
1651 if opts['limit']:
1652 try:
1652 try:
1653 limit = int(opts['limit'])
1653 limit = int(opts['limit'])
1654 except ValueError:
1654 except ValueError:
1655 raise util.Abort(_('limit must be a positive integer'))
1655 raise util.Abort(_('limit must be a positive integer'))
1656 if limit <= 0: raise util.Abort(_('limit must be positive'))
1656 if limit <= 0: raise util.Abort(_('limit must be positive'))
1657 else:
1657 else:
1658 limit = sys.maxint
1658 limit = sys.maxint
1659 count = 0
1659 count = 0
1660
1660
1661 if opts['copies'] and opts['rev']:
1661 if opts['copies'] and opts['rev']:
1662 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1662 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1663 else:
1663 else:
1664 endrev = repo.changelog.count()
1664 endrev = repo.changelog.count()
1665 rcache = {}
1665 rcache = {}
1666 ncache = {}
1666 ncache = {}
1667 dcache = []
1667 dcache = []
1668 def getrenamed(fn, rev, man):
1668 def getrenamed(fn, rev, man):
1669 '''looks up all renames for a file (up to endrev) the first
1669 '''looks up all renames for a file (up to endrev) the first
1670 time the file is given. It indexes on the changerev and only
1670 time the file is given. It indexes on the changerev and only
1671 parses the manifest if linkrev != changerev.
1671 parses the manifest if linkrev != changerev.
1672 Returns rename info for fn at changerev rev.'''
1672 Returns rename info for fn at changerev rev.'''
1673 if fn not in rcache:
1673 if fn not in rcache:
1674 rcache[fn] = {}
1674 rcache[fn] = {}
1675 ncache[fn] = {}
1675 ncache[fn] = {}
1676 fl = repo.file(fn)
1676 fl = repo.file(fn)
1677 for i in xrange(fl.count()):
1677 for i in xrange(fl.count()):
1678 node = fl.node(i)
1678 node = fl.node(i)
1679 lr = fl.linkrev(node)
1679 lr = fl.linkrev(node)
1680 renamed = fl.renamed(node)
1680 renamed = fl.renamed(node)
1681 rcache[fn][lr] = renamed
1681 rcache[fn][lr] = renamed
1682 if renamed:
1682 if renamed:
1683 ncache[fn][node] = renamed
1683 ncache[fn][node] = renamed
1684 if lr >= endrev:
1684 if lr >= endrev:
1685 break
1685 break
1686 if rev in rcache[fn]:
1686 if rev in rcache[fn]:
1687 return rcache[fn][rev]
1687 return rcache[fn][rev]
1688 mr = repo.manifest.rev(man)
1688 mr = repo.manifest.rev(man)
1689 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1689 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1690 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1690 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1691 if not dcache or dcache[0] != man:
1691 if not dcache or dcache[0] != man:
1692 dcache[:] = [man, repo.manifest.readdelta(man)]
1692 dcache[:] = [man, repo.manifest.readdelta(man)]
1693 if fn in dcache[1]:
1693 if fn in dcache[1]:
1694 return ncache[fn].get(dcache[1][fn])
1694 return ncache[fn].get(dcache[1][fn])
1695 return None
1695 return None
1696
1696
1697 df = False
1697 df = False
1698 if opts["date"]:
1698 if opts["date"]:
1699 df = util.matchdate(opts["date"])
1699 df = util.matchdate(opts["date"])
1700
1700
1701
1701
1702 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1702 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1703 for st, rev, fns in changeiter:
1703 for st, rev, fns in changeiter:
1704 if st == 'add':
1704 if st == 'add':
1705 changenode = repo.changelog.node(rev)
1705 changenode = repo.changelog.node(rev)
1706 parents = [p for p in repo.changelog.parentrevs(rev)
1706 parents = [p for p in repo.changelog.parentrevs(rev)
1707 if p != nullrev]
1707 if p != nullrev]
1708 if opts['no_merges'] and len(parents) == 2:
1708 if opts['no_merges'] and len(parents) == 2:
1709 continue
1709 continue
1710 if opts['only_merges'] and len(parents) != 2:
1710 if opts['only_merges'] and len(parents) != 2:
1711 continue
1711 continue
1712
1712
1713 if df:
1713 if df:
1714 changes = get(rev)
1714 changes = get(rev)
1715 if not df(changes[2][0]):
1715 if not df(changes[2][0]):
1716 continue
1716 continue
1717
1717
1718 if opts['keyword']:
1718 if opts['keyword']:
1719 changes = get(rev)
1719 changes = get(rev)
1720 miss = 0
1720 miss = 0
1721 for k in [kw.lower() for kw in opts['keyword']]:
1721 for k in [kw.lower() for kw in opts['keyword']]:
1722 if not (k in changes[1].lower() or
1722 if not (k in changes[1].lower() or
1723 k in changes[4].lower() or
1723 k in changes[4].lower() or
1724 k in " ".join(changes[3][:20]).lower()):
1724 k in " ".join(changes[3][:20]).lower()):
1725 miss = 1
1725 miss = 1
1726 break
1726 break
1727 if miss:
1727 if miss:
1728 continue
1728 continue
1729
1729
1730 copies = []
1730 copies = []
1731 if opts.get('copies') and rev:
1731 if opts.get('copies') and rev:
1732 mf = get(rev)[0]
1732 mf = get(rev)[0]
1733 for fn in get(rev)[3]:
1733 for fn in get(rev)[3]:
1734 rename = getrenamed(fn, rev, mf)
1734 rename = getrenamed(fn, rev, mf)
1735 if rename:
1735 if rename:
1736 copies.append((fn, rename[0]))
1736 copies.append((fn, rename[0]))
1737 displayer.show(rev, changenode, copies=copies)
1737 displayer.show(rev, changenode, copies=copies)
1738 elif st == 'iter':
1738 elif st == 'iter':
1739 if count == limit: break
1739 if count == limit: break
1740 if displayer.flush(rev):
1740 if displayer.flush(rev):
1741 count += 1
1741 count += 1
1742
1742
1743 def manifest(ui, repo, rev=None):
1743 def manifest(ui, repo, rev=None):
1744 """output the latest or given revision of the project manifest
1744 """output the latest or given revision of the project manifest
1745
1745
1746 Print a list of version controlled files for the given revision.
1746 Print a list of version controlled files for the given revision.
1747
1747
1748 The manifest is the list of files being version controlled. If no revision
1748 The manifest is the list of files being version controlled. If no revision
1749 is given then the first parent of the working directory is used.
1749 is given then the first parent of the working directory is used.
1750
1750
1751 With -v flag, print file permissions. With --debug flag, print
1751 With -v flag, print file permissions. With --debug flag, print
1752 file revision hashes.
1752 file revision hashes.
1753 """
1753 """
1754
1754
1755 m = repo.changectx(rev).manifest()
1755 m = repo.changectx(rev).manifest()
1756 files = m.keys()
1756 files = m.keys()
1757 files.sort()
1757 files.sort()
1758
1758
1759 for f in files:
1759 for f in files:
1760 if ui.debugflag:
1760 if ui.debugflag:
1761 ui.write("%40s " % hex(m[f]))
1761 ui.write("%40s " % hex(m[f]))
1762 if ui.verbose:
1762 if ui.verbose:
1763 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1763 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1764 ui.write("%s\n" % f)
1764 ui.write("%s\n" % f)
1765
1765
1766 def merge(ui, repo, node=None, force=None, branch=None):
1766 def merge(ui, repo, node=None, force=None, branch=None):
1767 """Merge working directory with another revision
1767 """Merge working directory with another revision
1768
1768
1769 Merge the contents of the current working directory and the
1769 Merge the contents of the current working directory and the
1770 requested revision. Files that changed between either parent are
1770 requested revision. Files that changed between either parent are
1771 marked as changed for the next commit and a commit must be
1771 marked as changed for the next commit and a commit must be
1772 performed before any further updates are allowed.
1772 performed before any further updates are allowed.
1773
1773
1774 If no revision is specified, the working directory's parent is a
1774 If no revision is specified, the working directory's parent is a
1775 head revision, and the repository contains exactly one other head,
1775 head revision, and the repository contains exactly one other head,
1776 the other head is merged with by default. Otherwise, an explicit
1776 the other head is merged with by default. Otherwise, an explicit
1777 revision to merge with must be provided.
1777 revision to merge with must be provided.
1778 """
1778 """
1779
1779
1780 if node or branch:
1780 if node or branch:
1781 node = _lookup(repo, node, branch)
1781 node = _lookup(repo, node, branch)
1782 else:
1782 else:
1783 heads = repo.heads()
1783 heads = repo.heads()
1784 if len(heads) > 2:
1784 if len(heads) > 2:
1785 raise util.Abort(_('repo has %d heads - '
1785 raise util.Abort(_('repo has %d heads - '
1786 'please merge with an explicit rev') %
1786 'please merge with an explicit rev') %
1787 len(heads))
1787 len(heads))
1788 if len(heads) == 1:
1788 if len(heads) == 1:
1789 raise util.Abort(_('there is nothing to merge - '
1789 raise util.Abort(_('there is nothing to merge - '
1790 'use "hg update" instead'))
1790 'use "hg update" instead'))
1791 parent = repo.dirstate.parents()[0]
1791 parent = repo.dirstate.parents()[0]
1792 if parent not in heads:
1792 if parent not in heads:
1793 raise util.Abort(_('working dir not at a head rev - '
1793 raise util.Abort(_('working dir not at a head rev - '
1794 'use "hg update" or merge with an explicit rev'))
1794 'use "hg update" or merge with an explicit rev'))
1795 node = parent == heads[0] and heads[-1] or heads[0]
1795 node = parent == heads[0] and heads[-1] or heads[0]
1796 return hg.merge(repo, node, force=force)
1796 return hg.merge(repo, node, force=force)
1797
1797
1798 def outgoing(ui, repo, dest=None, **opts):
1798 def outgoing(ui, repo, dest=None, **opts):
1799 """show changesets not found in destination
1799 """show changesets not found in destination
1800
1800
1801 Show changesets not found in the specified destination repository or
1801 Show changesets not found in the specified destination repository or
1802 the default push location. These are the changesets that would be pushed
1802 the default push location. These are the changesets that would be pushed
1803 if a push was requested.
1803 if a push was requested.
1804
1804
1805 See pull for valid destination format details.
1805 See pull for valid destination format details.
1806 """
1806 """
1807 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1807 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1808 setremoteconfig(ui, opts)
1808 setremoteconfig(ui, opts)
1809 revs = None
1809 revs = None
1810 if opts['rev']:
1810 if opts['rev']:
1811 revs = [repo.lookup(rev) for rev in opts['rev']]
1811 revs = [repo.lookup(rev) for rev in opts['rev']]
1812
1812
1813 other = hg.repository(ui, dest)
1813 other = hg.repository(ui, dest)
1814 o = repo.findoutgoing(other, force=opts['force'])
1814 o = repo.findoutgoing(other, force=opts['force'])
1815 if not o:
1815 if not o:
1816 ui.status(_("no changes found\n"))
1816 ui.status(_("no changes found\n"))
1817 return
1817 return
1818 o = repo.changelog.nodesbetween(o, revs)[0]
1818 o = repo.changelog.nodesbetween(o, revs)[0]
1819 if opts['newest_first']:
1819 if opts['newest_first']:
1820 o.reverse()
1820 o.reverse()
1821 displayer = cmdutil.show_changeset(ui, repo, opts)
1821 displayer = cmdutil.show_changeset(ui, repo, opts)
1822 for n in o:
1822 for n in o:
1823 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1823 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1824 if opts['no_merges'] and len(parents) == 2:
1824 if opts['no_merges'] and len(parents) == 2:
1825 continue
1825 continue
1826 displayer.show(changenode=n)
1826 displayer.show(changenode=n)
1827
1827
1828 def parents(ui, repo, file_=None, **opts):
1828 def parents(ui, repo, file_=None, **opts):
1829 """show the parents of the working dir or revision
1829 """show the parents of the working dir or revision
1830
1830
1831 Print the working directory's parent revisions.
1831 Print the working directory's parent revisions.
1832 """
1832 """
1833 rev = opts.get('rev')
1833 rev = opts.get('rev')
1834 if rev:
1834 if rev:
1835 if file_:
1835 if file_:
1836 ctx = repo.filectx(file_, changeid=rev)
1836 ctx = repo.filectx(file_, changeid=rev)
1837 else:
1837 else:
1838 ctx = repo.changectx(rev)
1838 ctx = repo.changectx(rev)
1839 p = [cp.node() for cp in ctx.parents()]
1839 p = [cp.node() for cp in ctx.parents()]
1840 else:
1840 else:
1841 p = repo.dirstate.parents()
1841 p = repo.dirstate.parents()
1842
1842
1843 displayer = cmdutil.show_changeset(ui, repo, opts)
1843 displayer = cmdutil.show_changeset(ui, repo, opts)
1844 for n in p:
1844 for n in p:
1845 if n != nullid:
1845 if n != nullid:
1846 displayer.show(changenode=n)
1846 displayer.show(changenode=n)
1847
1847
1848 def paths(ui, repo, search=None):
1848 def paths(ui, repo, search=None):
1849 """show definition of symbolic path names
1849 """show definition of symbolic path names
1850
1850
1851 Show definition of symbolic path name NAME. If no name is given, show
1851 Show definition of symbolic path name NAME. If no name is given, show
1852 definition of available names.
1852 definition of available names.
1853
1853
1854 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1854 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1855 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1855 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1856 """
1856 """
1857 if search:
1857 if search:
1858 for name, path in ui.configitems("paths"):
1858 for name, path in ui.configitems("paths"):
1859 if name == search:
1859 if name == search:
1860 ui.write("%s\n" % path)
1860 ui.write("%s\n" % path)
1861 return
1861 return
1862 ui.warn(_("not found!\n"))
1862 ui.warn(_("not found!\n"))
1863 return 1
1863 return 1
1864 else:
1864 else:
1865 for name, path in ui.configitems("paths"):
1865 for name, path in ui.configitems("paths"):
1866 ui.write("%s = %s\n" % (name, path))
1866 ui.write("%s = %s\n" % (name, path))
1867
1867
1868 def postincoming(ui, repo, modheads, optupdate):
1868 def postincoming(ui, repo, modheads, optupdate):
1869 if modheads == 0:
1869 if modheads == 0:
1870 return
1870 return
1871 if optupdate:
1871 if optupdate:
1872 if modheads == 1:
1872 if modheads == 1:
1873 return hg.update(repo, repo.changelog.tip()) # update
1873 return hg.update(repo, repo.changelog.tip()) # update
1874 else:
1874 else:
1875 ui.status(_("not updating, since new heads added\n"))
1875 ui.status(_("not updating, since new heads added\n"))
1876 if modheads > 1:
1876 if modheads > 1:
1877 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1877 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1878 else:
1878 else:
1879 ui.status(_("(run 'hg update' to get a working copy)\n"))
1879 ui.status(_("(run 'hg update' to get a working copy)\n"))
1880
1880
1881 def pull(ui, repo, source="default", **opts):
1881 def pull(ui, repo, source="default", **opts):
1882 """pull changes from the specified source
1882 """pull changes from the specified source
1883
1883
1884 Pull changes from a remote repository to a local one.
1884 Pull changes from a remote repository to a local one.
1885
1885
1886 This finds all changes from the repository at the specified path
1886 This finds all changes from the repository at the specified path
1887 or URL and adds them to the local repository. By default, this
1887 or URL and adds them to the local repository. By default, this
1888 does not update the copy of the project in the working directory.
1888 does not update the copy of the project in the working directory.
1889
1889
1890 Valid URLs are of the form:
1890 Valid URLs are of the form:
1891
1891
1892 local/filesystem/path (or file://local/filesystem/path)
1892 local/filesystem/path (or file://local/filesystem/path)
1893 http://[user@]host[:port]/[path]
1893 http://[user@]host[:port]/[path]
1894 https://[user@]host[:port]/[path]
1894 https://[user@]host[:port]/[path]
1895 ssh://[user@]host[:port]/[path]
1895 ssh://[user@]host[:port]/[path]
1896 static-http://host[:port]/[path]
1896 static-http://host[:port]/[path]
1897
1897
1898 Paths in the local filesystem can either point to Mercurial
1898 Paths in the local filesystem can either point to Mercurial
1899 repositories or to bundle files (as created by 'hg bundle' or
1899 repositories or to bundle files (as created by 'hg bundle' or
1900 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1900 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1901 allows access to a Mercurial repository where you simply use a web
1901 allows access to a Mercurial repository where you simply use a web
1902 server to publish the .hg directory as static content.
1902 server to publish the .hg directory as static content.
1903
1903
1904 Some notes about using SSH with Mercurial:
1904 Some notes about using SSH with Mercurial:
1905 - SSH requires an accessible shell account on the destination machine
1905 - SSH requires an accessible shell account on the destination machine
1906 and a copy of hg in the remote path or specified with as remotecmd.
1906 and a copy of hg in the remote path or specified with as remotecmd.
1907 - path is relative to the remote user's home directory by default.
1907 - path is relative to the remote user's home directory by default.
1908 Use an extra slash at the start of a path to specify an absolute path:
1908 Use an extra slash at the start of a path to specify an absolute path:
1909 ssh://example.com//tmp/repository
1909 ssh://example.com//tmp/repository
1910 - Mercurial doesn't use its own compression via SSH; the right thing
1910 - Mercurial doesn't use its own compression via SSH; the right thing
1911 to do is to configure it in your ~/.ssh/config, e.g.:
1911 to do is to configure it in your ~/.ssh/config, e.g.:
1912 Host *.mylocalnetwork.example.com
1912 Host *.mylocalnetwork.example.com
1913 Compression no
1913 Compression no
1914 Host *
1914 Host *
1915 Compression yes
1915 Compression yes
1916 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1916 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1917 with the --ssh command line option.
1917 with the --ssh command line option.
1918 """
1918 """
1919 source = ui.expandpath(source)
1919 source = ui.expandpath(source)
1920 setremoteconfig(ui, opts)
1920 setremoteconfig(ui, opts)
1921
1921
1922 other = hg.repository(ui, source)
1922 other = hg.repository(ui, source)
1923 ui.status(_('pulling from %s\n') % (source))
1923 ui.status(_('pulling from %s\n') % (source))
1924 revs = None
1924 revs = None
1925 if opts['rev']:
1925 if opts['rev']:
1926 if 'lookup' in other.capabilities:
1926 if 'lookup' in other.capabilities:
1927 revs = [other.lookup(rev) for rev in opts['rev']]
1927 revs = [other.lookup(rev) for rev in opts['rev']]
1928 else:
1928 else:
1929 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1929 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1930 raise util.Abort(error)
1930 raise util.Abort(error)
1931 modheads = repo.pull(other, heads=revs, force=opts['force'])
1931 modheads = repo.pull(other, heads=revs, force=opts['force'])
1932 return postincoming(ui, repo, modheads, opts['update'])
1932 return postincoming(ui, repo, modheads, opts['update'])
1933
1933
1934 def push(ui, repo, dest=None, **opts):
1934 def push(ui, repo, dest=None, **opts):
1935 """push changes to the specified destination
1935 """push changes to the specified destination
1936
1936
1937 Push changes from the local repository to the given destination.
1937 Push changes from the local repository to the given destination.
1938
1938
1939 This is the symmetrical operation for pull. It helps to move
1939 This is the symmetrical operation for pull. It helps to move
1940 changes from the current repository to a different one. If the
1940 changes from the current repository to a different one. If the
1941 destination is local this is identical to a pull in that directory
1941 destination is local this is identical to a pull in that directory
1942 from the current one.
1942 from the current one.
1943
1943
1944 By default, push will refuse to run if it detects the result would
1944 By default, push will refuse to run if it detects the result would
1945 increase the number of remote heads. This generally indicates the
1945 increase the number of remote heads. This generally indicates the
1946 the client has forgotten to sync and merge before pushing.
1946 the client has forgotten to sync and merge before pushing.
1947
1947
1948 Valid URLs are of the form:
1948 Valid URLs are of the form:
1949
1949
1950 local/filesystem/path (or file://local/filesystem/path)
1950 local/filesystem/path (or file://local/filesystem/path)
1951 ssh://[user@]host[:port]/[path]
1951 ssh://[user@]host[:port]/[path]
1952 http://[user@]host[:port]/[path]
1952 http://[user@]host[:port]/[path]
1953 https://[user@]host[:port]/[path]
1953 https://[user@]host[:port]/[path]
1954
1954
1955 Look at the help text for the pull command for important details
1955 Look at the help text for the pull command for important details
1956 about ssh:// URLs.
1956 about ssh:// URLs.
1957
1957
1958 Pushing to http:// and https:// URLs is only possible, if this
1958 Pushing to http:// and https:// URLs is only possible, if this
1959 feature is explicitly enabled on the remote Mercurial server.
1959 feature is explicitly enabled on the remote Mercurial server.
1960 """
1960 """
1961 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1961 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1962 setremoteconfig(ui, opts)
1962 setremoteconfig(ui, opts)
1963
1963
1964 other = hg.repository(ui, dest)
1964 other = hg.repository(ui, dest)
1965 ui.status('pushing to %s\n' % (dest))
1965 ui.status('pushing to %s\n' % (dest))
1966 revs = None
1966 revs = None
1967 if opts['rev']:
1967 if opts['rev']:
1968 revs = [repo.lookup(rev) for rev in opts['rev']]
1968 revs = [repo.lookup(rev) for rev in opts['rev']]
1969 r = repo.push(other, opts['force'], revs=revs)
1969 r = repo.push(other, opts['force'], revs=revs)
1970 return r == 0
1970 return r == 0
1971
1971
1972 def rawcommit(ui, repo, *pats, **opts):
1972 def rawcommit(ui, repo, *pats, **opts):
1973 """raw commit interface (DEPRECATED)
1973 """raw commit interface (DEPRECATED)
1974
1974
1975 (DEPRECATED)
1975 (DEPRECATED)
1976 Lowlevel commit, for use in helper scripts.
1976 Lowlevel commit, for use in helper scripts.
1977
1977
1978 This command is not intended to be used by normal users, as it is
1978 This command is not intended to be used by normal users, as it is
1979 primarily useful for importing from other SCMs.
1979 primarily useful for importing from other SCMs.
1980
1980
1981 This command is now deprecated and will be removed in a future
1981 This command is now deprecated and will be removed in a future
1982 release, please use debugsetparents and commit instead.
1982 release, please use debugsetparents and commit instead.
1983 """
1983 """
1984
1984
1985 ui.warn(_("(the rawcommit command is deprecated)\n"))
1985 ui.warn(_("(the rawcommit command is deprecated)\n"))
1986
1986
1987 message = logmessage(opts)
1987 message = logmessage(opts)
1988
1988
1989 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1989 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1990 if opts['files']:
1990 if opts['files']:
1991 files += open(opts['files']).read().splitlines()
1991 files += open(opts['files']).read().splitlines()
1992
1992
1993 parents = [repo.lookup(p) for p in opts['parent']]
1993 parents = [repo.lookup(p) for p in opts['parent']]
1994
1994
1995 try:
1995 try:
1996 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1996 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1997 except ValueError, inst:
1997 except ValueError, inst:
1998 raise util.Abort(str(inst))
1998 raise util.Abort(str(inst))
1999
1999
2000 def recover(ui, repo):
2000 def recover(ui, repo):
2001 """roll back an interrupted transaction
2001 """roll back an interrupted transaction
2002
2002
2003 Recover from an interrupted commit or pull.
2003 Recover from an interrupted commit or pull.
2004
2004
2005 This command tries to fix the repository status after an interrupted
2005 This command tries to fix the repository status after an interrupted
2006 operation. It should only be necessary when Mercurial suggests it.
2006 operation. It should only be necessary when Mercurial suggests it.
2007 """
2007 """
2008 if repo.recover():
2008 if repo.recover():
2009 return hg.verify(repo)
2009 return hg.verify(repo)
2010 return 1
2010 return 1
2011
2011
2012 def remove(ui, repo, *pats, **opts):
2012 def remove(ui, repo, *pats, **opts):
2013 """remove the specified files on the next commit
2013 """remove the specified files on the next commit
2014
2014
2015 Schedule the indicated files for removal from the repository.
2015 Schedule the indicated files for removal from the repository.
2016
2016
2017 This only removes files from the current branch, not from the
2017 This only removes files from the current branch, not from the
2018 entire project history. If the files still exist in the working
2018 entire project history. If the files still exist in the working
2019 directory, they will be deleted from it. If invoked with --after,
2019 directory, they will be deleted from it. If invoked with --after,
2020 files that have been manually deleted are marked as removed.
2020 files that have been manually deleted are marked as removed.
2021
2021
2022 This command schedules the files to be removed at the next commit.
2022 This command schedules the files to be removed at the next commit.
2023 To undo a remove before that, see hg revert.
2023 To undo a remove before that, see hg revert.
2024
2024
2025 Modified files and added files are not removed by default. To
2025 Modified files and added files are not removed by default. To
2026 remove them, use the -f/--force option.
2026 remove them, use the -f/--force option.
2027 """
2027 """
2028 names = []
2028 names = []
2029 if not opts['after'] and not pats:
2029 if not opts['after'] and not pats:
2030 raise util.Abort(_('no files specified'))
2030 raise util.Abort(_('no files specified'))
2031 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2031 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2032 exact = dict.fromkeys(files)
2032 exact = dict.fromkeys(files)
2033 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2033 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2034 modified, added, removed, deleted, unknown = mardu
2034 modified, added, removed, deleted, unknown = mardu
2035 remove, forget = [], []
2035 remove, forget = [], []
2036 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2036 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2037 reason = None
2037 reason = None
2038 if abs not in deleted and opts['after']:
2038 if abs not in deleted and opts['after']:
2039 reason = _('is still present')
2039 reason = _('is still present')
2040 elif abs in modified and not opts['force']:
2040 elif abs in modified and not opts['force']:
2041 reason = _('is modified (use -f to force removal)')
2041 reason = _('is modified (use -f to force removal)')
2042 elif abs in added:
2042 elif abs in added:
2043 if opts['force']:
2043 if opts['force']:
2044 forget.append(abs)
2044 forget.append(abs)
2045 continue
2045 continue
2046 reason = _('has been marked for add (use -f to force removal)')
2046 reason = _('has been marked for add (use -f to force removal)')
2047 elif abs in unknown:
2047 elif abs in unknown:
2048 reason = _('is not managed')
2048 reason = _('is not managed')
2049 elif abs in removed:
2049 elif abs in removed:
2050 continue
2050 continue
2051 if reason:
2051 if reason:
2052 if exact:
2052 if exact:
2053 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2053 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2054 else:
2054 else:
2055 if ui.verbose or not exact:
2055 if ui.verbose or not exact:
2056 ui.status(_('removing %s\n') % rel)
2056 ui.status(_('removing %s\n') % rel)
2057 remove.append(abs)
2057 remove.append(abs)
2058 repo.forget(forget)
2058 repo.forget(forget)
2059 repo.remove(remove, unlink=not opts['after'])
2059 repo.remove(remove, unlink=not opts['after'])
2060
2060
2061 def rename(ui, repo, *pats, **opts):
2061 def rename(ui, repo, *pats, **opts):
2062 """rename files; equivalent of copy + remove
2062 """rename files; equivalent of copy + remove
2063
2063
2064 Mark dest as copies of sources; mark sources for deletion. If
2064 Mark dest as copies of sources; mark sources for deletion. If
2065 dest is a directory, copies are put in that directory. If dest is
2065 dest is a directory, copies are put in that directory. If dest is
2066 a file, there can only be one source.
2066 a file, there can only be one source.
2067
2067
2068 By default, this command copies the contents of files as they
2068 By default, this command copies the contents of files as they
2069 stand in the working directory. If invoked with --after, the
2069 stand in the working directory. If invoked with --after, the
2070 operation is recorded, but no copying is performed.
2070 operation is recorded, but no copying is performed.
2071
2071
2072 This command takes effect in the next commit. To undo a rename
2072 This command takes effect in the next commit. To undo a rename
2073 before that, see hg revert.
2073 before that, see hg revert.
2074 """
2074 """
2075 wlock = repo.wlock(0)
2075 wlock = repo.wlock(0)
2076 errs, copied = docopy(ui, repo, pats, opts, wlock)
2076 errs, copied = docopy(ui, repo, pats, opts, wlock)
2077 names = []
2077 names = []
2078 for abs, rel, exact in copied:
2078 for abs, rel, exact in copied:
2079 if ui.verbose or not exact:
2079 if ui.verbose or not exact:
2080 ui.status(_('removing %s\n') % rel)
2080 ui.status(_('removing %s\n') % rel)
2081 names.append(abs)
2081 names.append(abs)
2082 if not opts.get('dry_run'):
2082 if not opts.get('dry_run'):
2083 repo.remove(names, True, wlock)
2083 repo.remove(names, True, wlock)
2084 return errs
2084 return errs
2085
2085
2086 def revert(ui, repo, *pats, **opts):
2086 def revert(ui, repo, *pats, **opts):
2087 """revert files or dirs to their states as of some revision
2087 """revert files or dirs to their states as of some revision
2088
2088
2089 With no revision specified, revert the named files or directories
2089 With no revision specified, revert the named files or directories
2090 to the contents they had in the parent of the working directory.
2090 to the contents they had in the parent of the working directory.
2091 This restores the contents of the affected files to an unmodified
2091 This restores the contents of the affected files to an unmodified
2092 state and unschedules adds, removes, copies, and renames. If the
2092 state and unschedules adds, removes, copies, and renames. If the
2093 working directory has two parents, you must explicitly specify the
2093 working directory has two parents, you must explicitly specify the
2094 revision to revert to.
2094 revision to revert to.
2095
2095
2096 Modified files are saved with a .orig suffix before reverting.
2096 Modified files are saved with a .orig suffix before reverting.
2097 To disable these backups, use --no-backup.
2097 To disable these backups, use --no-backup.
2098
2098
2099 Using the -r option, revert the given files or directories to their
2099 Using the -r option, revert the given files or directories to their
2100 contents as of a specific revision. This can be helpful to "roll
2100 contents as of a specific revision. This can be helpful to "roll
2101 back" some or all of a change that should not have been committed.
2101 back" some or all of a change that should not have been committed.
2102
2102
2103 Revert modifies the working directory. It does not commit any
2103 Revert modifies the working directory. It does not commit any
2104 changes, or change the parent of the working directory. If you
2104 changes, or change the parent of the working directory. If you
2105 revert to a revision other than the parent of the working
2105 revert to a revision other than the parent of the working
2106 directory, the reverted files will thus appear modified
2106 directory, the reverted files will thus appear modified
2107 afterwards.
2107 afterwards.
2108
2108
2109 If a file has been deleted, it is recreated. If the executable
2109 If a file has been deleted, it is recreated. If the executable
2110 mode of a file was changed, it is reset.
2110 mode of a file was changed, it is reset.
2111
2111
2112 If names are given, all files matching the names are reverted.
2112 If names are given, all files matching the names are reverted.
2113
2113
2114 If no arguments are given, no files are reverted.
2114 If no arguments are given, no files are reverted.
2115 """
2115 """
2116
2116
2117 if opts["date"]:
2117 if opts["date"]:
2118 if opts["rev"]:
2118 if opts["rev"]:
2119 raise util.Abort(_("you can't specify a revision and a date"))
2119 raise util.Abort(_("you can't specify a revision and a date"))
2120 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2120 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2121
2121
2122 if not pats and not opts['all']:
2122 if not pats and not opts['all']:
2123 raise util.Abort(_('no files or directories specified; '
2123 raise util.Abort(_('no files or directories specified; '
2124 'use --all to revert the whole repo'))
2124 'use --all to revert the whole repo'))
2125
2125
2126 parent, p2 = repo.dirstate.parents()
2126 parent, p2 = repo.dirstate.parents()
2127 if not opts['rev'] and p2 != nullid:
2127 if not opts['rev'] and p2 != nullid:
2128 raise util.Abort(_('uncommitted merge - please provide a '
2128 raise util.Abort(_('uncommitted merge - please provide a '
2129 'specific revision'))
2129 'specific revision'))
2130 node = repo.changectx(opts['rev']).node()
2130 node = repo.changectx(opts['rev']).node()
2131 mf = repo.manifest.read(repo.changelog.read(node)[0])
2131 mf = repo.manifest.read(repo.changelog.read(node)[0])
2132 if node == parent:
2132 if node == parent:
2133 pmf = mf
2133 pmf = mf
2134 else:
2134 else:
2135 pmf = None
2135 pmf = None
2136
2136
2137 wlock = repo.wlock()
2137 wlock = repo.wlock()
2138
2138
2139 # need all matching names in dirstate and manifest of target rev,
2139 # need all matching names in dirstate and manifest of target rev,
2140 # so have to walk both. do not print errors if files exist in one
2140 # so have to walk both. do not print errors if files exist in one
2141 # but not other.
2141 # but not other.
2142
2142
2143 names = {}
2143 names = {}
2144 target_only = {}
2144 target_only = {}
2145
2145
2146 # walk dirstate.
2146 # walk dirstate.
2147
2147
2148 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2148 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2149 badmatch=mf.has_key):
2149 badmatch=mf.has_key):
2150 names[abs] = (rel, exact)
2150 names[abs] = (rel, exact)
2151 if src == 'b':
2151 if src == 'b':
2152 target_only[abs] = True
2152 target_only[abs] = True
2153
2153
2154 # walk target manifest.
2154 # walk target manifest.
2155
2155
2156 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2156 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2157 badmatch=names.has_key):
2157 badmatch=names.has_key):
2158 if abs in names: continue
2158 if abs in names: continue
2159 names[abs] = (rel, exact)
2159 names[abs] = (rel, exact)
2160 target_only[abs] = True
2160 target_only[abs] = True
2161
2161
2162 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2162 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2163 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2163 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2164
2164
2165 revert = ([], _('reverting %s\n'))
2165 revert = ([], _('reverting %s\n'))
2166 add = ([], _('adding %s\n'))
2166 add = ([], _('adding %s\n'))
2167 remove = ([], _('removing %s\n'))
2167 remove = ([], _('removing %s\n'))
2168 forget = ([], _('forgetting %s\n'))
2168 forget = ([], _('forgetting %s\n'))
2169 undelete = ([], _('undeleting %s\n'))
2169 undelete = ([], _('undeleting %s\n'))
2170 update = {}
2170 update = {}
2171
2171
2172 disptable = (
2172 disptable = (
2173 # dispatch table:
2173 # dispatch table:
2174 # file state
2174 # file state
2175 # action if in target manifest
2175 # action if in target manifest
2176 # action if not in target manifest
2176 # action if not in target manifest
2177 # make backup if in target manifest
2177 # make backup if in target manifest
2178 # make backup if not in target manifest
2178 # make backup if not in target manifest
2179 (modified, revert, remove, True, True),
2179 (modified, revert, remove, True, True),
2180 (added, revert, forget, True, False),
2180 (added, revert, forget, True, False),
2181 (removed, undelete, None, False, False),
2181 (removed, undelete, None, False, False),
2182 (deleted, revert, remove, False, False),
2182 (deleted, revert, remove, False, False),
2183 (unknown, add, None, True, False),
2183 (unknown, add, None, True, False),
2184 (target_only, add, None, False, False),
2184 (target_only, add, None, False, False),
2185 )
2185 )
2186
2186
2187 entries = names.items()
2187 entries = names.items()
2188 entries.sort()
2188 entries.sort()
2189
2189
2190 for abs, (rel, exact) in entries:
2190 for abs, (rel, exact) in entries:
2191 mfentry = mf.get(abs)
2191 mfentry = mf.get(abs)
2192 def handle(xlist, dobackup):
2192 def handle(xlist, dobackup):
2193 xlist[0].append(abs)
2193 xlist[0].append(abs)
2194 update[abs] = 1
2194 update[abs] = 1
2195 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2195 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2196 bakname = "%s.orig" % rel
2196 bakname = "%s.orig" % rel
2197 ui.note(_('saving current version of %s as %s\n') %
2197 ui.note(_('saving current version of %s as %s\n') %
2198 (rel, bakname))
2198 (rel, bakname))
2199 if not opts.get('dry_run'):
2199 if not opts.get('dry_run'):
2200 util.copyfile(rel, bakname)
2200 util.copyfile(rel, bakname)
2201 if ui.verbose or not exact:
2201 if ui.verbose or not exact:
2202 ui.status(xlist[1] % rel)
2202 ui.status(xlist[1] % rel)
2203 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2203 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2204 if abs not in table: continue
2204 if abs not in table: continue
2205 # file has changed in dirstate
2205 # file has changed in dirstate
2206 if mfentry:
2206 if mfentry:
2207 handle(hitlist, backuphit)
2207 handle(hitlist, backuphit)
2208 elif misslist is not None:
2208 elif misslist is not None:
2209 handle(misslist, backupmiss)
2209 handle(misslist, backupmiss)
2210 else:
2210 else:
2211 if exact: ui.warn(_('file not managed: %s\n') % rel)
2211 if exact: ui.warn(_('file not managed: %s\n') % rel)
2212 break
2212 break
2213 else:
2213 else:
2214 # file has not changed in dirstate
2214 # file has not changed in dirstate
2215 if node == parent:
2215 if node == parent:
2216 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2216 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2217 continue
2217 continue
2218 if pmf is None:
2218 if pmf is None:
2219 # only need parent manifest in this unlikely case,
2219 # only need parent manifest in this unlikely case,
2220 # so do not read by default
2220 # so do not read by default
2221 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2221 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2222 if abs in pmf:
2222 if abs in pmf:
2223 if mfentry:
2223 if mfentry:
2224 # if version of file is same in parent and target
2224 # if version of file is same in parent and target
2225 # manifests, do nothing
2225 # manifests, do nothing
2226 if pmf[abs] != mfentry:
2226 if pmf[abs] != mfentry:
2227 handle(revert, False)
2227 handle(revert, False)
2228 else:
2228 else:
2229 handle(remove, False)
2229 handle(remove, False)
2230
2230
2231 if not opts.get('dry_run'):
2231 if not opts.get('dry_run'):
2232 repo.dirstate.forget(forget[0])
2232 repo.dirstate.forget(forget[0])
2233 r = hg.revert(repo, node, update.has_key, wlock)
2233 r = hg.revert(repo, node, update.has_key, wlock)
2234 repo.dirstate.update(add[0], 'a')
2234 repo.dirstate.update(add[0], 'a')
2235 repo.dirstate.update(undelete[0], 'n')
2235 repo.dirstate.update(undelete[0], 'n')
2236 repo.dirstate.update(remove[0], 'r')
2236 repo.dirstate.update(remove[0], 'r')
2237 return r
2237 return r
2238
2238
2239 def rollback(ui, repo):
2239 def rollback(ui, repo):
2240 """roll back the last transaction in this repository
2240 """roll back the last transaction in this repository
2241
2241
2242 Roll back the last transaction in this repository, restoring the
2242 Roll back the last transaction in this repository, restoring the
2243 project to its state prior to the transaction.
2243 project to its state prior to the transaction.
2244
2244
2245 Transactions are used to encapsulate the effects of all commands
2245 Transactions are used to encapsulate the effects of all commands
2246 that create new changesets or propagate existing changesets into a
2246 that create new changesets or propagate existing changesets into a
2247 repository. For example, the following commands are transactional,
2247 repository. For example, the following commands are transactional,
2248 and their effects can be rolled back:
2248 and their effects can be rolled back:
2249
2249
2250 commit
2250 commit
2251 import
2251 import
2252 pull
2252 pull
2253 push (with this repository as destination)
2253 push (with this repository as destination)
2254 unbundle
2254 unbundle
2255
2255
2256 This command should be used with care. There is only one level of
2256 This command should be used with care. There is only one level of
2257 rollback, and there is no way to undo a rollback.
2257 rollback, and there is no way to undo a rollback.
2258
2258
2259 This command is not intended for use on public repositories. Once
2259 This command is not intended for use on public repositories. Once
2260 changes are visible for pull by other users, rolling a transaction
2260 changes are visible for pull by other users, rolling a transaction
2261 back locally is ineffective (someone else may already have pulled
2261 back locally is ineffective (someone else may already have pulled
2262 the changes). Furthermore, a race is possible with readers of the
2262 the changes). Furthermore, a race is possible with readers of the
2263 repository; for example an in-progress pull from the repository
2263 repository; for example an in-progress pull from the repository
2264 may fail if a rollback is performed.
2264 may fail if a rollback is performed.
2265 """
2265 """
2266 repo.rollback()
2266 repo.rollback()
2267
2267
2268 def root(ui, repo):
2268 def root(ui, repo):
2269 """print the root (top) of the current working dir
2269 """print the root (top) of the current working dir
2270
2270
2271 Print the root directory of the current repository.
2271 Print the root directory of the current repository.
2272 """
2272 """
2273 ui.write(repo.root + "\n")
2273 ui.write(repo.root + "\n")
2274
2274
2275 def serve(ui, repo, **opts):
2275 def serve(ui, repo, **opts):
2276 """export the repository via HTTP
2276 """export the repository via HTTP
2277
2277
2278 Start a local HTTP repository browser and pull server.
2278 Start a local HTTP repository browser and pull server.
2279
2279
2280 By default, the server logs accesses to stdout and errors to
2280 By default, the server logs accesses to stdout and errors to
2281 stderr. Use the "-A" and "-E" options to log to files.
2281 stderr. Use the "-A" and "-E" options to log to files.
2282 """
2282 """
2283
2283
2284 if opts["stdio"]:
2284 if opts["stdio"]:
2285 if repo is None:
2285 if repo is None:
2286 raise hg.RepoError(_("There is no Mercurial repository here"
2286 raise hg.RepoError(_("There is no Mercurial repository here"
2287 " (.hg not found)"))
2287 " (.hg not found)"))
2288 s = sshserver.sshserver(ui, repo)
2288 s = sshserver.sshserver(ui, repo)
2289 s.serve_forever()
2289 s.serve_forever()
2290
2290
2291 optlist = ("name templates style address port ipv6"
2291 optlist = ("name templates style address port ipv6"
2292 " accesslog errorlog webdir_conf")
2292 " accesslog errorlog webdir_conf")
2293 for o in optlist.split():
2293 for o in optlist.split():
2294 if opts[o]:
2294 if opts[o]:
2295 ui.setconfig("web", o, str(opts[o]))
2295 ui.setconfig("web", o, str(opts[o]))
2296
2296
2297 if repo is None and not ui.config("web", "webdir_conf"):
2297 if repo is None and not ui.config("web", "webdir_conf"):
2298 raise hg.RepoError(_("There is no Mercurial repository here"
2298 raise hg.RepoError(_("There is no Mercurial repository here"
2299 " (.hg not found)"))
2299 " (.hg not found)"))
2300
2300
2301 if opts['daemon'] and not opts['daemon_pipefds']:
2301 if opts['daemon'] and not opts['daemon_pipefds']:
2302 rfd, wfd = os.pipe()
2302 rfd, wfd = os.pipe()
2303 args = sys.argv[:]
2303 args = sys.argv[:]
2304 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2304 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2305 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2305 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2306 args[0], args)
2306 args[0], args)
2307 os.close(wfd)
2307 os.close(wfd)
2308 os.read(rfd, 1)
2308 os.read(rfd, 1)
2309 os._exit(0)
2309 os._exit(0)
2310
2310
2311 httpd = hgweb.server.create_server(ui, repo)
2311 httpd = hgweb.server.create_server(ui, repo)
2312
2312
2313 if ui.verbose:
2313 if ui.verbose:
2314 if httpd.port != 80:
2314 if httpd.port != 80:
2315 ui.status(_('listening at http://%s:%d/\n') %
2315 ui.status(_('listening at http://%s:%d/\n') %
2316 (httpd.addr, httpd.port))
2316 (httpd.addr, httpd.port))
2317 else:
2317 else:
2318 ui.status(_('listening at http://%s/\n') % httpd.addr)
2318 ui.status(_('listening at http://%s/\n') % httpd.addr)
2319
2319
2320 if opts['pid_file']:
2320 if opts['pid_file']:
2321 fp = open(opts['pid_file'], 'w')
2321 fp = open(opts['pid_file'], 'w')
2322 fp.write(str(os.getpid()) + '\n')
2322 fp.write(str(os.getpid()) + '\n')
2323 fp.close()
2323 fp.close()
2324
2324
2325 if opts['daemon_pipefds']:
2325 if opts['daemon_pipefds']:
2326 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2326 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2327 os.close(rfd)
2327 os.close(rfd)
2328 os.write(wfd, 'y')
2328 os.write(wfd, 'y')
2329 os.close(wfd)
2329 os.close(wfd)
2330 sys.stdout.flush()
2330 sys.stdout.flush()
2331 sys.stderr.flush()
2331 sys.stderr.flush()
2332 fd = os.open(util.nulldev, os.O_RDWR)
2332 fd = os.open(util.nulldev, os.O_RDWR)
2333 if fd != 0: os.dup2(fd, 0)
2333 if fd != 0: os.dup2(fd, 0)
2334 if fd != 1: os.dup2(fd, 1)
2334 if fd != 1: os.dup2(fd, 1)
2335 if fd != 2: os.dup2(fd, 2)
2335 if fd != 2: os.dup2(fd, 2)
2336 if fd not in (0, 1, 2): os.close(fd)
2336 if fd not in (0, 1, 2): os.close(fd)
2337
2337
2338 httpd.serve_forever()
2338 httpd.serve_forever()
2339
2339
2340 def status(ui, repo, *pats, **opts):
2340 def status(ui, repo, *pats, **opts):
2341 """show changed files in the working directory
2341 """show changed files in the working directory
2342
2342
2343 Show status of files in the repository. If names are given, only
2343 Show status of files in the repository. If names are given, only
2344 files that match are shown. Files that are clean or ignored, are
2344 files that match are shown. Files that are clean or ignored, are
2345 not listed unless -c (clean), -i (ignored) or -A is given.
2345 not listed unless -c (clean), -i (ignored) or -A is given.
2346
2346
2347 NOTE: status may appear to disagree with diff if permissions have
2347 NOTE: status may appear to disagree with diff if permissions have
2348 changed or a merge has occurred. The standard diff format does not
2348 changed or a merge has occurred. The standard diff format does not
2349 report permission changes and diff only reports changes relative
2349 report permission changes and diff only reports changes relative
2350 to one merge parent.
2350 to one merge parent.
2351
2351
2352 If one revision is given, it is used as the base revision.
2352 If one revision is given, it is used as the base revision.
2353 If two revisions are given, the difference between them is shown.
2353 If two revisions are given, the difference between them is shown.
2354
2354
2355 The codes used to show the status of files are:
2355 The codes used to show the status of files are:
2356 M = modified
2356 M = modified
2357 A = added
2357 A = added
2358 R = removed
2358 R = removed
2359 C = clean
2359 C = clean
2360 ! = deleted, but still tracked
2360 ! = deleted, but still tracked
2361 ? = not tracked
2361 ? = not tracked
2362 I = ignored (not shown by default)
2362 I = ignored (not shown by default)
2363 = the previous added file was copied from here
2363 = the previous added file was copied from here
2364 """
2364 """
2365
2365
2366 all = opts['all']
2366 all = opts['all']
2367 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2367 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2368
2368
2369 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2369 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2370 cwd = (pats and repo.getcwd()) or ''
2370 cwd = (pats and repo.getcwd()) or ''
2371 modified, added, removed, deleted, unknown, ignored, clean = [
2371 modified, added, removed, deleted, unknown, ignored, clean = [
2372 [util.pathto(cwd, x) for x in n]
2372 [util.pathto(cwd, x) for x in n]
2373 for n in repo.status(node1=node1, node2=node2, files=files,
2373 for n in repo.status(node1=node1, node2=node2, files=files,
2374 match=matchfn,
2374 match=matchfn,
2375 list_ignored=all or opts['ignored'],
2375 list_ignored=all or opts['ignored'],
2376 list_clean=all or opts['clean'])]
2376 list_clean=all or opts['clean'])]
2377
2377
2378 changetypes = (('modified', 'M', modified),
2378 changetypes = (('modified', 'M', modified),
2379 ('added', 'A', added),
2379 ('added', 'A', added),
2380 ('removed', 'R', removed),
2380 ('removed', 'R', removed),
2381 ('deleted', '!', deleted),
2381 ('deleted', '!', deleted),
2382 ('unknown', '?', unknown),
2382 ('unknown', '?', unknown),
2383 ('ignored', 'I', ignored))
2383 ('ignored', 'I', ignored))
2384
2384
2385 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2385 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2386
2386
2387 end = opts['print0'] and '\0' or '\n'
2387 end = opts['print0'] and '\0' or '\n'
2388
2388
2389 for opt, char, changes in ([ct for ct in explicit_changetypes
2389 for opt, char, changes in ([ct for ct in explicit_changetypes
2390 if all or opts[ct[0]]]
2390 if all or opts[ct[0]]]
2391 or changetypes):
2391 or changetypes):
2392 if opts['no_status']:
2392 if opts['no_status']:
2393 format = "%%s%s" % end
2393 format = "%%s%s" % end
2394 else:
2394 else:
2395 format = "%s %%s%s" % (char, end)
2395 format = "%s %%s%s" % (char, end)
2396
2396
2397 for f in changes:
2397 for f in changes:
2398 ui.write(format % f)
2398 ui.write(format % f)
2399 if ((all or opts.get('copies')) and not opts.get('no_status')):
2399 if ((all or opts.get('copies')) and not opts.get('no_status')):
2400 copied = repo.dirstate.copied(f)
2400 copied = repo.dirstate.copied(f)
2401 if copied:
2401 if copied:
2402 ui.write(' %s%s' % (copied, end))
2402 ui.write(' %s%s' % (copied, end))
2403
2403
2404 def tag(ui, repo, name, rev_=None, **opts):
2404 def tag(ui, repo, name, rev_=None, **opts):
2405 """add a tag for the current tip or a given revision
2405 """add a tag for the current tip or a given revision
2406
2406
2407 Name a particular revision using <name>.
2407 Name a particular revision using <name>.
2408
2408
2409 Tags are used to name particular revisions of the repository and are
2409 Tags are used to name particular revisions of the repository and are
2410 very useful to compare different revision, to go back to significant
2410 very useful to compare different revision, to go back to significant
2411 earlier versions or to mark branch points as releases, etc.
2411 earlier versions or to mark branch points as releases, etc.
2412
2412
2413 If no revision is given, the parent of the working directory is used.
2413 If no revision is given, the parent of the working directory is used.
2414
2414
2415 To facilitate version control, distribution, and merging of tags,
2415 To facilitate version control, distribution, and merging of tags,
2416 they are stored as a file named ".hgtags" which is managed
2416 they are stored as a file named ".hgtags" which is managed
2417 similarly to other project files and can be hand-edited if
2417 similarly to other project files and can be hand-edited if
2418 necessary. The file '.hg/localtags' is used for local tags (not
2418 necessary. The file '.hg/localtags' is used for local tags (not
2419 shared among repositories).
2419 shared among repositories).
2420 """
2420 """
2421 if name in ['tip', '.', 'null']:
2421 if name in ['tip', '.', 'null']:
2422 raise util.Abort(_("the name '%s' is reserved") % name)
2422 raise util.Abort(_("the name '%s' is reserved") % name)
2423 if rev_ is not None:
2423 if rev_ is not None:
2424 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2424 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2425 "please use 'hg tag [-r REV] NAME' instead\n"))
2425 "please use 'hg tag [-r REV] NAME' instead\n"))
2426 if opts['rev']:
2426 if opts['rev']:
2427 raise util.Abort(_("use only one form to specify the revision"))
2427 raise util.Abort(_("use only one form to specify the revision"))
2428 if opts['rev']:
2428 if opts['rev']:
2429 rev_ = opts['rev']
2429 rev_ = opts['rev']
2430 if not rev_ and repo.dirstate.parents()[1] != nullid:
2430 if not rev_ and repo.dirstate.parents()[1] != nullid:
2431 raise util.Abort(_('uncommitted merge - please provide a '
2431 raise util.Abort(_('uncommitted merge - please provide a '
2432 'specific revision'))
2432 'specific revision'))
2433 r = repo.changectx(rev_).node()
2433 r = repo.changectx(rev_).node()
2434
2434
2435 message = opts['message']
2435 message = opts['message']
2436 if not message:
2436 if not message:
2437 message = _('Added tag %s for changeset %s') % (name, short(r))
2437 message = _('Added tag %s for changeset %s') % (name, short(r))
2438
2438
2439 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2439 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2440
2440
2441 def tags(ui, repo):
2441 def tags(ui, repo):
2442 """list repository tags
2442 """list repository tags
2443
2443
2444 List the repository tags.
2444 List the repository tags.
2445
2445
2446 This lists both regular and local tags.
2446 This lists both regular and local tags.
2447 """
2447 """
2448
2448
2449 l = repo.tagslist()
2449 l = repo.tagslist()
2450 l.reverse()
2450 l.reverse()
2451 hexfunc = ui.debugflag and hex or short
2451 hexfunc = ui.debugflag and hex or short
2452 for t, n in l:
2452 for t, n in l:
2453 try:
2453 try:
2454 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2454 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2455 except KeyError:
2455 except KeyError:
2456 r = " ?:?"
2456 r = " ?:?"
2457 if ui.quiet:
2457 if ui.quiet:
2458 ui.write("%s\n" % t)
2458 ui.write("%s\n" % t)
2459 else:
2459 else:
2460 t = util.localsub(t, 30)
2460 t = util.localsub(t, 30)
2461 t += " " * (30 - util.locallen(t))
2461 t += " " * (30 - util.locallen(t))
2462 ui.write("%s %s\n" % (t, r))
2462 ui.write("%s %s\n" % (t, r))
2463
2463
2464 def tip(ui, repo, **opts):
2464 def tip(ui, repo, **opts):
2465 """show the tip revision
2465 """show the tip revision
2466
2466
2467 Show the tip revision.
2467 Show the tip revision.
2468 """
2468 """
2469 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2469 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2470
2470
2471 def unbundle(ui, repo, fname, **opts):
2471 def unbundle(ui, repo, fname, **opts):
2472 """apply a changegroup file
2472 """apply a changegroup file
2473
2473
2474 Apply a compressed changegroup file generated by the bundle
2474 Apply a compressed changegroup file generated by the bundle
2475 command.
2475 command.
2476 """
2476 """
2477 gen = changegroup.readbundle(urllib.urlopen(fname))
2477 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2478 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2478 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2479 return postincoming(ui, repo, modheads, opts['update'])
2479 return postincoming(ui, repo, modheads, opts['update'])
2480
2480
2481 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2481 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2482 """update or merge working directory
2482 """update or merge working directory
2483
2483
2484 Update the working directory to the specified revision.
2484 Update the working directory to the specified revision.
2485
2485
2486 If there are no outstanding changes in the working directory and
2486 If there are no outstanding changes in the working directory and
2487 there is a linear relationship between the current version and the
2487 there is a linear relationship between the current version and the
2488 requested version, the result is the requested version.
2488 requested version, the result is the requested version.
2489
2489
2490 To merge the working directory with another revision, use the
2490 To merge the working directory with another revision, use the
2491 merge command.
2491 merge command.
2492
2492
2493 By default, update will refuse to run if doing so would require
2493 By default, update will refuse to run if doing so would require
2494 merging or discarding local changes.
2494 merging or discarding local changes.
2495 """
2495 """
2496 if date:
2496 if date:
2497 if node:
2497 if node:
2498 raise util.Abort(_("you can't specify a revision and a date"))
2498 raise util.Abort(_("you can't specify a revision and a date"))
2499 node = cmdutil.finddate(ui, repo, date)
2499 node = cmdutil.finddate(ui, repo, date)
2500
2500
2501 node = _lookup(repo, node, branch)
2501 node = _lookup(repo, node, branch)
2502 if clean:
2502 if clean:
2503 return hg.clean(repo, node)
2503 return hg.clean(repo, node)
2504 else:
2504 else:
2505 return hg.update(repo, node)
2505 return hg.update(repo, node)
2506
2506
2507 def _lookup(repo, node, branch=None):
2507 def _lookup(repo, node, branch=None):
2508 if branch:
2508 if branch:
2509 repo.ui.warn(_("the --branch option is deprecated, "
2509 repo.ui.warn(_("the --branch option is deprecated, "
2510 "please use 'hg branch' instead\n"))
2510 "please use 'hg branch' instead\n"))
2511 br = repo.branchlookup(branch=branch)
2511 br = repo.branchlookup(branch=branch)
2512 found = []
2512 found = []
2513 for x in br:
2513 for x in br:
2514 if branch in br[x]:
2514 if branch in br[x]:
2515 found.append(x)
2515 found.append(x)
2516 if len(found) > 1:
2516 if len(found) > 1:
2517 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2517 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2518 for x in found:
2518 for x in found:
2519 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2519 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2520 raise util.Abort("")
2520 raise util.Abort("")
2521 if len(found) == 1:
2521 if len(found) == 1:
2522 node = found[0]
2522 node = found[0]
2523 repo.ui.warn(_("Using head %s for branch %s\n")
2523 repo.ui.warn(_("Using head %s for branch %s\n")
2524 % (short(node), branch))
2524 % (short(node), branch))
2525 else:
2525 else:
2526 raise util.Abort(_("branch %s not found") % branch)
2526 raise util.Abort(_("branch %s not found") % branch)
2527 else:
2527 else:
2528 node = node and repo.lookup(node) or repo.changelog.tip()
2528 node = node and repo.lookup(node) or repo.changelog.tip()
2529 return node
2529 return node
2530
2530
2531 def verify(ui, repo):
2531 def verify(ui, repo):
2532 """verify the integrity of the repository
2532 """verify the integrity of the repository
2533
2533
2534 Verify the integrity of the current repository.
2534 Verify the integrity of the current repository.
2535
2535
2536 This will perform an extensive check of the repository's
2536 This will perform an extensive check of the repository's
2537 integrity, validating the hashes and checksums of each entry in
2537 integrity, validating the hashes and checksums of each entry in
2538 the changelog, manifest, and tracked files, as well as the
2538 the changelog, manifest, and tracked files, as well as the
2539 integrity of their crosslinks and indices.
2539 integrity of their crosslinks and indices.
2540 """
2540 """
2541 return hg.verify(repo)
2541 return hg.verify(repo)
2542
2542
2543 def version_(ui):
2543 def version_(ui):
2544 """output version and copyright information"""
2544 """output version and copyright information"""
2545 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2545 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2546 % version.get_version())
2546 % version.get_version())
2547 ui.status(_(
2547 ui.status(_(
2548 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2548 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2549 "This is free software; see the source for copying conditions. "
2549 "This is free software; see the source for copying conditions. "
2550 "There is NO\nwarranty; "
2550 "There is NO\nwarranty; "
2551 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2551 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2552 ))
2552 ))
2553
2553
2554 # Command options and aliases are listed here, alphabetically
2554 # Command options and aliases are listed here, alphabetically
2555
2555
2556 globalopts = [
2556 globalopts = [
2557 ('R', 'repository', '',
2557 ('R', 'repository', '',
2558 _('repository root directory or symbolic path name')),
2558 _('repository root directory or symbolic path name')),
2559 ('', 'cwd', '', _('change working directory')),
2559 ('', 'cwd', '', _('change working directory')),
2560 ('y', 'noninteractive', None,
2560 ('y', 'noninteractive', None,
2561 _('do not prompt, assume \'yes\' for any required answers')),
2561 _('do not prompt, assume \'yes\' for any required answers')),
2562 ('q', 'quiet', None, _('suppress output')),
2562 ('q', 'quiet', None, _('suppress output')),
2563 ('v', 'verbose', None, _('enable additional output')),
2563 ('v', 'verbose', None, _('enable additional output')),
2564 ('', 'config', [], _('set/override config option')),
2564 ('', 'config', [], _('set/override config option')),
2565 ('', 'debug', None, _('enable debugging output')),
2565 ('', 'debug', None, _('enable debugging output')),
2566 ('', 'debugger', None, _('start debugger')),
2566 ('', 'debugger', None, _('start debugger')),
2567 ('', 'encoding', util._encoding, _('set the charset encoding')),
2567 ('', 'encoding', util._encoding, _('set the charset encoding')),
2568 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2568 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2569 ('', 'lsprof', None, _('print improved command execution profile')),
2569 ('', 'lsprof', None, _('print improved command execution profile')),
2570 ('', 'traceback', None, _('print traceback on exception')),
2570 ('', 'traceback', None, _('print traceback on exception')),
2571 ('', 'time', None, _('time how long the command takes')),
2571 ('', 'time', None, _('time how long the command takes')),
2572 ('', 'profile', None, _('print command execution profile')),
2572 ('', 'profile', None, _('print command execution profile')),
2573 ('', 'version', None, _('output version information and exit')),
2573 ('', 'version', None, _('output version information and exit')),
2574 ('h', 'help', None, _('display help and exit')),
2574 ('h', 'help', None, _('display help and exit')),
2575 ]
2575 ]
2576
2576
2577 dryrunopts = [('n', 'dry-run', None,
2577 dryrunopts = [('n', 'dry-run', None,
2578 _('do not perform actions, just print output'))]
2578 _('do not perform actions, just print output'))]
2579
2579
2580 remoteopts = [
2580 remoteopts = [
2581 ('e', 'ssh', '', _('specify ssh command to use')),
2581 ('e', 'ssh', '', _('specify ssh command to use')),
2582 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2582 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2583 ]
2583 ]
2584
2584
2585 walkopts = [
2585 walkopts = [
2586 ('I', 'include', [], _('include names matching the given patterns')),
2586 ('I', 'include', [], _('include names matching the given patterns')),
2587 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2587 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2588 ]
2588 ]
2589
2589
2590 commitopts = [
2590 commitopts = [
2591 ('m', 'message', '', _('use <text> as commit message')),
2591 ('m', 'message', '', _('use <text> as commit message')),
2592 ('l', 'logfile', '', _('read commit message from <file>')),
2592 ('l', 'logfile', '', _('read commit message from <file>')),
2593 ]
2593 ]
2594
2594
2595 table = {
2595 table = {
2596 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2596 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2597 "addremove":
2597 "addremove":
2598 (addremove,
2598 (addremove,
2599 [('s', 'similarity', '',
2599 [('s', 'similarity', '',
2600 _('guess renamed files by similarity (0<=s<=100)')),
2600 _('guess renamed files by similarity (0<=s<=100)')),
2601 ] + walkopts + dryrunopts,
2601 ] + walkopts + dryrunopts,
2602 _('hg addremove [OPTION]... [FILE]...')),
2602 _('hg addremove [OPTION]... [FILE]...')),
2603 "^annotate":
2603 "^annotate":
2604 (annotate,
2604 (annotate,
2605 [('r', 'rev', '', _('annotate the specified revision')),
2605 [('r', 'rev', '', _('annotate the specified revision')),
2606 ('f', 'follow', None, _('follow file copies and renames')),
2606 ('f', 'follow', None, _('follow file copies and renames')),
2607 ('a', 'text', None, _('treat all files as text')),
2607 ('a', 'text', None, _('treat all files as text')),
2608 ('u', 'user', None, _('list the author')),
2608 ('u', 'user', None, _('list the author')),
2609 ('d', 'date', None, _('list the date')),
2609 ('d', 'date', None, _('list the date')),
2610 ('n', 'number', None, _('list the revision number (default)')),
2610 ('n', 'number', None, _('list the revision number (default)')),
2611 ('c', 'changeset', None, _('list the changeset')),
2611 ('c', 'changeset', None, _('list the changeset')),
2612 ] + walkopts,
2612 ] + walkopts,
2613 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2613 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2614 "archive":
2614 "archive":
2615 (archive,
2615 (archive,
2616 [('', 'no-decode', None, _('do not pass files through decoders')),
2616 [('', 'no-decode', None, _('do not pass files through decoders')),
2617 ('p', 'prefix', '', _('directory prefix for files in archive')),
2617 ('p', 'prefix', '', _('directory prefix for files in archive')),
2618 ('r', 'rev', '', _('revision to distribute')),
2618 ('r', 'rev', '', _('revision to distribute')),
2619 ('t', 'type', '', _('type of distribution to create')),
2619 ('t', 'type', '', _('type of distribution to create')),
2620 ] + walkopts,
2620 ] + walkopts,
2621 _('hg archive [OPTION]... DEST')),
2621 _('hg archive [OPTION]... DEST')),
2622 "backout":
2622 "backout":
2623 (backout,
2623 (backout,
2624 [('', 'merge', None,
2624 [('', 'merge', None,
2625 _('merge with old dirstate parent after backout')),
2625 _('merge with old dirstate parent after backout')),
2626 ('d', 'date', '', _('record datecode as commit date')),
2626 ('d', 'date', '', _('record datecode as commit date')),
2627 ('', 'parent', '', _('parent to choose when backing out merge')),
2627 ('', 'parent', '', _('parent to choose when backing out merge')),
2628 ('u', 'user', '', _('record user as committer')),
2628 ('u', 'user', '', _('record user as committer')),
2629 ] + walkopts + commitopts,
2629 ] + walkopts + commitopts,
2630 _('hg backout [OPTION]... REV')),
2630 _('hg backout [OPTION]... REV')),
2631 "branch": (branch, [], _('hg branch [NAME]')),
2631 "branch": (branch, [], _('hg branch [NAME]')),
2632 "branches": (branches, [], _('hg branches')),
2632 "branches": (branches, [], _('hg branches')),
2633 "bundle":
2633 "bundle":
2634 (bundle,
2634 (bundle,
2635 [('f', 'force', None,
2635 [('f', 'force', None,
2636 _('run even when remote repository is unrelated')),
2636 _('run even when remote repository is unrelated')),
2637 ('r', 'rev', [],
2637 ('r', 'rev', [],
2638 _('a changeset you would like to bundle')),
2638 _('a changeset you would like to bundle')),
2639 ('', 'base', [],
2639 ('', 'base', [],
2640 _('a base changeset to specify instead of a destination')),
2640 _('a base changeset to specify instead of a destination')),
2641 ] + remoteopts,
2641 ] + remoteopts,
2642 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2642 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2643 "cat":
2643 "cat":
2644 (cat,
2644 (cat,
2645 [('o', 'output', '', _('print output to file with formatted name')),
2645 [('o', 'output', '', _('print output to file with formatted name')),
2646 ('r', 'rev', '', _('print the given revision')),
2646 ('r', 'rev', '', _('print the given revision')),
2647 ] + walkopts,
2647 ] + walkopts,
2648 _('hg cat [OPTION]... FILE...')),
2648 _('hg cat [OPTION]... FILE...')),
2649 "^clone":
2649 "^clone":
2650 (clone,
2650 (clone,
2651 [('U', 'noupdate', None, _('do not update the new working directory')),
2651 [('U', 'noupdate', None, _('do not update the new working directory')),
2652 ('r', 'rev', [],
2652 ('r', 'rev', [],
2653 _('a changeset you would like to have after cloning')),
2653 _('a changeset you would like to have after cloning')),
2654 ('', 'pull', None, _('use pull protocol to copy metadata')),
2654 ('', 'pull', None, _('use pull protocol to copy metadata')),
2655 ('', 'uncompressed', None,
2655 ('', 'uncompressed', None,
2656 _('use uncompressed transfer (fast over LAN)')),
2656 _('use uncompressed transfer (fast over LAN)')),
2657 ] + remoteopts,
2657 ] + remoteopts,
2658 _('hg clone [OPTION]... SOURCE [DEST]')),
2658 _('hg clone [OPTION]... SOURCE [DEST]')),
2659 "^commit|ci":
2659 "^commit|ci":
2660 (commit,
2660 (commit,
2661 [('A', 'addremove', None,
2661 [('A', 'addremove', None,
2662 _('mark new/missing files as added/removed before committing')),
2662 _('mark new/missing files as added/removed before committing')),
2663 ('d', 'date', '', _('record datecode as commit date')),
2663 ('d', 'date', '', _('record datecode as commit date')),
2664 ('u', 'user', '', _('record user as commiter')),
2664 ('u', 'user', '', _('record user as commiter')),
2665 ] + walkopts + commitopts,
2665 ] + walkopts + commitopts,
2666 _('hg commit [OPTION]... [FILE]...')),
2666 _('hg commit [OPTION]... [FILE]...')),
2667 "copy|cp":
2667 "copy|cp":
2668 (copy,
2668 (copy,
2669 [('A', 'after', None, _('record a copy that has already occurred')),
2669 [('A', 'after', None, _('record a copy that has already occurred')),
2670 ('f', 'force', None,
2670 ('f', 'force', None,
2671 _('forcibly copy over an existing managed file')),
2671 _('forcibly copy over an existing managed file')),
2672 ] + walkopts + dryrunopts,
2672 ] + walkopts + dryrunopts,
2673 _('hg copy [OPTION]... [SOURCE]... DEST')),
2673 _('hg copy [OPTION]... [SOURCE]... DEST')),
2674 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2674 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2675 "debugcomplete":
2675 "debugcomplete":
2676 (debugcomplete,
2676 (debugcomplete,
2677 [('o', 'options', None, _('show the command options'))],
2677 [('o', 'options', None, _('show the command options'))],
2678 _('debugcomplete [-o] CMD')),
2678 _('debugcomplete [-o] CMD')),
2679 "debuginstall": (debuginstall, [], _('debuginstall')),
2679 "debuginstall": (debuginstall, [], _('debuginstall')),
2680 "debugrebuildstate":
2680 "debugrebuildstate":
2681 (debugrebuildstate,
2681 (debugrebuildstate,
2682 [('r', 'rev', '', _('revision to rebuild to'))],
2682 [('r', 'rev', '', _('revision to rebuild to'))],
2683 _('debugrebuildstate [-r REV] [REV]')),
2683 _('debugrebuildstate [-r REV] [REV]')),
2684 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2684 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2685 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2685 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2686 "debugstate": (debugstate, [], _('debugstate')),
2686 "debugstate": (debugstate, [], _('debugstate')),
2687 "debugdate":
2687 "debugdate":
2688 (debugdate,
2688 (debugdate,
2689 [('e', 'extended', None, _('try extended date formats'))],
2689 [('e', 'extended', None, _('try extended date formats'))],
2690 _('debugdate [-e] DATE [RANGE]')),
2690 _('debugdate [-e] DATE [RANGE]')),
2691 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2691 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2692 "debugindex": (debugindex, [], _('debugindex FILE')),
2692 "debugindex": (debugindex, [], _('debugindex FILE')),
2693 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2693 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2694 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2694 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2695 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2695 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2696 "^diff":
2696 "^diff":
2697 (diff,
2697 (diff,
2698 [('r', 'rev', [], _('revision')),
2698 [('r', 'rev', [], _('revision')),
2699 ('a', 'text', None, _('treat all files as text')),
2699 ('a', 'text', None, _('treat all files as text')),
2700 ('p', 'show-function', None,
2700 ('p', 'show-function', None,
2701 _('show which function each change is in')),
2701 _('show which function each change is in')),
2702 ('g', 'git', None, _('use git extended diff format')),
2702 ('g', 'git', None, _('use git extended diff format')),
2703 ('', 'nodates', None, _("don't include dates in diff headers")),
2703 ('', 'nodates', None, _("don't include dates in diff headers")),
2704 ('w', 'ignore-all-space', None,
2704 ('w', 'ignore-all-space', None,
2705 _('ignore white space when comparing lines')),
2705 _('ignore white space when comparing lines')),
2706 ('b', 'ignore-space-change', None,
2706 ('b', 'ignore-space-change', None,
2707 _('ignore changes in the amount of white space')),
2707 _('ignore changes in the amount of white space')),
2708 ('B', 'ignore-blank-lines', None,
2708 ('B', 'ignore-blank-lines', None,
2709 _('ignore changes whose lines are all blank')),
2709 _('ignore changes whose lines are all blank')),
2710 ] + walkopts,
2710 ] + walkopts,
2711 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2711 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2712 "^export":
2712 "^export":
2713 (export,
2713 (export,
2714 [('o', 'output', '', _('print output to file with formatted name')),
2714 [('o', 'output', '', _('print output to file with formatted name')),
2715 ('a', 'text', None, _('treat all files as text')),
2715 ('a', 'text', None, _('treat all files as text')),
2716 ('g', 'git', None, _('use git extended diff format')),
2716 ('g', 'git', None, _('use git extended diff format')),
2717 ('', 'nodates', None, _("don't include dates in diff headers")),
2717 ('', 'nodates', None, _("don't include dates in diff headers")),
2718 ('', 'switch-parent', None, _('diff against the second parent'))],
2718 ('', 'switch-parent', None, _('diff against the second parent'))],
2719 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2719 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2720 "grep":
2720 "grep":
2721 (grep,
2721 (grep,
2722 [('0', 'print0', None, _('end fields with NUL')),
2722 [('0', 'print0', None, _('end fields with NUL')),
2723 ('', 'all', None, _('print all revisions that match')),
2723 ('', 'all', None, _('print all revisions that match')),
2724 ('f', 'follow', None,
2724 ('f', 'follow', None,
2725 _('follow changeset history, or file history across copies and renames')),
2725 _('follow changeset history, or file history across copies and renames')),
2726 ('i', 'ignore-case', None, _('ignore case when matching')),
2726 ('i', 'ignore-case', None, _('ignore case when matching')),
2727 ('l', 'files-with-matches', None,
2727 ('l', 'files-with-matches', None,
2728 _('print only filenames and revs that match')),
2728 _('print only filenames and revs that match')),
2729 ('n', 'line-number', None, _('print matching line numbers')),
2729 ('n', 'line-number', None, _('print matching line numbers')),
2730 ('r', 'rev', [], _('search in given revision range')),
2730 ('r', 'rev', [], _('search in given revision range')),
2731 ('u', 'user', None, _('print user who committed change')),
2731 ('u', 'user', None, _('print user who committed change')),
2732 ] + walkopts,
2732 ] + walkopts,
2733 _('hg grep [OPTION]... PATTERN [FILE]...')),
2733 _('hg grep [OPTION]... PATTERN [FILE]...')),
2734 "heads":
2734 "heads":
2735 (heads,
2735 (heads,
2736 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2736 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2737 ('', 'style', '', _('display using template map file')),
2737 ('', 'style', '', _('display using template map file')),
2738 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2738 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2739 ('', 'template', '', _('display with template'))],
2739 ('', 'template', '', _('display with template'))],
2740 _('hg heads [-r REV]')),
2740 _('hg heads [-r REV]')),
2741 "help": (help_, [], _('hg help [COMMAND]')),
2741 "help": (help_, [], _('hg help [COMMAND]')),
2742 "identify|id": (identify, [], _('hg identify')),
2742 "identify|id": (identify, [], _('hg identify')),
2743 "import|patch":
2743 "import|patch":
2744 (import_,
2744 (import_,
2745 [('p', 'strip', 1,
2745 [('p', 'strip', 1,
2746 _('directory strip option for patch. This has the same\n'
2746 _('directory strip option for patch. This has the same\n'
2747 'meaning as the corresponding patch option')),
2747 'meaning as the corresponding patch option')),
2748 ('b', 'base', '', _('base path (DEPRECATED)')),
2748 ('b', 'base', '', _('base path (DEPRECATED)')),
2749 ('f', 'force', None,
2749 ('f', 'force', None,
2750 _('skip check for outstanding uncommitted changes'))] + commitopts,
2750 _('skip check for outstanding uncommitted changes'))] + commitopts,
2751 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2751 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2752 "incoming|in": (incoming,
2752 "incoming|in": (incoming,
2753 [('M', 'no-merges', None, _('do not show merges')),
2753 [('M', 'no-merges', None, _('do not show merges')),
2754 ('f', 'force', None,
2754 ('f', 'force', None,
2755 _('run even when remote repository is unrelated')),
2755 _('run even when remote repository is unrelated')),
2756 ('', 'style', '', _('display using template map file')),
2756 ('', 'style', '', _('display using template map file')),
2757 ('n', 'newest-first', None, _('show newest record first')),
2757 ('n', 'newest-first', None, _('show newest record first')),
2758 ('', 'bundle', '', _('file to store the bundles into')),
2758 ('', 'bundle', '', _('file to store the bundles into')),
2759 ('p', 'patch', None, _('show patch')),
2759 ('p', 'patch', None, _('show patch')),
2760 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2760 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2761 ('', 'template', '', _('display with template')),
2761 ('', 'template', '', _('display with template')),
2762 ] + remoteopts,
2762 ] + remoteopts,
2763 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2763 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2764 ' [--bundle FILENAME] [SOURCE]')),
2764 ' [--bundle FILENAME] [SOURCE]')),
2765 "^init":
2765 "^init":
2766 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2766 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2767 "locate":
2767 "locate":
2768 (locate,
2768 (locate,
2769 [('r', 'rev', '', _('search the repository as it stood at rev')),
2769 [('r', 'rev', '', _('search the repository as it stood at rev')),
2770 ('0', 'print0', None,
2770 ('0', 'print0', None,
2771 _('end filenames with NUL, for use with xargs')),
2771 _('end filenames with NUL, for use with xargs')),
2772 ('f', 'fullpath', None,
2772 ('f', 'fullpath', None,
2773 _('print complete paths from the filesystem root')),
2773 _('print complete paths from the filesystem root')),
2774 ] + walkopts,
2774 ] + walkopts,
2775 _('hg locate [OPTION]... [PATTERN]...')),
2775 _('hg locate [OPTION]... [PATTERN]...')),
2776 "^log|history":
2776 "^log|history":
2777 (log,
2777 (log,
2778 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2778 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2779 ('f', 'follow', None,
2779 ('f', 'follow', None,
2780 _('follow changeset history, or file history across copies and renames')),
2780 _('follow changeset history, or file history across copies and renames')),
2781 ('', 'follow-first', None,
2781 ('', 'follow-first', None,
2782 _('only follow the first parent of merge changesets')),
2782 _('only follow the first parent of merge changesets')),
2783 ('d', 'date', '', _('show revs matching date spec')),
2783 ('d', 'date', '', _('show revs matching date spec')),
2784 ('C', 'copies', None, _('show copied files')),
2784 ('C', 'copies', None, _('show copied files')),
2785 ('k', 'keyword', [], _('search for a keyword')),
2785 ('k', 'keyword', [], _('search for a keyword')),
2786 ('l', 'limit', '', _('limit number of changes displayed')),
2786 ('l', 'limit', '', _('limit number of changes displayed')),
2787 ('r', 'rev', [], _('show the specified revision or range')),
2787 ('r', 'rev', [], _('show the specified revision or range')),
2788 ('', 'removed', None, _('include revs where files were removed')),
2788 ('', 'removed', None, _('include revs where files were removed')),
2789 ('M', 'no-merges', None, _('do not show merges')),
2789 ('M', 'no-merges', None, _('do not show merges')),
2790 ('', 'style', '', _('display using template map file')),
2790 ('', 'style', '', _('display using template map file')),
2791 ('m', 'only-merges', None, _('show only merges')),
2791 ('m', 'only-merges', None, _('show only merges')),
2792 ('p', 'patch', None, _('show patch')),
2792 ('p', 'patch', None, _('show patch')),
2793 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2793 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2794 ('', 'template', '', _('display with template')),
2794 ('', 'template', '', _('display with template')),
2795 ] + walkopts,
2795 ] + walkopts,
2796 _('hg log [OPTION]... [FILE]')),
2796 _('hg log [OPTION]... [FILE]')),
2797 "manifest": (manifest, [], _('hg manifest [REV]')),
2797 "manifest": (manifest, [], _('hg manifest [REV]')),
2798 "merge":
2798 "merge":
2799 (merge,
2799 (merge,
2800 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2800 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2801 ('f', 'force', None, _('force a merge with outstanding changes'))],
2801 ('f', 'force', None, _('force a merge with outstanding changes'))],
2802 _('hg merge [-f] [REV]')),
2802 _('hg merge [-f] [REV]')),
2803 "outgoing|out": (outgoing,
2803 "outgoing|out": (outgoing,
2804 [('M', 'no-merges', None, _('do not show merges')),
2804 [('M', 'no-merges', None, _('do not show merges')),
2805 ('f', 'force', None,
2805 ('f', 'force', None,
2806 _('run even when remote repository is unrelated')),
2806 _('run even when remote repository is unrelated')),
2807 ('p', 'patch', None, _('show patch')),
2807 ('p', 'patch', None, _('show patch')),
2808 ('', 'style', '', _('display using template map file')),
2808 ('', 'style', '', _('display using template map file')),
2809 ('r', 'rev', [], _('a specific revision you would like to push')),
2809 ('r', 'rev', [], _('a specific revision you would like to push')),
2810 ('n', 'newest-first', None, _('show newest record first')),
2810 ('n', 'newest-first', None, _('show newest record first')),
2811 ('', 'template', '', _('display with template')),
2811 ('', 'template', '', _('display with template')),
2812 ] + remoteopts,
2812 ] + remoteopts,
2813 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2813 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2814 "^parents":
2814 "^parents":
2815 (parents,
2815 (parents,
2816 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2816 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2817 ('r', 'rev', '', _('show parents from the specified rev')),
2817 ('r', 'rev', '', _('show parents from the specified rev')),
2818 ('', 'style', '', _('display using template map file')),
2818 ('', 'style', '', _('display using template map file')),
2819 ('', 'template', '', _('display with template'))],
2819 ('', 'template', '', _('display with template'))],
2820 _('hg parents [-r REV] [FILE]')),
2820 _('hg parents [-r REV] [FILE]')),
2821 "paths": (paths, [], _('hg paths [NAME]')),
2821 "paths": (paths, [], _('hg paths [NAME]')),
2822 "^pull":
2822 "^pull":
2823 (pull,
2823 (pull,
2824 [('u', 'update', None,
2824 [('u', 'update', None,
2825 _('update to new tip if changesets were pulled')),
2825 _('update to new tip if changesets were pulled')),
2826 ('f', 'force', None,
2826 ('f', 'force', None,
2827 _('run even when remote repository is unrelated')),
2827 _('run even when remote repository is unrelated')),
2828 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2828 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2829 ] + remoteopts,
2829 ] + remoteopts,
2830 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2830 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2831 "^push":
2831 "^push":
2832 (push,
2832 (push,
2833 [('f', 'force', None, _('force push')),
2833 [('f', 'force', None, _('force push')),
2834 ('r', 'rev', [], _('a specific revision you would like to push')),
2834 ('r', 'rev', [], _('a specific revision you would like to push')),
2835 ] + remoteopts,
2835 ] + remoteopts,
2836 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2836 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2837 "debugrawcommit|rawcommit":
2837 "debugrawcommit|rawcommit":
2838 (rawcommit,
2838 (rawcommit,
2839 [('p', 'parent', [], _('parent')),
2839 [('p', 'parent', [], _('parent')),
2840 ('d', 'date', '', _('date code')),
2840 ('d', 'date', '', _('date code')),
2841 ('u', 'user', '', _('user')),
2841 ('u', 'user', '', _('user')),
2842 ('F', 'files', '', _('file list'))
2842 ('F', 'files', '', _('file list'))
2843 ] + commitopts,
2843 ] + commitopts,
2844 _('hg debugrawcommit [OPTION]... [FILE]...')),
2844 _('hg debugrawcommit [OPTION]... [FILE]...')),
2845 "recover": (recover, [], _('hg recover')),
2845 "recover": (recover, [], _('hg recover')),
2846 "^remove|rm":
2846 "^remove|rm":
2847 (remove,
2847 (remove,
2848 [('A', 'after', None, _('record remove that has already occurred')),
2848 [('A', 'after', None, _('record remove that has already occurred')),
2849 ('f', 'force', None, _('remove file even if modified')),
2849 ('f', 'force', None, _('remove file even if modified')),
2850 ] + walkopts,
2850 ] + walkopts,
2851 _('hg remove [OPTION]... FILE...')),
2851 _('hg remove [OPTION]... FILE...')),
2852 "rename|mv":
2852 "rename|mv":
2853 (rename,
2853 (rename,
2854 [('A', 'after', None, _('record a rename that has already occurred')),
2854 [('A', 'after', None, _('record a rename that has already occurred')),
2855 ('f', 'force', None,
2855 ('f', 'force', None,
2856 _('forcibly copy over an existing managed file')),
2856 _('forcibly copy over an existing managed file')),
2857 ] + walkopts + dryrunopts,
2857 ] + walkopts + dryrunopts,
2858 _('hg rename [OPTION]... SOURCE... DEST')),
2858 _('hg rename [OPTION]... SOURCE... DEST')),
2859 "^revert":
2859 "^revert":
2860 (revert,
2860 (revert,
2861 [('a', 'all', None, _('revert all changes when no arguments given')),
2861 [('a', 'all', None, _('revert all changes when no arguments given')),
2862 ('d', 'date', '', _('tipmost revision matching date')),
2862 ('d', 'date', '', _('tipmost revision matching date')),
2863 ('r', 'rev', '', _('revision to revert to')),
2863 ('r', 'rev', '', _('revision to revert to')),
2864 ('', 'no-backup', None, _('do not save backup copies of files')),
2864 ('', 'no-backup', None, _('do not save backup copies of files')),
2865 ] + walkopts + dryrunopts,
2865 ] + walkopts + dryrunopts,
2866 _('hg revert [-r REV] [NAME]...')),
2866 _('hg revert [-r REV] [NAME]...')),
2867 "rollback": (rollback, [], _('hg rollback')),
2867 "rollback": (rollback, [], _('hg rollback')),
2868 "root": (root, [], _('hg root')),
2868 "root": (root, [], _('hg root')),
2869 "showconfig|debugconfig":
2869 "showconfig|debugconfig":
2870 (showconfig,
2870 (showconfig,
2871 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2871 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2872 _('showconfig [-u] [NAME]...')),
2872 _('showconfig [-u] [NAME]...')),
2873 "^serve":
2873 "^serve":
2874 (serve,
2874 (serve,
2875 [('A', 'accesslog', '', _('name of access log file to write to')),
2875 [('A', 'accesslog', '', _('name of access log file to write to')),
2876 ('d', 'daemon', None, _('run server in background')),
2876 ('d', 'daemon', None, _('run server in background')),
2877 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2877 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2878 ('E', 'errorlog', '', _('name of error log file to write to')),
2878 ('E', 'errorlog', '', _('name of error log file to write to')),
2879 ('p', 'port', 0, _('port to use (default: 8000)')),
2879 ('p', 'port', 0, _('port to use (default: 8000)')),
2880 ('a', 'address', '', _('address to use')),
2880 ('a', 'address', '', _('address to use')),
2881 ('n', 'name', '',
2881 ('n', 'name', '',
2882 _('name to show in web pages (default: working dir)')),
2882 _('name to show in web pages (default: working dir)')),
2883 ('', 'webdir-conf', '', _('name of the webdir config file'
2883 ('', 'webdir-conf', '', _('name of the webdir config file'
2884 ' (serve more than one repo)')),
2884 ' (serve more than one repo)')),
2885 ('', 'pid-file', '', _('name of file to write process ID to')),
2885 ('', 'pid-file', '', _('name of file to write process ID to')),
2886 ('', 'stdio', None, _('for remote clients')),
2886 ('', 'stdio', None, _('for remote clients')),
2887 ('t', 'templates', '', _('web templates to use')),
2887 ('t', 'templates', '', _('web templates to use')),
2888 ('', 'style', '', _('template style to use')),
2888 ('', 'style', '', _('template style to use')),
2889 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2889 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2890 _('hg serve [OPTION]...')),
2890 _('hg serve [OPTION]...')),
2891 "^status|st":
2891 "^status|st":
2892 (status,
2892 (status,
2893 [('A', 'all', None, _('show status of all files')),
2893 [('A', 'all', None, _('show status of all files')),
2894 ('m', 'modified', None, _('show only modified files')),
2894 ('m', 'modified', None, _('show only modified files')),
2895 ('a', 'added', None, _('show only added files')),
2895 ('a', 'added', None, _('show only added files')),
2896 ('r', 'removed', None, _('show only removed files')),
2896 ('r', 'removed', None, _('show only removed files')),
2897 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2897 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2898 ('c', 'clean', None, _('show only files without changes')),
2898 ('c', 'clean', None, _('show only files without changes')),
2899 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2899 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2900 ('i', 'ignored', None, _('show ignored files')),
2900 ('i', 'ignored', None, _('show ignored files')),
2901 ('n', 'no-status', None, _('hide status prefix')),
2901 ('n', 'no-status', None, _('hide status prefix')),
2902 ('C', 'copies', None, _('show source of copied files')),
2902 ('C', 'copies', None, _('show source of copied files')),
2903 ('0', 'print0', None,
2903 ('0', 'print0', None,
2904 _('end filenames with NUL, for use with xargs')),
2904 _('end filenames with NUL, for use with xargs')),
2905 ('', 'rev', [], _('show difference from revision')),
2905 ('', 'rev', [], _('show difference from revision')),
2906 ] + walkopts,
2906 ] + walkopts,
2907 _('hg status [OPTION]... [FILE]...')),
2907 _('hg status [OPTION]... [FILE]...')),
2908 "tag":
2908 "tag":
2909 (tag,
2909 (tag,
2910 [('l', 'local', None, _('make the tag local')),
2910 [('l', 'local', None, _('make the tag local')),
2911 ('m', 'message', '', _('message for tag commit log entry')),
2911 ('m', 'message', '', _('message for tag commit log entry')),
2912 ('d', 'date', '', _('record datecode as commit date')),
2912 ('d', 'date', '', _('record datecode as commit date')),
2913 ('u', 'user', '', _('record user as commiter')),
2913 ('u', 'user', '', _('record user as commiter')),
2914 ('r', 'rev', '', _('revision to tag'))],
2914 ('r', 'rev', '', _('revision to tag'))],
2915 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2915 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2916 "tags": (tags, [], _('hg tags')),
2916 "tags": (tags, [], _('hg tags')),
2917 "tip":
2917 "tip":
2918 (tip,
2918 (tip,
2919 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2919 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2920 ('', 'style', '', _('display using template map file')),
2920 ('', 'style', '', _('display using template map file')),
2921 ('p', 'patch', None, _('show patch')),
2921 ('p', 'patch', None, _('show patch')),
2922 ('', 'template', '', _('display with template'))],
2922 ('', 'template', '', _('display with template'))],
2923 _('hg tip [-p]')),
2923 _('hg tip [-p]')),
2924 "unbundle":
2924 "unbundle":
2925 (unbundle,
2925 (unbundle,
2926 [('u', 'update', None,
2926 [('u', 'update', None,
2927 _('update to new tip if changesets were unbundled'))],
2927 _('update to new tip if changesets were unbundled'))],
2928 _('hg unbundle [-u] FILE')),
2928 _('hg unbundle [-u] FILE')),
2929 "^update|up|checkout|co":
2929 "^update|up|checkout|co":
2930 (update,
2930 (update,
2931 [('b', 'branch', '',
2931 [('b', 'branch', '',
2932 _('checkout the head of a specific branch (DEPRECATED)')),
2932 _('checkout the head of a specific branch (DEPRECATED)')),
2933 ('C', 'clean', None, _('overwrite locally modified files')),
2933 ('C', 'clean', None, _('overwrite locally modified files')),
2934 ('d', 'date', '', _('tipmost revision matching date'))],
2934 ('d', 'date', '', _('tipmost revision matching date'))],
2935 _('hg update [-C] [REV]')),
2935 _('hg update [-C] [REV]')),
2936 "verify": (verify, [], _('hg verify')),
2936 "verify": (verify, [], _('hg verify')),
2937 "version": (version_, [], _('hg version')),
2937 "version": (version_, [], _('hg version')),
2938 }
2938 }
2939
2939
2940 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2940 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2941 " debugindex debugindexdot debugdate debuginstall")
2941 " debugindex debugindexdot debugdate debuginstall")
2942 optionalrepo = ("paths serve showconfig")
2942 optionalrepo = ("paths serve showconfig")
2943
2943
2944 def findpossible(ui, cmd):
2944 def findpossible(ui, cmd):
2945 """
2945 """
2946 Return cmd -> (aliases, command table entry)
2946 Return cmd -> (aliases, command table entry)
2947 for each matching command.
2947 for each matching command.
2948 Return debug commands (or their aliases) only if no normal command matches.
2948 Return debug commands (or their aliases) only if no normal command matches.
2949 """
2949 """
2950 choice = {}
2950 choice = {}
2951 debugchoice = {}
2951 debugchoice = {}
2952 for e in table.keys():
2952 for e in table.keys():
2953 aliases = e.lstrip("^").split("|")
2953 aliases = e.lstrip("^").split("|")
2954 found = None
2954 found = None
2955 if cmd in aliases:
2955 if cmd in aliases:
2956 found = cmd
2956 found = cmd
2957 elif not ui.config("ui", "strict"):
2957 elif not ui.config("ui", "strict"):
2958 for a in aliases:
2958 for a in aliases:
2959 if a.startswith(cmd):
2959 if a.startswith(cmd):
2960 found = a
2960 found = a
2961 break
2961 break
2962 if found is not None:
2962 if found is not None:
2963 if aliases[0].startswith("debug") or found.startswith("debug"):
2963 if aliases[0].startswith("debug") or found.startswith("debug"):
2964 debugchoice[found] = (aliases, table[e])
2964 debugchoice[found] = (aliases, table[e])
2965 else:
2965 else:
2966 choice[found] = (aliases, table[e])
2966 choice[found] = (aliases, table[e])
2967
2967
2968 if not choice and debugchoice:
2968 if not choice and debugchoice:
2969 choice = debugchoice
2969 choice = debugchoice
2970
2970
2971 return choice
2971 return choice
2972
2972
2973 def findcmd(ui, cmd):
2973 def findcmd(ui, cmd):
2974 """Return (aliases, command table entry) for command string."""
2974 """Return (aliases, command table entry) for command string."""
2975 choice = findpossible(ui, cmd)
2975 choice = findpossible(ui, cmd)
2976
2976
2977 if choice.has_key(cmd):
2977 if choice.has_key(cmd):
2978 return choice[cmd]
2978 return choice[cmd]
2979
2979
2980 if len(choice) > 1:
2980 if len(choice) > 1:
2981 clist = choice.keys()
2981 clist = choice.keys()
2982 clist.sort()
2982 clist.sort()
2983 raise AmbiguousCommand(cmd, clist)
2983 raise AmbiguousCommand(cmd, clist)
2984
2984
2985 if choice:
2985 if choice:
2986 return choice.values()[0]
2986 return choice.values()[0]
2987
2987
2988 raise UnknownCommand(cmd)
2988 raise UnknownCommand(cmd)
2989
2989
2990 def catchterm(*args):
2990 def catchterm(*args):
2991 raise util.SignalInterrupt
2991 raise util.SignalInterrupt
2992
2992
2993 def run():
2993 def run():
2994 sys.exit(dispatch(sys.argv[1:]))
2994 sys.exit(dispatch(sys.argv[1:]))
2995
2995
2996 class ParseError(Exception):
2996 class ParseError(Exception):
2997 """Exception raised on errors in parsing the command line."""
2997 """Exception raised on errors in parsing the command line."""
2998
2998
2999 def parse(ui, args):
2999 def parse(ui, args):
3000 options = {}
3000 options = {}
3001 cmdoptions = {}
3001 cmdoptions = {}
3002
3002
3003 try:
3003 try:
3004 args = fancyopts.fancyopts(args, globalopts, options)
3004 args = fancyopts.fancyopts(args, globalopts, options)
3005 except fancyopts.getopt.GetoptError, inst:
3005 except fancyopts.getopt.GetoptError, inst:
3006 raise ParseError(None, inst)
3006 raise ParseError(None, inst)
3007
3007
3008 if args:
3008 if args:
3009 cmd, args = args[0], args[1:]
3009 cmd, args = args[0], args[1:]
3010 aliases, i = findcmd(ui, cmd)
3010 aliases, i = findcmd(ui, cmd)
3011 cmd = aliases[0]
3011 cmd = aliases[0]
3012 defaults = ui.config("defaults", cmd)
3012 defaults = ui.config("defaults", cmd)
3013 if defaults:
3013 if defaults:
3014 args = shlex.split(defaults) + args
3014 args = shlex.split(defaults) + args
3015 c = list(i[1])
3015 c = list(i[1])
3016 else:
3016 else:
3017 cmd = None
3017 cmd = None
3018 c = []
3018 c = []
3019
3019
3020 # combine global options into local
3020 # combine global options into local
3021 for o in globalopts:
3021 for o in globalopts:
3022 c.append((o[0], o[1], options[o[1]], o[3]))
3022 c.append((o[0], o[1], options[o[1]], o[3]))
3023
3023
3024 try:
3024 try:
3025 args = fancyopts.fancyopts(args, c, cmdoptions)
3025 args = fancyopts.fancyopts(args, c, cmdoptions)
3026 except fancyopts.getopt.GetoptError, inst:
3026 except fancyopts.getopt.GetoptError, inst:
3027 raise ParseError(cmd, inst)
3027 raise ParseError(cmd, inst)
3028
3028
3029 # separate global options back out
3029 # separate global options back out
3030 for o in globalopts:
3030 for o in globalopts:
3031 n = o[1]
3031 n = o[1]
3032 options[n] = cmdoptions[n]
3032 options[n] = cmdoptions[n]
3033 del cmdoptions[n]
3033 del cmdoptions[n]
3034
3034
3035 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3035 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3036
3036
3037 external = {}
3037 external = {}
3038
3038
3039 def findext(name):
3039 def findext(name):
3040 '''return module with given extension name'''
3040 '''return module with given extension name'''
3041 try:
3041 try:
3042 return sys.modules[external[name]]
3042 return sys.modules[external[name]]
3043 except KeyError:
3043 except KeyError:
3044 for k, v in external.iteritems():
3044 for k, v in external.iteritems():
3045 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3045 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3046 return sys.modules[v]
3046 return sys.modules[v]
3047 raise KeyError(name)
3047 raise KeyError(name)
3048
3048
3049 def load_extensions(ui):
3049 def load_extensions(ui):
3050 added = []
3050 added = []
3051 for ext_name, load_from_name in ui.extensions():
3051 for ext_name, load_from_name in ui.extensions():
3052 if ext_name in external:
3052 if ext_name in external:
3053 continue
3053 continue
3054 try:
3054 try:
3055 if load_from_name:
3055 if load_from_name:
3056 # the module will be loaded in sys.modules
3056 # the module will be loaded in sys.modules
3057 # choose an unique name so that it doesn't
3057 # choose an unique name so that it doesn't
3058 # conflicts with other modules
3058 # conflicts with other modules
3059 module_name = "hgext_%s" % ext_name.replace('.', '_')
3059 module_name = "hgext_%s" % ext_name.replace('.', '_')
3060 mod = imp.load_source(module_name, load_from_name)
3060 mod = imp.load_source(module_name, load_from_name)
3061 else:
3061 else:
3062 def importh(name):
3062 def importh(name):
3063 mod = __import__(name)
3063 mod = __import__(name)
3064 components = name.split('.')
3064 components = name.split('.')
3065 for comp in components[1:]:
3065 for comp in components[1:]:
3066 mod = getattr(mod, comp)
3066 mod = getattr(mod, comp)
3067 return mod
3067 return mod
3068 try:
3068 try:
3069 mod = importh("hgext.%s" % ext_name)
3069 mod = importh("hgext.%s" % ext_name)
3070 except ImportError:
3070 except ImportError:
3071 mod = importh(ext_name)
3071 mod = importh(ext_name)
3072 external[ext_name] = mod.__name__
3072 external[ext_name] = mod.__name__
3073 added.append((mod, ext_name))
3073 added.append((mod, ext_name))
3074 except (util.SignalInterrupt, KeyboardInterrupt):
3074 except (util.SignalInterrupt, KeyboardInterrupt):
3075 raise
3075 raise
3076 except Exception, inst:
3076 except Exception, inst:
3077 ui.warn(_("*** failed to import extension %s: %s\n") %
3077 ui.warn(_("*** failed to import extension %s: %s\n") %
3078 (ext_name, inst))
3078 (ext_name, inst))
3079 if ui.print_exc():
3079 if ui.print_exc():
3080 return 1
3080 return 1
3081
3081
3082 for mod, name in added:
3082 for mod, name in added:
3083 uisetup = getattr(mod, 'uisetup', None)
3083 uisetup = getattr(mod, 'uisetup', None)
3084 if uisetup:
3084 if uisetup:
3085 uisetup(ui)
3085 uisetup(ui)
3086 cmdtable = getattr(mod, 'cmdtable', {})
3086 cmdtable = getattr(mod, 'cmdtable', {})
3087 for t in cmdtable:
3087 for t in cmdtable:
3088 if t in table:
3088 if t in table:
3089 ui.warn(_("module %s overrides %s\n") % (name, t))
3089 ui.warn(_("module %s overrides %s\n") % (name, t))
3090 table.update(cmdtable)
3090 table.update(cmdtable)
3091
3091
3092 def parseconfig(config):
3092 def parseconfig(config):
3093 """parse the --config options from the command line"""
3093 """parse the --config options from the command line"""
3094 parsed = []
3094 parsed = []
3095 for cfg in config:
3095 for cfg in config:
3096 try:
3096 try:
3097 name, value = cfg.split('=', 1)
3097 name, value = cfg.split('=', 1)
3098 section, name = name.split('.', 1)
3098 section, name = name.split('.', 1)
3099 if not section or not name:
3099 if not section or not name:
3100 raise IndexError
3100 raise IndexError
3101 parsed.append((section, name, value))
3101 parsed.append((section, name, value))
3102 except (IndexError, ValueError):
3102 except (IndexError, ValueError):
3103 raise util.Abort(_('malformed --config option: %s') % cfg)
3103 raise util.Abort(_('malformed --config option: %s') % cfg)
3104 return parsed
3104 return parsed
3105
3105
3106 def dispatch(args):
3106 def dispatch(args):
3107 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3107 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3108 num = getattr(signal, name, None)
3108 num = getattr(signal, name, None)
3109 if num: signal.signal(num, catchterm)
3109 if num: signal.signal(num, catchterm)
3110
3110
3111 try:
3111 try:
3112 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3112 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3113 except util.Abort, inst:
3113 except util.Abort, inst:
3114 sys.stderr.write(_("abort: %s\n") % inst)
3114 sys.stderr.write(_("abort: %s\n") % inst)
3115 return -1
3115 return -1
3116
3116
3117 load_extensions(u)
3117 load_extensions(u)
3118 u.addreadhook(load_extensions)
3118 u.addreadhook(load_extensions)
3119
3119
3120 try:
3120 try:
3121 cmd, func, args, options, cmdoptions = parse(u, args)
3121 cmd, func, args, options, cmdoptions = parse(u, args)
3122 if options["encoding"]:
3122 if options["encoding"]:
3123 util._encoding = options["encoding"]
3123 util._encoding = options["encoding"]
3124 if options["encodingmode"]:
3124 if options["encodingmode"]:
3125 util._encodingmode = options["encodingmode"]
3125 util._encodingmode = options["encodingmode"]
3126 if options["time"]:
3126 if options["time"]:
3127 def get_times():
3127 def get_times():
3128 t = os.times()
3128 t = os.times()
3129 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3129 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3130 t = (t[0], t[1], t[2], t[3], time.clock())
3130 t = (t[0], t[1], t[2], t[3], time.clock())
3131 return t
3131 return t
3132 s = get_times()
3132 s = get_times()
3133 def print_time():
3133 def print_time():
3134 t = get_times()
3134 t = get_times()
3135 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3135 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3136 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3136 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3137 atexit.register(print_time)
3137 atexit.register(print_time)
3138
3138
3139 # enter the debugger before command execution
3139 # enter the debugger before command execution
3140 if options['debugger']:
3140 if options['debugger']:
3141 pdb.set_trace()
3141 pdb.set_trace()
3142
3142
3143 try:
3143 try:
3144 if options['cwd']:
3144 if options['cwd']:
3145 os.chdir(options['cwd'])
3145 os.chdir(options['cwd'])
3146
3146
3147 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3147 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3148 not options["noninteractive"], options["traceback"],
3148 not options["noninteractive"], options["traceback"],
3149 parseconfig(options["config"]))
3149 parseconfig(options["config"]))
3150
3150
3151 path = u.expandpath(options["repository"]) or ""
3151 path = u.expandpath(options["repository"]) or ""
3152 repo = path and hg.repository(u, path=path) or None
3152 repo = path and hg.repository(u, path=path) or None
3153 if repo and not repo.local():
3153 if repo and not repo.local():
3154 raise util.Abort(_("repository '%s' is not local") % path)
3154 raise util.Abort(_("repository '%s' is not local") % path)
3155
3155
3156 if options['help']:
3156 if options['help']:
3157 return help_(u, cmd, options['version'])
3157 return help_(u, cmd, options['version'])
3158 elif options['version']:
3158 elif options['version']:
3159 return version_(u)
3159 return version_(u)
3160 elif not cmd:
3160 elif not cmd:
3161 return help_(u, 'shortlist')
3161 return help_(u, 'shortlist')
3162
3162
3163 if cmd not in norepo.split():
3163 if cmd not in norepo.split():
3164 try:
3164 try:
3165 if not repo:
3165 if not repo:
3166 repo = hg.repository(u, path=path)
3166 repo = hg.repository(u, path=path)
3167 u = repo.ui
3167 u = repo.ui
3168 for name in external.itervalues():
3168 for name in external.itervalues():
3169 mod = sys.modules[name]
3169 mod = sys.modules[name]
3170 if hasattr(mod, 'reposetup'):
3170 if hasattr(mod, 'reposetup'):
3171 mod.reposetup(u, repo)
3171 mod.reposetup(u, repo)
3172 hg.repo_setup_hooks.append(mod.reposetup)
3172 hg.repo_setup_hooks.append(mod.reposetup)
3173 except hg.RepoError:
3173 except hg.RepoError:
3174 if cmd not in optionalrepo.split():
3174 if cmd not in optionalrepo.split():
3175 raise
3175 raise
3176 d = lambda: func(u, repo, *args, **cmdoptions)
3176 d = lambda: func(u, repo, *args, **cmdoptions)
3177 else:
3177 else:
3178 d = lambda: func(u, *args, **cmdoptions)
3178 d = lambda: func(u, *args, **cmdoptions)
3179
3179
3180 try:
3180 try:
3181 if options['profile']:
3181 if options['profile']:
3182 import hotshot, hotshot.stats
3182 import hotshot, hotshot.stats
3183 prof = hotshot.Profile("hg.prof")
3183 prof = hotshot.Profile("hg.prof")
3184 try:
3184 try:
3185 try:
3185 try:
3186 return prof.runcall(d)
3186 return prof.runcall(d)
3187 except:
3187 except:
3188 try:
3188 try:
3189 u.warn(_('exception raised - generating '
3189 u.warn(_('exception raised - generating '
3190 'profile anyway\n'))
3190 'profile anyway\n'))
3191 except:
3191 except:
3192 pass
3192 pass
3193 raise
3193 raise
3194 finally:
3194 finally:
3195 prof.close()
3195 prof.close()
3196 stats = hotshot.stats.load("hg.prof")
3196 stats = hotshot.stats.load("hg.prof")
3197 stats.strip_dirs()
3197 stats.strip_dirs()
3198 stats.sort_stats('time', 'calls')
3198 stats.sort_stats('time', 'calls')
3199 stats.print_stats(40)
3199 stats.print_stats(40)
3200 elif options['lsprof']:
3200 elif options['lsprof']:
3201 try:
3201 try:
3202 from mercurial import lsprof
3202 from mercurial import lsprof
3203 except ImportError:
3203 except ImportError:
3204 raise util.Abort(_(
3204 raise util.Abort(_(
3205 'lsprof not available - install from '
3205 'lsprof not available - install from '
3206 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3206 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3207 p = lsprof.Profiler()
3207 p = lsprof.Profiler()
3208 p.enable(subcalls=True)
3208 p.enable(subcalls=True)
3209 try:
3209 try:
3210 return d()
3210 return d()
3211 finally:
3211 finally:
3212 p.disable()
3212 p.disable()
3213 stats = lsprof.Stats(p.getstats())
3213 stats = lsprof.Stats(p.getstats())
3214 stats.sort()
3214 stats.sort()
3215 stats.pprint(top=10, file=sys.stderr, climit=5)
3215 stats.pprint(top=10, file=sys.stderr, climit=5)
3216 else:
3216 else:
3217 return d()
3217 return d()
3218 finally:
3218 finally:
3219 u.flush()
3219 u.flush()
3220 except:
3220 except:
3221 # enter the debugger when we hit an exception
3221 # enter the debugger when we hit an exception
3222 if options['debugger']:
3222 if options['debugger']:
3223 pdb.post_mortem(sys.exc_info()[2])
3223 pdb.post_mortem(sys.exc_info()[2])
3224 u.print_exc()
3224 u.print_exc()
3225 raise
3225 raise
3226 except ParseError, inst:
3226 except ParseError, inst:
3227 if inst.args[0]:
3227 if inst.args[0]:
3228 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3228 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3229 help_(u, inst.args[0])
3229 help_(u, inst.args[0])
3230 else:
3230 else:
3231 u.warn(_("hg: %s\n") % inst.args[1])
3231 u.warn(_("hg: %s\n") % inst.args[1])
3232 help_(u, 'shortlist')
3232 help_(u, 'shortlist')
3233 except AmbiguousCommand, inst:
3233 except AmbiguousCommand, inst:
3234 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3234 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3235 (inst.args[0], " ".join(inst.args[1])))
3235 (inst.args[0], " ".join(inst.args[1])))
3236 except UnknownCommand, inst:
3236 except UnknownCommand, inst:
3237 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3237 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3238 help_(u, 'shortlist')
3238 help_(u, 'shortlist')
3239 except hg.RepoError, inst:
3239 except hg.RepoError, inst:
3240 u.warn(_("abort: %s!\n") % inst)
3240 u.warn(_("abort: %s!\n") % inst)
3241 except lock.LockHeld, inst:
3241 except lock.LockHeld, inst:
3242 if inst.errno == errno.ETIMEDOUT:
3242 if inst.errno == errno.ETIMEDOUT:
3243 reason = _('timed out waiting for lock held by %s') % inst.locker
3243 reason = _('timed out waiting for lock held by %s') % inst.locker
3244 else:
3244 else:
3245 reason = _('lock held by %s') % inst.locker
3245 reason = _('lock held by %s') % inst.locker
3246 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3246 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3247 except lock.LockUnavailable, inst:
3247 except lock.LockUnavailable, inst:
3248 u.warn(_("abort: could not lock %s: %s\n") %
3248 u.warn(_("abort: could not lock %s: %s\n") %
3249 (inst.desc or inst.filename, inst.strerror))
3249 (inst.desc or inst.filename, inst.strerror))
3250 except revlog.RevlogError, inst:
3250 except revlog.RevlogError, inst:
3251 u.warn(_("abort: %s!\n") % inst)
3251 u.warn(_("abort: %s!\n") % inst)
3252 except util.SignalInterrupt:
3252 except util.SignalInterrupt:
3253 u.warn(_("killed!\n"))
3253 u.warn(_("killed!\n"))
3254 except KeyboardInterrupt:
3254 except KeyboardInterrupt:
3255 try:
3255 try:
3256 u.warn(_("interrupted!\n"))
3256 u.warn(_("interrupted!\n"))
3257 except IOError, inst:
3257 except IOError, inst:
3258 if inst.errno == errno.EPIPE:
3258 if inst.errno == errno.EPIPE:
3259 if u.debugflag:
3259 if u.debugflag:
3260 u.warn(_("\nbroken pipe\n"))
3260 u.warn(_("\nbroken pipe\n"))
3261 else:
3261 else:
3262 raise
3262 raise
3263 except IOError, inst:
3263 except IOError, inst:
3264 if hasattr(inst, "code"):
3264 if hasattr(inst, "code"):
3265 u.warn(_("abort: %s\n") % inst)
3265 u.warn(_("abort: %s\n") % inst)
3266 elif hasattr(inst, "reason"):
3266 elif hasattr(inst, "reason"):
3267 u.warn(_("abort: error: %s\n") % inst.reason[1])
3267 u.warn(_("abort: error: %s\n") % inst.reason[1])
3268 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3268 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3269 if u.debugflag:
3269 if u.debugflag:
3270 u.warn(_("broken pipe\n"))
3270 u.warn(_("broken pipe\n"))
3271 elif getattr(inst, "strerror", None):
3271 elif getattr(inst, "strerror", None):
3272 if getattr(inst, "filename", None):
3272 if getattr(inst, "filename", None):
3273 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3273 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3274 else:
3274 else:
3275 u.warn(_("abort: %s\n") % inst.strerror)
3275 u.warn(_("abort: %s\n") % inst.strerror)
3276 else:
3276 else:
3277 raise
3277 raise
3278 except OSError, inst:
3278 except OSError, inst:
3279 if getattr(inst, "filename", None):
3279 if getattr(inst, "filename", None):
3280 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3280 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3281 else:
3281 else:
3282 u.warn(_("abort: %s\n") % inst.strerror)
3282 u.warn(_("abort: %s\n") % inst.strerror)
3283 except util.UnexpectedOutput, inst:
3283 except util.UnexpectedOutput, inst:
3284 u.warn(_("abort: %s") % inst[0])
3284 u.warn(_("abort: %s") % inst[0])
3285 if not isinstance(inst[1], basestring):
3285 if not isinstance(inst[1], basestring):
3286 u.warn(" %r\n" % (inst[1],))
3286 u.warn(" %r\n" % (inst[1],))
3287 elif not inst[1]:
3287 elif not inst[1]:
3288 u.warn(_(" empty string\n"))
3288 u.warn(_(" empty string\n"))
3289 else:
3289 else:
3290 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3290 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3291 except util.Abort, inst:
3291 except util.Abort, inst:
3292 u.warn(_("abort: %s\n") % inst)
3292 u.warn(_("abort: %s\n") % inst)
3293 except TypeError, inst:
3293 except TypeError, inst:
3294 # was this an argument error?
3294 # was this an argument error?
3295 tb = traceback.extract_tb(sys.exc_info()[2])
3295 tb = traceback.extract_tb(sys.exc_info()[2])
3296 if len(tb) > 2: # no
3296 if len(tb) > 2: # no
3297 raise
3297 raise
3298 u.debug(inst, "\n")
3298 u.debug(inst, "\n")
3299 u.warn(_("%s: invalid arguments\n") % cmd)
3299 u.warn(_("%s: invalid arguments\n") % cmd)
3300 help_(u, cmd)
3300 help_(u, cmd)
3301 except SystemExit, inst:
3301 except SystemExit, inst:
3302 # Commands shouldn't sys.exit directly, but give a return code.
3302 # Commands shouldn't sys.exit directly, but give a return code.
3303 # Just in case catch this and and pass exit code to caller.
3303 # Just in case catch this and and pass exit code to caller.
3304 return inst.code
3304 return inst.code
3305 except:
3305 except:
3306 u.warn(_("** unknown exception encountered, details follow\n"))
3306 u.warn(_("** unknown exception encountered, details follow\n"))
3307 u.warn(_("** report bug details to "
3307 u.warn(_("** report bug details to "
3308 "http://www.selenic.com/mercurial/bts\n"))
3308 "http://www.selenic.com/mercurial/bts\n"))
3309 u.warn(_("** or mercurial@selenic.com\n"))
3309 u.warn(_("** or mercurial@selenic.com\n"))
3310 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3310 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3311 % version.get_version())
3311 % version.get_version())
3312 raise
3312 raise
3313
3313
3314 return -1
3314 return -1
@@ -1,86 +1,86 b''
1 # statichttprepo.py - simple http repository class for mercurial
1 # statichttprepo.py - simple http repository class for mercurial
2 #
2 #
3 # This provides read-only repo access to repositories exported via static http
3 # This provides read-only repo access to repositories exported via static http
4 #
4 #
5 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
9
9
10 from demandload import *
10 from demandload import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 demandload(globals(), "changelog filelog httprangereader")
12 demandload(globals(), "changelog filelog httprangereader")
13 demandload(globals(), "localrepo manifest os urllib urllib2 util")
13 demandload(globals(), "repo localrepo manifest os urllib urllib2 util")
14
14
15 class rangereader(httprangereader.httprangereader):
15 class rangereader(httprangereader.httprangereader):
16 def read(self, size=None):
16 def read(self, size=None):
17 try:
17 try:
18 return httprangereader.httprangereader.read(self, size)
18 return httprangereader.httprangereader.read(self, size)
19 except urllib2.HTTPError, inst:
19 except urllib2.HTTPError, inst:
20 raise IOError(None, inst)
20 raise IOError(None, inst)
21 except urllib2.URLError, inst:
21 except urllib2.URLError, inst:
22 raise IOError(None, inst.reason[1])
22 raise IOError(None, inst.reason[1])
23
23
24 def opener(base):
24 def opener(base):
25 """return a function that opens files over http"""
25 """return a function that opens files over http"""
26 p = base
26 p = base
27 def o(path, mode="r"):
27 def o(path, mode="r"):
28 f = "/".join((p, urllib.quote(path)))
28 f = "/".join((p, urllib.quote(path)))
29 return rangereader(f)
29 return rangereader(f)
30 return o
30 return o
31
31
32 class statichttprepository(localrepo.localrepository):
32 class statichttprepository(localrepo.localrepository):
33 def __init__(self, ui, path):
33 def __init__(self, ui, path):
34 self._url = path
34 self._url = path
35 self.ui = ui
35 self.ui = ui
36 self.revlogversion = 0
36 self.revlogversion = 0
37
37
38 self.path = (path + "/.hg")
38 self.path = (path + "/.hg")
39 self.opener = opener(self.path)
39 self.opener = opener(self.path)
40 # find requirements
40 # find requirements
41 try:
41 try:
42 requirements = self.opener("requires").read().splitlines()
42 requirements = self.opener("requires").read().splitlines()
43 except IOError:
43 except IOError:
44 requirements = []
44 requirements = []
45 # check them
45 # check them
46 for r in requirements:
46 for r in requirements:
47 if r not in self.supported:
47 if r not in self.supported:
48 raise repo.RepoError(_("requirement '%s' not supported") % r)
48 raise repo.RepoError(_("requirement '%s' not supported") % r)
49
49
50 # setup store
50 # setup store
51 if "store" in requirements:
51 if "store" in requirements:
52 self.encodefn = util.encodefilename
52 self.encodefn = util.encodefilename
53 self.decodefn = util.decodefilename
53 self.decodefn = util.decodefilename
54 self.spath = self.path + "/store"
54 self.spath = self.path + "/store"
55 else:
55 else:
56 self.encodefn = lambda x: x
56 self.encodefn = lambda x: x
57 self.decodefn = lambda x: x
57 self.decodefn = lambda x: x
58 self.spath = self.path
58 self.spath = self.path
59 self.sopener = util.encodedopener(opener(self.spath), self.encodefn)
59 self.sopener = util.encodedopener(opener(self.spath), self.encodefn)
60
60
61 self.manifest = manifest.manifest(self.sopener)
61 self.manifest = manifest.manifest(self.sopener)
62 self.changelog = changelog.changelog(self.sopener)
62 self.changelog = changelog.changelog(self.sopener)
63 self.tagscache = None
63 self.tagscache = None
64 self.nodetagscache = None
64 self.nodetagscache = None
65 self.encodepats = None
65 self.encodepats = None
66 self.decodepats = None
66 self.decodepats = None
67
67
68 def url(self):
68 def url(self):
69 return 'static-' + self._url
69 return 'static-' + self._url
70
70
71 def dev(self):
71 def dev(self):
72 return -1
72 return -1
73
73
74 def local(self):
74 def local(self):
75 return False
75 return False
76
76
77 def instance(ui, path, create):
77 def instance(ui, path, create):
78 if create:
78 if create:
79 raise util.Abort(_('cannot create new static-http repository'))
79 raise util.Abort(_('cannot create new static-http repository'))
80 if path.startswith('old-http:'):
80 if path.startswith('old-http:'):
81 ui.warn(_("old-http:// syntax is deprecated, "
81 ui.warn(_("old-http:// syntax is deprecated, "
82 "please use static-http:// instead\n"))
82 "please use static-http:// instead\n"))
83 path = path[4:]
83 path = path[4:]
84 else:
84 else:
85 path = path[7:]
85 path = path[7:]
86 return statichttprepository(ui, path)
86 return statichttprepository(ui, path)
@@ -1,1322 +1,1322 b''
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, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 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 gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser locale")
18 demandload(globals(), "os threading time calendar ConfigParser locale")
19
19
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
21 or "ascii"
21 or "ascii"
22 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
22 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
23 _fallbackencoding = 'ISO-8859-1'
23 _fallbackencoding = 'ISO-8859-1'
24
24
25 def tolocal(s):
25 def tolocal(s):
26 """
26 """
27 Convert a string from internal UTF-8 to local encoding
27 Convert a string from internal UTF-8 to local encoding
28
28
29 All internal strings should be UTF-8 but some repos before the
29 All internal strings should be UTF-8 but some repos before the
30 implementation of locale support may contain latin1 or possibly
30 implementation of locale support may contain latin1 or possibly
31 other character sets. We attempt to decode everything strictly
31 other character sets. We attempt to decode everything strictly
32 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
32 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
33 replace unknown characters.
33 replace unknown characters.
34 """
34 """
35 for e in ('UTF-8', _fallbackencoding):
35 for e in ('UTF-8', _fallbackencoding):
36 try:
36 try:
37 u = s.decode(e) # attempt strict decoding
37 u = s.decode(e) # attempt strict decoding
38 return u.encode(_encoding, "replace")
38 return u.encode(_encoding, "replace")
39 except LookupError, k:
39 except LookupError, k:
40 raise Abort(_("%s, please check your locale settings") % k)
40 raise Abort(_("%s, please check your locale settings") % k)
41 except UnicodeDecodeError:
41 except UnicodeDecodeError:
42 pass
42 pass
43 u = s.decode("utf-8", "replace") # last ditch
43 u = s.decode("utf-8", "replace") # last ditch
44 return u.encode(_encoding, "replace")
44 return u.encode(_encoding, "replace")
45
45
46 def fromlocal(s):
46 def fromlocal(s):
47 """
47 """
48 Convert a string from the local character encoding to UTF-8
48 Convert a string from the local character encoding to UTF-8
49
49
50 We attempt to decode strings using the encoding mode set by
50 We attempt to decode strings using the encoding mode set by
51 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
51 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
52 characters will cause an error message. Other modes include
52 characters will cause an error message. Other modes include
53 'replace', which replaces unknown characters with a special
53 'replace', which replaces unknown characters with a special
54 Unicode character, and 'ignore', which drops the character.
54 Unicode character, and 'ignore', which drops the character.
55 """
55 """
56 try:
56 try:
57 return s.decode(_encoding, _encodingmode).encode("utf-8")
57 return s.decode(_encoding, _encodingmode).encode("utf-8")
58 except UnicodeDecodeError, inst:
58 except UnicodeDecodeError, inst:
59 sub = s[max(0, inst.start-10):inst.start+10]
59 sub = s[max(0, inst.start-10):inst.start+10]
60 raise Abort("decoding near '%s': %s!" % (sub, inst))
60 raise Abort("decoding near '%s': %s!" % (sub, inst))
61 except LookupError, k:
61 except LookupError, k:
62 raise Abort(_("%s, please check your locale settings") % k)
62 raise Abort(_("%s, please check your locale settings") % k)
63
63
64 def locallen(s):
64 def locallen(s):
65 """Find the length in characters of a local string"""
65 """Find the length in characters of a local string"""
66 return len(s.decode(_encoding, "replace"))
66 return len(s.decode(_encoding, "replace"))
67
67
68 def localsub(s, a, b=None):
68 def localsub(s, a, b=None):
69 try:
69 try:
70 u = s.decode(_encoding, _encodingmode)
70 u = s.decode(_encoding, _encodingmode)
71 if b is not None:
71 if b is not None:
72 u = u[a:b]
72 u = u[a:b]
73 else:
73 else:
74 u = u[:a]
74 u = u[:a]
75 return u.encode(_encoding, _encodingmode)
75 return u.encode(_encoding, _encodingmode)
76 except UnicodeDecodeError, inst:
76 except UnicodeDecodeError, inst:
77 sub = s[max(0, inst.start-10), inst.start+10]
77 sub = s[max(0, inst.start-10), inst.start+10]
78 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
78 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
79
79
80 # used by parsedate
80 # used by parsedate
81 defaultdateformats = (
81 defaultdateformats = (
82 '%Y-%m-%d %H:%M:%S',
82 '%Y-%m-%d %H:%M:%S',
83 '%Y-%m-%d %I:%M:%S%p',
83 '%Y-%m-%d %I:%M:%S%p',
84 '%Y-%m-%d %H:%M',
84 '%Y-%m-%d %H:%M',
85 '%Y-%m-%d %I:%M%p',
85 '%Y-%m-%d %I:%M%p',
86 '%Y-%m-%d',
86 '%Y-%m-%d',
87 '%m-%d',
87 '%m-%d',
88 '%m/%d',
88 '%m/%d',
89 '%m/%d/%y',
89 '%m/%d/%y',
90 '%m/%d/%Y',
90 '%m/%d/%Y',
91 '%a %b %d %H:%M:%S %Y',
91 '%a %b %d %H:%M:%S %Y',
92 '%a %b %d %I:%M:%S%p %Y',
92 '%a %b %d %I:%M:%S%p %Y',
93 '%b %d %H:%M:%S %Y',
93 '%b %d %H:%M:%S %Y',
94 '%b %d %I:%M:%S%p %Y',
94 '%b %d %I:%M:%S%p %Y',
95 '%b %d %H:%M:%S',
95 '%b %d %H:%M:%S',
96 '%b %d %I:%M:%S%p',
96 '%b %d %I:%M:%S%p',
97 '%b %d %H:%M',
97 '%b %d %H:%M',
98 '%b %d %I:%M%p',
98 '%b %d %I:%M%p',
99 '%b %d %Y',
99 '%b %d %Y',
100 '%b %d',
100 '%b %d',
101 '%H:%M:%S',
101 '%H:%M:%S',
102 '%I:%M:%SP',
102 '%I:%M:%SP',
103 '%H:%M',
103 '%H:%M',
104 '%I:%M%p',
104 '%I:%M%p',
105 )
105 )
106
106
107 extendeddateformats = defaultdateformats + (
107 extendeddateformats = defaultdateformats + (
108 "%Y",
108 "%Y",
109 "%Y-%m",
109 "%Y-%m",
110 "%b",
110 "%b",
111 "%b %Y",
111 "%b %Y",
112 )
112 )
113
113
114 class SignalInterrupt(Exception):
114 class SignalInterrupt(Exception):
115 """Exception raised on SIGTERM and SIGHUP."""
115 """Exception raised on SIGTERM and SIGHUP."""
116
116
117 # like SafeConfigParser but with case-sensitive keys
117 # like SafeConfigParser but with case-sensitive keys
118 class configparser(ConfigParser.SafeConfigParser):
118 class configparser(ConfigParser.SafeConfigParser):
119 def optionxform(self, optionstr):
119 def optionxform(self, optionstr):
120 return optionstr
120 return optionstr
121
121
122 def cachefunc(func):
122 def cachefunc(func):
123 '''cache the result of function calls'''
123 '''cache the result of function calls'''
124 # XXX doesn't handle keywords args
124 # XXX doesn't handle keywords args
125 cache = {}
125 cache = {}
126 if func.func_code.co_argcount == 1:
126 if func.func_code.co_argcount == 1:
127 # we gain a small amount of time because
127 # we gain a small amount of time because
128 # we don't need to pack/unpack the list
128 # we don't need to pack/unpack the list
129 def f(arg):
129 def f(arg):
130 if arg not in cache:
130 if arg not in cache:
131 cache[arg] = func(arg)
131 cache[arg] = func(arg)
132 return cache[arg]
132 return cache[arg]
133 else:
133 else:
134 def f(*args):
134 def f(*args):
135 if args not in cache:
135 if args not in cache:
136 cache[args] = func(*args)
136 cache[args] = func(*args)
137 return cache[args]
137 return cache[args]
138
138
139 return f
139 return f
140
140
141 def pipefilter(s, cmd):
141 def pipefilter(s, cmd):
142 '''filter string S through command CMD, returning its output'''
142 '''filter string S through command CMD, returning its output'''
143 (pout, pin) = popen2.popen2(cmd, -1, 'b')
143 (pout, pin) = popen2.popen2(cmd, -1, 'b')
144 def writer():
144 def writer():
145 try:
145 try:
146 pin.write(s)
146 pin.write(s)
147 pin.close()
147 pin.close()
148 except IOError, inst:
148 except IOError, inst:
149 if inst.errno != errno.EPIPE:
149 if inst.errno != errno.EPIPE:
150 raise
150 raise
151
151
152 # we should use select instead on UNIX, but this will work on most
152 # we should use select instead on UNIX, but this will work on most
153 # systems, including Windows
153 # systems, including Windows
154 w = threading.Thread(target=writer)
154 w = threading.Thread(target=writer)
155 w.start()
155 w.start()
156 f = pout.read()
156 f = pout.read()
157 pout.close()
157 pout.close()
158 w.join()
158 w.join()
159 return f
159 return f
160
160
161 def tempfilter(s, cmd):
161 def tempfilter(s, cmd):
162 '''filter string S through a pair of temporary files with CMD.
162 '''filter string S through a pair of temporary files with CMD.
163 CMD is used as a template to create the real command to be run,
163 CMD is used as a template to create the real command to be run,
164 with the strings INFILE and OUTFILE replaced by the real names of
164 with the strings INFILE and OUTFILE replaced by the real names of
165 the temporary files generated.'''
165 the temporary files generated.'''
166 inname, outname = None, None
166 inname, outname = None, None
167 try:
167 try:
168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
169 fp = os.fdopen(infd, 'wb')
169 fp = os.fdopen(infd, 'wb')
170 fp.write(s)
170 fp.write(s)
171 fp.close()
171 fp.close()
172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
173 os.close(outfd)
173 os.close(outfd)
174 cmd = cmd.replace('INFILE', inname)
174 cmd = cmd.replace('INFILE', inname)
175 cmd = cmd.replace('OUTFILE', outname)
175 cmd = cmd.replace('OUTFILE', outname)
176 code = os.system(cmd)
176 code = os.system(cmd)
177 if code: raise Abort(_("command '%s' failed: %s") %
177 if code: raise Abort(_("command '%s' failed: %s") %
178 (cmd, explain_exit(code)))
178 (cmd, explain_exit(code)))
179 return open(outname, 'rb').read()
179 return open(outname, 'rb').read()
180 finally:
180 finally:
181 try:
181 try:
182 if inname: os.unlink(inname)
182 if inname: os.unlink(inname)
183 except: pass
183 except: pass
184 try:
184 try:
185 if outname: os.unlink(outname)
185 if outname: os.unlink(outname)
186 except: pass
186 except: pass
187
187
188 filtertable = {
188 filtertable = {
189 'tempfile:': tempfilter,
189 'tempfile:': tempfilter,
190 'pipe:': pipefilter,
190 'pipe:': pipefilter,
191 }
191 }
192
192
193 def filter(s, cmd):
193 def filter(s, cmd):
194 "filter a string through a command that transforms its input to its output"
194 "filter a string through a command that transforms its input to its output"
195 for name, fn in filtertable.iteritems():
195 for name, fn in filtertable.iteritems():
196 if cmd.startswith(name):
196 if cmd.startswith(name):
197 return fn(s, cmd[len(name):].lstrip())
197 return fn(s, cmd[len(name):].lstrip())
198 return pipefilter(s, cmd)
198 return pipefilter(s, cmd)
199
199
200 def find_in_path(name, path, default=None):
200 def find_in_path(name, path, default=None):
201 '''find name in search path. path can be string (will be split
201 '''find name in search path. path can be string (will be split
202 with os.pathsep), or iterable thing that returns strings. if name
202 with os.pathsep), or iterable thing that returns strings. if name
203 found, return path to name. else return default.'''
203 found, return path to name. else return default.'''
204 if isinstance(path, str):
204 if isinstance(path, str):
205 path = path.split(os.pathsep)
205 path = path.split(os.pathsep)
206 for p in path:
206 for p in path:
207 p_name = os.path.join(p, name)
207 p_name = os.path.join(p, name)
208 if os.path.exists(p_name):
208 if os.path.exists(p_name):
209 return p_name
209 return p_name
210 return default
210 return default
211
211
212 def binary(s):
212 def binary(s):
213 """return true if a string is binary data using diff's heuristic"""
213 """return true if a string is binary data using diff's heuristic"""
214 if s and '\0' in s[:4096]:
214 if s and '\0' in s[:4096]:
215 return True
215 return True
216 return False
216 return False
217
217
218 def unique(g):
218 def unique(g):
219 """return the uniq elements of iterable g"""
219 """return the uniq elements of iterable g"""
220 seen = {}
220 seen = {}
221 l = []
221 l = []
222 for f in g:
222 for f in g:
223 if f not in seen:
223 if f not in seen:
224 seen[f] = 1
224 seen[f] = 1
225 l.append(f)
225 l.append(f)
226 return l
226 return l
227
227
228 class Abort(Exception):
228 class Abort(Exception):
229 """Raised if a command needs to print an error and exit."""
229 """Raised if a command needs to print an error and exit."""
230
230
231 class UnexpectedOutput(Abort):
231 class UnexpectedOutput(Abort):
232 """Raised to print an error with part of output and exit."""
232 """Raised to print an error with part of output and exit."""
233
233
234 def always(fn): return True
234 def always(fn): return True
235 def never(fn): return False
235 def never(fn): return False
236
236
237 def patkind(name, dflt_pat='glob'):
237 def patkind(name, dflt_pat='glob'):
238 """Split a string into an optional pattern kind prefix and the
238 """Split a string into an optional pattern kind prefix and the
239 actual pattern."""
239 actual pattern."""
240 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
240 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
241 if name.startswith(prefix + ':'): return name.split(':', 1)
241 if name.startswith(prefix + ':'): return name.split(':', 1)
242 return dflt_pat, name
242 return dflt_pat, name
243
243
244 def globre(pat, head='^', tail='$'):
244 def globre(pat, head='^', tail='$'):
245 "convert a glob pattern into a regexp"
245 "convert a glob pattern into a regexp"
246 i, n = 0, len(pat)
246 i, n = 0, len(pat)
247 res = ''
247 res = ''
248 group = False
248 group = False
249 def peek(): return i < n and pat[i]
249 def peek(): return i < n and pat[i]
250 while i < n:
250 while i < n:
251 c = pat[i]
251 c = pat[i]
252 i = i+1
252 i = i+1
253 if c == '*':
253 if c == '*':
254 if peek() == '*':
254 if peek() == '*':
255 i += 1
255 i += 1
256 res += '.*'
256 res += '.*'
257 else:
257 else:
258 res += '[^/]*'
258 res += '[^/]*'
259 elif c == '?':
259 elif c == '?':
260 res += '.'
260 res += '.'
261 elif c == '[':
261 elif c == '[':
262 j = i
262 j = i
263 if j < n and pat[j] in '!]':
263 if j < n and pat[j] in '!]':
264 j += 1
264 j += 1
265 while j < n and pat[j] != ']':
265 while j < n and pat[j] != ']':
266 j += 1
266 j += 1
267 if j >= n:
267 if j >= n:
268 res += '\\['
268 res += '\\['
269 else:
269 else:
270 stuff = pat[i:j].replace('\\','\\\\')
270 stuff = pat[i:j].replace('\\','\\\\')
271 i = j + 1
271 i = j + 1
272 if stuff[0] == '!':
272 if stuff[0] == '!':
273 stuff = '^' + stuff[1:]
273 stuff = '^' + stuff[1:]
274 elif stuff[0] == '^':
274 elif stuff[0] == '^':
275 stuff = '\\' + stuff
275 stuff = '\\' + stuff
276 res = '%s[%s]' % (res, stuff)
276 res = '%s[%s]' % (res, stuff)
277 elif c == '{':
277 elif c == '{':
278 group = True
278 group = True
279 res += '(?:'
279 res += '(?:'
280 elif c == '}' and group:
280 elif c == '}' and group:
281 res += ')'
281 res += ')'
282 group = False
282 group = False
283 elif c == ',' and group:
283 elif c == ',' and group:
284 res += '|'
284 res += '|'
285 elif c == '\\':
285 elif c == '\\':
286 p = peek()
286 p = peek()
287 if p:
287 if p:
288 i += 1
288 i += 1
289 res += re.escape(p)
289 res += re.escape(p)
290 else:
290 else:
291 res += re.escape(c)
291 res += re.escape(c)
292 else:
292 else:
293 res += re.escape(c)
293 res += re.escape(c)
294 return head + res + tail
294 return head + res + tail
295
295
296 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
296 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
297
297
298 def pathto(n1, n2):
298 def pathto(n1, n2):
299 '''return the relative path from one place to another.
299 '''return the relative path from one place to another.
300 n1 should use os.sep to separate directories
300 n1 should use os.sep to separate directories
301 n2 should use "/" to separate directories
301 n2 should use "/" to separate directories
302 returns an os.sep-separated path.
302 returns an os.sep-separated path.
303 '''
303 '''
304 if not n1: return localpath(n2)
304 if not n1: return localpath(n2)
305 a, b = n1.split(os.sep), n2.split('/')
305 a, b = n1.split(os.sep), n2.split('/')
306 a.reverse()
306 a.reverse()
307 b.reverse()
307 b.reverse()
308 while a and b and a[-1] == b[-1]:
308 while a and b and a[-1] == b[-1]:
309 a.pop()
309 a.pop()
310 b.pop()
310 b.pop()
311 b.reverse()
311 b.reverse()
312 return os.sep.join((['..'] * len(a)) + b)
312 return os.sep.join((['..'] * len(a)) + b)
313
313
314 def canonpath(root, cwd, myname):
314 def canonpath(root, cwd, myname):
315 """return the canonical path of myname, given cwd and root"""
315 """return the canonical path of myname, given cwd and root"""
316 if root == os.sep:
316 if root == os.sep:
317 rootsep = os.sep
317 rootsep = os.sep
318 elif root.endswith(os.sep):
318 elif root.endswith(os.sep):
319 rootsep = root
319 rootsep = root
320 else:
320 else:
321 rootsep = root + os.sep
321 rootsep = root + os.sep
322 name = myname
322 name = myname
323 if not os.path.isabs(name):
323 if not os.path.isabs(name):
324 name = os.path.join(root, cwd, name)
324 name = os.path.join(root, cwd, name)
325 name = os.path.normpath(name)
325 name = os.path.normpath(name)
326 if name != rootsep and name.startswith(rootsep):
326 if name != rootsep and name.startswith(rootsep):
327 name = name[len(rootsep):]
327 name = name[len(rootsep):]
328 audit_path(name)
328 audit_path(name)
329 return pconvert(name)
329 return pconvert(name)
330 elif name == root:
330 elif name == root:
331 return ''
331 return ''
332 else:
332 else:
333 # Determine whether `name' is in the hierarchy at or beneath `root',
333 # Determine whether `name' is in the hierarchy at or beneath `root',
334 # by iterating name=dirname(name) until that causes no change (can't
334 # by iterating name=dirname(name) until that causes no change (can't
335 # check name == '/', because that doesn't work on windows). For each
335 # check name == '/', because that doesn't work on windows). For each
336 # `name', compare dev/inode numbers. If they match, the list `rel'
336 # `name', compare dev/inode numbers. If they match, the list `rel'
337 # holds the reversed list of components making up the relative file
337 # holds the reversed list of components making up the relative file
338 # name we want.
338 # name we want.
339 root_st = os.stat(root)
339 root_st = os.stat(root)
340 rel = []
340 rel = []
341 while True:
341 while True:
342 try:
342 try:
343 name_st = os.stat(name)
343 name_st = os.stat(name)
344 except OSError:
344 except OSError:
345 break
345 break
346 if samestat(name_st, root_st):
346 if samestat(name_st, root_st):
347 rel.reverse()
347 rel.reverse()
348 name = os.path.join(*rel)
348 name = os.path.join(*rel)
349 audit_path(name)
349 audit_path(name)
350 return pconvert(name)
350 return pconvert(name)
351 dirname, basename = os.path.split(name)
351 dirname, basename = os.path.split(name)
352 rel.append(basename)
352 rel.append(basename)
353 if dirname == name:
353 if dirname == name:
354 break
354 break
355 name = dirname
355 name = dirname
356
356
357 raise Abort('%s not under root' % myname)
357 raise Abort('%s not under root' % myname)
358
358
359 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
359 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
360 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
360 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
361
361
362 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
362 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
363 if os.name == 'nt':
363 if os.name == 'nt':
364 dflt_pat = 'glob'
364 dflt_pat = 'glob'
365 else:
365 else:
366 dflt_pat = 'relpath'
366 dflt_pat = 'relpath'
367 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
367 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
368
368
369 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
369 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
370 """build a function to match a set of file patterns
370 """build a function to match a set of file patterns
371
371
372 arguments:
372 arguments:
373 canonroot - the canonical root of the tree you're matching against
373 canonroot - the canonical root of the tree you're matching against
374 cwd - the current working directory, if relevant
374 cwd - the current working directory, if relevant
375 names - patterns to find
375 names - patterns to find
376 inc - patterns to include
376 inc - patterns to include
377 exc - patterns to exclude
377 exc - patterns to exclude
378 head - a regex to prepend to patterns to control whether a match is rooted
378 head - a regex to prepend to patterns to control whether a match is rooted
379
379
380 a pattern is one of:
380 a pattern is one of:
381 'glob:<rooted glob>'
381 'glob:<rooted glob>'
382 're:<rooted regexp>'
382 're:<rooted regexp>'
383 'path:<rooted path>'
383 'path:<rooted path>'
384 'relglob:<relative glob>'
384 'relglob:<relative glob>'
385 'relpath:<relative path>'
385 'relpath:<relative path>'
386 'relre:<relative regexp>'
386 'relre:<relative regexp>'
387 '<rooted path or regexp>'
387 '<rooted path or regexp>'
388
388
389 returns:
389 returns:
390 a 3-tuple containing
390 a 3-tuple containing
391 - list of explicit non-pattern names passed in
391 - list of explicit non-pattern names passed in
392 - a bool match(filename) function
392 - a bool match(filename) function
393 - a bool indicating if any patterns were passed in
393 - a bool indicating if any patterns were passed in
394
394
395 todo:
395 todo:
396 make head regex a rooted bool
396 make head regex a rooted bool
397 """
397 """
398
398
399 def contains_glob(name):
399 def contains_glob(name):
400 for c in name:
400 for c in name:
401 if c in _globchars: return True
401 if c in _globchars: return True
402 return False
402 return False
403
403
404 def regex(kind, name, tail):
404 def regex(kind, name, tail):
405 '''convert a pattern into a regular expression'''
405 '''convert a pattern into a regular expression'''
406 if kind == 're':
406 if kind == 're':
407 return name
407 return name
408 elif kind == 'path':
408 elif kind == 'path':
409 return '^' + re.escape(name) + '(?:/|$)'
409 return '^' + re.escape(name) + '(?:/|$)'
410 elif kind == 'relglob':
410 elif kind == 'relglob':
411 return head + globre(name, '(?:|.*/)', tail)
411 return head + globre(name, '(?:|.*/)', tail)
412 elif kind == 'relpath':
412 elif kind == 'relpath':
413 return head + re.escape(name) + tail
413 return head + re.escape(name) + tail
414 elif kind == 'relre':
414 elif kind == 'relre':
415 if name.startswith('^'):
415 if name.startswith('^'):
416 return name
416 return name
417 return '.*' + name
417 return '.*' + name
418 return head + globre(name, '', tail)
418 return head + globre(name, '', tail)
419
419
420 def matchfn(pats, tail):
420 def matchfn(pats, tail):
421 """build a matching function from a set of patterns"""
421 """build a matching function from a set of patterns"""
422 if not pats:
422 if not pats:
423 return
423 return
424 matches = []
424 matches = []
425 for k, p in pats:
425 for k, p in pats:
426 try:
426 try:
427 pat = '(?:%s)' % regex(k, p, tail)
427 pat = '(?:%s)' % regex(k, p, tail)
428 matches.append(re.compile(pat).match)
428 matches.append(re.compile(pat).match)
429 except re.error:
429 except re.error:
430 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
430 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
431 else: raise Abort("invalid pattern (%s): %s" % (k, p))
431 else: raise Abort("invalid pattern (%s): %s" % (k, p))
432
432
433 def buildfn(text):
433 def buildfn(text):
434 for m in matches:
434 for m in matches:
435 r = m(text)
435 r = m(text)
436 if r:
436 if r:
437 return r
437 return r
438
438
439 return buildfn
439 return buildfn
440
440
441 def globprefix(pat):
441 def globprefix(pat):
442 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
442 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
443 root = []
443 root = []
444 for p in pat.split(os.sep):
444 for p in pat.split(os.sep):
445 if contains_glob(p): break
445 if contains_glob(p): break
446 root.append(p)
446 root.append(p)
447 return '/'.join(root)
447 return '/'.join(root)
448
448
449 pats = []
449 pats = []
450 files = []
450 files = []
451 roots = []
451 roots = []
452 for kind, name in [patkind(p, dflt_pat) for p in names]:
452 for kind, name in [patkind(p, dflt_pat) for p in names]:
453 if kind in ('glob', 'relpath'):
453 if kind in ('glob', 'relpath'):
454 name = canonpath(canonroot, cwd, name)
454 name = canonpath(canonroot, cwd, name)
455 if name == '':
455 if name == '':
456 kind, name = 'glob', '**'
456 kind, name = 'glob', '**'
457 if kind in ('glob', 'path', 're'):
457 if kind in ('glob', 'path', 're'):
458 pats.append((kind, name))
458 pats.append((kind, name))
459 if kind == 'glob':
459 if kind == 'glob':
460 root = globprefix(name)
460 root = globprefix(name)
461 if root: roots.append(root)
461 if root: roots.append(root)
462 elif kind == 'relpath':
462 elif kind == 'relpath':
463 files.append((kind, name))
463 files.append((kind, name))
464 roots.append(name)
464 roots.append(name)
465
465
466 patmatch = matchfn(pats, '$') or always
466 patmatch = matchfn(pats, '$') or always
467 filematch = matchfn(files, '(?:/|$)') or always
467 filematch = matchfn(files, '(?:/|$)') or always
468 incmatch = always
468 incmatch = always
469 if inc:
469 if inc:
470 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
470 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
471 incmatch = matchfn(inckinds, '(?:/|$)')
471 incmatch = matchfn(inckinds, '(?:/|$)')
472 excmatch = lambda fn: False
472 excmatch = lambda fn: False
473 if exc:
473 if exc:
474 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
474 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
475 excmatch = matchfn(exckinds, '(?:/|$)')
475 excmatch = matchfn(exckinds, '(?:/|$)')
476
476
477 return (roots,
477 return (roots,
478 lambda fn: (incmatch(fn) and not excmatch(fn) and
478 lambda fn: (incmatch(fn) and not excmatch(fn) and
479 (fn.endswith('/') or
479 (fn.endswith('/') or
480 (not pats and not files) or
480 (not pats and not files) or
481 (pats and patmatch(fn)) or
481 (pats and patmatch(fn)) or
482 (files and filematch(fn)))),
482 (files and filematch(fn)))),
483 (inc or exc or (pats and pats != [('glob', '**')])) and True)
483 (inc or exc or (pats and pats != [('glob', '**')])) and True)
484
484
485 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
485 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
486 '''enhanced shell command execution.
486 '''enhanced shell command execution.
487 run with environment maybe modified, maybe in different dir.
487 run with environment maybe modified, maybe in different dir.
488
488
489 if command fails and onerr is None, return status. if ui object,
489 if command fails and onerr is None, return status. if ui object,
490 print error message and return status, else raise onerr object as
490 print error message and return status, else raise onerr object as
491 exception.'''
491 exception.'''
492 def py2shell(val):
492 def py2shell(val):
493 'convert python object into string that is useful to shell'
493 'convert python object into string that is useful to shell'
494 if val in (None, False):
494 if val in (None, False):
495 return '0'
495 return '0'
496 if val == True:
496 if val == True:
497 return '1'
497 return '1'
498 return str(val)
498 return str(val)
499 oldenv = {}
499 oldenv = {}
500 for k in environ:
500 for k in environ:
501 oldenv[k] = os.environ.get(k)
501 oldenv[k] = os.environ.get(k)
502 if cwd is not None:
502 if cwd is not None:
503 oldcwd = os.getcwd()
503 oldcwd = os.getcwd()
504 try:
504 try:
505 for k, v in environ.iteritems():
505 for k, v in environ.iteritems():
506 os.environ[k] = py2shell(v)
506 os.environ[k] = py2shell(v)
507 if cwd is not None and oldcwd != cwd:
507 if cwd is not None and oldcwd != cwd:
508 os.chdir(cwd)
508 os.chdir(cwd)
509 rc = os.system(cmd)
509 rc = os.system(cmd)
510 if rc and onerr:
510 if rc and onerr:
511 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
511 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
512 explain_exit(rc)[0])
512 explain_exit(rc)[0])
513 if errprefix:
513 if errprefix:
514 errmsg = '%s: %s' % (errprefix, errmsg)
514 errmsg = '%s: %s' % (errprefix, errmsg)
515 try:
515 try:
516 onerr.warn(errmsg + '\n')
516 onerr.warn(errmsg + '\n')
517 except AttributeError:
517 except AttributeError:
518 raise onerr(errmsg)
518 raise onerr(errmsg)
519 return rc
519 return rc
520 finally:
520 finally:
521 for k, v in oldenv.iteritems():
521 for k, v in oldenv.iteritems():
522 if v is None:
522 if v is None:
523 del os.environ[k]
523 del os.environ[k]
524 else:
524 else:
525 os.environ[k] = v
525 os.environ[k] = v
526 if cwd is not None and oldcwd != cwd:
526 if cwd is not None and oldcwd != cwd:
527 os.chdir(oldcwd)
527 os.chdir(oldcwd)
528
528
529 def rename(src, dst):
529 def rename(src, dst):
530 """forcibly rename a file"""
530 """forcibly rename a file"""
531 try:
531 try:
532 os.rename(src, dst)
532 os.rename(src, dst)
533 except OSError, err:
533 except OSError, err:
534 # on windows, rename to existing file is not allowed, so we
534 # on windows, rename to existing file is not allowed, so we
535 # must delete destination first. but if file is open, unlink
535 # must delete destination first. but if file is open, unlink
536 # schedules it for delete but does not delete it. rename
536 # schedules it for delete but does not delete it. rename
537 # happens immediately even for open files, so we create
537 # happens immediately even for open files, so we create
538 # temporary file, delete it, rename destination to that name,
538 # temporary file, delete it, rename destination to that name,
539 # then delete that. then rename is safe to do.
539 # then delete that. then rename is safe to do.
540 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
540 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
541 os.close(fd)
541 os.close(fd)
542 os.unlink(temp)
542 os.unlink(temp)
543 os.rename(dst, temp)
543 os.rename(dst, temp)
544 os.unlink(temp)
544 os.unlink(temp)
545 os.rename(src, dst)
545 os.rename(src, dst)
546
546
547 def unlink(f):
547 def unlink(f):
548 """unlink and remove the directory if it is empty"""
548 """unlink and remove the directory if it is empty"""
549 os.unlink(f)
549 os.unlink(f)
550 # try removing directories that might now be empty
550 # try removing directories that might now be empty
551 try:
551 try:
552 os.removedirs(os.path.dirname(f))
552 os.removedirs(os.path.dirname(f))
553 except OSError:
553 except OSError:
554 pass
554 pass
555
555
556 def copyfile(src, dest):
556 def copyfile(src, dest):
557 "copy a file, preserving mode"
557 "copy a file, preserving mode"
558 try:
558 try:
559 shutil.copyfile(src, dest)
559 shutil.copyfile(src, dest)
560 shutil.copymode(src, dest)
560 shutil.copymode(src, dest)
561 except shutil.Error, inst:
561 except shutil.Error, inst:
562 raise util.Abort(str(inst))
562 raise util.Abort(str(inst))
563
563
564 def copyfiles(src, dst, hardlink=None):
564 def copyfiles(src, dst, hardlink=None):
565 """Copy a directory tree using hardlinks if possible"""
565 """Copy a directory tree using hardlinks if possible"""
566
566
567 if hardlink is None:
567 if hardlink is None:
568 hardlink = (os.stat(src).st_dev ==
568 hardlink = (os.stat(src).st_dev ==
569 os.stat(os.path.dirname(dst)).st_dev)
569 os.stat(os.path.dirname(dst)).st_dev)
570
570
571 if os.path.isdir(src):
571 if os.path.isdir(src):
572 os.mkdir(dst)
572 os.mkdir(dst)
573 for name in os.listdir(src):
573 for name in os.listdir(src):
574 srcname = os.path.join(src, name)
574 srcname = os.path.join(src, name)
575 dstname = os.path.join(dst, name)
575 dstname = os.path.join(dst, name)
576 copyfiles(srcname, dstname, hardlink)
576 copyfiles(srcname, dstname, hardlink)
577 else:
577 else:
578 if hardlink:
578 if hardlink:
579 try:
579 try:
580 os_link(src, dst)
580 os_link(src, dst)
581 except (IOError, OSError):
581 except (IOError, OSError):
582 hardlink = False
582 hardlink = False
583 shutil.copy(src, dst)
583 shutil.copy(src, dst)
584 else:
584 else:
585 shutil.copy(src, dst)
585 shutil.copy(src, dst)
586
586
587 def audit_path(path):
587 def audit_path(path):
588 """Abort if path contains dangerous components"""
588 """Abort if path contains dangerous components"""
589 parts = os.path.normcase(path).split(os.sep)
589 parts = os.path.normcase(path).split(os.sep)
590 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
590 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
591 or os.pardir in parts):
591 or os.pardir in parts):
592 raise Abort(_("path contains illegal component: %s\n") % path)
592 raise Abort(_("path contains illegal component: %s\n") % path)
593
593
594 def _makelock_file(info, pathname):
594 def _makelock_file(info, pathname):
595 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
595 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
596 os.write(ld, info)
596 os.write(ld, info)
597 os.close(ld)
597 os.close(ld)
598
598
599 def _readlock_file(pathname):
599 def _readlock_file(pathname):
600 return posixfile(pathname).read()
600 return posixfile(pathname).read()
601
601
602 def nlinks(pathname):
602 def nlinks(pathname):
603 """Return number of hardlinks for the given file."""
603 """Return number of hardlinks for the given file."""
604 return os.lstat(pathname).st_nlink
604 return os.lstat(pathname).st_nlink
605
605
606 if hasattr(os, 'link'):
606 if hasattr(os, 'link'):
607 os_link = os.link
607 os_link = os.link
608 else:
608 else:
609 def os_link(src, dst):
609 def os_link(src, dst):
610 raise OSError(0, _("Hardlinks not supported"))
610 raise OSError(0, _("Hardlinks not supported"))
611
611
612 def fstat(fp):
612 def fstat(fp):
613 '''stat file object that may not have fileno method.'''
613 '''stat file object that may not have fileno method.'''
614 try:
614 try:
615 return os.fstat(fp.fileno())
615 return os.fstat(fp.fileno())
616 except AttributeError:
616 except AttributeError:
617 return os.stat(fp.name)
617 return os.stat(fp.name)
618
618
619 posixfile = file
619 posixfile = file
620
620
621 def is_win_9x():
621 def is_win_9x():
622 '''return true if run on windows 95, 98 or me.'''
622 '''return true if run on windows 95, 98 or me.'''
623 try:
623 try:
624 return sys.getwindowsversion()[3] == 1
624 return sys.getwindowsversion()[3] == 1
625 except AttributeError:
625 except AttributeError:
626 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
626 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
627
627
628 getuser_fallback = None
628 getuser_fallback = None
629
629
630 def getuser():
630 def getuser():
631 '''return name of current user'''
631 '''return name of current user'''
632 try:
632 try:
633 return getpass.getuser()
633 return getpass.getuser()
634 except ImportError:
634 except ImportError:
635 # import of pwd will fail on windows - try fallback
635 # import of pwd will fail on windows - try fallback
636 if getuser_fallback:
636 if getuser_fallback:
637 return getuser_fallback()
637 return getuser_fallback()
638 # raised if win32api not available
638 # raised if win32api not available
639 raise Abort(_('user name not available - set USERNAME '
639 raise Abort(_('user name not available - set USERNAME '
640 'environment variable'))
640 'environment variable'))
641
641
642 def username(uid=None):
642 def username(uid=None):
643 """Return the name of the user with the given uid.
643 """Return the name of the user with the given uid.
644
644
645 If uid is None, return the name of the current user."""
645 If uid is None, return the name of the current user."""
646 try:
646 try:
647 import pwd
647 import pwd
648 if uid is None:
648 if uid is None:
649 uid = os.getuid()
649 uid = os.getuid()
650 try:
650 try:
651 return pwd.getpwuid(uid)[0]
651 return pwd.getpwuid(uid)[0]
652 except KeyError:
652 except KeyError:
653 return str(uid)
653 return str(uid)
654 except ImportError:
654 except ImportError:
655 return None
655 return None
656
656
657 def groupname(gid=None):
657 def groupname(gid=None):
658 """Return the name of the group with the given gid.
658 """Return the name of the group with the given gid.
659
659
660 If gid is None, return the name of the current group."""
660 If gid is None, return the name of the current group."""
661 try:
661 try:
662 import grp
662 import grp
663 if gid is None:
663 if gid is None:
664 gid = os.getgid()
664 gid = os.getgid()
665 try:
665 try:
666 return grp.getgrgid(gid)[0]
666 return grp.getgrgid(gid)[0]
667 except KeyError:
667 except KeyError:
668 return str(gid)
668 return str(gid)
669 except ImportError:
669 except ImportError:
670 return None
670 return None
671
671
672 # File system features
672 # File system features
673
673
674 def checkfolding(path):
674 def checkfolding(path):
675 """
675 """
676 Check whether the given path is on a case-sensitive filesystem
676 Check whether the given path is on a case-sensitive filesystem
677
677
678 Requires a path (like /foo/.hg) ending with a foldable final
678 Requires a path (like /foo/.hg) ending with a foldable final
679 directory component.
679 directory component.
680 """
680 """
681 s1 = os.stat(path)
681 s1 = os.stat(path)
682 d, b = os.path.split(path)
682 d, b = os.path.split(path)
683 p2 = os.path.join(d, b.upper())
683 p2 = os.path.join(d, b.upper())
684 if path == p2:
684 if path == p2:
685 p2 = os.path.join(d, b.lower())
685 p2 = os.path.join(d, b.lower())
686 try:
686 try:
687 s2 = os.stat(p2)
687 s2 = os.stat(p2)
688 if s2 == s1:
688 if s2 == s1:
689 return False
689 return False
690 return True
690 return True
691 except:
691 except:
692 return True
692 return True
693
693
694 # Platform specific variants
694 # Platform specific variants
695 if os.name == 'nt':
695 if os.name == 'nt':
696 demandload(globals(), "msvcrt")
696 demandload(globals(), "msvcrt")
697 nulldev = 'NUL:'
697 nulldev = 'NUL:'
698
698
699 class winstdout:
699 class winstdout:
700 '''stdout on windows misbehaves if sent through a pipe'''
700 '''stdout on windows misbehaves if sent through a pipe'''
701
701
702 def __init__(self, fp):
702 def __init__(self, fp):
703 self.fp = fp
703 self.fp = fp
704
704
705 def __getattr__(self, key):
705 def __getattr__(self, key):
706 return getattr(self.fp, key)
706 return getattr(self.fp, key)
707
707
708 def close(self):
708 def close(self):
709 try:
709 try:
710 self.fp.close()
710 self.fp.close()
711 except: pass
711 except: pass
712
712
713 def write(self, s):
713 def write(self, s):
714 try:
714 try:
715 return self.fp.write(s)
715 return self.fp.write(s)
716 except IOError, inst:
716 except IOError, inst:
717 if inst.errno != 0: raise
717 if inst.errno != 0: raise
718 self.close()
718 self.close()
719 raise IOError(errno.EPIPE, 'Broken pipe')
719 raise IOError(errno.EPIPE, 'Broken pipe')
720
720
721 sys.stdout = winstdout(sys.stdout)
721 sys.stdout = winstdout(sys.stdout)
722
722
723 def system_rcpath():
723 def system_rcpath():
724 try:
724 try:
725 return system_rcpath_win32()
725 return system_rcpath_win32()
726 except:
726 except:
727 return [r'c:\mercurial\mercurial.ini']
727 return [r'c:\mercurial\mercurial.ini']
728
728
729 def os_rcpath():
729 def os_rcpath():
730 '''return default os-specific hgrc search path'''
730 '''return default os-specific hgrc search path'''
731 path = system_rcpath()
731 path = system_rcpath()
732 path.append(user_rcpath())
732 path.append(user_rcpath())
733 userprofile = os.environ.get('USERPROFILE')
733 userprofile = os.environ.get('USERPROFILE')
734 if userprofile:
734 if userprofile:
735 path.append(os.path.join(userprofile, 'mercurial.ini'))
735 path.append(os.path.join(userprofile, 'mercurial.ini'))
736 return path
736 return path
737
737
738 def user_rcpath():
738 def user_rcpath():
739 '''return os-specific hgrc search path to the user dir'''
739 '''return os-specific hgrc search path to the user dir'''
740 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
740 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
741
741
742 def parse_patch_output(output_line):
742 def parse_patch_output(output_line):
743 """parses the output produced by patch and returns the file name"""
743 """parses the output produced by patch and returns the file name"""
744 pf = output_line[14:]
744 pf = output_line[14:]
745 if pf[0] == '`':
745 if pf[0] == '`':
746 pf = pf[1:-1] # Remove the quotes
746 pf = pf[1:-1] # Remove the quotes
747 return pf
747 return pf
748
748
749 def testpid(pid):
749 def testpid(pid):
750 '''return False if pid dead, True if running or not known'''
750 '''return False if pid dead, True if running or not known'''
751 return True
751 return True
752
752
753 def is_exec(f, last):
753 def is_exec(f, last):
754 return last
754 return last
755
755
756 def set_exec(f, mode):
756 def set_exec(f, mode):
757 pass
757 pass
758
758
759 def set_binary(fd):
759 def set_binary(fd):
760 msvcrt.setmode(fd.fileno(), os.O_BINARY)
760 msvcrt.setmode(fd.fileno(), os.O_BINARY)
761
761
762 def pconvert(path):
762 def pconvert(path):
763 return path.replace("\\", "/")
763 return path.replace("\\", "/")
764
764
765 def localpath(path):
765 def localpath(path):
766 return path.replace('/', '\\')
766 return path.replace('/', '\\')
767
767
768 def normpath(path):
768 def normpath(path):
769 return pconvert(os.path.normpath(path))
769 return pconvert(os.path.normpath(path))
770
770
771 makelock = _makelock_file
771 makelock = _makelock_file
772 readlock = _readlock_file
772 readlock = _readlock_file
773
773
774 def samestat(s1, s2):
774 def samestat(s1, s2):
775 return False
775 return False
776
776
777 def shellquote(s):
777 def shellquote(s):
778 return '"%s"' % s.replace('"', '\\"')
778 return '"%s"' % s.replace('"', '\\"')
779
779
780 def explain_exit(code):
780 def explain_exit(code):
781 return _("exited with status %d") % code, code
781 return _("exited with status %d") % code, code
782
782
783 # if you change this stub into a real check, please try to implement the
783 # if you change this stub into a real check, please try to implement the
784 # username and groupname functions above, too.
784 # username and groupname functions above, too.
785 def isowner(fp, st=None):
785 def isowner(fp, st=None):
786 return True
786 return True
787
787
788 try:
788 try:
789 # override functions with win32 versions if possible
789 # override functions with win32 versions if possible
790 from util_win32 import *
790 from util_win32 import *
791 if not is_win_9x():
791 if not is_win_9x():
792 posixfile = posixfile_nt
792 posixfile = posixfile_nt
793 except ImportError:
793 except ImportError:
794 pass
794 pass
795
795
796 else:
796 else:
797 nulldev = '/dev/null'
797 nulldev = '/dev/null'
798
798
799 def rcfiles(path):
799 def rcfiles(path):
800 rcs = [os.path.join(path, 'hgrc')]
800 rcs = [os.path.join(path, 'hgrc')]
801 rcdir = os.path.join(path, 'hgrc.d')
801 rcdir = os.path.join(path, 'hgrc.d')
802 try:
802 try:
803 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
803 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
804 if f.endswith(".rc")])
804 if f.endswith(".rc")])
805 except OSError:
805 except OSError:
806 pass
806 pass
807 return rcs
807 return rcs
808
808
809 def os_rcpath():
809 def os_rcpath():
810 '''return default os-specific hgrc search path'''
810 '''return default os-specific hgrc search path'''
811 path = []
811 path = []
812 # old mod_python does not set sys.argv
812 # old mod_python does not set sys.argv
813 if len(getattr(sys, 'argv', [])) > 0:
813 if len(getattr(sys, 'argv', [])) > 0:
814 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
814 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
815 '/../etc/mercurial'))
815 '/../etc/mercurial'))
816 path.extend(rcfiles('/etc/mercurial'))
816 path.extend(rcfiles('/etc/mercurial'))
817 path.append(os.path.expanduser('~/.hgrc'))
817 path.append(os.path.expanduser('~/.hgrc'))
818 path = [os.path.normpath(f) for f in path]
818 path = [os.path.normpath(f) for f in path]
819 return path
819 return path
820
820
821 def parse_patch_output(output_line):
821 def parse_patch_output(output_line):
822 """parses the output produced by patch and returns the file name"""
822 """parses the output produced by patch and returns the file name"""
823 pf = output_line[14:]
823 pf = output_line[14:]
824 if pf.startswith("'") and pf.endswith("'") and " " in pf:
824 if pf.startswith("'") and pf.endswith("'") and " " in pf:
825 pf = pf[1:-1] # Remove the quotes
825 pf = pf[1:-1] # Remove the quotes
826 return pf
826 return pf
827
827
828 def is_exec(f, last):
828 def is_exec(f, last):
829 """check whether a file is executable"""
829 """check whether a file is executable"""
830 return (os.lstat(f).st_mode & 0100 != 0)
830 return (os.lstat(f).st_mode & 0100 != 0)
831
831
832 def set_exec(f, mode):
832 def set_exec(f, mode):
833 s = os.lstat(f).st_mode
833 s = os.lstat(f).st_mode
834 if (s & 0100 != 0) == mode:
834 if (s & 0100 != 0) == mode:
835 return
835 return
836 if mode:
836 if mode:
837 # Turn on +x for every +r bit when making a file executable
837 # Turn on +x for every +r bit when making a file executable
838 # and obey umask.
838 # and obey umask.
839 umask = os.umask(0)
839 umask = os.umask(0)
840 os.umask(umask)
840 os.umask(umask)
841 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
841 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
842 else:
842 else:
843 os.chmod(f, s & 0666)
843 os.chmod(f, s & 0666)
844
844
845 def set_binary(fd):
845 def set_binary(fd):
846 pass
846 pass
847
847
848 def pconvert(path):
848 def pconvert(path):
849 return path
849 return path
850
850
851 def localpath(path):
851 def localpath(path):
852 return path
852 return path
853
853
854 normpath = os.path.normpath
854 normpath = os.path.normpath
855 samestat = os.path.samestat
855 samestat = os.path.samestat
856
856
857 def makelock(info, pathname):
857 def makelock(info, pathname):
858 try:
858 try:
859 os.symlink(info, pathname)
859 os.symlink(info, pathname)
860 except OSError, why:
860 except OSError, why:
861 if why.errno == errno.EEXIST:
861 if why.errno == errno.EEXIST:
862 raise
862 raise
863 else:
863 else:
864 _makelock_file(info, pathname)
864 _makelock_file(info, pathname)
865
865
866 def readlock(pathname):
866 def readlock(pathname):
867 try:
867 try:
868 return os.readlink(pathname)
868 return os.readlink(pathname)
869 except OSError, why:
869 except OSError, why:
870 if why.errno == errno.EINVAL:
870 if why.errno == errno.EINVAL:
871 return _readlock_file(pathname)
871 return _readlock_file(pathname)
872 else:
872 else:
873 raise
873 raise
874
874
875 def shellquote(s):
875 def shellquote(s):
876 return "'%s'" % s.replace("'", "'\\''")
876 return "'%s'" % s.replace("'", "'\\''")
877
877
878 def testpid(pid):
878 def testpid(pid):
879 '''return False if pid dead, True if running or not sure'''
879 '''return False if pid dead, True if running or not sure'''
880 try:
880 try:
881 os.kill(pid, 0)
881 os.kill(pid, 0)
882 return True
882 return True
883 except OSError, inst:
883 except OSError, inst:
884 return inst.errno != errno.ESRCH
884 return inst.errno != errno.ESRCH
885
885
886 def explain_exit(code):
886 def explain_exit(code):
887 """return a 2-tuple (desc, code) describing a process's status"""
887 """return a 2-tuple (desc, code) describing a process's status"""
888 if os.WIFEXITED(code):
888 if os.WIFEXITED(code):
889 val = os.WEXITSTATUS(code)
889 val = os.WEXITSTATUS(code)
890 return _("exited with status %d") % val, val
890 return _("exited with status %d") % val, val
891 elif os.WIFSIGNALED(code):
891 elif os.WIFSIGNALED(code):
892 val = os.WTERMSIG(code)
892 val = os.WTERMSIG(code)
893 return _("killed by signal %d") % val, val
893 return _("killed by signal %d") % val, val
894 elif os.WIFSTOPPED(code):
894 elif os.WIFSTOPPED(code):
895 val = os.WSTOPSIG(code)
895 val = os.WSTOPSIG(code)
896 return _("stopped by signal %d") % val, val
896 return _("stopped by signal %d") % val, val
897 raise ValueError(_("invalid exit code"))
897 raise ValueError(_("invalid exit code"))
898
898
899 def isowner(fp, st=None):
899 def isowner(fp, st=None):
900 """Return True if the file object f belongs to the current user.
900 """Return True if the file object f belongs to the current user.
901
901
902 The return value of a util.fstat(f) may be passed as the st argument.
902 The return value of a util.fstat(f) may be passed as the st argument.
903 """
903 """
904 if st is None:
904 if st is None:
905 st = fstat(f)
905 st = fstat(fp)
906 return st.st_uid == os.getuid()
906 return st.st_uid == os.getuid()
907
907
908 def _buildencodefun():
908 def _buildencodefun():
909 e = '_'
909 e = '_'
910 win_reserved = [ord(x) for x in '|\?*<":>+[]']
910 win_reserved = [ord(x) for x in '|\?*<":>+[]']
911 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
911 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
912 for x in (range(32) + range(126, 256) + win_reserved):
912 for x in (range(32) + range(126, 256) + win_reserved):
913 cmap[chr(x)] = "~%02x" % x
913 cmap[chr(x)] = "~%02x" % x
914 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
914 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
915 cmap[chr(x)] = e + chr(x).lower()
915 cmap[chr(x)] = e + chr(x).lower()
916 dmap = {}
916 dmap = {}
917 for k, v in cmap.iteritems():
917 for k, v in cmap.iteritems():
918 dmap[v] = k
918 dmap[v] = k
919 def decode(s):
919 def decode(s):
920 i = 0
920 i = 0
921 while i < len(s):
921 while i < len(s):
922 for l in xrange(1, 4):
922 for l in xrange(1, 4):
923 try:
923 try:
924 yield dmap[s[i:i+l]]
924 yield dmap[s[i:i+l]]
925 i += l
925 i += l
926 break
926 break
927 except KeyError:
927 except KeyError:
928 pass
928 pass
929 else:
929 else:
930 raise KeyError
930 raise KeyError
931 return (lambda s: "".join([cmap[c] for c in s]),
931 return (lambda s: "".join([cmap[c] for c in s]),
932 lambda s: "".join(list(decode(s))))
932 lambda s: "".join(list(decode(s))))
933
933
934 encodefilename, decodefilename = _buildencodefun()
934 encodefilename, decodefilename = _buildencodefun()
935
935
936 def encodedopener(openerfn, fn):
936 def encodedopener(openerfn, fn):
937 def o(path, *args, **kw):
937 def o(path, *args, **kw):
938 return openerfn(fn(path), *args, **kw)
938 return openerfn(fn(path), *args, **kw)
939 return o
939 return o
940
940
941 def opener(base, audit=True):
941 def opener(base, audit=True):
942 """
942 """
943 return a function that opens files relative to base
943 return a function that opens files relative to base
944
944
945 this function is used to hide the details of COW semantics and
945 this function is used to hide the details of COW semantics and
946 remote file access from higher level code.
946 remote file access from higher level code.
947 """
947 """
948 p = base
948 p = base
949 audit_p = audit
949 audit_p = audit
950
950
951 def mktempcopy(name):
951 def mktempcopy(name):
952 d, fn = os.path.split(name)
952 d, fn = os.path.split(name)
953 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
953 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
954 os.close(fd)
954 os.close(fd)
955 ofp = posixfile(temp, "wb")
955 ofp = posixfile(temp, "wb")
956 try:
956 try:
957 try:
957 try:
958 ifp = posixfile(name, "rb")
958 ifp = posixfile(name, "rb")
959 except IOError, inst:
959 except IOError, inst:
960 if not getattr(inst, 'filename', None):
960 if not getattr(inst, 'filename', None):
961 inst.filename = name
961 inst.filename = name
962 raise
962 raise
963 for chunk in filechunkiter(ifp):
963 for chunk in filechunkiter(ifp):
964 ofp.write(chunk)
964 ofp.write(chunk)
965 ifp.close()
965 ifp.close()
966 ofp.close()
966 ofp.close()
967 except:
967 except:
968 try: os.unlink(temp)
968 try: os.unlink(temp)
969 except: pass
969 except: pass
970 raise
970 raise
971 st = os.lstat(name)
971 st = os.lstat(name)
972 os.chmod(temp, st.st_mode)
972 os.chmod(temp, st.st_mode)
973 return temp
973 return temp
974
974
975 class atomictempfile(posixfile):
975 class atomictempfile(posixfile):
976 """the file will only be copied when rename is called"""
976 """the file will only be copied when rename is called"""
977 def __init__(self, name, mode):
977 def __init__(self, name, mode):
978 self.__name = name
978 self.__name = name
979 self.temp = mktempcopy(name)
979 self.temp = mktempcopy(name)
980 posixfile.__init__(self, self.temp, mode)
980 posixfile.__init__(self, self.temp, mode)
981 def rename(self):
981 def rename(self):
982 if not self.closed:
982 if not self.closed:
983 posixfile.close(self)
983 posixfile.close(self)
984 rename(self.temp, localpath(self.__name))
984 rename(self.temp, localpath(self.__name))
985 def __del__(self):
985 def __del__(self):
986 if not self.closed:
986 if not self.closed:
987 try:
987 try:
988 os.unlink(self.temp)
988 os.unlink(self.temp)
989 except: pass
989 except: pass
990 posixfile.close(self)
990 posixfile.close(self)
991
991
992 class atomicfile(atomictempfile):
992 class atomicfile(atomictempfile):
993 """the file will only be copied on close"""
993 """the file will only be copied on close"""
994 def __init__(self, name, mode):
994 def __init__(self, name, mode):
995 atomictempfile.__init__(self, name, mode)
995 atomictempfile.__init__(self, name, mode)
996 def close(self):
996 def close(self):
997 self.rename()
997 self.rename()
998 def __del__(self):
998 def __del__(self):
999 self.rename()
999 self.rename()
1000
1000
1001 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1001 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1002 if audit_p:
1002 if audit_p:
1003 audit_path(path)
1003 audit_path(path)
1004 f = os.path.join(p, path)
1004 f = os.path.join(p, path)
1005
1005
1006 if not text:
1006 if not text:
1007 mode += "b" # for that other OS
1007 mode += "b" # for that other OS
1008
1008
1009 if mode[0] != "r":
1009 if mode[0] != "r":
1010 try:
1010 try:
1011 nlink = nlinks(f)
1011 nlink = nlinks(f)
1012 except OSError:
1012 except OSError:
1013 d = os.path.dirname(f)
1013 d = os.path.dirname(f)
1014 if not os.path.isdir(d):
1014 if not os.path.isdir(d):
1015 os.makedirs(d)
1015 os.makedirs(d)
1016 else:
1016 else:
1017 if atomic:
1017 if atomic:
1018 return atomicfile(f, mode)
1018 return atomicfile(f, mode)
1019 elif atomictemp:
1019 elif atomictemp:
1020 return atomictempfile(f, mode)
1020 return atomictempfile(f, mode)
1021 if nlink > 1:
1021 if nlink > 1:
1022 rename(mktempcopy(f), f)
1022 rename(mktempcopy(f), f)
1023 return posixfile(f, mode)
1023 return posixfile(f, mode)
1024
1024
1025 return o
1025 return o
1026
1026
1027 class chunkbuffer(object):
1027 class chunkbuffer(object):
1028 """Allow arbitrary sized chunks of data to be efficiently read from an
1028 """Allow arbitrary sized chunks of data to be efficiently read from an
1029 iterator over chunks of arbitrary size."""
1029 iterator over chunks of arbitrary size."""
1030
1030
1031 def __init__(self, in_iter, targetsize = 2**16):
1031 def __init__(self, in_iter, targetsize = 2**16):
1032 """in_iter is the iterator that's iterating over the input chunks.
1032 """in_iter is the iterator that's iterating over the input chunks.
1033 targetsize is how big a buffer to try to maintain."""
1033 targetsize is how big a buffer to try to maintain."""
1034 self.in_iter = iter(in_iter)
1034 self.in_iter = iter(in_iter)
1035 self.buf = ''
1035 self.buf = ''
1036 self.targetsize = int(targetsize)
1036 self.targetsize = int(targetsize)
1037 if self.targetsize <= 0:
1037 if self.targetsize <= 0:
1038 raise ValueError(_("targetsize must be greater than 0, was %d") %
1038 raise ValueError(_("targetsize must be greater than 0, was %d") %
1039 targetsize)
1039 targetsize)
1040 self.iterempty = False
1040 self.iterempty = False
1041
1041
1042 def fillbuf(self):
1042 def fillbuf(self):
1043 """Ignore target size; read every chunk from iterator until empty."""
1043 """Ignore target size; read every chunk from iterator until empty."""
1044 if not self.iterempty:
1044 if not self.iterempty:
1045 collector = cStringIO.StringIO()
1045 collector = cStringIO.StringIO()
1046 collector.write(self.buf)
1046 collector.write(self.buf)
1047 for ch in self.in_iter:
1047 for ch in self.in_iter:
1048 collector.write(ch)
1048 collector.write(ch)
1049 self.buf = collector.getvalue()
1049 self.buf = collector.getvalue()
1050 self.iterempty = True
1050 self.iterempty = True
1051
1051
1052 def read(self, l):
1052 def read(self, l):
1053 """Read L bytes of data from the iterator of chunks of data.
1053 """Read L bytes of data from the iterator of chunks of data.
1054 Returns less than L bytes if the iterator runs dry."""
1054 Returns less than L bytes if the iterator runs dry."""
1055 if l > len(self.buf) and not self.iterempty:
1055 if l > len(self.buf) and not self.iterempty:
1056 # Clamp to a multiple of self.targetsize
1056 # Clamp to a multiple of self.targetsize
1057 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1057 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1058 collector = cStringIO.StringIO()
1058 collector = cStringIO.StringIO()
1059 collector.write(self.buf)
1059 collector.write(self.buf)
1060 collected = len(self.buf)
1060 collected = len(self.buf)
1061 for chunk in self.in_iter:
1061 for chunk in self.in_iter:
1062 collector.write(chunk)
1062 collector.write(chunk)
1063 collected += len(chunk)
1063 collected += len(chunk)
1064 if collected >= targetsize:
1064 if collected >= targetsize:
1065 break
1065 break
1066 if collected < targetsize:
1066 if collected < targetsize:
1067 self.iterempty = True
1067 self.iterempty = True
1068 self.buf = collector.getvalue()
1068 self.buf = collector.getvalue()
1069 s, self.buf = self.buf[:l], buffer(self.buf, l)
1069 s, self.buf = self.buf[:l], buffer(self.buf, l)
1070 return s
1070 return s
1071
1071
1072 def filechunkiter(f, size=65536, limit=None):
1072 def filechunkiter(f, size=65536, limit=None):
1073 """Create a generator that produces the data in the file size
1073 """Create a generator that produces the data in the file size
1074 (default 65536) bytes at a time, up to optional limit (default is
1074 (default 65536) bytes at a time, up to optional limit (default is
1075 to read all data). Chunks may be less than size bytes if the
1075 to read all data). Chunks may be less than size bytes if the
1076 chunk is the last chunk in the file, or the file is a socket or
1076 chunk is the last chunk in the file, or the file is a socket or
1077 some other type of file that sometimes reads less data than is
1077 some other type of file that sometimes reads less data than is
1078 requested."""
1078 requested."""
1079 assert size >= 0
1079 assert size >= 0
1080 assert limit is None or limit >= 0
1080 assert limit is None or limit >= 0
1081 while True:
1081 while True:
1082 if limit is None: nbytes = size
1082 if limit is None: nbytes = size
1083 else: nbytes = min(limit, size)
1083 else: nbytes = min(limit, size)
1084 s = nbytes and f.read(nbytes)
1084 s = nbytes and f.read(nbytes)
1085 if not s: break
1085 if not s: break
1086 if limit: limit -= len(s)
1086 if limit: limit -= len(s)
1087 yield s
1087 yield s
1088
1088
1089 def makedate():
1089 def makedate():
1090 lt = time.localtime()
1090 lt = time.localtime()
1091 if lt[8] == 1 and time.daylight:
1091 if lt[8] == 1 and time.daylight:
1092 tz = time.altzone
1092 tz = time.altzone
1093 else:
1093 else:
1094 tz = time.timezone
1094 tz = time.timezone
1095 return time.mktime(lt), tz
1095 return time.mktime(lt), tz
1096
1096
1097 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1097 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1098 """represent a (unixtime, offset) tuple as a localized time.
1098 """represent a (unixtime, offset) tuple as a localized time.
1099 unixtime is seconds since the epoch, and offset is the time zone's
1099 unixtime is seconds since the epoch, and offset is the time zone's
1100 number of seconds away from UTC. if timezone is false, do not
1100 number of seconds away from UTC. if timezone is false, do not
1101 append time zone to string."""
1101 append time zone to string."""
1102 t, tz = date or makedate()
1102 t, tz = date or makedate()
1103 s = time.strftime(format, time.gmtime(float(t) - tz))
1103 s = time.strftime(format, time.gmtime(float(t) - tz))
1104 if timezone:
1104 if timezone:
1105 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1105 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1106 return s
1106 return s
1107
1107
1108 def strdate(string, format, defaults):
1108 def strdate(string, format, defaults):
1109 """parse a localized time string and return a (unixtime, offset) tuple.
1109 """parse a localized time string and return a (unixtime, offset) tuple.
1110 if the string cannot be parsed, ValueError is raised."""
1110 if the string cannot be parsed, ValueError is raised."""
1111 def timezone(string):
1111 def timezone(string):
1112 tz = string.split()[-1]
1112 tz = string.split()[-1]
1113 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1113 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1114 tz = int(tz)
1114 tz = int(tz)
1115 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1115 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1116 return offset
1116 return offset
1117 if tz == "GMT" or tz == "UTC":
1117 if tz == "GMT" or tz == "UTC":
1118 return 0
1118 return 0
1119 return None
1119 return None
1120
1120
1121 # NOTE: unixtime = localunixtime + offset
1121 # NOTE: unixtime = localunixtime + offset
1122 offset, date = timezone(string), string
1122 offset, date = timezone(string), string
1123 if offset != None:
1123 if offset != None:
1124 date = " ".join(string.split()[:-1])
1124 date = " ".join(string.split()[:-1])
1125
1125
1126 # add missing elements from defaults
1126 # add missing elements from defaults
1127 for part in defaults:
1127 for part in defaults:
1128 found = [True for p in part if ("%"+p) in format]
1128 found = [True for p in part if ("%"+p) in format]
1129 if not found:
1129 if not found:
1130 date += "@" + defaults[part]
1130 date += "@" + defaults[part]
1131 format += "@%" + part[0]
1131 format += "@%" + part[0]
1132
1132
1133 timetuple = time.strptime(date, format)
1133 timetuple = time.strptime(date, format)
1134 localunixtime = int(calendar.timegm(timetuple))
1134 localunixtime = int(calendar.timegm(timetuple))
1135 if offset is None:
1135 if offset is None:
1136 # local timezone
1136 # local timezone
1137 unixtime = int(time.mktime(timetuple))
1137 unixtime = int(time.mktime(timetuple))
1138 offset = unixtime - localunixtime
1138 offset = unixtime - localunixtime
1139 else:
1139 else:
1140 unixtime = localunixtime + offset
1140 unixtime = localunixtime + offset
1141 return unixtime, offset
1141 return unixtime, offset
1142
1142
1143 def parsedate(string, formats=None, defaults=None):
1143 def parsedate(string, formats=None, defaults=None):
1144 """parse a localized time string and return a (unixtime, offset) tuple.
1144 """parse a localized time string and return a (unixtime, offset) tuple.
1145 The date may be a "unixtime offset" string or in one of the specified
1145 The date may be a "unixtime offset" string or in one of the specified
1146 formats."""
1146 formats."""
1147 if not string:
1147 if not string:
1148 return 0, 0
1148 return 0, 0
1149 if not formats:
1149 if not formats:
1150 formats = defaultdateformats
1150 formats = defaultdateformats
1151 string = string.strip()
1151 string = string.strip()
1152 try:
1152 try:
1153 when, offset = map(int, string.split(' '))
1153 when, offset = map(int, string.split(' '))
1154 except ValueError:
1154 except ValueError:
1155 # fill out defaults
1155 # fill out defaults
1156 if not defaults:
1156 if not defaults:
1157 defaults = {}
1157 defaults = {}
1158 now = makedate()
1158 now = makedate()
1159 for part in "d mb yY HI M S".split():
1159 for part in "d mb yY HI M S".split():
1160 if part not in defaults:
1160 if part not in defaults:
1161 if part[0] in "HMS":
1161 if part[0] in "HMS":
1162 defaults[part] = "00"
1162 defaults[part] = "00"
1163 elif part[0] in "dm":
1163 elif part[0] in "dm":
1164 defaults[part] = "1"
1164 defaults[part] = "1"
1165 else:
1165 else:
1166 defaults[part] = datestr(now, "%" + part[0], False)
1166 defaults[part] = datestr(now, "%" + part[0], False)
1167
1167
1168 for format in formats:
1168 for format in formats:
1169 try:
1169 try:
1170 when, offset = strdate(string, format, defaults)
1170 when, offset = strdate(string, format, defaults)
1171 except ValueError:
1171 except ValueError:
1172 pass
1172 pass
1173 else:
1173 else:
1174 break
1174 break
1175 else:
1175 else:
1176 raise Abort(_('invalid date: %r ') % string)
1176 raise Abort(_('invalid date: %r ') % string)
1177 # validate explicit (probably user-specified) date and
1177 # validate explicit (probably user-specified) date and
1178 # time zone offset. values must fit in signed 32 bits for
1178 # time zone offset. values must fit in signed 32 bits for
1179 # current 32-bit linux runtimes. timezones go from UTC-12
1179 # current 32-bit linux runtimes. timezones go from UTC-12
1180 # to UTC+14
1180 # to UTC+14
1181 if abs(when) > 0x7fffffff:
1181 if abs(when) > 0x7fffffff:
1182 raise Abort(_('date exceeds 32 bits: %d') % when)
1182 raise Abort(_('date exceeds 32 bits: %d') % when)
1183 if offset < -50400 or offset > 43200:
1183 if offset < -50400 or offset > 43200:
1184 raise Abort(_('impossible time zone offset: %d') % offset)
1184 raise Abort(_('impossible time zone offset: %d') % offset)
1185 return when, offset
1185 return when, offset
1186
1186
1187 def matchdate(date):
1187 def matchdate(date):
1188 """Return a function that matches a given date match specifier
1188 """Return a function that matches a given date match specifier
1189
1189
1190 Formats include:
1190 Formats include:
1191
1191
1192 '{date}' match a given date to the accuracy provided
1192 '{date}' match a given date to the accuracy provided
1193
1193
1194 '<{date}' on or before a given date
1194 '<{date}' on or before a given date
1195
1195
1196 '>{date}' on or after a given date
1196 '>{date}' on or after a given date
1197
1197
1198 """
1198 """
1199
1199
1200 def lower(date):
1200 def lower(date):
1201 return parsedate(date, extendeddateformats)[0]
1201 return parsedate(date, extendeddateformats)[0]
1202
1202
1203 def upper(date):
1203 def upper(date):
1204 d = dict(mb="12", HI="23", M="59", S="59")
1204 d = dict(mb="12", HI="23", M="59", S="59")
1205 for days in "31 30 29".split():
1205 for days in "31 30 29".split():
1206 try:
1206 try:
1207 d["d"] = days
1207 d["d"] = days
1208 return parsedate(date, extendeddateformats, d)[0]
1208 return parsedate(date, extendeddateformats, d)[0]
1209 except:
1209 except:
1210 pass
1210 pass
1211 d["d"] = "28"
1211 d["d"] = "28"
1212 return parsedate(date, extendeddateformats, d)[0]
1212 return parsedate(date, extendeddateformats, d)[0]
1213
1213
1214 if date[0] == "<":
1214 if date[0] == "<":
1215 when = upper(date[1:])
1215 when = upper(date[1:])
1216 return lambda x: x <= when
1216 return lambda x: x <= when
1217 elif date[0] == ">":
1217 elif date[0] == ">":
1218 when = lower(date[1:])
1218 when = lower(date[1:])
1219 return lambda x: x >= when
1219 return lambda x: x >= when
1220 elif date[0] == "-":
1220 elif date[0] == "-":
1221 try:
1221 try:
1222 days = int(date[1:])
1222 days = int(date[1:])
1223 except ValueError:
1223 except ValueError:
1224 raise Abort(_("invalid day spec: %s") % date[1:])
1224 raise Abort(_("invalid day spec: %s") % date[1:])
1225 when = makedate()[0] - days * 3600 * 24
1225 when = makedate()[0] - days * 3600 * 24
1226 return lambda x: x >= when
1226 return lambda x: x >= when
1227 elif " to " in date:
1227 elif " to " in date:
1228 a, b = date.split(" to ")
1228 a, b = date.split(" to ")
1229 start, stop = lower(a), upper(b)
1229 start, stop = lower(a), upper(b)
1230 return lambda x: x >= start and x <= stop
1230 return lambda x: x >= start and x <= stop
1231 else:
1231 else:
1232 start, stop = lower(date), upper(date)
1232 start, stop = lower(date), upper(date)
1233 return lambda x: x >= start and x <= stop
1233 return lambda x: x >= start and x <= stop
1234
1234
1235 def shortuser(user):
1235 def shortuser(user):
1236 """Return a short representation of a user name or email address."""
1236 """Return a short representation of a user name or email address."""
1237 f = user.find('@')
1237 f = user.find('@')
1238 if f >= 0:
1238 if f >= 0:
1239 user = user[:f]
1239 user = user[:f]
1240 f = user.find('<')
1240 f = user.find('<')
1241 if f >= 0:
1241 if f >= 0:
1242 user = user[f+1:]
1242 user = user[f+1:]
1243 f = user.find(' ')
1243 f = user.find(' ')
1244 if f >= 0:
1244 if f >= 0:
1245 user = user[:f]
1245 user = user[:f]
1246 f = user.find('.')
1246 f = user.find('.')
1247 if f >= 0:
1247 if f >= 0:
1248 user = user[:f]
1248 user = user[:f]
1249 return user
1249 return user
1250
1250
1251 def ellipsis(text, maxlength=400):
1251 def ellipsis(text, maxlength=400):
1252 """Trim string to at most maxlength (default: 400) characters."""
1252 """Trim string to at most maxlength (default: 400) characters."""
1253 if len(text) <= maxlength:
1253 if len(text) <= maxlength:
1254 return text
1254 return text
1255 else:
1255 else:
1256 return "%s..." % (text[:maxlength-3])
1256 return "%s..." % (text[:maxlength-3])
1257
1257
1258 def walkrepos(path):
1258 def walkrepos(path):
1259 '''yield every hg repository under path, recursively.'''
1259 '''yield every hg repository under path, recursively.'''
1260 def errhandler(err):
1260 def errhandler(err):
1261 if err.filename == path:
1261 if err.filename == path:
1262 raise err
1262 raise err
1263
1263
1264 for root, dirs, files in os.walk(path, onerror=errhandler):
1264 for root, dirs, files in os.walk(path, onerror=errhandler):
1265 for d in dirs:
1265 for d in dirs:
1266 if d == '.hg':
1266 if d == '.hg':
1267 yield root
1267 yield root
1268 dirs[:] = []
1268 dirs[:] = []
1269 break
1269 break
1270
1270
1271 _rcpath = None
1271 _rcpath = None
1272
1272
1273 def rcpath():
1273 def rcpath():
1274 '''return hgrc search path. if env var HGRCPATH is set, use it.
1274 '''return hgrc search path. if env var HGRCPATH is set, use it.
1275 for each item in path, if directory, use files ending in .rc,
1275 for each item in path, if directory, use files ending in .rc,
1276 else use item.
1276 else use item.
1277 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1277 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1278 if no HGRCPATH, use default os-specific path.'''
1278 if no HGRCPATH, use default os-specific path.'''
1279 global _rcpath
1279 global _rcpath
1280 if _rcpath is None:
1280 if _rcpath is None:
1281 if 'HGRCPATH' in os.environ:
1281 if 'HGRCPATH' in os.environ:
1282 _rcpath = []
1282 _rcpath = []
1283 for p in os.environ['HGRCPATH'].split(os.pathsep):
1283 for p in os.environ['HGRCPATH'].split(os.pathsep):
1284 if not p: continue
1284 if not p: continue
1285 if os.path.isdir(p):
1285 if os.path.isdir(p):
1286 for f in os.listdir(p):
1286 for f in os.listdir(p):
1287 if f.endswith('.rc'):
1287 if f.endswith('.rc'):
1288 _rcpath.append(os.path.join(p, f))
1288 _rcpath.append(os.path.join(p, f))
1289 else:
1289 else:
1290 _rcpath.append(p)
1290 _rcpath.append(p)
1291 else:
1291 else:
1292 _rcpath = os_rcpath()
1292 _rcpath = os_rcpath()
1293 return _rcpath
1293 return _rcpath
1294
1294
1295 def bytecount(nbytes):
1295 def bytecount(nbytes):
1296 '''return byte count formatted as readable string, with units'''
1296 '''return byte count formatted as readable string, with units'''
1297
1297
1298 units = (
1298 units = (
1299 (100, 1<<30, _('%.0f GB')),
1299 (100, 1<<30, _('%.0f GB')),
1300 (10, 1<<30, _('%.1f GB')),
1300 (10, 1<<30, _('%.1f GB')),
1301 (1, 1<<30, _('%.2f GB')),
1301 (1, 1<<30, _('%.2f GB')),
1302 (100, 1<<20, _('%.0f MB')),
1302 (100, 1<<20, _('%.0f MB')),
1303 (10, 1<<20, _('%.1f MB')),
1303 (10, 1<<20, _('%.1f MB')),
1304 (1, 1<<20, _('%.2f MB')),
1304 (1, 1<<20, _('%.2f MB')),
1305 (100, 1<<10, _('%.0f KB')),
1305 (100, 1<<10, _('%.0f KB')),
1306 (10, 1<<10, _('%.1f KB')),
1306 (10, 1<<10, _('%.1f KB')),
1307 (1, 1<<10, _('%.2f KB')),
1307 (1, 1<<10, _('%.2f KB')),
1308 (1, 1, _('%.0f bytes')),
1308 (1, 1, _('%.0f bytes')),
1309 )
1309 )
1310
1310
1311 for multiplier, divisor, format in units:
1311 for multiplier, divisor, format in units:
1312 if nbytes >= divisor * multiplier:
1312 if nbytes >= divisor * multiplier:
1313 return format % (nbytes / float(divisor))
1313 return format % (nbytes / float(divisor))
1314 return units[-1][2] % nbytes
1314 return units[-1][2] % nbytes
1315
1315
1316 def drop_scheme(scheme, path):
1316 def drop_scheme(scheme, path):
1317 sc = scheme + ':'
1317 sc = scheme + ':'
1318 if path.startswith(sc):
1318 if path.startswith(sc):
1319 path = path[len(sc):]
1319 path = path[len(sc):]
1320 if path.startswith('//'):
1320 if path.startswith('//'):
1321 path = path[2:]
1321 path = path[2:]
1322 return path
1322 return path
General Comments 0
You need to be logged in to leave comments. Login now