##// END OF EJS Templates
mail/hgweb: support service names for ports (issue2350)...
Brodie Rao -
r12076:49463314 default
parent child Browse files
Show More
@@ -1,173 +1,173 b''
1 # zeroconf.py - zeroconf support for Mercurial
1 # zeroconf.py - zeroconf support for Mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''discover and advertise repositories on the local network
8 '''discover and advertise repositories on the local network
9
9
10 Zeroconf-enabled repositories will be announced in a network without
10 Zeroconf-enabled repositories will be announced in a network without
11 the need to configure a server or a service. They can be discovered
11 the need to configure a server or a service. They can be discovered
12 without knowing their actual IP address.
12 without knowing their actual IP address.
13
13
14 To allow other people to discover your repository using run
14 To allow other people to discover your repository using run
15 :hg:`serve` in your repository::
15 :hg:`serve` in your repository::
16
16
17 $ cd test
17 $ cd test
18 $ hg serve
18 $ hg serve
19
19
20 You can discover Zeroconf-enabled repositories by running
20 You can discover Zeroconf-enabled repositories by running
21 :hg:`paths`::
21 :hg:`paths`::
22
22
23 $ hg paths
23 $ hg paths
24 zc-test = http://example.com:8000/test
24 zc-test = http://example.com:8000/test
25 '''
25 '''
26
26
27 import socket, time, os
27 import socket, time, os
28
28
29 import Zeroconf
29 import Zeroconf
30 from mercurial import ui, hg, encoding
30 from mercurial import ui, hg, encoding, util
31 from mercurial import extensions
31 from mercurial import extensions
32 from mercurial.hgweb import hgweb_mod
32 from mercurial.hgweb import hgweb_mod
33 from mercurial.hgweb import hgwebdir_mod
33 from mercurial.hgweb import hgwebdir_mod
34
34
35 # publish
35 # publish
36
36
37 server = None
37 server = None
38 localip = None
38 localip = None
39
39
40 def getip():
40 def getip():
41 # finds external-facing interface without sending any packets (Linux)
41 # finds external-facing interface without sending any packets (Linux)
42 try:
42 try:
43 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
43 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
44 s.connect(('1.0.0.1', 0))
44 s.connect(('1.0.0.1', 0))
45 ip = s.getsockname()[0]
45 ip = s.getsockname()[0]
46 return ip
46 return ip
47 except:
47 except:
48 pass
48 pass
49
49
50 # Generic method, sometimes gives useless results
50 # Generic method, sometimes gives useless results
51 try:
51 try:
52 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
52 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
53 if not dumbip.startswith('127.') and ':' not in dumbip:
53 if not dumbip.startswith('127.') and ':' not in dumbip:
54 return dumbip
54 return dumbip
55 except (socket.gaierror, socket.herror):
55 except (socket.gaierror, socket.herror):
56 dumbip = '127.0.0.1'
56 dumbip = '127.0.0.1'
57
57
58 # works elsewhere, but actually sends a packet
58 # works elsewhere, but actually sends a packet
59 try:
59 try:
60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
61 s.connect(('1.0.0.1', 1))
61 s.connect(('1.0.0.1', 1))
62 ip = s.getsockname()[0]
62 ip = s.getsockname()[0]
63 return ip
63 return ip
64 except:
64 except:
65 pass
65 pass
66
66
67 return dumbip
67 return dumbip
68
68
69 def publish(name, desc, path, port):
69 def publish(name, desc, path, port):
70 global server, localip
70 global server, localip
71 if not server:
71 if not server:
72 ip = getip()
72 ip = getip()
73 if ip.startswith('127.'):
73 if ip.startswith('127.'):
74 # if we have no internet connection, this can happen.
74 # if we have no internet connection, this can happen.
75 return
75 return
76 localip = socket.inet_aton(ip)
76 localip = socket.inet_aton(ip)
77 server = Zeroconf.Zeroconf(ip)
77 server = Zeroconf.Zeroconf(ip)
78
78
79 hostname = socket.gethostname().split('.')[0]
79 hostname = socket.gethostname().split('.')[0]
80 host = hostname + ".local"
80 host = hostname + ".local"
81 name = "%s-%s" % (hostname, name)
81 name = "%s-%s" % (hostname, name)
82
82
83 # advertise to browsers
83 # advertise to browsers
84 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
84 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
85 name + '._http._tcp.local.',
85 name + '._http._tcp.local.',
86 server = host,
86 server = host,
87 port = port,
87 port = port,
88 properties = {'description': desc,
88 properties = {'description': desc,
89 'path': "/" + path},
89 'path': "/" + path},
90 address = localip, weight = 0, priority = 0)
90 address = localip, weight = 0, priority = 0)
91 server.registerService(svc)
91 server.registerService(svc)
92
92
93 # advertise to Mercurial clients
93 # advertise to Mercurial clients
94 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
94 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
95 name + '._hg._tcp.local.',
95 name + '._hg._tcp.local.',
96 server = host,
96 server = host,
97 port = port,
97 port = port,
98 properties = {'description': desc,
98 properties = {'description': desc,
99 'path': "/" + path},
99 'path': "/" + path},
100 address = localip, weight = 0, priority = 0)
100 address = localip, weight = 0, priority = 0)
101 server.registerService(svc)
101 server.registerService(svc)
102
102
103 class hgwebzc(hgweb_mod.hgweb):
103 class hgwebzc(hgweb_mod.hgweb):
104 def __init__(self, repo, name=None, baseui=None):
104 def __init__(self, repo, name=None, baseui=None):
105 super(hgwebzc, self).__init__(repo, name=name, baseui=baseui)
105 super(hgwebzc, self).__init__(repo, name=name, baseui=baseui)
106 name = self.reponame or os.path.basename(self.repo.root)
106 name = self.reponame or os.path.basename(self.repo.root)
107 path = self.repo.ui.config("web", "prefix", "").strip('/')
107 path = self.repo.ui.config("web", "prefix", "").strip('/')
108 desc = self.repo.ui.config("web", "description", name)
108 desc = self.repo.ui.config("web", "description", name)
109 publish(name, desc, path,
109 publish(name, desc, path,
110 int(self.repo.ui.config("web", "port", 8000)))
110 util.getport(self.repo.ui.config("web", "port", 8000)))
111
111
112 class hgwebdirzc(hgwebdir_mod.hgwebdir):
112 class hgwebdirzc(hgwebdir_mod.hgwebdir):
113 def __init__(self, conf, baseui=None):
113 def __init__(self, conf, baseui=None):
114 super(hgwebdirzc, self).__init__(conf, baseui=baseui)
114 super(hgwebdirzc, self).__init__(conf, baseui=baseui)
115 prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
115 prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
116 for repo, path in self.repos:
116 for repo, path in self.repos:
117 u = self.ui.copy()
117 u = self.ui.copy()
118 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
118 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
119 name = os.path.basename(repo)
119 name = os.path.basename(repo)
120 path = (prefix + repo).strip('/')
120 path = (prefix + repo).strip('/')
121 desc = u.config('web', 'description', name)
121 desc = u.config('web', 'description', name)
122 publish(name, desc, path, int(u.config("web", "port", 8000)))
122 publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
123
123
124 # listen
124 # listen
125
125
126 class listener(object):
126 class listener(object):
127 def __init__(self):
127 def __init__(self):
128 self.found = {}
128 self.found = {}
129 def removeService(self, server, type, name):
129 def removeService(self, server, type, name):
130 if repr(name) in self.found:
130 if repr(name) in self.found:
131 del self.found[repr(name)]
131 del self.found[repr(name)]
132 def addService(self, server, type, name):
132 def addService(self, server, type, name):
133 self.found[repr(name)] = server.getServiceInfo(type, name)
133 self.found[repr(name)] = server.getServiceInfo(type, name)
134
134
135 def getzcpaths():
135 def getzcpaths():
136 ip = getip()
136 ip = getip()
137 if ip.startswith('127.'):
137 if ip.startswith('127.'):
138 return
138 return
139 server = Zeroconf.Zeroconf(ip)
139 server = Zeroconf.Zeroconf(ip)
140 l = listener()
140 l = listener()
141 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
141 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
142 time.sleep(1)
142 time.sleep(1)
143 server.close()
143 server.close()
144 for value in l.found.values():
144 for value in l.found.values():
145 name = value.name[:value.name.index('.')]
145 name = value.name[:value.name.index('.')]
146 url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
146 url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
147 value.properties.get("path", "/"))
147 value.properties.get("path", "/"))
148 yield "zc-" + name, url
148 yield "zc-" + name, url
149
149
150 def config(orig, self, section, key, default=None, untrusted=False):
150 def config(orig, self, section, key, default=None, untrusted=False):
151 if section == "paths" and key.startswith("zc-"):
151 if section == "paths" and key.startswith("zc-"):
152 for name, path in getzcpaths():
152 for name, path in getzcpaths():
153 if name == key:
153 if name == key:
154 return path
154 return path
155 return orig(self, section, key, default, untrusted)
155 return orig(self, section, key, default, untrusted)
156
156
157 def configitems(orig, self, section, untrusted=False):
157 def configitems(orig, self, section, untrusted=False):
158 repos = orig(self, section, untrusted)
158 repos = orig(self, section, untrusted)
159 if section == "paths":
159 if section == "paths":
160 repos += getzcpaths()
160 repos += getzcpaths()
161 return repos
161 return repos
162
162
163 def defaultdest(orig, source):
163 def defaultdest(orig, source):
164 for name, path in getzcpaths():
164 for name, path in getzcpaths():
165 if path == source:
165 if path == source:
166 return name.encode(encoding.encoding)
166 return name.encode(encoding.encoding)
167 return orig(source)
167 return orig(source)
168
168
169 extensions.wrapfunction(ui.ui, 'config', config)
169 extensions.wrapfunction(ui.ui, 'config', config)
170 extensions.wrapfunction(ui.ui, 'configitems', configitems)
170 extensions.wrapfunction(ui.ui, 'configitems', configitems)
171 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
171 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
172 hgweb_mod.hgweb = hgwebzc
172 hgweb_mod.hgweb = hgwebzc
173 hgwebdir_mod.hgwebdir = hgwebdirzc
173 hgwebdir_mod.hgwebdir = hgwebdirzc
@@ -1,4476 +1,4476 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset
16 import minirst, revset
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 bad = []
49 bad = []
50 names = []
50 names = []
51 m = cmdutil.match(repo, pats, opts)
51 m = cmdutil.match(repo, pats, opts)
52 oldbad = m.bad
52 oldbad = m.bad
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
53 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
54
54
55 for f in repo.walk(m):
55 for f in repo.walk(m):
56 exact = m.exact(f)
56 exact = m.exact(f)
57 if exact or f not in repo.dirstate:
57 if exact or f not in repo.dirstate:
58 names.append(f)
58 names.append(f)
59 if ui.verbose or not exact:
59 if ui.verbose or not exact:
60 ui.status(_('adding %s\n') % m.rel(f))
60 ui.status(_('adding %s\n') % m.rel(f))
61 if not opts.get('dry_run'):
61 if not opts.get('dry_run'):
62 bad += [f for f in repo[None].add(names) if f in m.files()]
62 bad += [f for f in repo[None].add(names) if f in m.files()]
63 return bad and 1 or 0
63 return bad and 1 or 0
64
64
65 def addremove(ui, repo, *pats, **opts):
65 def addremove(ui, repo, *pats, **opts):
66 """add all new files, delete all missing files
66 """add all new files, delete all missing files
67
67
68 Add all new files and remove all missing files from the
68 Add all new files and remove all missing files from the
69 repository.
69 repository.
70
70
71 New files are ignored if they match any of the patterns in
71 New files are ignored if they match any of the patterns in
72 .hgignore. As with add, these changes take effect at the next
72 .hgignore. As with add, these changes take effect at the next
73 commit.
73 commit.
74
74
75 Use the -s/--similarity option to detect renamed files. With a
75 Use the -s/--similarity option to detect renamed files. With a
76 parameter greater than 0, this compares every removed file with
76 parameter greater than 0, this compares every removed file with
77 every added file and records those similar enough as renames. This
77 every added file and records those similar enough as renames. This
78 option takes a percentage between 0 (disabled) and 100 (files must
78 option takes a percentage between 0 (disabled) and 100 (files must
79 be identical) as its parameter. Detecting renamed files this way
79 be identical) as its parameter. Detecting renamed files this way
80 can be expensive. After using this option, :hg:`status -C` can be
80 can be expensive. After using this option, :hg:`status -C` can be
81 used to check which files were identified as moved or renamed.
81 used to check which files were identified as moved or renamed.
82
82
83 Returns 0 if all files are successfully added.
83 Returns 0 if all files are successfully added.
84 """
84 """
85 try:
85 try:
86 sim = float(opts.get('similarity') or 100)
86 sim = float(opts.get('similarity') or 100)
87 except ValueError:
87 except ValueError:
88 raise util.Abort(_('similarity must be a number'))
88 raise util.Abort(_('similarity must be a number'))
89 if sim < 0 or sim > 100:
89 if sim < 0 or sim > 100:
90 raise util.Abort(_('similarity must be between 0 and 100'))
90 raise util.Abort(_('similarity must be between 0 and 100'))
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
91 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
92
92
93 def annotate(ui, repo, *pats, **opts):
93 def annotate(ui, repo, *pats, **opts):
94 """show changeset information by line for each file
94 """show changeset information by line for each file
95
95
96 List changes in files, showing the revision id responsible for
96 List changes in files, showing the revision id responsible for
97 each line
97 each line
98
98
99 This command is useful for discovering when a change was made and
99 This command is useful for discovering when a change was made and
100 by whom.
100 by whom.
101
101
102 Without the -a/--text option, annotate will avoid processing files
102 Without the -a/--text option, annotate will avoid processing files
103 it detects as binary. With -a, annotate will annotate the file
103 it detects as binary. With -a, annotate will annotate the file
104 anyway, although the results will probably be neither useful
104 anyway, although the results will probably be neither useful
105 nor desirable.
105 nor desirable.
106
106
107 Returns 0 on success.
107 Returns 0 on success.
108 """
108 """
109 if opts.get('follow'):
109 if opts.get('follow'):
110 # --follow is deprecated and now just an alias for -f/--file
110 # --follow is deprecated and now just an alias for -f/--file
111 # to mimic the behavior of Mercurial before version 1.5
111 # to mimic the behavior of Mercurial before version 1.5
112 opts['file'] = 1
112 opts['file'] = 1
113
113
114 datefunc = ui.quiet and util.shortdate or util.datestr
114 datefunc = ui.quiet and util.shortdate or util.datestr
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
115 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
116
116
117 if not pats:
117 if not pats:
118 raise util.Abort(_('at least one filename or pattern is required'))
118 raise util.Abort(_('at least one filename or pattern is required'))
119
119
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
120 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
121 ('number', lambda x: str(x[0].rev())),
121 ('number', lambda x: str(x[0].rev())),
122 ('changeset', lambda x: short(x[0].node())),
122 ('changeset', lambda x: short(x[0].node())),
123 ('date', getdate),
123 ('date', getdate),
124 ('file', lambda x: x[0].path()),
124 ('file', lambda x: x[0].path()),
125 ]
125 ]
126
126
127 if (not opts.get('user') and not opts.get('changeset')
127 if (not opts.get('user') and not opts.get('changeset')
128 and not opts.get('date') and not opts.get('file')):
128 and not opts.get('date') and not opts.get('file')):
129 opts['number'] = 1
129 opts['number'] = 1
130
130
131 linenumber = opts.get('line_number') is not None
131 linenumber = opts.get('line_number') is not None
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
132 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
133 raise util.Abort(_('at least one of -n/-c is required for -l'))
134
134
135 funcmap = [func for op, func in opmap if opts.get(op)]
135 funcmap = [func for op, func in opmap if opts.get(op)]
136 if linenumber:
136 if linenumber:
137 lastfunc = funcmap[-1]
137 lastfunc = funcmap[-1]
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
138 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
139
139
140 ctx = repo[opts.get('rev')]
140 ctx = repo[opts.get('rev')]
141 m = cmdutil.match(repo, pats, opts)
141 m = cmdutil.match(repo, pats, opts)
142 follow = not opts.get('no_follow')
142 follow = not opts.get('no_follow')
143 for abs in ctx.walk(m):
143 for abs in ctx.walk(m):
144 fctx = ctx[abs]
144 fctx = ctx[abs]
145 if not opts.get('text') and util.binary(fctx.data()):
145 if not opts.get('text') and util.binary(fctx.data()):
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
146 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
147 continue
147 continue
148
148
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
149 lines = fctx.annotate(follow=follow, linenumber=linenumber)
150 pieces = []
150 pieces = []
151
151
152 for f in funcmap:
152 for f in funcmap:
153 l = [f(n) for n, dummy in lines]
153 l = [f(n) for n, dummy in lines]
154 if l:
154 if l:
155 sized = [(x, encoding.colwidth(x)) for x in l]
155 sized = [(x, encoding.colwidth(x)) for x in l]
156 ml = max([w for x, w in sized])
156 ml = max([w for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
157 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
158
158
159 if pieces:
159 if pieces:
160 for p, l in zip(zip(*pieces), lines):
160 for p, l in zip(zip(*pieces), lines):
161 ui.write("%s: %s" % (" ".join(p), l[1]))
161 ui.write("%s: %s" % (" ".join(p), l[1]))
162
162
163 def archive(ui, repo, dest, **opts):
163 def archive(ui, repo, dest, **opts):
164 '''create an unversioned archive of a repository revision
164 '''create an unversioned archive of a repository revision
165
165
166 By default, the revision used is the parent of the working
166 By default, the revision used is the parent of the working
167 directory; use -r/--rev to specify a different revision.
167 directory; use -r/--rev to specify a different revision.
168
168
169 The archive type is automatically detected based on file
169 The archive type is automatically detected based on file
170 extension (or override using -t/--type).
170 extension (or override using -t/--type).
171
171
172 Valid types are:
172 Valid types are:
173
173
174 :``files``: a directory full of files (default)
174 :``files``: a directory full of files (default)
175 :``tar``: tar archive, uncompressed
175 :``tar``: tar archive, uncompressed
176 :``tbz2``: tar archive, compressed using bzip2
176 :``tbz2``: tar archive, compressed using bzip2
177 :``tgz``: tar archive, compressed using gzip
177 :``tgz``: tar archive, compressed using gzip
178 :``uzip``: zip archive, uncompressed
178 :``uzip``: zip archive, uncompressed
179 :``zip``: zip archive, compressed using deflate
179 :``zip``: zip archive, compressed using deflate
180
180
181 The exact name of the destination archive or directory is given
181 The exact name of the destination archive or directory is given
182 using a format string; see :hg:`help export` for details.
182 using a format string; see :hg:`help export` for details.
183
183
184 Each member added to an archive file has a directory prefix
184 Each member added to an archive file has a directory prefix
185 prepended. Use -p/--prefix to specify a format string for the
185 prepended. Use -p/--prefix to specify a format string for the
186 prefix. The default is the basename of the archive, with suffixes
186 prefix. The default is the basename of the archive, with suffixes
187 removed.
187 removed.
188
188
189 Returns 0 on success.
189 Returns 0 on success.
190 '''
190 '''
191
191
192 ctx = repo[opts.get('rev')]
192 ctx = repo[opts.get('rev')]
193 if not ctx:
193 if not ctx:
194 raise util.Abort(_('no working directory: please specify a revision'))
194 raise util.Abort(_('no working directory: please specify a revision'))
195 node = ctx.node()
195 node = ctx.node()
196 dest = cmdutil.make_filename(repo, dest, node)
196 dest = cmdutil.make_filename(repo, dest, node)
197 if os.path.realpath(dest) == repo.root:
197 if os.path.realpath(dest) == repo.root:
198 raise util.Abort(_('repository root cannot be destination'))
198 raise util.Abort(_('repository root cannot be destination'))
199
199
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
200 kind = opts.get('type') or archival.guesskind(dest) or 'files'
201 prefix = opts.get('prefix')
201 prefix = opts.get('prefix')
202
202
203 if dest == '-':
203 if dest == '-':
204 if kind == 'files':
204 if kind == 'files':
205 raise util.Abort(_('cannot archive plain files to stdout'))
205 raise util.Abort(_('cannot archive plain files to stdout'))
206 dest = sys.stdout
206 dest = sys.stdout
207 if not prefix:
207 if not prefix:
208 prefix = os.path.basename(repo.root) + '-%h'
208 prefix = os.path.basename(repo.root) + '-%h'
209
209
210 prefix = cmdutil.make_filename(repo, prefix, node)
210 prefix = cmdutil.make_filename(repo, prefix, node)
211 matchfn = cmdutil.match(repo, [], opts)
211 matchfn = cmdutil.match(repo, [], opts)
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
212 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
213 matchfn, prefix)
213 matchfn, prefix)
214
214
215 def backout(ui, repo, node=None, rev=None, **opts):
215 def backout(ui, repo, node=None, rev=None, **opts):
216 '''reverse effect of earlier changeset
216 '''reverse effect of earlier changeset
217
217
218 Commit the backed out changes as a new changeset. The new
218 Commit the backed out changes as a new changeset. The new
219 changeset is a child of the backed out changeset.
219 changeset is a child of the backed out changeset.
220
220
221 If you backout a changeset other than the tip, a new head is
221 If you backout a changeset other than the tip, a new head is
222 created. This head will be the new tip and you should merge this
222 created. This head will be the new tip and you should merge this
223 backout changeset with another head.
223 backout changeset with another head.
224
224
225 The --merge option remembers the parent of the working directory
225 The --merge option remembers the parent of the working directory
226 before starting the backout, then merges the new head with that
226 before starting the backout, then merges the new head with that
227 changeset afterwards. This saves you from doing the merge by hand.
227 changeset afterwards. This saves you from doing the merge by hand.
228 The result of this merge is not committed, as with a normal merge.
228 The result of this merge is not committed, as with a normal merge.
229
229
230 See :hg:`help dates` for a list of formats valid for -d/--date.
230 See :hg:`help dates` for a list of formats valid for -d/--date.
231
231
232 Returns 0 on success.
232 Returns 0 on success.
233 '''
233 '''
234 if rev and node:
234 if rev and node:
235 raise util.Abort(_("please specify just one revision"))
235 raise util.Abort(_("please specify just one revision"))
236
236
237 if not rev:
237 if not rev:
238 rev = node
238 rev = node
239
239
240 if not rev:
240 if not rev:
241 raise util.Abort(_("please specify a revision to backout"))
241 raise util.Abort(_("please specify a revision to backout"))
242
242
243 date = opts.get('date')
243 date = opts.get('date')
244 if date:
244 if date:
245 opts['date'] = util.parsedate(date)
245 opts['date'] = util.parsedate(date)
246
246
247 cmdutil.bail_if_changed(repo)
247 cmdutil.bail_if_changed(repo)
248 node = repo.lookup(rev)
248 node = repo.lookup(rev)
249
249
250 op1, op2 = repo.dirstate.parents()
250 op1, op2 = repo.dirstate.parents()
251 a = repo.changelog.ancestor(op1, node)
251 a = repo.changelog.ancestor(op1, node)
252 if a != node:
252 if a != node:
253 raise util.Abort(_('cannot backout change on a different branch'))
253 raise util.Abort(_('cannot backout change on a different branch'))
254
254
255 p1, p2 = repo.changelog.parents(node)
255 p1, p2 = repo.changelog.parents(node)
256 if p1 == nullid:
256 if p1 == nullid:
257 raise util.Abort(_('cannot backout a change with no parents'))
257 raise util.Abort(_('cannot backout a change with no parents'))
258 if p2 != nullid:
258 if p2 != nullid:
259 if not opts.get('parent'):
259 if not opts.get('parent'):
260 raise util.Abort(_('cannot backout a merge changeset without '
260 raise util.Abort(_('cannot backout a merge changeset without '
261 '--parent'))
261 '--parent'))
262 p = repo.lookup(opts['parent'])
262 p = repo.lookup(opts['parent'])
263 if p not in (p1, p2):
263 if p not in (p1, p2):
264 raise util.Abort(_('%s is not a parent of %s') %
264 raise util.Abort(_('%s is not a parent of %s') %
265 (short(p), short(node)))
265 (short(p), short(node)))
266 parent = p
266 parent = p
267 else:
267 else:
268 if opts.get('parent'):
268 if opts.get('parent'):
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 parent = p1
270 parent = p1
271
271
272 # the backout should appear on the same branch
272 # the backout should appear on the same branch
273 branch = repo.dirstate.branch()
273 branch = repo.dirstate.branch()
274 hg.clean(repo, node, show_stats=False)
274 hg.clean(repo, node, show_stats=False)
275 repo.dirstate.setbranch(branch)
275 repo.dirstate.setbranch(branch)
276 revert_opts = opts.copy()
276 revert_opts = opts.copy()
277 revert_opts['date'] = None
277 revert_opts['date'] = None
278 revert_opts['all'] = True
278 revert_opts['all'] = True
279 revert_opts['rev'] = hex(parent)
279 revert_opts['rev'] = hex(parent)
280 revert_opts['no_backup'] = None
280 revert_opts['no_backup'] = None
281 revert(ui, repo, **revert_opts)
281 revert(ui, repo, **revert_opts)
282 commit_opts = opts.copy()
282 commit_opts = opts.copy()
283 commit_opts['addremove'] = False
283 commit_opts['addremove'] = False
284 if not commit_opts['message'] and not commit_opts['logfile']:
284 if not commit_opts['message'] and not commit_opts['logfile']:
285 # we don't translate commit messages
285 # we don't translate commit messages
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['force_editor'] = True
287 commit_opts['force_editor'] = True
288 commit(ui, repo, **commit_opts)
288 commit(ui, repo, **commit_opts)
289 def nice(node):
289 def nice(node):
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 ui.status(_('changeset %s backs out changeset %s\n') %
291 ui.status(_('changeset %s backs out changeset %s\n') %
292 (nice(repo.changelog.tip()), nice(node)))
292 (nice(repo.changelog.tip()), nice(node)))
293 if op1 != node:
293 if op1 != node:
294 hg.clean(repo, op1, show_stats=False)
294 hg.clean(repo, op1, show_stats=False)
295 if opts.get('merge'):
295 if opts.get('merge'):
296 ui.status(_('merging with changeset %s\n')
296 ui.status(_('merging with changeset %s\n')
297 % nice(repo.changelog.tip()))
297 % nice(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
299 else:
299 else:
300 ui.status(_('the backout changeset is a new head - '
300 ui.status(_('the backout changeset is a new head - '
301 'do not forget to merge\n'))
301 'do not forget to merge\n'))
302 ui.status(_('(use "backout --merge" '
302 ui.status(_('(use "backout --merge" '
303 'if you want to auto-merge)\n'))
303 'if you want to auto-merge)\n'))
304
304
305 def bisect(ui, repo, rev=None, extra=None, command=None,
305 def bisect(ui, repo, rev=None, extra=None, command=None,
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 """subdivision search of changesets
307 """subdivision search of changesets
308
308
309 This command helps to find changesets which introduce problems. To
309 This command helps to find changesets which introduce problems. To
310 use, mark the earliest changeset you know exhibits the problem as
310 use, mark the earliest changeset you know exhibits the problem as
311 bad, then mark the latest changeset which is free from the problem
311 bad, then mark the latest changeset which is free from the problem
312 as good. Bisect will update your working directory to a revision
312 as good. Bisect will update your working directory to a revision
313 for testing (unless the -U/--noupdate option is specified). Once
313 for testing (unless the -U/--noupdate option is specified). Once
314 you have performed tests, mark the working directory as good or
314 you have performed tests, mark the working directory as good or
315 bad, and bisect will either update to another candidate changeset
315 bad, and bisect will either update to another candidate changeset
316 or announce that it has found the bad revision.
316 or announce that it has found the bad revision.
317
317
318 As a shortcut, you can also use the revision argument to mark a
318 As a shortcut, you can also use the revision argument to mark a
319 revision as good or bad without checking it out first.
319 revision as good or bad without checking it out first.
320
320
321 If you supply a command, it will be used for automatic bisection.
321 If you supply a command, it will be used for automatic bisection.
322 Its exit status will be used to mark revisions as good or bad:
322 Its exit status will be used to mark revisions as good or bad:
323 status 0 means good, 125 means to skip the revision, 127
323 status 0 means good, 125 means to skip the revision, 127
324 (command not found) will abort the bisection, and any other
324 (command not found) will abort the bisection, and any other
325 non-zero exit status means the revision is bad.
325 non-zero exit status means the revision is bad.
326
326
327 Returns 0 on success.
327 Returns 0 on success.
328 """
328 """
329 def print_result(nodes, good):
329 def print_result(nodes, good):
330 displayer = cmdutil.show_changeset(ui, repo, {})
330 displayer = cmdutil.show_changeset(ui, repo, {})
331 if len(nodes) == 1:
331 if len(nodes) == 1:
332 # narrowed it down to a single revision
332 # narrowed it down to a single revision
333 if good:
333 if good:
334 ui.write(_("The first good revision is:\n"))
334 ui.write(_("The first good revision is:\n"))
335 else:
335 else:
336 ui.write(_("The first bad revision is:\n"))
336 ui.write(_("The first bad revision is:\n"))
337 displayer.show(repo[nodes[0]])
337 displayer.show(repo[nodes[0]])
338 else:
338 else:
339 # multiple possible revisions
339 # multiple possible revisions
340 if good:
340 if good:
341 ui.write(_("Due to skipped revisions, the first "
341 ui.write(_("Due to skipped revisions, the first "
342 "good revision could be any of:\n"))
342 "good revision could be any of:\n"))
343 else:
343 else:
344 ui.write(_("Due to skipped revisions, the first "
344 ui.write(_("Due to skipped revisions, the first "
345 "bad revision could be any of:\n"))
345 "bad revision could be any of:\n"))
346 for n in nodes:
346 for n in nodes:
347 displayer.show(repo[n])
347 displayer.show(repo[n])
348 displayer.close()
348 displayer.close()
349
349
350 def check_state(state, interactive=True):
350 def check_state(state, interactive=True):
351 if not state['good'] or not state['bad']:
351 if not state['good'] or not state['bad']:
352 if (good or bad or skip or reset) and interactive:
352 if (good or bad or skip or reset) and interactive:
353 return
353 return
354 if not state['good']:
354 if not state['good']:
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
355 raise util.Abort(_('cannot bisect (no known good revisions)'))
356 else:
356 else:
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
357 raise util.Abort(_('cannot bisect (no known bad revisions)'))
358 return True
358 return True
359
359
360 # backward compatibility
360 # backward compatibility
361 if rev in "good bad reset init".split():
361 if rev in "good bad reset init".split():
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
362 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
363 cmd, rev, extra = rev, extra, None
363 cmd, rev, extra = rev, extra, None
364 if cmd == "good":
364 if cmd == "good":
365 good = True
365 good = True
366 elif cmd == "bad":
366 elif cmd == "bad":
367 bad = True
367 bad = True
368 else:
368 else:
369 reset = True
369 reset = True
370 elif extra or good + bad + skip + reset + bool(command) > 1:
370 elif extra or good + bad + skip + reset + bool(command) > 1:
371 raise util.Abort(_('incompatible arguments'))
371 raise util.Abort(_('incompatible arguments'))
372
372
373 if reset:
373 if reset:
374 p = repo.join("bisect.state")
374 p = repo.join("bisect.state")
375 if os.path.exists(p):
375 if os.path.exists(p):
376 os.unlink(p)
376 os.unlink(p)
377 return
377 return
378
378
379 state = hbisect.load_state(repo)
379 state = hbisect.load_state(repo)
380
380
381 if command:
381 if command:
382 changesets = 1
382 changesets = 1
383 try:
383 try:
384 while changesets:
384 while changesets:
385 # update state
385 # update state
386 status = util.system(command)
386 status = util.system(command)
387 if status == 125:
387 if status == 125:
388 transition = "skip"
388 transition = "skip"
389 elif status == 0:
389 elif status == 0:
390 transition = "good"
390 transition = "good"
391 # status < 0 means process was killed
391 # status < 0 means process was killed
392 elif status == 127:
392 elif status == 127:
393 raise util.Abort(_("failed to execute %s") % command)
393 raise util.Abort(_("failed to execute %s") % command)
394 elif status < 0:
394 elif status < 0:
395 raise util.Abort(_("%s killed") % command)
395 raise util.Abort(_("%s killed") % command)
396 else:
396 else:
397 transition = "bad"
397 transition = "bad"
398 ctx = repo[rev or '.']
398 ctx = repo[rev or '.']
399 state[transition].append(ctx.node())
399 state[transition].append(ctx.node())
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
400 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
401 check_state(state, interactive=False)
401 check_state(state, interactive=False)
402 # bisect
402 # bisect
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
403 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
404 # update to next check
404 # update to next check
405 cmdutil.bail_if_changed(repo)
405 cmdutil.bail_if_changed(repo)
406 hg.clean(repo, nodes[0], show_stats=False)
406 hg.clean(repo, nodes[0], show_stats=False)
407 finally:
407 finally:
408 hbisect.save_state(repo, state)
408 hbisect.save_state(repo, state)
409 print_result(nodes, good)
409 print_result(nodes, good)
410 return
410 return
411
411
412 # update state
412 # update state
413 node = repo.lookup(rev or '.')
413 node = repo.lookup(rev or '.')
414 if good or bad or skip:
414 if good or bad or skip:
415 if good:
415 if good:
416 state['good'].append(node)
416 state['good'].append(node)
417 elif bad:
417 elif bad:
418 state['bad'].append(node)
418 state['bad'].append(node)
419 elif skip:
419 elif skip:
420 state['skip'].append(node)
420 state['skip'].append(node)
421 hbisect.save_state(repo, state)
421 hbisect.save_state(repo, state)
422
422
423 if not check_state(state):
423 if not check_state(state):
424 return
424 return
425
425
426 # actually bisect
426 # actually bisect
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 if changesets == 0:
428 if changesets == 0:
429 print_result(nodes, good)
429 print_result(nodes, good)
430 else:
430 else:
431 assert len(nodes) == 1 # only a single node can be tested next
431 assert len(nodes) == 1 # only a single node can be tested next
432 node = nodes[0]
432 node = nodes[0]
433 # compute the approximate number of remaining tests
433 # compute the approximate number of remaining tests
434 tests, size = 0, 2
434 tests, size = 0, 2
435 while size <= changesets:
435 while size <= changesets:
436 tests, size = tests + 1, size * 2
436 tests, size = tests + 1, size * 2
437 rev = repo.changelog.rev(node)
437 rev = repo.changelog.rev(node)
438 ui.write(_("Testing changeset %d:%s "
438 ui.write(_("Testing changeset %d:%s "
439 "(%d changesets remaining, ~%d tests)\n")
439 "(%d changesets remaining, ~%d tests)\n")
440 % (rev, short(node), changesets, tests))
440 % (rev, short(node), changesets, tests))
441 if not noupdate:
441 if not noupdate:
442 cmdutil.bail_if_changed(repo)
442 cmdutil.bail_if_changed(repo)
443 return hg.clean(repo, node)
443 return hg.clean(repo, node)
444
444
445 def branch(ui, repo, label=None, **opts):
445 def branch(ui, repo, label=None, **opts):
446 """set or show the current branch name
446 """set or show the current branch name
447
447
448 With no argument, show the current branch name. With one argument,
448 With no argument, show the current branch name. With one argument,
449 set the working directory branch name (the branch will not exist
449 set the working directory branch name (the branch will not exist
450 in the repository until the next commit). Standard practice
450 in the repository until the next commit). Standard practice
451 recommends that primary development take place on the 'default'
451 recommends that primary development take place on the 'default'
452 branch.
452 branch.
453
453
454 Unless -f/--force is specified, branch will not let you set a
454 Unless -f/--force is specified, branch will not let you set a
455 branch name that already exists, even if it's inactive.
455 branch name that already exists, even if it's inactive.
456
456
457 Use -C/--clean to reset the working directory branch to that of
457 Use -C/--clean to reset the working directory branch to that of
458 the parent of the working directory, negating a previous branch
458 the parent of the working directory, negating a previous branch
459 change.
459 change.
460
460
461 Use the command :hg:`update` to switch to an existing branch. Use
461 Use the command :hg:`update` to switch to an existing branch. Use
462 :hg:`commit --close-branch` to mark this branch as closed.
462 :hg:`commit --close-branch` to mark this branch as closed.
463
463
464 Returns 0 on success.
464 Returns 0 on success.
465 """
465 """
466
466
467 if opts.get('clean'):
467 if opts.get('clean'):
468 label = repo[None].parents()[0].branch()
468 label = repo[None].parents()[0].branch()
469 repo.dirstate.setbranch(label)
469 repo.dirstate.setbranch(label)
470 ui.status(_('reset working directory to branch %s\n') % label)
470 ui.status(_('reset working directory to branch %s\n') % label)
471 elif label:
471 elif label:
472 utflabel = encoding.fromlocal(label)
472 utflabel = encoding.fromlocal(label)
473 if not opts.get('force') and utflabel in repo.branchtags():
473 if not opts.get('force') and utflabel in repo.branchtags():
474 if label not in [p.branch() for p in repo.parents()]:
474 if label not in [p.branch() for p in repo.parents()]:
475 raise util.Abort(_('a branch of the same name already exists'
475 raise util.Abort(_('a branch of the same name already exists'
476 " (use 'hg update' to switch to it)"))
476 " (use 'hg update' to switch to it)"))
477 repo.dirstate.setbranch(utflabel)
477 repo.dirstate.setbranch(utflabel)
478 ui.status(_('marked working directory as branch %s\n') % label)
478 ui.status(_('marked working directory as branch %s\n') % label)
479 else:
479 else:
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
480 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
481
481
482 def branches(ui, repo, active=False, closed=False):
482 def branches(ui, repo, active=False, closed=False):
483 """list repository named branches
483 """list repository named branches
484
484
485 List the repository's named branches, indicating which ones are
485 List the repository's named branches, indicating which ones are
486 inactive. If -c/--closed is specified, also list branches which have
486 inactive. If -c/--closed is specified, also list branches which have
487 been marked closed (see :hg:`commit --close-branch`).
487 been marked closed (see :hg:`commit --close-branch`).
488
488
489 If -a/--active is specified, only show active branches. A branch
489 If -a/--active is specified, only show active branches. A branch
490 is considered active if it contains repository heads.
490 is considered active if it contains repository heads.
491
491
492 Use the command :hg:`update` to switch to an existing branch.
492 Use the command :hg:`update` to switch to an existing branch.
493
493
494 Returns 0.
494 Returns 0.
495 """
495 """
496
496
497 hexfunc = ui.debugflag and hex or short
497 hexfunc = ui.debugflag and hex or short
498 activebranches = [repo[n].branch() for n in repo.heads()]
498 activebranches = [repo[n].branch() for n in repo.heads()]
499 def testactive(tag, node):
499 def testactive(tag, node):
500 realhead = tag in activebranches
500 realhead = tag in activebranches
501 open = node in repo.branchheads(tag, closed=False)
501 open = node in repo.branchheads(tag, closed=False)
502 return realhead and open
502 return realhead and open
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
503 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
504 for tag, node in repo.branchtags().items()],
504 for tag, node in repo.branchtags().items()],
505 reverse=True)
505 reverse=True)
506
506
507 for isactive, node, tag in branches:
507 for isactive, node, tag in branches:
508 if (not active) or isactive:
508 if (not active) or isactive:
509 encodedtag = encoding.tolocal(tag)
509 encodedtag = encoding.tolocal(tag)
510 if ui.quiet:
510 if ui.quiet:
511 ui.write("%s\n" % encodedtag)
511 ui.write("%s\n" % encodedtag)
512 else:
512 else:
513 hn = repo.lookup(node)
513 hn = repo.lookup(node)
514 if isactive:
514 if isactive:
515 label = 'branches.active'
515 label = 'branches.active'
516 notice = ''
516 notice = ''
517 elif hn not in repo.branchheads(tag, closed=False):
517 elif hn not in repo.branchheads(tag, closed=False):
518 if not closed:
518 if not closed:
519 continue
519 continue
520 label = 'branches.closed'
520 label = 'branches.closed'
521 notice = _(' (closed)')
521 notice = _(' (closed)')
522 else:
522 else:
523 label = 'branches.inactive'
523 label = 'branches.inactive'
524 notice = _(' (inactive)')
524 notice = _(' (inactive)')
525 if tag == repo.dirstate.branch():
525 if tag == repo.dirstate.branch():
526 label = 'branches.current'
526 label = 'branches.current'
527 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
527 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
528 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
528 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
529 encodedtag = ui.label(encodedtag, label)
529 encodedtag = ui.label(encodedtag, label)
530 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
530 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
531
531
532 def bundle(ui, repo, fname, dest=None, **opts):
532 def bundle(ui, repo, fname, dest=None, **opts):
533 """create a changegroup file
533 """create a changegroup file
534
534
535 Generate a compressed changegroup file collecting changesets not
535 Generate a compressed changegroup file collecting changesets not
536 known to be in another repository.
536 known to be in another repository.
537
537
538 If you omit the destination repository, then hg assumes the
538 If you omit the destination repository, then hg assumes the
539 destination will have all the nodes you specify with --base
539 destination will have all the nodes you specify with --base
540 parameters. To create a bundle containing all changesets, use
540 parameters. To create a bundle containing all changesets, use
541 -a/--all (or --base null).
541 -a/--all (or --base null).
542
542
543 You can change compression method with the -t/--type option.
543 You can change compression method with the -t/--type option.
544 The available compression methods are: none, bzip2, and
544 The available compression methods are: none, bzip2, and
545 gzip (by default, bundles are compressed using bzip2).
545 gzip (by default, bundles are compressed using bzip2).
546
546
547 The bundle file can then be transferred using conventional means
547 The bundle file can then be transferred using conventional means
548 and applied to another repository with the unbundle or pull
548 and applied to another repository with the unbundle or pull
549 command. This is useful when direct push and pull are not
549 command. This is useful when direct push and pull are not
550 available or when exporting an entire repository is undesirable.
550 available or when exporting an entire repository is undesirable.
551
551
552 Applying bundles preserves all changeset contents including
552 Applying bundles preserves all changeset contents including
553 permissions, copy/rename information, and revision history.
553 permissions, copy/rename information, and revision history.
554
554
555 Returns 0 on success, 1 if no changes found.
555 Returns 0 on success, 1 if no changes found.
556 """
556 """
557 revs = opts.get('rev') or None
557 revs = opts.get('rev') or None
558 if opts.get('all'):
558 if opts.get('all'):
559 base = ['null']
559 base = ['null']
560 else:
560 else:
561 base = opts.get('base')
561 base = opts.get('base')
562 if base:
562 if base:
563 if dest:
563 if dest:
564 raise util.Abort(_("--base is incompatible with specifying "
564 raise util.Abort(_("--base is incompatible with specifying "
565 "a destination"))
565 "a destination"))
566 base = [repo.lookup(rev) for rev in base]
566 base = [repo.lookup(rev) for rev in base]
567 # create the right base
567 # create the right base
568 # XXX: nodesbetween / changegroup* should be "fixed" instead
568 # XXX: nodesbetween / changegroup* should be "fixed" instead
569 o = []
569 o = []
570 has = set((nullid,))
570 has = set((nullid,))
571 for n in base:
571 for n in base:
572 has.update(repo.changelog.reachable(n))
572 has.update(repo.changelog.reachable(n))
573 if revs:
573 if revs:
574 revs = [repo.lookup(rev) for rev in revs]
574 revs = [repo.lookup(rev) for rev in revs]
575 visit = revs[:]
575 visit = revs[:]
576 has.difference_update(visit)
576 has.difference_update(visit)
577 else:
577 else:
578 visit = repo.changelog.heads()
578 visit = repo.changelog.heads()
579 seen = {}
579 seen = {}
580 while visit:
580 while visit:
581 n = visit.pop(0)
581 n = visit.pop(0)
582 parents = [p for p in repo.changelog.parents(n) if p not in has]
582 parents = [p for p in repo.changelog.parents(n) if p not in has]
583 if len(parents) == 0:
583 if len(parents) == 0:
584 if n not in has:
584 if n not in has:
585 o.append(n)
585 o.append(n)
586 else:
586 else:
587 for p in parents:
587 for p in parents:
588 if p not in seen:
588 if p not in seen:
589 seen[p] = 1
589 seen[p] = 1
590 visit.append(p)
590 visit.append(p)
591 else:
591 else:
592 dest = ui.expandpath(dest or 'default-push', dest or 'default')
592 dest = ui.expandpath(dest or 'default-push', dest or 'default')
593 dest, branches = hg.parseurl(dest, opts.get('branch'))
593 dest, branches = hg.parseurl(dest, opts.get('branch'))
594 other = hg.repository(hg.remoteui(repo, opts), dest)
594 other = hg.repository(hg.remoteui(repo, opts), dest)
595 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
595 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
596 if revs:
596 if revs:
597 revs = [repo.lookup(rev) for rev in revs]
597 revs = [repo.lookup(rev) for rev in revs]
598 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
598 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
599
599
600 if not o:
600 if not o:
601 ui.status(_("no changes found\n"))
601 ui.status(_("no changes found\n"))
602 return 1
602 return 1
603
603
604 if revs:
604 if revs:
605 cg = repo.changegroupsubset(o, revs, 'bundle')
605 cg = repo.changegroupsubset(o, revs, 'bundle')
606 else:
606 else:
607 cg = repo.changegroup(o, 'bundle')
607 cg = repo.changegroup(o, 'bundle')
608
608
609 bundletype = opts.get('type', 'bzip2').lower()
609 bundletype = opts.get('type', 'bzip2').lower()
610 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
610 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
611 bundletype = btypes.get(bundletype)
611 bundletype = btypes.get(bundletype)
612 if bundletype not in changegroup.bundletypes:
612 if bundletype not in changegroup.bundletypes:
613 raise util.Abort(_('unknown bundle type specified with --type'))
613 raise util.Abort(_('unknown bundle type specified with --type'))
614
614
615 changegroup.writebundle(cg, fname, bundletype)
615 changegroup.writebundle(cg, fname, bundletype)
616
616
617 def cat(ui, repo, file1, *pats, **opts):
617 def cat(ui, repo, file1, *pats, **opts):
618 """output the current or given revision of files
618 """output the current or given revision of files
619
619
620 Print the specified files as they were at the given revision. If
620 Print the specified files as they were at the given revision. If
621 no revision is given, the parent of the working directory is used,
621 no revision is given, the parent of the working directory is used,
622 or tip if no revision is checked out.
622 or tip if no revision is checked out.
623
623
624 Output may be to a file, in which case the name of the file is
624 Output may be to a file, in which case the name of the file is
625 given using a format string. The formatting rules are the same as
625 given using a format string. The formatting rules are the same as
626 for the export command, with the following additions:
626 for the export command, with the following additions:
627
627
628 :``%s``: basename of file being printed
628 :``%s``: basename of file being printed
629 :``%d``: dirname of file being printed, or '.' if in repository root
629 :``%d``: dirname of file being printed, or '.' if in repository root
630 :``%p``: root-relative path name of file being printed
630 :``%p``: root-relative path name of file being printed
631
631
632 Returns 0 on success.
632 Returns 0 on success.
633 """
633 """
634 ctx = repo[opts.get('rev')]
634 ctx = repo[opts.get('rev')]
635 err = 1
635 err = 1
636 m = cmdutil.match(repo, (file1,) + pats, opts)
636 m = cmdutil.match(repo, (file1,) + pats, opts)
637 for abs in ctx.walk(m):
637 for abs in ctx.walk(m):
638 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
638 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
639 data = ctx[abs].data()
639 data = ctx[abs].data()
640 if opts.get('decode'):
640 if opts.get('decode'):
641 data = repo.wwritedata(abs, data)
641 data = repo.wwritedata(abs, data)
642 fp.write(data)
642 fp.write(data)
643 err = 0
643 err = 0
644 return err
644 return err
645
645
646 def clone(ui, source, dest=None, **opts):
646 def clone(ui, source, dest=None, **opts):
647 """make a copy of an existing repository
647 """make a copy of an existing repository
648
648
649 Create a copy of an existing repository in a new directory.
649 Create a copy of an existing repository in a new directory.
650
650
651 If no destination directory name is specified, it defaults to the
651 If no destination directory name is specified, it defaults to the
652 basename of the source.
652 basename of the source.
653
653
654 The location of the source is added to the new repository's
654 The location of the source is added to the new repository's
655 .hg/hgrc file, as the default to be used for future pulls.
655 .hg/hgrc file, as the default to be used for future pulls.
656
656
657 See :hg:`help urls` for valid source format details.
657 See :hg:`help urls` for valid source format details.
658
658
659 It is possible to specify an ``ssh://`` URL as the destination, but no
659 It is possible to specify an ``ssh://`` URL as the destination, but no
660 .hg/hgrc and working directory will be created on the remote side.
660 .hg/hgrc and working directory will be created on the remote side.
661 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
661 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
662
662
663 A set of changesets (tags, or branch names) to pull may be specified
663 A set of changesets (tags, or branch names) to pull may be specified
664 by listing each changeset (tag, or branch name) with -r/--rev.
664 by listing each changeset (tag, or branch name) with -r/--rev.
665 If -r/--rev is used, the cloned repository will contain only a subset
665 If -r/--rev is used, the cloned repository will contain only a subset
666 of the changesets of the source repository. Only the set of changesets
666 of the changesets of the source repository. Only the set of changesets
667 defined by all -r/--rev options (including all their ancestors)
667 defined by all -r/--rev options (including all their ancestors)
668 will be pulled into the destination repository.
668 will be pulled into the destination repository.
669 No subsequent changesets (including subsequent tags) will be present
669 No subsequent changesets (including subsequent tags) will be present
670 in the destination.
670 in the destination.
671
671
672 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
672 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
673 local source repositories.
673 local source repositories.
674
674
675 For efficiency, hardlinks are used for cloning whenever the source
675 For efficiency, hardlinks are used for cloning whenever the source
676 and destination are on the same filesystem (note this applies only
676 and destination are on the same filesystem (note this applies only
677 to the repository data, not to the working directory). Some
677 to the repository data, not to the working directory). Some
678 filesystems, such as AFS, implement hardlinking incorrectly, but
678 filesystems, such as AFS, implement hardlinking incorrectly, but
679 do not report errors. In these cases, use the --pull option to
679 do not report errors. In these cases, use the --pull option to
680 avoid hardlinking.
680 avoid hardlinking.
681
681
682 In some cases, you can clone repositories and the working directory
682 In some cases, you can clone repositories and the working directory
683 using full hardlinks with ::
683 using full hardlinks with ::
684
684
685 $ cp -al REPO REPOCLONE
685 $ cp -al REPO REPOCLONE
686
686
687 This is the fastest way to clone, but it is not always safe. The
687 This is the fastest way to clone, but it is not always safe. The
688 operation is not atomic (making sure REPO is not modified during
688 operation is not atomic (making sure REPO is not modified during
689 the operation is up to you) and you have to make sure your editor
689 the operation is up to you) and you have to make sure your editor
690 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
690 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
691 this is not compatible with certain extensions that place their
691 this is not compatible with certain extensions that place their
692 metadata under the .hg directory, such as mq.
692 metadata under the .hg directory, such as mq.
693
693
694 Mercurial will update the working directory to the first applicable
694 Mercurial will update the working directory to the first applicable
695 revision from this list:
695 revision from this list:
696
696
697 a) null if -U or the source repository has no changesets
697 a) null if -U or the source repository has no changesets
698 b) if -u . and the source repository is local, the first parent of
698 b) if -u . and the source repository is local, the first parent of
699 the source repository's working directory
699 the source repository's working directory
700 c) the changeset specified with -u (if a branch name, this means the
700 c) the changeset specified with -u (if a branch name, this means the
701 latest head of that branch)
701 latest head of that branch)
702 d) the changeset specified with -r
702 d) the changeset specified with -r
703 e) the tipmost head specified with -b
703 e) the tipmost head specified with -b
704 f) the tipmost head specified with the url#branch source syntax
704 f) the tipmost head specified with the url#branch source syntax
705 g) the tipmost head of the default branch
705 g) the tipmost head of the default branch
706 h) tip
706 h) tip
707
707
708 Returns 0 on success.
708 Returns 0 on success.
709 """
709 """
710 if opts.get('noupdate') and opts.get('updaterev'):
710 if opts.get('noupdate') and opts.get('updaterev'):
711 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
711 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
712
712
713 r = hg.clone(hg.remoteui(ui, opts), source, dest,
713 r = hg.clone(hg.remoteui(ui, opts), source, dest,
714 pull=opts.get('pull'),
714 pull=opts.get('pull'),
715 stream=opts.get('uncompressed'),
715 stream=opts.get('uncompressed'),
716 rev=opts.get('rev'),
716 rev=opts.get('rev'),
717 update=opts.get('updaterev') or not opts.get('noupdate'),
717 update=opts.get('updaterev') or not opts.get('noupdate'),
718 branch=opts.get('branch'))
718 branch=opts.get('branch'))
719
719
720 return r is None
720 return r is None
721
721
722 def commit(ui, repo, *pats, **opts):
722 def commit(ui, repo, *pats, **opts):
723 """commit the specified files or all outstanding changes
723 """commit the specified files or all outstanding changes
724
724
725 Commit changes to the given files into the repository. Unlike a
725 Commit changes to the given files into the repository. Unlike a
726 centralized RCS, this operation is a local operation. See
726 centralized RCS, this operation is a local operation. See
727 :hg:`push` for a way to actively distribute your changes.
727 :hg:`push` for a way to actively distribute your changes.
728
728
729 If a list of files is omitted, all changes reported by :hg:`status`
729 If a list of files is omitted, all changes reported by :hg:`status`
730 will be committed.
730 will be committed.
731
731
732 If you are committing the result of a merge, do not provide any
732 If you are committing the result of a merge, do not provide any
733 filenames or -I/-X filters.
733 filenames or -I/-X filters.
734
734
735 If no commit message is specified, Mercurial starts your
735 If no commit message is specified, Mercurial starts your
736 configured editor where you can enter a message. In case your
736 configured editor where you can enter a message. In case your
737 commit fails, you will find a backup of your message in
737 commit fails, you will find a backup of your message in
738 ``.hg/last-message.txt``.
738 ``.hg/last-message.txt``.
739
739
740 See :hg:`help dates` for a list of formats valid for -d/--date.
740 See :hg:`help dates` for a list of formats valid for -d/--date.
741
741
742 Returns 0 on success, 1 if nothing changed.
742 Returns 0 on success, 1 if nothing changed.
743 """
743 """
744 extra = {}
744 extra = {}
745 if opts.get('close_branch'):
745 if opts.get('close_branch'):
746 if repo['.'].node() not in repo.branchheads():
746 if repo['.'].node() not in repo.branchheads():
747 # The topo heads set is included in the branch heads set of the
747 # The topo heads set is included in the branch heads set of the
748 # current branch, so it's sufficient to test branchheads
748 # current branch, so it's sufficient to test branchheads
749 raise util.Abort(_('can only close branch heads'))
749 raise util.Abort(_('can only close branch heads'))
750 extra['close'] = 1
750 extra['close'] = 1
751 e = cmdutil.commiteditor
751 e = cmdutil.commiteditor
752 if opts.get('force_editor'):
752 if opts.get('force_editor'):
753 e = cmdutil.commitforceeditor
753 e = cmdutil.commitforceeditor
754
754
755 def commitfunc(ui, repo, message, match, opts):
755 def commitfunc(ui, repo, message, match, opts):
756 return repo.commit(message, opts.get('user'), opts.get('date'), match,
756 return repo.commit(message, opts.get('user'), opts.get('date'), match,
757 editor=e, extra=extra)
757 editor=e, extra=extra)
758
758
759 branch = repo[None].branch()
759 branch = repo[None].branch()
760 bheads = repo.branchheads(branch)
760 bheads = repo.branchheads(branch)
761
761
762 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
762 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
763 if not node:
763 if not node:
764 ui.status(_("nothing changed\n"))
764 ui.status(_("nothing changed\n"))
765 return 1
765 return 1
766
766
767 ctx = repo[node]
767 ctx = repo[node]
768 parents = ctx.parents()
768 parents = ctx.parents()
769
769
770 if bheads and not [x for x in parents
770 if bheads and not [x for x in parents
771 if x.node() in bheads and x.branch() == branch]:
771 if x.node() in bheads and x.branch() == branch]:
772 ui.status(_('created new head\n'))
772 ui.status(_('created new head\n'))
773 # The message is not printed for initial roots. For the other
773 # The message is not printed for initial roots. For the other
774 # changesets, it is printed in the following situations:
774 # changesets, it is printed in the following situations:
775 #
775 #
776 # Par column: for the 2 parents with ...
776 # Par column: for the 2 parents with ...
777 # N: null or no parent
777 # N: null or no parent
778 # B: parent is on another named branch
778 # B: parent is on another named branch
779 # C: parent is a regular non head changeset
779 # C: parent is a regular non head changeset
780 # H: parent was a branch head of the current branch
780 # H: parent was a branch head of the current branch
781 # Msg column: whether we print "created new head" message
781 # Msg column: whether we print "created new head" message
782 # In the following, it is assumed that there already exists some
782 # In the following, it is assumed that there already exists some
783 # initial branch heads of the current branch, otherwise nothing is
783 # initial branch heads of the current branch, otherwise nothing is
784 # printed anyway.
784 # printed anyway.
785 #
785 #
786 # Par Msg Comment
786 # Par Msg Comment
787 # NN y additional topo root
787 # NN y additional topo root
788 #
788 #
789 # BN y additional branch root
789 # BN y additional branch root
790 # CN y additional topo head
790 # CN y additional topo head
791 # HN n usual case
791 # HN n usual case
792 #
792 #
793 # BB y weird additional branch root
793 # BB y weird additional branch root
794 # CB y branch merge
794 # CB y branch merge
795 # HB n merge with named branch
795 # HB n merge with named branch
796 #
796 #
797 # CC y additional head from merge
797 # CC y additional head from merge
798 # CH n merge with a head
798 # CH n merge with a head
799 #
799 #
800 # HH n head merge: head count decreases
800 # HH n head merge: head count decreases
801
801
802 if not opts.get('close_branch'):
802 if not opts.get('close_branch'):
803 for r in parents:
803 for r in parents:
804 if r.extra().get('close') and r.branch() == branch:
804 if r.extra().get('close') and r.branch() == branch:
805 ui.status(_('reopening closed branch head %d\n') % r)
805 ui.status(_('reopening closed branch head %d\n') % r)
806
806
807 if ui.debugflag:
807 if ui.debugflag:
808 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
808 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
809 elif ui.verbose:
809 elif ui.verbose:
810 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
810 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
811
811
812 def copy(ui, repo, *pats, **opts):
812 def copy(ui, repo, *pats, **opts):
813 """mark files as copied for the next commit
813 """mark files as copied for the next commit
814
814
815 Mark dest as having copies of source files. If dest is a
815 Mark dest as having copies of source files. If dest is a
816 directory, copies are put in that directory. If dest is a file,
816 directory, copies are put in that directory. If dest is a file,
817 the source must be a single file.
817 the source must be a single file.
818
818
819 By default, this command copies the contents of files as they
819 By default, this command copies the contents of files as they
820 exist in the working directory. If invoked with -A/--after, the
820 exist in the working directory. If invoked with -A/--after, the
821 operation is recorded, but no copying is performed.
821 operation is recorded, but no copying is performed.
822
822
823 This command takes effect with the next commit. To undo a copy
823 This command takes effect with the next commit. To undo a copy
824 before that, see :hg:`revert`.
824 before that, see :hg:`revert`.
825
825
826 Returns 0 on success, 1 if errors are encountered.
826 Returns 0 on success, 1 if errors are encountered.
827 """
827 """
828 wlock = repo.wlock(False)
828 wlock = repo.wlock(False)
829 try:
829 try:
830 return cmdutil.copy(ui, repo, pats, opts)
830 return cmdutil.copy(ui, repo, pats, opts)
831 finally:
831 finally:
832 wlock.release()
832 wlock.release()
833
833
834 def debugancestor(ui, repo, *args):
834 def debugancestor(ui, repo, *args):
835 """find the ancestor revision of two revisions in a given index"""
835 """find the ancestor revision of two revisions in a given index"""
836 if len(args) == 3:
836 if len(args) == 3:
837 index, rev1, rev2 = args
837 index, rev1, rev2 = args
838 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
838 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
839 lookup = r.lookup
839 lookup = r.lookup
840 elif len(args) == 2:
840 elif len(args) == 2:
841 if not repo:
841 if not repo:
842 raise util.Abort(_("there is no Mercurial repository here "
842 raise util.Abort(_("there is no Mercurial repository here "
843 "(.hg not found)"))
843 "(.hg not found)"))
844 rev1, rev2 = args
844 rev1, rev2 = args
845 r = repo.changelog
845 r = repo.changelog
846 lookup = repo.lookup
846 lookup = repo.lookup
847 else:
847 else:
848 raise util.Abort(_('either two or three arguments required'))
848 raise util.Abort(_('either two or three arguments required'))
849 a = r.ancestor(lookup(rev1), lookup(rev2))
849 a = r.ancestor(lookup(rev1), lookup(rev2))
850 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
850 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
851
851
852 def debugbuilddag(ui, repo, text,
852 def debugbuilddag(ui, repo, text,
853 mergeable_file=False,
853 mergeable_file=False,
854 appended_file=False,
854 appended_file=False,
855 overwritten_file=False,
855 overwritten_file=False,
856 new_file=False):
856 new_file=False):
857 """builds a repo with a given dag from scratch in the current empty repo
857 """builds a repo with a given dag from scratch in the current empty repo
858
858
859 Elements:
859 Elements:
860
860
861 - "+n" is a linear run of n nodes based on the current default parent
861 - "+n" is a linear run of n nodes based on the current default parent
862 - "." is a single node based on the current default parent
862 - "." is a single node based on the current default parent
863 - "$" resets the default parent to null (implied at the start);
863 - "$" resets the default parent to null (implied at the start);
864 otherwise the default parent is always the last node created
864 otherwise the default parent is always the last node created
865 - "<p" sets the default parent to the backref p
865 - "<p" sets the default parent to the backref p
866 - "*p" is a fork at parent p, which is a backref
866 - "*p" is a fork at parent p, which is a backref
867 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
867 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
868 - "/p2" is a merge of the preceding node and p2
868 - "/p2" is a merge of the preceding node and p2
869 - ":tag" defines a local tag for the preceding node
869 - ":tag" defines a local tag for the preceding node
870 - "@branch" sets the named branch for subsequent nodes
870 - "@branch" sets the named branch for subsequent nodes
871 - "!command" runs the command using your shell
871 - "!command" runs the command using your shell
872 - "!!my command\\n" is like "!", but to the end of the line
872 - "!!my command\\n" is like "!", but to the end of the line
873 - "#...\\n" is a comment up to the end of the line
873 - "#...\\n" is a comment up to the end of the line
874
874
875 Whitespace between the above elements is ignored.
875 Whitespace between the above elements is ignored.
876
876
877 A backref is either
877 A backref is either
878
878
879 - a number n, which references the node curr-n, where curr is the current
879 - a number n, which references the node curr-n, where curr is the current
880 node, or
880 node, or
881 - the name of a local tag you placed earlier using ":tag", or
881 - the name of a local tag you placed earlier using ":tag", or
882 - empty to denote the default parent.
882 - empty to denote the default parent.
883
883
884 All string valued-elements are either strictly alphanumeric, or must
884 All string valued-elements are either strictly alphanumeric, or must
885 be enclosed in double quotes ("..."), with "\\" as escape character.
885 be enclosed in double quotes ("..."), with "\\" as escape character.
886
886
887 Note that the --overwritten-file and --appended-file options imply the
887 Note that the --overwritten-file and --appended-file options imply the
888 use of "HGMERGE=internal:local" during DAG buildup.
888 use of "HGMERGE=internal:local" during DAG buildup.
889 """
889 """
890
890
891 if not (mergeable_file or appended_file or overwritten_file or new_file):
891 if not (mergeable_file or appended_file or overwritten_file or new_file):
892 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
892 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
893
893
894 if len(repo.changelog) > 0:
894 if len(repo.changelog) > 0:
895 raise util.Abort(_('repository is not empty'))
895 raise util.Abort(_('repository is not empty'))
896
896
897 if overwritten_file or appended_file:
897 if overwritten_file or appended_file:
898 # we don't want to fail in merges during buildup
898 # we don't want to fail in merges during buildup
899 os.environ['HGMERGE'] = 'internal:local'
899 os.environ['HGMERGE'] = 'internal:local'
900
900
901 def writefile(fname, text, fmode="wb"):
901 def writefile(fname, text, fmode="wb"):
902 f = open(fname, fmode)
902 f = open(fname, fmode)
903 try:
903 try:
904 f.write(text)
904 f.write(text)
905 finally:
905 finally:
906 f.close()
906 f.close()
907
907
908 if mergeable_file:
908 if mergeable_file:
909 linesperrev = 2
909 linesperrev = 2
910 # determine number of revs in DAG
910 # determine number of revs in DAG
911 n = 0
911 n = 0
912 for type, data in dagparser.parsedag(text):
912 for type, data in dagparser.parsedag(text):
913 if type == 'n':
913 if type == 'n':
914 n += 1
914 n += 1
915 # make a file with k lines per rev
915 # make a file with k lines per rev
916 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
916 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
917 + "\n")
917 + "\n")
918
918
919 at = -1
919 at = -1
920 atbranch = 'default'
920 atbranch = 'default'
921 for type, data in dagparser.parsedag(text):
921 for type, data in dagparser.parsedag(text):
922 if type == 'n':
922 if type == 'n':
923 ui.status('node %s\n' % str(data))
923 ui.status('node %s\n' % str(data))
924 id, ps = data
924 id, ps = data
925 p1 = ps[0]
925 p1 = ps[0]
926 if p1 != at:
926 if p1 != at:
927 update(ui, repo, node=p1, clean=True)
927 update(ui, repo, node=p1, clean=True)
928 at = p1
928 at = p1
929 if repo.dirstate.branch() != atbranch:
929 if repo.dirstate.branch() != atbranch:
930 branch(ui, repo, atbranch, force=True)
930 branch(ui, repo, atbranch, force=True)
931 if len(ps) > 1:
931 if len(ps) > 1:
932 p2 = ps[1]
932 p2 = ps[1]
933 merge(ui, repo, node=p2)
933 merge(ui, repo, node=p2)
934
934
935 if mergeable_file:
935 if mergeable_file:
936 f = open("mf", "rb+")
936 f = open("mf", "rb+")
937 try:
937 try:
938 lines = f.read().split("\n")
938 lines = f.read().split("\n")
939 lines[id * linesperrev] += " r%i" % id
939 lines[id * linesperrev] += " r%i" % id
940 f.seek(0)
940 f.seek(0)
941 f.write("\n".join(lines))
941 f.write("\n".join(lines))
942 finally:
942 finally:
943 f.close()
943 f.close()
944
944
945 if appended_file:
945 if appended_file:
946 writefile("af", "r%i\n" % id, "ab")
946 writefile("af", "r%i\n" % id, "ab")
947
947
948 if overwritten_file:
948 if overwritten_file:
949 writefile("of", "r%i\n" % id)
949 writefile("of", "r%i\n" % id)
950
950
951 if new_file:
951 if new_file:
952 writefile("nf%i" % id, "r%i\n" % id)
952 writefile("nf%i" % id, "r%i\n" % id)
953
953
954 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
954 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
955 at = id
955 at = id
956 elif type == 'l':
956 elif type == 'l':
957 id, name = data
957 id, name = data
958 ui.status('tag %s\n' % name)
958 ui.status('tag %s\n' % name)
959 tag(ui, repo, name, local=True)
959 tag(ui, repo, name, local=True)
960 elif type == 'a':
960 elif type == 'a':
961 ui.status('branch %s\n' % data)
961 ui.status('branch %s\n' % data)
962 atbranch = data
962 atbranch = data
963 elif type in 'cC':
963 elif type in 'cC':
964 r = util.system(data, cwd=repo.root)
964 r = util.system(data, cwd=repo.root)
965 if r:
965 if r:
966 desc, r = util.explain_exit(r)
966 desc, r = util.explain_exit(r)
967 raise util.Abort(_('%s command %s') % (data, desc))
967 raise util.Abort(_('%s command %s') % (data, desc))
968
968
969 def debugcommands(ui, cmd='', *args):
969 def debugcommands(ui, cmd='', *args):
970 """list all available commands and options"""
970 """list all available commands and options"""
971 for cmd, vals in sorted(table.iteritems()):
971 for cmd, vals in sorted(table.iteritems()):
972 cmd = cmd.split('|')[0].strip('^')
972 cmd = cmd.split('|')[0].strip('^')
973 opts = ', '.join([i[1] for i in vals[1]])
973 opts = ', '.join([i[1] for i in vals[1]])
974 ui.write('%s: %s\n' % (cmd, opts))
974 ui.write('%s: %s\n' % (cmd, opts))
975
975
976 def debugcomplete(ui, cmd='', **opts):
976 def debugcomplete(ui, cmd='', **opts):
977 """returns the completion list associated with the given command"""
977 """returns the completion list associated with the given command"""
978
978
979 if opts.get('options'):
979 if opts.get('options'):
980 options = []
980 options = []
981 otables = [globalopts]
981 otables = [globalopts]
982 if cmd:
982 if cmd:
983 aliases, entry = cmdutil.findcmd(cmd, table, False)
983 aliases, entry = cmdutil.findcmd(cmd, table, False)
984 otables.append(entry[1])
984 otables.append(entry[1])
985 for t in otables:
985 for t in otables:
986 for o in t:
986 for o in t:
987 if "(DEPRECATED)" in o[3]:
987 if "(DEPRECATED)" in o[3]:
988 continue
988 continue
989 if o[0]:
989 if o[0]:
990 options.append('-%s' % o[0])
990 options.append('-%s' % o[0])
991 options.append('--%s' % o[1])
991 options.append('--%s' % o[1])
992 ui.write("%s\n" % "\n".join(options))
992 ui.write("%s\n" % "\n".join(options))
993 return
993 return
994
994
995 cmdlist = cmdutil.findpossible(cmd, table)
995 cmdlist = cmdutil.findpossible(cmd, table)
996 if ui.verbose:
996 if ui.verbose:
997 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
997 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
998 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
998 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
999
999
1000 def debugfsinfo(ui, path = "."):
1000 def debugfsinfo(ui, path = "."):
1001 """show information detected about current filesystem"""
1001 """show information detected about current filesystem"""
1002 open('.debugfsinfo', 'w').write('')
1002 open('.debugfsinfo', 'w').write('')
1003 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1003 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1004 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1004 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1005 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1005 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1006 and 'yes' or 'no'))
1006 and 'yes' or 'no'))
1007 os.unlink('.debugfsinfo')
1007 os.unlink('.debugfsinfo')
1008
1008
1009 def debugrebuildstate(ui, repo, rev="tip"):
1009 def debugrebuildstate(ui, repo, rev="tip"):
1010 """rebuild the dirstate as it would look like for the given revision"""
1010 """rebuild the dirstate as it would look like for the given revision"""
1011 ctx = repo[rev]
1011 ctx = repo[rev]
1012 wlock = repo.wlock()
1012 wlock = repo.wlock()
1013 try:
1013 try:
1014 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1014 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1015 finally:
1015 finally:
1016 wlock.release()
1016 wlock.release()
1017
1017
1018 def debugcheckstate(ui, repo):
1018 def debugcheckstate(ui, repo):
1019 """validate the correctness of the current dirstate"""
1019 """validate the correctness of the current dirstate"""
1020 parent1, parent2 = repo.dirstate.parents()
1020 parent1, parent2 = repo.dirstate.parents()
1021 m1 = repo[parent1].manifest()
1021 m1 = repo[parent1].manifest()
1022 m2 = repo[parent2].manifest()
1022 m2 = repo[parent2].manifest()
1023 errors = 0
1023 errors = 0
1024 for f in repo.dirstate:
1024 for f in repo.dirstate:
1025 state = repo.dirstate[f]
1025 state = repo.dirstate[f]
1026 if state in "nr" and f not in m1:
1026 if state in "nr" and f not in m1:
1027 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1027 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1028 errors += 1
1028 errors += 1
1029 if state in "a" and f in m1:
1029 if state in "a" and f in m1:
1030 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1030 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1031 errors += 1
1031 errors += 1
1032 if state in "m" and f not in m1 and f not in m2:
1032 if state in "m" and f not in m1 and f not in m2:
1033 ui.warn(_("%s in state %s, but not in either manifest\n") %
1033 ui.warn(_("%s in state %s, but not in either manifest\n") %
1034 (f, state))
1034 (f, state))
1035 errors += 1
1035 errors += 1
1036 for f in m1:
1036 for f in m1:
1037 state = repo.dirstate[f]
1037 state = repo.dirstate[f]
1038 if state not in "nrm":
1038 if state not in "nrm":
1039 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1039 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1040 errors += 1
1040 errors += 1
1041 if errors:
1041 if errors:
1042 error = _(".hg/dirstate inconsistent with current parent's manifest")
1042 error = _(".hg/dirstate inconsistent with current parent's manifest")
1043 raise util.Abort(error)
1043 raise util.Abort(error)
1044
1044
1045 def showconfig(ui, repo, *values, **opts):
1045 def showconfig(ui, repo, *values, **opts):
1046 """show combined config settings from all hgrc files
1046 """show combined config settings from all hgrc files
1047
1047
1048 With no arguments, print names and values of all config items.
1048 With no arguments, print names and values of all config items.
1049
1049
1050 With one argument of the form section.name, print just the value
1050 With one argument of the form section.name, print just the value
1051 of that config item.
1051 of that config item.
1052
1052
1053 With multiple arguments, print names and values of all config
1053 With multiple arguments, print names and values of all config
1054 items with matching section names.
1054 items with matching section names.
1055
1055
1056 With --debug, the source (filename and line number) is printed
1056 With --debug, the source (filename and line number) is printed
1057 for each config item.
1057 for each config item.
1058
1058
1059 Returns 0 on success.
1059 Returns 0 on success.
1060 """
1060 """
1061
1061
1062 for f in util.rcpath():
1062 for f in util.rcpath():
1063 ui.debug(_('read config from: %s\n') % f)
1063 ui.debug(_('read config from: %s\n') % f)
1064 untrusted = bool(opts.get('untrusted'))
1064 untrusted = bool(opts.get('untrusted'))
1065 if values:
1065 if values:
1066 if len([v for v in values if '.' in v]) > 1:
1066 if len([v for v in values if '.' in v]) > 1:
1067 raise util.Abort(_('only one config item permitted'))
1067 raise util.Abort(_('only one config item permitted'))
1068 for section, name, value in ui.walkconfig(untrusted=untrusted):
1068 for section, name, value in ui.walkconfig(untrusted=untrusted):
1069 sectname = section + '.' + name
1069 sectname = section + '.' + name
1070 if values:
1070 if values:
1071 for v in values:
1071 for v in values:
1072 if v == section:
1072 if v == section:
1073 ui.debug('%s: ' %
1073 ui.debug('%s: ' %
1074 ui.configsource(section, name, untrusted))
1074 ui.configsource(section, name, untrusted))
1075 ui.write('%s=%s\n' % (sectname, value))
1075 ui.write('%s=%s\n' % (sectname, value))
1076 elif v == sectname:
1076 elif v == sectname:
1077 ui.debug('%s: ' %
1077 ui.debug('%s: ' %
1078 ui.configsource(section, name, untrusted))
1078 ui.configsource(section, name, untrusted))
1079 ui.write(value, '\n')
1079 ui.write(value, '\n')
1080 else:
1080 else:
1081 ui.debug('%s: ' %
1081 ui.debug('%s: ' %
1082 ui.configsource(section, name, untrusted))
1082 ui.configsource(section, name, untrusted))
1083 ui.write('%s=%s\n' % (sectname, value))
1083 ui.write('%s=%s\n' % (sectname, value))
1084
1084
1085 def debugpushkey(ui, repopath, namespace, *keyinfo):
1085 def debugpushkey(ui, repopath, namespace, *keyinfo):
1086 '''access the pushkey key/value protocol
1086 '''access the pushkey key/value protocol
1087
1087
1088 With two args, list the keys in the given namespace.
1088 With two args, list the keys in the given namespace.
1089
1089
1090 With five args, set a key to new if it currently is set to old.
1090 With five args, set a key to new if it currently is set to old.
1091 Reports success or failure.
1091 Reports success or failure.
1092 '''
1092 '''
1093
1093
1094 target = hg.repository(ui, repopath)
1094 target = hg.repository(ui, repopath)
1095 if keyinfo:
1095 if keyinfo:
1096 key, old, new = keyinfo
1096 key, old, new = keyinfo
1097 r = target.pushkey(namespace, key, old, new)
1097 r = target.pushkey(namespace, key, old, new)
1098 ui.status(str(r) + '\n')
1098 ui.status(str(r) + '\n')
1099 return not(r)
1099 return not(r)
1100 else:
1100 else:
1101 for k, v in target.listkeys(namespace).iteritems():
1101 for k, v in target.listkeys(namespace).iteritems():
1102 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1102 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1103 v.encode('string-escape')))
1103 v.encode('string-escape')))
1104
1104
1105 def debugrevspec(ui, repo, expr):
1105 def debugrevspec(ui, repo, expr):
1106 '''parse and apply a revision specification'''
1106 '''parse and apply a revision specification'''
1107 if ui.verbose:
1107 if ui.verbose:
1108 tree = revset.parse(expr)
1108 tree = revset.parse(expr)
1109 ui.note(tree, "\n")
1109 ui.note(tree, "\n")
1110 func = revset.match(expr)
1110 func = revset.match(expr)
1111 for c in func(repo, range(len(repo))):
1111 for c in func(repo, range(len(repo))):
1112 ui.write("%s\n" % c)
1112 ui.write("%s\n" % c)
1113
1113
1114 def debugsetparents(ui, repo, rev1, rev2=None):
1114 def debugsetparents(ui, repo, rev1, rev2=None):
1115 """manually set the parents of the current working directory
1115 """manually set the parents of the current working directory
1116
1116
1117 This is useful for writing repository conversion tools, but should
1117 This is useful for writing repository conversion tools, but should
1118 be used with care.
1118 be used with care.
1119
1119
1120 Returns 0 on success.
1120 Returns 0 on success.
1121 """
1121 """
1122
1122
1123 if not rev2:
1123 if not rev2:
1124 rev2 = hex(nullid)
1124 rev2 = hex(nullid)
1125
1125
1126 wlock = repo.wlock()
1126 wlock = repo.wlock()
1127 try:
1127 try:
1128 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1128 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1129 finally:
1129 finally:
1130 wlock.release()
1130 wlock.release()
1131
1131
1132 def debugstate(ui, repo, nodates=None):
1132 def debugstate(ui, repo, nodates=None):
1133 """show the contents of the current dirstate"""
1133 """show the contents of the current dirstate"""
1134 timestr = ""
1134 timestr = ""
1135 showdate = not nodates
1135 showdate = not nodates
1136 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1136 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1137 if showdate:
1137 if showdate:
1138 if ent[3] == -1:
1138 if ent[3] == -1:
1139 # Pad or slice to locale representation
1139 # Pad or slice to locale representation
1140 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1140 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1141 time.localtime(0)))
1141 time.localtime(0)))
1142 timestr = 'unset'
1142 timestr = 'unset'
1143 timestr = (timestr[:locale_len] +
1143 timestr = (timestr[:locale_len] +
1144 ' ' * (locale_len - len(timestr)))
1144 ' ' * (locale_len - len(timestr)))
1145 else:
1145 else:
1146 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1146 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1147 time.localtime(ent[3]))
1147 time.localtime(ent[3]))
1148 if ent[1] & 020000:
1148 if ent[1] & 020000:
1149 mode = 'lnk'
1149 mode = 'lnk'
1150 else:
1150 else:
1151 mode = '%3o' % (ent[1] & 0777)
1151 mode = '%3o' % (ent[1] & 0777)
1152 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1152 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1153 for f in repo.dirstate.copies():
1153 for f in repo.dirstate.copies():
1154 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1154 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1155
1155
1156 def debugsub(ui, repo, rev=None):
1156 def debugsub(ui, repo, rev=None):
1157 if rev == '':
1157 if rev == '':
1158 rev = None
1158 rev = None
1159 for k, v in sorted(repo[rev].substate.items()):
1159 for k, v in sorted(repo[rev].substate.items()):
1160 ui.write('path %s\n' % k)
1160 ui.write('path %s\n' % k)
1161 ui.write(' source %s\n' % v[0])
1161 ui.write(' source %s\n' % v[0])
1162 ui.write(' revision %s\n' % v[1])
1162 ui.write(' revision %s\n' % v[1])
1163
1163
1164 def debugdag(ui, repo, file_=None, *revs, **opts):
1164 def debugdag(ui, repo, file_=None, *revs, **opts):
1165 """format the changelog or an index DAG as a concise textual description
1165 """format the changelog or an index DAG as a concise textual description
1166
1166
1167 If you pass a revlog index, the revlog's DAG is emitted. If you list
1167 If you pass a revlog index, the revlog's DAG is emitted. If you list
1168 revision numbers, they get labelled in the output as rN.
1168 revision numbers, they get labelled in the output as rN.
1169
1169
1170 Otherwise, the changelog DAG of the current repo is emitted.
1170 Otherwise, the changelog DAG of the current repo is emitted.
1171 """
1171 """
1172 spaces = opts.get('spaces')
1172 spaces = opts.get('spaces')
1173 dots = opts.get('dots')
1173 dots = opts.get('dots')
1174 if file_:
1174 if file_:
1175 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1175 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1176 revs = set((int(r) for r in revs))
1176 revs = set((int(r) for r in revs))
1177 def events():
1177 def events():
1178 for r in rlog:
1178 for r in rlog:
1179 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1179 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1180 if r in revs:
1180 if r in revs:
1181 yield 'l', (r, "r%i" % r)
1181 yield 'l', (r, "r%i" % r)
1182 elif repo:
1182 elif repo:
1183 cl = repo.changelog
1183 cl = repo.changelog
1184 tags = opts.get('tags')
1184 tags = opts.get('tags')
1185 branches = opts.get('branches')
1185 branches = opts.get('branches')
1186 if tags:
1186 if tags:
1187 labels = {}
1187 labels = {}
1188 for l, n in repo.tags().items():
1188 for l, n in repo.tags().items():
1189 labels.setdefault(cl.rev(n), []).append(l)
1189 labels.setdefault(cl.rev(n), []).append(l)
1190 def events():
1190 def events():
1191 b = "default"
1191 b = "default"
1192 for r in cl:
1192 for r in cl:
1193 if branches:
1193 if branches:
1194 newb = cl.read(cl.node(r))[5]['branch']
1194 newb = cl.read(cl.node(r))[5]['branch']
1195 if newb != b:
1195 if newb != b:
1196 yield 'a', newb
1196 yield 'a', newb
1197 b = newb
1197 b = newb
1198 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1198 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1199 if tags:
1199 if tags:
1200 ls = labels.get(r)
1200 ls = labels.get(r)
1201 if ls:
1201 if ls:
1202 for l in ls:
1202 for l in ls:
1203 yield 'l', (r, l)
1203 yield 'l', (r, l)
1204 else:
1204 else:
1205 raise util.Abort(_('need repo for changelog dag'))
1205 raise util.Abort(_('need repo for changelog dag'))
1206
1206
1207 for line in dagparser.dagtextlines(events(),
1207 for line in dagparser.dagtextlines(events(),
1208 addspaces=spaces,
1208 addspaces=spaces,
1209 wraplabels=True,
1209 wraplabels=True,
1210 wrapannotations=True,
1210 wrapannotations=True,
1211 wrapnonlinear=dots,
1211 wrapnonlinear=dots,
1212 usedots=dots,
1212 usedots=dots,
1213 maxlinewidth=70):
1213 maxlinewidth=70):
1214 ui.write(line)
1214 ui.write(line)
1215 ui.write("\n")
1215 ui.write("\n")
1216
1216
1217 def debugdata(ui, file_, rev):
1217 def debugdata(ui, file_, rev):
1218 """dump the contents of a data file revision"""
1218 """dump the contents of a data file revision"""
1219 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1219 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1220 try:
1220 try:
1221 ui.write(r.revision(r.lookup(rev)))
1221 ui.write(r.revision(r.lookup(rev)))
1222 except KeyError:
1222 except KeyError:
1223 raise util.Abort(_('invalid revision identifier %s') % rev)
1223 raise util.Abort(_('invalid revision identifier %s') % rev)
1224
1224
1225 def debugdate(ui, date, range=None, **opts):
1225 def debugdate(ui, date, range=None, **opts):
1226 """parse and display a date"""
1226 """parse and display a date"""
1227 if opts["extended"]:
1227 if opts["extended"]:
1228 d = util.parsedate(date, util.extendeddateformats)
1228 d = util.parsedate(date, util.extendeddateformats)
1229 else:
1229 else:
1230 d = util.parsedate(date)
1230 d = util.parsedate(date)
1231 ui.write("internal: %s %s\n" % d)
1231 ui.write("internal: %s %s\n" % d)
1232 ui.write("standard: %s\n" % util.datestr(d))
1232 ui.write("standard: %s\n" % util.datestr(d))
1233 if range:
1233 if range:
1234 m = util.matchdate(range)
1234 m = util.matchdate(range)
1235 ui.write("match: %s\n" % m(d[0]))
1235 ui.write("match: %s\n" % m(d[0]))
1236
1236
1237 def debugindex(ui, file_):
1237 def debugindex(ui, file_):
1238 """dump the contents of an index file"""
1238 """dump the contents of an index file"""
1239 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1239 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1240 ui.write(" rev offset length base linkrev"
1240 ui.write(" rev offset length base linkrev"
1241 " nodeid p1 p2\n")
1241 " nodeid p1 p2\n")
1242 for i in r:
1242 for i in r:
1243 node = r.node(i)
1243 node = r.node(i)
1244 try:
1244 try:
1245 pp = r.parents(node)
1245 pp = r.parents(node)
1246 except:
1246 except:
1247 pp = [nullid, nullid]
1247 pp = [nullid, nullid]
1248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1248 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1249 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1249 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1250 short(node), short(pp[0]), short(pp[1])))
1250 short(node), short(pp[0]), short(pp[1])))
1251
1251
1252 def debugindexdot(ui, file_):
1252 def debugindexdot(ui, file_):
1253 """dump an index DAG as a graphviz dot file"""
1253 """dump an index DAG as a graphviz dot file"""
1254 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1254 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1255 ui.write("digraph G {\n")
1255 ui.write("digraph G {\n")
1256 for i in r:
1256 for i in r:
1257 node = r.node(i)
1257 node = r.node(i)
1258 pp = r.parents(node)
1258 pp = r.parents(node)
1259 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1259 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1260 if pp[1] != nullid:
1260 if pp[1] != nullid:
1261 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1261 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1262 ui.write("}\n")
1262 ui.write("}\n")
1263
1263
1264 def debuginstall(ui):
1264 def debuginstall(ui):
1265 '''test Mercurial installation
1265 '''test Mercurial installation
1266
1266
1267 Returns 0 on success.
1267 Returns 0 on success.
1268 '''
1268 '''
1269
1269
1270 def writetemp(contents):
1270 def writetemp(contents):
1271 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1271 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1272 f = os.fdopen(fd, "wb")
1272 f = os.fdopen(fd, "wb")
1273 f.write(contents)
1273 f.write(contents)
1274 f.close()
1274 f.close()
1275 return name
1275 return name
1276
1276
1277 problems = 0
1277 problems = 0
1278
1278
1279 # encoding
1279 # encoding
1280 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1280 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1281 try:
1281 try:
1282 encoding.fromlocal("test")
1282 encoding.fromlocal("test")
1283 except util.Abort, inst:
1283 except util.Abort, inst:
1284 ui.write(" %s\n" % inst)
1284 ui.write(" %s\n" % inst)
1285 ui.write(_(" (check that your locale is properly set)\n"))
1285 ui.write(_(" (check that your locale is properly set)\n"))
1286 problems += 1
1286 problems += 1
1287
1287
1288 # compiled modules
1288 # compiled modules
1289 ui.status(_("Checking installed modules (%s)...\n")
1289 ui.status(_("Checking installed modules (%s)...\n")
1290 % os.path.dirname(__file__))
1290 % os.path.dirname(__file__))
1291 try:
1291 try:
1292 import bdiff, mpatch, base85, osutil
1292 import bdiff, mpatch, base85, osutil
1293 except Exception, inst:
1293 except Exception, inst:
1294 ui.write(" %s\n" % inst)
1294 ui.write(" %s\n" % inst)
1295 ui.write(_(" One or more extensions could not be found"))
1295 ui.write(_(" One or more extensions could not be found"))
1296 ui.write(_(" (check that you compiled the extensions)\n"))
1296 ui.write(_(" (check that you compiled the extensions)\n"))
1297 problems += 1
1297 problems += 1
1298
1298
1299 # templates
1299 # templates
1300 ui.status(_("Checking templates...\n"))
1300 ui.status(_("Checking templates...\n"))
1301 try:
1301 try:
1302 import templater
1302 import templater
1303 templater.templater(templater.templatepath("map-cmdline.default"))
1303 templater.templater(templater.templatepath("map-cmdline.default"))
1304 except Exception, inst:
1304 except Exception, inst:
1305 ui.write(" %s\n" % inst)
1305 ui.write(" %s\n" % inst)
1306 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1306 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1307 problems += 1
1307 problems += 1
1308
1308
1309 # patch
1309 # patch
1310 ui.status(_("Checking patch...\n"))
1310 ui.status(_("Checking patch...\n"))
1311 patchproblems = 0
1311 patchproblems = 0
1312 a = "1\n2\n3\n4\n"
1312 a = "1\n2\n3\n4\n"
1313 b = "1\n2\n3\ninsert\n4\n"
1313 b = "1\n2\n3\ninsert\n4\n"
1314 fa = writetemp(a)
1314 fa = writetemp(a)
1315 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1315 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1316 os.path.basename(fa))
1316 os.path.basename(fa))
1317 fd = writetemp(d)
1317 fd = writetemp(d)
1318
1318
1319 files = {}
1319 files = {}
1320 try:
1320 try:
1321 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1321 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1322 except util.Abort, e:
1322 except util.Abort, e:
1323 ui.write(_(" patch call failed:\n"))
1323 ui.write(_(" patch call failed:\n"))
1324 ui.write(" " + str(e) + "\n")
1324 ui.write(" " + str(e) + "\n")
1325 patchproblems += 1
1325 patchproblems += 1
1326 else:
1326 else:
1327 if list(files) != [os.path.basename(fa)]:
1327 if list(files) != [os.path.basename(fa)]:
1328 ui.write(_(" unexpected patch output!\n"))
1328 ui.write(_(" unexpected patch output!\n"))
1329 patchproblems += 1
1329 patchproblems += 1
1330 a = open(fa).read()
1330 a = open(fa).read()
1331 if a != b:
1331 if a != b:
1332 ui.write(_(" patch test failed!\n"))
1332 ui.write(_(" patch test failed!\n"))
1333 patchproblems += 1
1333 patchproblems += 1
1334
1334
1335 if patchproblems:
1335 if patchproblems:
1336 if ui.config('ui', 'patch'):
1336 if ui.config('ui', 'patch'):
1337 ui.write(_(" (Current patch tool may be incompatible with patch,"
1337 ui.write(_(" (Current patch tool may be incompatible with patch,"
1338 " or misconfigured. Please check your .hgrc file)\n"))
1338 " or misconfigured. Please check your .hgrc file)\n"))
1339 else:
1339 else:
1340 ui.write(_(" Internal patcher failure, please report this error"
1340 ui.write(_(" Internal patcher failure, please report this error"
1341 " to http://mercurial.selenic.com/bts/\n"))
1341 " to http://mercurial.selenic.com/bts/\n"))
1342 problems += patchproblems
1342 problems += patchproblems
1343
1343
1344 os.unlink(fa)
1344 os.unlink(fa)
1345 os.unlink(fd)
1345 os.unlink(fd)
1346
1346
1347 # editor
1347 # editor
1348 ui.status(_("Checking commit editor...\n"))
1348 ui.status(_("Checking commit editor...\n"))
1349 editor = ui.geteditor()
1349 editor = ui.geteditor()
1350 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1350 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1351 if not cmdpath:
1351 if not cmdpath:
1352 if editor == 'vi':
1352 if editor == 'vi':
1353 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1353 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1354 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1354 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1355 else:
1355 else:
1356 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1356 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1357 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1357 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1358 problems += 1
1358 problems += 1
1359
1359
1360 # check username
1360 # check username
1361 ui.status(_("Checking username...\n"))
1361 ui.status(_("Checking username...\n"))
1362 try:
1362 try:
1363 ui.username()
1363 ui.username()
1364 except util.Abort, e:
1364 except util.Abort, e:
1365 ui.write(" %s\n" % e)
1365 ui.write(" %s\n" % e)
1366 ui.write(_(" (specify a username in your .hgrc file)\n"))
1366 ui.write(_(" (specify a username in your .hgrc file)\n"))
1367 problems += 1
1367 problems += 1
1368
1368
1369 if not problems:
1369 if not problems:
1370 ui.status(_("No problems detected\n"))
1370 ui.status(_("No problems detected\n"))
1371 else:
1371 else:
1372 ui.write(_("%s problems detected,"
1372 ui.write(_("%s problems detected,"
1373 " please check your install!\n") % problems)
1373 " please check your install!\n") % problems)
1374
1374
1375 return problems
1375 return problems
1376
1376
1377 def debugrename(ui, repo, file1, *pats, **opts):
1377 def debugrename(ui, repo, file1, *pats, **opts):
1378 """dump rename information"""
1378 """dump rename information"""
1379
1379
1380 ctx = repo[opts.get('rev')]
1380 ctx = repo[opts.get('rev')]
1381 m = cmdutil.match(repo, (file1,) + pats, opts)
1381 m = cmdutil.match(repo, (file1,) + pats, opts)
1382 for abs in ctx.walk(m):
1382 for abs in ctx.walk(m):
1383 fctx = ctx[abs]
1383 fctx = ctx[abs]
1384 o = fctx.filelog().renamed(fctx.filenode())
1384 o = fctx.filelog().renamed(fctx.filenode())
1385 rel = m.rel(abs)
1385 rel = m.rel(abs)
1386 if o:
1386 if o:
1387 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1387 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1388 else:
1388 else:
1389 ui.write(_("%s not renamed\n") % rel)
1389 ui.write(_("%s not renamed\n") % rel)
1390
1390
1391 def debugwalk(ui, repo, *pats, **opts):
1391 def debugwalk(ui, repo, *pats, **opts):
1392 """show how files match on given patterns"""
1392 """show how files match on given patterns"""
1393 m = cmdutil.match(repo, pats, opts)
1393 m = cmdutil.match(repo, pats, opts)
1394 items = list(repo.walk(m))
1394 items = list(repo.walk(m))
1395 if not items:
1395 if not items:
1396 return
1396 return
1397 fmt = 'f %%-%ds %%-%ds %%s' % (
1397 fmt = 'f %%-%ds %%-%ds %%s' % (
1398 max([len(abs) for abs in items]),
1398 max([len(abs) for abs in items]),
1399 max([len(m.rel(abs)) for abs in items]))
1399 max([len(m.rel(abs)) for abs in items]))
1400 for abs in items:
1400 for abs in items:
1401 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1401 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1402 ui.write("%s\n" % line.rstrip())
1402 ui.write("%s\n" % line.rstrip())
1403
1403
1404 def diff(ui, repo, *pats, **opts):
1404 def diff(ui, repo, *pats, **opts):
1405 """diff repository (or selected files)
1405 """diff repository (or selected files)
1406
1406
1407 Show differences between revisions for the specified files.
1407 Show differences between revisions for the specified files.
1408
1408
1409 Differences between files are shown using the unified diff format.
1409 Differences between files are shown using the unified diff format.
1410
1410
1411 NOTE: diff may generate unexpected results for merges, as it will
1411 NOTE: diff may generate unexpected results for merges, as it will
1412 default to comparing against the working directory's first parent
1412 default to comparing against the working directory's first parent
1413 changeset if no revisions are specified.
1413 changeset if no revisions are specified.
1414
1414
1415 When two revision arguments are given, then changes are shown
1415 When two revision arguments are given, then changes are shown
1416 between those revisions. If only one revision is specified then
1416 between those revisions. If only one revision is specified then
1417 that revision is compared to the working directory, and, when no
1417 that revision is compared to the working directory, and, when no
1418 revisions are specified, the working directory files are compared
1418 revisions are specified, the working directory files are compared
1419 to its parent.
1419 to its parent.
1420
1420
1421 Alternatively you can specify -c/--change with a revision to see
1421 Alternatively you can specify -c/--change with a revision to see
1422 the changes in that changeset relative to its first parent.
1422 the changes in that changeset relative to its first parent.
1423
1423
1424 Without the -a/--text option, diff will avoid generating diffs of
1424 Without the -a/--text option, diff will avoid generating diffs of
1425 files it detects as binary. With -a, diff will generate a diff
1425 files it detects as binary. With -a, diff will generate a diff
1426 anyway, probably with undesirable results.
1426 anyway, probably with undesirable results.
1427
1427
1428 Use the -g/--git option to generate diffs in the git extended diff
1428 Use the -g/--git option to generate diffs in the git extended diff
1429 format. For more information, read :hg:`help diffs`.
1429 format. For more information, read :hg:`help diffs`.
1430
1430
1431 Returns 0 on success.
1431 Returns 0 on success.
1432 """
1432 """
1433
1433
1434 revs = opts.get('rev')
1434 revs = opts.get('rev')
1435 change = opts.get('change')
1435 change = opts.get('change')
1436 stat = opts.get('stat')
1436 stat = opts.get('stat')
1437 reverse = opts.get('reverse')
1437 reverse = opts.get('reverse')
1438
1438
1439 if revs and change:
1439 if revs and change:
1440 msg = _('cannot specify --rev and --change at the same time')
1440 msg = _('cannot specify --rev and --change at the same time')
1441 raise util.Abort(msg)
1441 raise util.Abort(msg)
1442 elif change:
1442 elif change:
1443 node2 = repo.lookup(change)
1443 node2 = repo.lookup(change)
1444 node1 = repo[node2].parents()[0].node()
1444 node1 = repo[node2].parents()[0].node()
1445 else:
1445 else:
1446 node1, node2 = cmdutil.revpair(repo, revs)
1446 node1, node2 = cmdutil.revpair(repo, revs)
1447
1447
1448 if reverse:
1448 if reverse:
1449 node1, node2 = node2, node1
1449 node1, node2 = node2, node1
1450
1450
1451 diffopts = patch.diffopts(ui, opts)
1451 diffopts = patch.diffopts(ui, opts)
1452 m = cmdutil.match(repo, pats, opts)
1452 m = cmdutil.match(repo, pats, opts)
1453 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1453 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1454
1454
1455 def export(ui, repo, *changesets, **opts):
1455 def export(ui, repo, *changesets, **opts):
1456 """dump the header and diffs for one or more changesets
1456 """dump the header and diffs for one or more changesets
1457
1457
1458 Print the changeset header and diffs for one or more revisions.
1458 Print the changeset header and diffs for one or more revisions.
1459
1459
1460 The information shown in the changeset header is: author, date,
1460 The information shown in the changeset header is: author, date,
1461 branch name (if non-default), changeset hash, parent(s) and commit
1461 branch name (if non-default), changeset hash, parent(s) and commit
1462 comment.
1462 comment.
1463
1463
1464 NOTE: export may generate unexpected diff output for merge
1464 NOTE: export may generate unexpected diff output for merge
1465 changesets, as it will compare the merge changeset against its
1465 changesets, as it will compare the merge changeset against its
1466 first parent only.
1466 first parent only.
1467
1467
1468 Output may be to a file, in which case the name of the file is
1468 Output may be to a file, in which case the name of the file is
1469 given using a format string. The formatting rules are as follows:
1469 given using a format string. The formatting rules are as follows:
1470
1470
1471 :``%%``: literal "%" character
1471 :``%%``: literal "%" character
1472 :``%H``: changeset hash (40 hexadecimal digits)
1472 :``%H``: changeset hash (40 hexadecimal digits)
1473 :``%N``: number of patches being generated
1473 :``%N``: number of patches being generated
1474 :``%R``: changeset revision number
1474 :``%R``: changeset revision number
1475 :``%b``: basename of the exporting repository
1475 :``%b``: basename of the exporting repository
1476 :``%h``: short-form changeset hash (12 hexadecimal digits)
1476 :``%h``: short-form changeset hash (12 hexadecimal digits)
1477 :``%n``: zero-padded sequence number, starting at 1
1477 :``%n``: zero-padded sequence number, starting at 1
1478 :``%r``: zero-padded changeset revision number
1478 :``%r``: zero-padded changeset revision number
1479
1479
1480 Without the -a/--text option, export will avoid generating diffs
1480 Without the -a/--text option, export will avoid generating diffs
1481 of files it detects as binary. With -a, export will generate a
1481 of files it detects as binary. With -a, export will generate a
1482 diff anyway, probably with undesirable results.
1482 diff anyway, probably with undesirable results.
1483
1483
1484 Use the -g/--git option to generate diffs in the git extended diff
1484 Use the -g/--git option to generate diffs in the git extended diff
1485 format. See :hg:`help diffs` for more information.
1485 format. See :hg:`help diffs` for more information.
1486
1486
1487 With the --switch-parent option, the diff will be against the
1487 With the --switch-parent option, the diff will be against the
1488 second parent. It can be useful to review a merge.
1488 second parent. It can be useful to review a merge.
1489
1489
1490 Returns 0 on success.
1490 Returns 0 on success.
1491 """
1491 """
1492 changesets += tuple(opts.get('rev', []))
1492 changesets += tuple(opts.get('rev', []))
1493 if not changesets:
1493 if not changesets:
1494 raise util.Abort(_("export requires at least one changeset"))
1494 raise util.Abort(_("export requires at least one changeset"))
1495 revs = cmdutil.revrange(repo, changesets)
1495 revs = cmdutil.revrange(repo, changesets)
1496 if len(revs) > 1:
1496 if len(revs) > 1:
1497 ui.note(_('exporting patches:\n'))
1497 ui.note(_('exporting patches:\n'))
1498 else:
1498 else:
1499 ui.note(_('exporting patch:\n'))
1499 ui.note(_('exporting patch:\n'))
1500 cmdutil.export(repo, revs, template=opts.get('output'),
1500 cmdutil.export(repo, revs, template=opts.get('output'),
1501 switch_parent=opts.get('switch_parent'),
1501 switch_parent=opts.get('switch_parent'),
1502 opts=patch.diffopts(ui, opts))
1502 opts=patch.diffopts(ui, opts))
1503
1503
1504 def forget(ui, repo, *pats, **opts):
1504 def forget(ui, repo, *pats, **opts):
1505 """forget the specified files on the next commit
1505 """forget the specified files on the next commit
1506
1506
1507 Mark the specified files so they will no longer be tracked
1507 Mark the specified files so they will no longer be tracked
1508 after the next commit.
1508 after the next commit.
1509
1509
1510 This only removes files from the current branch, not from the
1510 This only removes files from the current branch, not from the
1511 entire project history, and it does not delete them from the
1511 entire project history, and it does not delete them from the
1512 working directory.
1512 working directory.
1513
1513
1514 To undo a forget before the next commit, see :hg:`add`.
1514 To undo a forget before the next commit, see :hg:`add`.
1515
1515
1516 Returns 0 on success.
1516 Returns 0 on success.
1517 """
1517 """
1518
1518
1519 if not pats:
1519 if not pats:
1520 raise util.Abort(_('no files specified'))
1520 raise util.Abort(_('no files specified'))
1521
1521
1522 m = cmdutil.match(repo, pats, opts)
1522 m = cmdutil.match(repo, pats, opts)
1523 s = repo.status(match=m, clean=True)
1523 s = repo.status(match=m, clean=True)
1524 forget = sorted(s[0] + s[1] + s[3] + s[6])
1524 forget = sorted(s[0] + s[1] + s[3] + s[6])
1525 errs = 0
1525 errs = 0
1526
1526
1527 for f in m.files():
1527 for f in m.files():
1528 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1528 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1529 ui.warn(_('not removing %s: file is already untracked\n')
1529 ui.warn(_('not removing %s: file is already untracked\n')
1530 % m.rel(f))
1530 % m.rel(f))
1531 errs = 1
1531 errs = 1
1532
1532
1533 for f in forget:
1533 for f in forget:
1534 if ui.verbose or not m.exact(f):
1534 if ui.verbose or not m.exact(f):
1535 ui.status(_('removing %s\n') % m.rel(f))
1535 ui.status(_('removing %s\n') % m.rel(f))
1536
1536
1537 repo[None].remove(forget, unlink=False)
1537 repo[None].remove(forget, unlink=False)
1538 return errs
1538 return errs
1539
1539
1540 def grep(ui, repo, pattern, *pats, **opts):
1540 def grep(ui, repo, pattern, *pats, **opts):
1541 """search for a pattern in specified files and revisions
1541 """search for a pattern in specified files and revisions
1542
1542
1543 Search revisions of files for a regular expression.
1543 Search revisions of files for a regular expression.
1544
1544
1545 This command behaves differently than Unix grep. It only accepts
1545 This command behaves differently than Unix grep. It only accepts
1546 Python/Perl regexps. It searches repository history, not the
1546 Python/Perl regexps. It searches repository history, not the
1547 working directory. It always prints the revision number in which a
1547 working directory. It always prints the revision number in which a
1548 match appears.
1548 match appears.
1549
1549
1550 By default, grep only prints output for the first revision of a
1550 By default, grep only prints output for the first revision of a
1551 file in which it finds a match. To get it to print every revision
1551 file in which it finds a match. To get it to print every revision
1552 that contains a change in match status ("-" for a match that
1552 that contains a change in match status ("-" for a match that
1553 becomes a non-match, or "+" for a non-match that becomes a match),
1553 becomes a non-match, or "+" for a non-match that becomes a match),
1554 use the --all flag.
1554 use the --all flag.
1555
1555
1556 Returns 0 if a match is found, 1 otherwise.
1556 Returns 0 if a match is found, 1 otherwise.
1557 """
1557 """
1558 reflags = 0
1558 reflags = 0
1559 if opts.get('ignore_case'):
1559 if opts.get('ignore_case'):
1560 reflags |= re.I
1560 reflags |= re.I
1561 try:
1561 try:
1562 regexp = re.compile(pattern, reflags)
1562 regexp = re.compile(pattern, reflags)
1563 except Exception, inst:
1563 except Exception, inst:
1564 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1564 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1565 return 1
1565 return 1
1566 sep, eol = ':', '\n'
1566 sep, eol = ':', '\n'
1567 if opts.get('print0'):
1567 if opts.get('print0'):
1568 sep = eol = '\0'
1568 sep = eol = '\0'
1569
1569
1570 getfile = util.lrucachefunc(repo.file)
1570 getfile = util.lrucachefunc(repo.file)
1571
1571
1572 def matchlines(body):
1572 def matchlines(body):
1573 begin = 0
1573 begin = 0
1574 linenum = 0
1574 linenum = 0
1575 while True:
1575 while True:
1576 match = regexp.search(body, begin)
1576 match = regexp.search(body, begin)
1577 if not match:
1577 if not match:
1578 break
1578 break
1579 mstart, mend = match.span()
1579 mstart, mend = match.span()
1580 linenum += body.count('\n', begin, mstart) + 1
1580 linenum += body.count('\n', begin, mstart) + 1
1581 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1581 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1582 begin = body.find('\n', mend) + 1 or len(body)
1582 begin = body.find('\n', mend) + 1 or len(body)
1583 lend = begin - 1
1583 lend = begin - 1
1584 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1584 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1585
1585
1586 class linestate(object):
1586 class linestate(object):
1587 def __init__(self, line, linenum, colstart, colend):
1587 def __init__(self, line, linenum, colstart, colend):
1588 self.line = line
1588 self.line = line
1589 self.linenum = linenum
1589 self.linenum = linenum
1590 self.colstart = colstart
1590 self.colstart = colstart
1591 self.colend = colend
1591 self.colend = colend
1592
1592
1593 def __hash__(self):
1593 def __hash__(self):
1594 return hash((self.linenum, self.line))
1594 return hash((self.linenum, self.line))
1595
1595
1596 def __eq__(self, other):
1596 def __eq__(self, other):
1597 return self.line == other.line
1597 return self.line == other.line
1598
1598
1599 matches = {}
1599 matches = {}
1600 copies = {}
1600 copies = {}
1601 def grepbody(fn, rev, body):
1601 def grepbody(fn, rev, body):
1602 matches[rev].setdefault(fn, [])
1602 matches[rev].setdefault(fn, [])
1603 m = matches[rev][fn]
1603 m = matches[rev][fn]
1604 for lnum, cstart, cend, line in matchlines(body):
1604 for lnum, cstart, cend, line in matchlines(body):
1605 s = linestate(line, lnum, cstart, cend)
1605 s = linestate(line, lnum, cstart, cend)
1606 m.append(s)
1606 m.append(s)
1607
1607
1608 def difflinestates(a, b):
1608 def difflinestates(a, b):
1609 sm = difflib.SequenceMatcher(None, a, b)
1609 sm = difflib.SequenceMatcher(None, a, b)
1610 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1610 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1611 if tag == 'insert':
1611 if tag == 'insert':
1612 for i in xrange(blo, bhi):
1612 for i in xrange(blo, bhi):
1613 yield ('+', b[i])
1613 yield ('+', b[i])
1614 elif tag == 'delete':
1614 elif tag == 'delete':
1615 for i in xrange(alo, ahi):
1615 for i in xrange(alo, ahi):
1616 yield ('-', a[i])
1616 yield ('-', a[i])
1617 elif tag == 'replace':
1617 elif tag == 'replace':
1618 for i in xrange(alo, ahi):
1618 for i in xrange(alo, ahi):
1619 yield ('-', a[i])
1619 yield ('-', a[i])
1620 for i in xrange(blo, bhi):
1620 for i in xrange(blo, bhi):
1621 yield ('+', b[i])
1621 yield ('+', b[i])
1622
1622
1623 def display(fn, ctx, pstates, states):
1623 def display(fn, ctx, pstates, states):
1624 rev = ctx.rev()
1624 rev = ctx.rev()
1625 datefunc = ui.quiet and util.shortdate or util.datestr
1625 datefunc = ui.quiet and util.shortdate or util.datestr
1626 found = False
1626 found = False
1627 filerevmatches = {}
1627 filerevmatches = {}
1628 if opts.get('all'):
1628 if opts.get('all'):
1629 iter = difflinestates(pstates, states)
1629 iter = difflinestates(pstates, states)
1630 else:
1630 else:
1631 iter = [('', l) for l in states]
1631 iter = [('', l) for l in states]
1632 for change, l in iter:
1632 for change, l in iter:
1633 cols = [fn, str(rev)]
1633 cols = [fn, str(rev)]
1634 before, match, after = None, None, None
1634 before, match, after = None, None, None
1635 if opts.get('line_number'):
1635 if opts.get('line_number'):
1636 cols.append(str(l.linenum))
1636 cols.append(str(l.linenum))
1637 if opts.get('all'):
1637 if opts.get('all'):
1638 cols.append(change)
1638 cols.append(change)
1639 if opts.get('user'):
1639 if opts.get('user'):
1640 cols.append(ui.shortuser(ctx.user()))
1640 cols.append(ui.shortuser(ctx.user()))
1641 if opts.get('date'):
1641 if opts.get('date'):
1642 cols.append(datefunc(ctx.date()))
1642 cols.append(datefunc(ctx.date()))
1643 if opts.get('files_with_matches'):
1643 if opts.get('files_with_matches'):
1644 c = (fn, rev)
1644 c = (fn, rev)
1645 if c in filerevmatches:
1645 if c in filerevmatches:
1646 continue
1646 continue
1647 filerevmatches[c] = 1
1647 filerevmatches[c] = 1
1648 else:
1648 else:
1649 before = l.line[:l.colstart]
1649 before = l.line[:l.colstart]
1650 match = l.line[l.colstart:l.colend]
1650 match = l.line[l.colstart:l.colend]
1651 after = l.line[l.colend:]
1651 after = l.line[l.colend:]
1652 ui.write(sep.join(cols))
1652 ui.write(sep.join(cols))
1653 if before is not None:
1653 if before is not None:
1654 ui.write(sep + before)
1654 ui.write(sep + before)
1655 ui.write(match, label='grep.match')
1655 ui.write(match, label='grep.match')
1656 ui.write(after)
1656 ui.write(after)
1657 ui.write(eol)
1657 ui.write(eol)
1658 found = True
1658 found = True
1659 return found
1659 return found
1660
1660
1661 skip = {}
1661 skip = {}
1662 revfiles = {}
1662 revfiles = {}
1663 matchfn = cmdutil.match(repo, pats, opts)
1663 matchfn = cmdutil.match(repo, pats, opts)
1664 found = False
1664 found = False
1665 follow = opts.get('follow')
1665 follow = opts.get('follow')
1666
1666
1667 def prep(ctx, fns):
1667 def prep(ctx, fns):
1668 rev = ctx.rev()
1668 rev = ctx.rev()
1669 pctx = ctx.parents()[0]
1669 pctx = ctx.parents()[0]
1670 parent = pctx.rev()
1670 parent = pctx.rev()
1671 matches.setdefault(rev, {})
1671 matches.setdefault(rev, {})
1672 matches.setdefault(parent, {})
1672 matches.setdefault(parent, {})
1673 files = revfiles.setdefault(rev, [])
1673 files = revfiles.setdefault(rev, [])
1674 for fn in fns:
1674 for fn in fns:
1675 flog = getfile(fn)
1675 flog = getfile(fn)
1676 try:
1676 try:
1677 fnode = ctx.filenode(fn)
1677 fnode = ctx.filenode(fn)
1678 except error.LookupError:
1678 except error.LookupError:
1679 continue
1679 continue
1680
1680
1681 copied = flog.renamed(fnode)
1681 copied = flog.renamed(fnode)
1682 copy = follow and copied and copied[0]
1682 copy = follow and copied and copied[0]
1683 if copy:
1683 if copy:
1684 copies.setdefault(rev, {})[fn] = copy
1684 copies.setdefault(rev, {})[fn] = copy
1685 if fn in skip:
1685 if fn in skip:
1686 if copy:
1686 if copy:
1687 skip[copy] = True
1687 skip[copy] = True
1688 continue
1688 continue
1689 files.append(fn)
1689 files.append(fn)
1690
1690
1691 if fn not in matches[rev]:
1691 if fn not in matches[rev]:
1692 grepbody(fn, rev, flog.read(fnode))
1692 grepbody(fn, rev, flog.read(fnode))
1693
1693
1694 pfn = copy or fn
1694 pfn = copy or fn
1695 if pfn not in matches[parent]:
1695 if pfn not in matches[parent]:
1696 try:
1696 try:
1697 fnode = pctx.filenode(pfn)
1697 fnode = pctx.filenode(pfn)
1698 grepbody(pfn, parent, flog.read(fnode))
1698 grepbody(pfn, parent, flog.read(fnode))
1699 except error.LookupError:
1699 except error.LookupError:
1700 pass
1700 pass
1701
1701
1702 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1702 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1703 rev = ctx.rev()
1703 rev = ctx.rev()
1704 parent = ctx.parents()[0].rev()
1704 parent = ctx.parents()[0].rev()
1705 for fn in sorted(revfiles.get(rev, [])):
1705 for fn in sorted(revfiles.get(rev, [])):
1706 states = matches[rev][fn]
1706 states = matches[rev][fn]
1707 copy = copies.get(rev, {}).get(fn)
1707 copy = copies.get(rev, {}).get(fn)
1708 if fn in skip:
1708 if fn in skip:
1709 if copy:
1709 if copy:
1710 skip[copy] = True
1710 skip[copy] = True
1711 continue
1711 continue
1712 pstates = matches.get(parent, {}).get(copy or fn, [])
1712 pstates = matches.get(parent, {}).get(copy or fn, [])
1713 if pstates or states:
1713 if pstates or states:
1714 r = display(fn, ctx, pstates, states)
1714 r = display(fn, ctx, pstates, states)
1715 found = found or r
1715 found = found or r
1716 if r and not opts.get('all'):
1716 if r and not opts.get('all'):
1717 skip[fn] = True
1717 skip[fn] = True
1718 if copy:
1718 if copy:
1719 skip[copy] = True
1719 skip[copy] = True
1720 del matches[rev]
1720 del matches[rev]
1721 del revfiles[rev]
1721 del revfiles[rev]
1722
1722
1723 return not found
1723 return not found
1724
1724
1725 def heads(ui, repo, *branchrevs, **opts):
1725 def heads(ui, repo, *branchrevs, **opts):
1726 """show current repository heads or show branch heads
1726 """show current repository heads or show branch heads
1727
1727
1728 With no arguments, show all repository branch heads.
1728 With no arguments, show all repository branch heads.
1729
1729
1730 Repository "heads" are changesets with no child changesets. They are
1730 Repository "heads" are changesets with no child changesets. They are
1731 where development generally takes place and are the usual targets
1731 where development generally takes place and are the usual targets
1732 for update and merge operations. Branch heads are changesets that have
1732 for update and merge operations. Branch heads are changesets that have
1733 no child changeset on the same branch.
1733 no child changeset on the same branch.
1734
1734
1735 If one or more REVs are given, only branch heads on the branches
1735 If one or more REVs are given, only branch heads on the branches
1736 associated with the specified changesets are shown.
1736 associated with the specified changesets are shown.
1737
1737
1738 If -c/--closed is specified, also show branch heads marked closed
1738 If -c/--closed is specified, also show branch heads marked closed
1739 (see :hg:`commit --close-branch`).
1739 (see :hg:`commit --close-branch`).
1740
1740
1741 If STARTREV is specified, only those heads that are descendants of
1741 If STARTREV is specified, only those heads that are descendants of
1742 STARTREV will be displayed.
1742 STARTREV will be displayed.
1743
1743
1744 If -t/--topo is specified, named branch mechanics will be ignored and only
1744 If -t/--topo is specified, named branch mechanics will be ignored and only
1745 changesets without children will be shown.
1745 changesets without children will be shown.
1746
1746
1747 Returns 0 if matching heads are found, 1 if not.
1747 Returns 0 if matching heads are found, 1 if not.
1748 """
1748 """
1749
1749
1750 if opts.get('rev'):
1750 if opts.get('rev'):
1751 start = repo.lookup(opts['rev'])
1751 start = repo.lookup(opts['rev'])
1752 else:
1752 else:
1753 start = None
1753 start = None
1754
1754
1755 if opts.get('topo'):
1755 if opts.get('topo'):
1756 heads = [repo[h] for h in repo.heads(start)]
1756 heads = [repo[h] for h in repo.heads(start)]
1757 else:
1757 else:
1758 heads = []
1758 heads = []
1759 for b, ls in repo.branchmap().iteritems():
1759 for b, ls in repo.branchmap().iteritems():
1760 if start is None:
1760 if start is None:
1761 heads += [repo[h] for h in ls]
1761 heads += [repo[h] for h in ls]
1762 continue
1762 continue
1763 startrev = repo.changelog.rev(start)
1763 startrev = repo.changelog.rev(start)
1764 descendants = set(repo.changelog.descendants(startrev))
1764 descendants = set(repo.changelog.descendants(startrev))
1765 descendants.add(startrev)
1765 descendants.add(startrev)
1766 rev = repo.changelog.rev
1766 rev = repo.changelog.rev
1767 heads += [repo[h] for h in ls if rev(h) in descendants]
1767 heads += [repo[h] for h in ls if rev(h) in descendants]
1768
1768
1769 if branchrevs:
1769 if branchrevs:
1770 decode, encode = encoding.fromlocal, encoding.tolocal
1770 decode, encode = encoding.fromlocal, encoding.tolocal
1771 branches = set(repo[decode(br)].branch() for br in branchrevs)
1771 branches = set(repo[decode(br)].branch() for br in branchrevs)
1772 heads = [h for h in heads if h.branch() in branches]
1772 heads = [h for h in heads if h.branch() in branches]
1773
1773
1774 if not opts.get('closed'):
1774 if not opts.get('closed'):
1775 heads = [h for h in heads if not h.extra().get('close')]
1775 heads = [h for h in heads if not h.extra().get('close')]
1776
1776
1777 if opts.get('active') and branchrevs:
1777 if opts.get('active') and branchrevs:
1778 dagheads = repo.heads(start)
1778 dagheads = repo.heads(start)
1779 heads = [h for h in heads if h.node() in dagheads]
1779 heads = [h for h in heads if h.node() in dagheads]
1780
1780
1781 if branchrevs:
1781 if branchrevs:
1782 haveheads = set(h.branch() for h in heads)
1782 haveheads = set(h.branch() for h in heads)
1783 if branches - haveheads:
1783 if branches - haveheads:
1784 headless = ', '.join(encode(b) for b in branches - haveheads)
1784 headless = ', '.join(encode(b) for b in branches - haveheads)
1785 msg = _('no open branch heads found on branches %s')
1785 msg = _('no open branch heads found on branches %s')
1786 if opts.get('rev'):
1786 if opts.get('rev'):
1787 msg += _(' (started at %s)' % opts['rev'])
1787 msg += _(' (started at %s)' % opts['rev'])
1788 ui.warn((msg + '\n') % headless)
1788 ui.warn((msg + '\n') % headless)
1789
1789
1790 if not heads:
1790 if not heads:
1791 return 1
1791 return 1
1792
1792
1793 heads = sorted(heads, key=lambda x: -x.rev())
1793 heads = sorted(heads, key=lambda x: -x.rev())
1794 displayer = cmdutil.show_changeset(ui, repo, opts)
1794 displayer = cmdutil.show_changeset(ui, repo, opts)
1795 for ctx in heads:
1795 for ctx in heads:
1796 displayer.show(ctx)
1796 displayer.show(ctx)
1797 displayer.close()
1797 displayer.close()
1798
1798
1799 def help_(ui, name=None, with_version=False, unknowncmd=False):
1799 def help_(ui, name=None, with_version=False, unknowncmd=False):
1800 """show help for a given topic or a help overview
1800 """show help for a given topic or a help overview
1801
1801
1802 With no arguments, print a list of commands with short help messages.
1802 With no arguments, print a list of commands with short help messages.
1803
1803
1804 Given a topic, extension, or command name, print help for that
1804 Given a topic, extension, or command name, print help for that
1805 topic.
1805 topic.
1806
1806
1807 Returns 0 if successful.
1807 Returns 0 if successful.
1808 """
1808 """
1809 option_lists = []
1809 option_lists = []
1810 textwidth = util.termwidth() - 2
1810 textwidth = util.termwidth() - 2
1811
1811
1812 def addglobalopts(aliases):
1812 def addglobalopts(aliases):
1813 if ui.verbose:
1813 if ui.verbose:
1814 option_lists.append((_("global options:"), globalopts))
1814 option_lists.append((_("global options:"), globalopts))
1815 if name == 'shortlist':
1815 if name == 'shortlist':
1816 option_lists.append((_('use "hg help" for the full list '
1816 option_lists.append((_('use "hg help" for the full list '
1817 'of commands'), ()))
1817 'of commands'), ()))
1818 else:
1818 else:
1819 if name == 'shortlist':
1819 if name == 'shortlist':
1820 msg = _('use "hg help" for the full list of commands '
1820 msg = _('use "hg help" for the full list of commands '
1821 'or "hg -v" for details')
1821 'or "hg -v" for details')
1822 elif aliases:
1822 elif aliases:
1823 msg = _('use "hg -v help%s" to show aliases and '
1823 msg = _('use "hg -v help%s" to show aliases and '
1824 'global options') % (name and " " + name or "")
1824 'global options') % (name and " " + name or "")
1825 else:
1825 else:
1826 msg = _('use "hg -v help %s" to show global options') % name
1826 msg = _('use "hg -v help %s" to show global options') % name
1827 option_lists.append((msg, ()))
1827 option_lists.append((msg, ()))
1828
1828
1829 def helpcmd(name):
1829 def helpcmd(name):
1830 if with_version:
1830 if with_version:
1831 version_(ui)
1831 version_(ui)
1832 ui.write('\n')
1832 ui.write('\n')
1833
1833
1834 try:
1834 try:
1835 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1835 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1836 except error.AmbiguousCommand, inst:
1836 except error.AmbiguousCommand, inst:
1837 # py3k fix: except vars can't be used outside the scope of the
1837 # py3k fix: except vars can't be used outside the scope of the
1838 # except block, nor can be used inside a lambda. python issue4617
1838 # except block, nor can be used inside a lambda. python issue4617
1839 prefix = inst.args[0]
1839 prefix = inst.args[0]
1840 select = lambda c: c.lstrip('^').startswith(prefix)
1840 select = lambda c: c.lstrip('^').startswith(prefix)
1841 helplist(_('list of commands:\n\n'), select)
1841 helplist(_('list of commands:\n\n'), select)
1842 return
1842 return
1843
1843
1844 # check if it's an invalid alias and display its error if it is
1844 # check if it's an invalid alias and display its error if it is
1845 if getattr(entry[0], 'badalias', False):
1845 if getattr(entry[0], 'badalias', False):
1846 if not unknowncmd:
1846 if not unknowncmd:
1847 entry[0](ui)
1847 entry[0](ui)
1848 return
1848 return
1849
1849
1850 # synopsis
1850 # synopsis
1851 if len(entry) > 2:
1851 if len(entry) > 2:
1852 if entry[2].startswith('hg'):
1852 if entry[2].startswith('hg'):
1853 ui.write("%s\n" % entry[2])
1853 ui.write("%s\n" % entry[2])
1854 else:
1854 else:
1855 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1855 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1856 else:
1856 else:
1857 ui.write('hg %s\n' % aliases[0])
1857 ui.write('hg %s\n' % aliases[0])
1858
1858
1859 # aliases
1859 # aliases
1860 if not ui.quiet and len(aliases) > 1:
1860 if not ui.quiet and len(aliases) > 1:
1861 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1861 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1862
1862
1863 # description
1863 # description
1864 doc = gettext(entry[0].__doc__)
1864 doc = gettext(entry[0].__doc__)
1865 if not doc:
1865 if not doc:
1866 doc = _("(no help text available)")
1866 doc = _("(no help text available)")
1867 if hasattr(entry[0], 'definition'): # aliased command
1867 if hasattr(entry[0], 'definition'): # aliased command
1868 if entry[0].definition.startswith('!'): # shell alias
1868 if entry[0].definition.startswith('!'): # shell alias
1869 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1869 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1870 else:
1870 else:
1871 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1871 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1872 if ui.quiet:
1872 if ui.quiet:
1873 doc = doc.splitlines()[0]
1873 doc = doc.splitlines()[0]
1874 keep = ui.verbose and ['verbose'] or []
1874 keep = ui.verbose and ['verbose'] or []
1875 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1875 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1876 ui.write("\n%s\n" % formatted)
1876 ui.write("\n%s\n" % formatted)
1877 if pruned:
1877 if pruned:
1878 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1878 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1879
1879
1880 if not ui.quiet:
1880 if not ui.quiet:
1881 # options
1881 # options
1882 if entry[1]:
1882 if entry[1]:
1883 option_lists.append((_("options:\n"), entry[1]))
1883 option_lists.append((_("options:\n"), entry[1]))
1884
1884
1885 addglobalopts(False)
1885 addglobalopts(False)
1886
1886
1887 def helplist(header, select=None):
1887 def helplist(header, select=None):
1888 h = {}
1888 h = {}
1889 cmds = {}
1889 cmds = {}
1890 for c, e in table.iteritems():
1890 for c, e in table.iteritems():
1891 f = c.split("|", 1)[0]
1891 f = c.split("|", 1)[0]
1892 if select and not select(f):
1892 if select and not select(f):
1893 continue
1893 continue
1894 if (not select and name != 'shortlist' and
1894 if (not select and name != 'shortlist' and
1895 e[0].__module__ != __name__):
1895 e[0].__module__ != __name__):
1896 continue
1896 continue
1897 if name == "shortlist" and not f.startswith("^"):
1897 if name == "shortlist" and not f.startswith("^"):
1898 continue
1898 continue
1899 f = f.lstrip("^")
1899 f = f.lstrip("^")
1900 if not ui.debugflag and f.startswith("debug"):
1900 if not ui.debugflag and f.startswith("debug"):
1901 continue
1901 continue
1902 doc = e[0].__doc__
1902 doc = e[0].__doc__
1903 if doc and 'DEPRECATED' in doc and not ui.verbose:
1903 if doc and 'DEPRECATED' in doc and not ui.verbose:
1904 continue
1904 continue
1905 doc = gettext(doc)
1905 doc = gettext(doc)
1906 if not doc:
1906 if not doc:
1907 doc = _("(no help text available)")
1907 doc = _("(no help text available)")
1908 h[f] = doc.splitlines()[0].rstrip()
1908 h[f] = doc.splitlines()[0].rstrip()
1909 cmds[f] = c.lstrip("^")
1909 cmds[f] = c.lstrip("^")
1910
1910
1911 if not h:
1911 if not h:
1912 ui.status(_('no commands defined\n'))
1912 ui.status(_('no commands defined\n'))
1913 return
1913 return
1914
1914
1915 ui.status(header)
1915 ui.status(header)
1916 fns = sorted(h)
1916 fns = sorted(h)
1917 m = max(map(len, fns))
1917 m = max(map(len, fns))
1918 for f in fns:
1918 for f in fns:
1919 if ui.verbose:
1919 if ui.verbose:
1920 commands = cmds[f].replace("|",", ")
1920 commands = cmds[f].replace("|",", ")
1921 ui.write(" %s:\n %s\n"%(commands, h[f]))
1921 ui.write(" %s:\n %s\n"%(commands, h[f]))
1922 else:
1922 else:
1923 ui.write('%s\n' % (util.wrap(h[f],
1923 ui.write('%s\n' % (util.wrap(h[f],
1924 initindent=' %-*s ' % (m, f),
1924 initindent=' %-*s ' % (m, f),
1925 hangindent=' ' * (m + 4))))
1925 hangindent=' ' * (m + 4))))
1926
1926
1927 if not ui.quiet:
1927 if not ui.quiet:
1928 addglobalopts(True)
1928 addglobalopts(True)
1929
1929
1930 def helptopic(name):
1930 def helptopic(name):
1931 for names, header, doc in help.helptable:
1931 for names, header, doc in help.helptable:
1932 if name in names:
1932 if name in names:
1933 break
1933 break
1934 else:
1934 else:
1935 raise error.UnknownCommand(name)
1935 raise error.UnknownCommand(name)
1936
1936
1937 # description
1937 # description
1938 if not doc:
1938 if not doc:
1939 doc = _("(no help text available)")
1939 doc = _("(no help text available)")
1940 if hasattr(doc, '__call__'):
1940 if hasattr(doc, '__call__'):
1941 doc = doc()
1941 doc = doc()
1942
1942
1943 ui.write("%s\n\n" % header)
1943 ui.write("%s\n\n" % header)
1944 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1944 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1945
1945
1946 def helpext(name):
1946 def helpext(name):
1947 try:
1947 try:
1948 mod = extensions.find(name)
1948 mod = extensions.find(name)
1949 doc = gettext(mod.__doc__) or _('no help text available')
1949 doc = gettext(mod.__doc__) or _('no help text available')
1950 except KeyError:
1950 except KeyError:
1951 mod = None
1951 mod = None
1952 doc = extensions.disabledext(name)
1952 doc = extensions.disabledext(name)
1953 if not doc:
1953 if not doc:
1954 raise error.UnknownCommand(name)
1954 raise error.UnknownCommand(name)
1955
1955
1956 if '\n' not in doc:
1956 if '\n' not in doc:
1957 head, tail = doc, ""
1957 head, tail = doc, ""
1958 else:
1958 else:
1959 head, tail = doc.split('\n', 1)
1959 head, tail = doc.split('\n', 1)
1960 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1960 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1961 if tail:
1961 if tail:
1962 ui.write(minirst.format(tail, textwidth))
1962 ui.write(minirst.format(tail, textwidth))
1963 ui.status('\n\n')
1963 ui.status('\n\n')
1964
1964
1965 if mod:
1965 if mod:
1966 try:
1966 try:
1967 ct = mod.cmdtable
1967 ct = mod.cmdtable
1968 except AttributeError:
1968 except AttributeError:
1969 ct = {}
1969 ct = {}
1970 modcmds = set([c.split('|', 1)[0] for c in ct])
1970 modcmds = set([c.split('|', 1)[0] for c in ct])
1971 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1971 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1972 else:
1972 else:
1973 ui.write(_('use "hg help extensions" for information on enabling '
1973 ui.write(_('use "hg help extensions" for information on enabling '
1974 'extensions\n'))
1974 'extensions\n'))
1975
1975
1976 def helpextcmd(name):
1976 def helpextcmd(name):
1977 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1977 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1978 doc = gettext(mod.__doc__).splitlines()[0]
1978 doc = gettext(mod.__doc__).splitlines()[0]
1979
1979
1980 msg = help.listexts(_("'%s' is provided by the following "
1980 msg = help.listexts(_("'%s' is provided by the following "
1981 "extension:") % cmd, {ext: doc}, len(ext),
1981 "extension:") % cmd, {ext: doc}, len(ext),
1982 indent=4)
1982 indent=4)
1983 ui.write(minirst.format(msg, textwidth))
1983 ui.write(minirst.format(msg, textwidth))
1984 ui.write('\n\n')
1984 ui.write('\n\n')
1985 ui.write(_('use "hg help extensions" for information on enabling '
1985 ui.write(_('use "hg help extensions" for information on enabling '
1986 'extensions\n'))
1986 'extensions\n'))
1987
1987
1988 if name and name != 'shortlist':
1988 if name and name != 'shortlist':
1989 i = None
1989 i = None
1990 if unknowncmd:
1990 if unknowncmd:
1991 queries = (helpextcmd,)
1991 queries = (helpextcmd,)
1992 else:
1992 else:
1993 queries = (helptopic, helpcmd, helpext, helpextcmd)
1993 queries = (helptopic, helpcmd, helpext, helpextcmd)
1994 for f in queries:
1994 for f in queries:
1995 try:
1995 try:
1996 f(name)
1996 f(name)
1997 i = None
1997 i = None
1998 break
1998 break
1999 except error.UnknownCommand, inst:
1999 except error.UnknownCommand, inst:
2000 i = inst
2000 i = inst
2001 if i:
2001 if i:
2002 raise i
2002 raise i
2003
2003
2004 else:
2004 else:
2005 # program name
2005 # program name
2006 if ui.verbose or with_version:
2006 if ui.verbose or with_version:
2007 version_(ui)
2007 version_(ui)
2008 else:
2008 else:
2009 ui.status(_("Mercurial Distributed SCM\n"))
2009 ui.status(_("Mercurial Distributed SCM\n"))
2010 ui.status('\n')
2010 ui.status('\n')
2011
2011
2012 # list of commands
2012 # list of commands
2013 if name == "shortlist":
2013 if name == "shortlist":
2014 header = _('basic commands:\n\n')
2014 header = _('basic commands:\n\n')
2015 else:
2015 else:
2016 header = _('list of commands:\n\n')
2016 header = _('list of commands:\n\n')
2017
2017
2018 helplist(header)
2018 helplist(header)
2019 if name != 'shortlist':
2019 if name != 'shortlist':
2020 exts, maxlength = extensions.enabled()
2020 exts, maxlength = extensions.enabled()
2021 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2021 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2022 if text:
2022 if text:
2023 ui.write("\n%s\n" % minirst.format(text, textwidth))
2023 ui.write("\n%s\n" % minirst.format(text, textwidth))
2024
2024
2025 # list all option lists
2025 # list all option lists
2026 opt_output = []
2026 opt_output = []
2027 multioccur = False
2027 multioccur = False
2028 for title, options in option_lists:
2028 for title, options in option_lists:
2029 opt_output.append(("\n%s" % title, None))
2029 opt_output.append(("\n%s" % title, None))
2030 for option in options:
2030 for option in options:
2031 if len(option) == 5:
2031 if len(option) == 5:
2032 shortopt, longopt, default, desc, optlabel = option
2032 shortopt, longopt, default, desc, optlabel = option
2033 else:
2033 else:
2034 shortopt, longopt, default, desc = option
2034 shortopt, longopt, default, desc = option
2035 optlabel = _("VALUE") # default label
2035 optlabel = _("VALUE") # default label
2036
2036
2037 if _("DEPRECATED") in desc and not ui.verbose:
2037 if _("DEPRECATED") in desc and not ui.verbose:
2038 continue
2038 continue
2039 if isinstance(default, list):
2039 if isinstance(default, list):
2040 numqualifier = " %s [+]" % optlabel
2040 numqualifier = " %s [+]" % optlabel
2041 multioccur = True
2041 multioccur = True
2042 elif (default is not None) and not isinstance(default, bool):
2042 elif (default is not None) and not isinstance(default, bool):
2043 numqualifier = " %s" % optlabel
2043 numqualifier = " %s" % optlabel
2044 else:
2044 else:
2045 numqualifier = ""
2045 numqualifier = ""
2046 opt_output.append(("%2s%s" %
2046 opt_output.append(("%2s%s" %
2047 (shortopt and "-%s" % shortopt,
2047 (shortopt and "-%s" % shortopt,
2048 longopt and " --%s%s" %
2048 longopt and " --%s%s" %
2049 (longopt, numqualifier)),
2049 (longopt, numqualifier)),
2050 "%s%s" % (desc,
2050 "%s%s" % (desc,
2051 default
2051 default
2052 and _(" (default: %s)") % default
2052 and _(" (default: %s)") % default
2053 or "")))
2053 or "")))
2054 if multioccur:
2054 if multioccur:
2055 msg = _("\n[+] marked option can be specified multiple times")
2055 msg = _("\n[+] marked option can be specified multiple times")
2056 if ui.verbose and name != 'shortlist':
2056 if ui.verbose and name != 'shortlist':
2057 opt_output.append((msg, None))
2057 opt_output.append((msg, None))
2058 else:
2058 else:
2059 opt_output.insert(-1, (msg, None))
2059 opt_output.insert(-1, (msg, None))
2060
2060
2061 if not name:
2061 if not name:
2062 ui.write(_("\nadditional help topics:\n\n"))
2062 ui.write(_("\nadditional help topics:\n\n"))
2063 topics = []
2063 topics = []
2064 for names, header, doc in help.helptable:
2064 for names, header, doc in help.helptable:
2065 topics.append((sorted(names, key=len, reverse=True)[0], header))
2065 topics.append((sorted(names, key=len, reverse=True)[0], header))
2066 topics_len = max([len(s[0]) for s in topics])
2066 topics_len = max([len(s[0]) for s in topics])
2067 for t, desc in topics:
2067 for t, desc in topics:
2068 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2068 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2069
2069
2070 if opt_output:
2070 if opt_output:
2071 colwidth = encoding.colwidth
2071 colwidth = encoding.colwidth
2072 # normalize: (opt or message, desc or None, width of opt)
2072 # normalize: (opt or message, desc or None, width of opt)
2073 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2073 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2074 for opt, desc in opt_output]
2074 for opt, desc in opt_output]
2075 hanging = max([e[2] for e in entries])
2075 hanging = max([e[2] for e in entries])
2076 for opt, desc, width in entries:
2076 for opt, desc, width in entries:
2077 if desc:
2077 if desc:
2078 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2078 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2079 hangindent = ' ' * (hanging + 3)
2079 hangindent = ' ' * (hanging + 3)
2080 ui.write('%s\n' % (util.wrap(desc,
2080 ui.write('%s\n' % (util.wrap(desc,
2081 initindent=initindent,
2081 initindent=initindent,
2082 hangindent=hangindent)))
2082 hangindent=hangindent)))
2083 else:
2083 else:
2084 ui.write("%s\n" % opt)
2084 ui.write("%s\n" % opt)
2085
2085
2086 def identify(ui, repo, source=None,
2086 def identify(ui, repo, source=None,
2087 rev=None, num=None, id=None, branch=None, tags=None):
2087 rev=None, num=None, id=None, branch=None, tags=None):
2088 """identify the working copy or specified revision
2088 """identify the working copy or specified revision
2089
2089
2090 With no revision, print a summary of the current state of the
2090 With no revision, print a summary of the current state of the
2091 repository.
2091 repository.
2092
2092
2093 Specifying a path to a repository root or Mercurial bundle will
2093 Specifying a path to a repository root or Mercurial bundle will
2094 cause lookup to operate on that repository/bundle.
2094 cause lookup to operate on that repository/bundle.
2095
2095
2096 This summary identifies the repository state using one or two
2096 This summary identifies the repository state using one or two
2097 parent hash identifiers, followed by a "+" if there are
2097 parent hash identifiers, followed by a "+" if there are
2098 uncommitted changes in the working directory, a list of tags for
2098 uncommitted changes in the working directory, a list of tags for
2099 this revision and a branch name for non-default branches.
2099 this revision and a branch name for non-default branches.
2100
2100
2101 Returns 0 if successful.
2101 Returns 0 if successful.
2102 """
2102 """
2103
2103
2104 if not repo and not source:
2104 if not repo and not source:
2105 raise util.Abort(_("there is no Mercurial repository here "
2105 raise util.Abort(_("there is no Mercurial repository here "
2106 "(.hg not found)"))
2106 "(.hg not found)"))
2107
2107
2108 hexfunc = ui.debugflag and hex or short
2108 hexfunc = ui.debugflag and hex or short
2109 default = not (num or id or branch or tags)
2109 default = not (num or id or branch or tags)
2110 output = []
2110 output = []
2111
2111
2112 revs = []
2112 revs = []
2113 if source:
2113 if source:
2114 source, branches = hg.parseurl(ui.expandpath(source))
2114 source, branches = hg.parseurl(ui.expandpath(source))
2115 repo = hg.repository(ui, source)
2115 repo = hg.repository(ui, source)
2116 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2116 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2117
2117
2118 if not repo.local():
2118 if not repo.local():
2119 if not rev and revs:
2119 if not rev and revs:
2120 rev = revs[0]
2120 rev = revs[0]
2121 if not rev:
2121 if not rev:
2122 rev = "tip"
2122 rev = "tip"
2123 if num or branch or tags:
2123 if num or branch or tags:
2124 raise util.Abort(
2124 raise util.Abort(
2125 "can't query remote revision number, branch, or tags")
2125 "can't query remote revision number, branch, or tags")
2126 output = [hexfunc(repo.lookup(rev))]
2126 output = [hexfunc(repo.lookup(rev))]
2127 elif not rev:
2127 elif not rev:
2128 ctx = repo[None]
2128 ctx = repo[None]
2129 parents = ctx.parents()
2129 parents = ctx.parents()
2130 changed = False
2130 changed = False
2131 if default or id or num:
2131 if default or id or num:
2132 changed = util.any(repo.status())
2132 changed = util.any(repo.status())
2133 if default or id:
2133 if default or id:
2134 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2134 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2135 (changed) and "+" or "")]
2135 (changed) and "+" or "")]
2136 if num:
2136 if num:
2137 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2137 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2138 (changed) and "+" or ""))
2138 (changed) and "+" or ""))
2139 else:
2139 else:
2140 ctx = repo[rev]
2140 ctx = repo[rev]
2141 if default or id:
2141 if default or id:
2142 output = [hexfunc(ctx.node())]
2142 output = [hexfunc(ctx.node())]
2143 if num:
2143 if num:
2144 output.append(str(ctx.rev()))
2144 output.append(str(ctx.rev()))
2145
2145
2146 if repo.local() and default and not ui.quiet:
2146 if repo.local() and default and not ui.quiet:
2147 b = encoding.tolocal(ctx.branch())
2147 b = encoding.tolocal(ctx.branch())
2148 if b != 'default':
2148 if b != 'default':
2149 output.append("(%s)" % b)
2149 output.append("(%s)" % b)
2150
2150
2151 # multiple tags for a single parent separated by '/'
2151 # multiple tags for a single parent separated by '/'
2152 t = "/".join(ctx.tags())
2152 t = "/".join(ctx.tags())
2153 if t:
2153 if t:
2154 output.append(t)
2154 output.append(t)
2155
2155
2156 if branch:
2156 if branch:
2157 output.append(encoding.tolocal(ctx.branch()))
2157 output.append(encoding.tolocal(ctx.branch()))
2158
2158
2159 if tags:
2159 if tags:
2160 output.extend(ctx.tags())
2160 output.extend(ctx.tags())
2161
2161
2162 ui.write("%s\n" % ' '.join(output))
2162 ui.write("%s\n" % ' '.join(output))
2163
2163
2164 def import_(ui, repo, patch1, *patches, **opts):
2164 def import_(ui, repo, patch1, *patches, **opts):
2165 """import an ordered set of patches
2165 """import an ordered set of patches
2166
2166
2167 Import a list of patches and commit them individually (unless
2167 Import a list of patches and commit them individually (unless
2168 --no-commit is specified).
2168 --no-commit is specified).
2169
2169
2170 If there are outstanding changes in the working directory, import
2170 If there are outstanding changes in the working directory, import
2171 will abort unless given the -f/--force flag.
2171 will abort unless given the -f/--force flag.
2172
2172
2173 You can import a patch straight from a mail message. Even patches
2173 You can import a patch straight from a mail message. Even patches
2174 as attachments work (to use the body part, it must have type
2174 as attachments work (to use the body part, it must have type
2175 text/plain or text/x-patch). From and Subject headers of email
2175 text/plain or text/x-patch). From and Subject headers of email
2176 message are used as default committer and commit message. All
2176 message are used as default committer and commit message. All
2177 text/plain body parts before first diff are added to commit
2177 text/plain body parts before first diff are added to commit
2178 message.
2178 message.
2179
2179
2180 If the imported patch was generated by :hg:`export`, user and
2180 If the imported patch was generated by :hg:`export`, user and
2181 description from patch override values from message headers and
2181 description from patch override values from message headers and
2182 body. Values given on command line with -m/--message and -u/--user
2182 body. Values given on command line with -m/--message and -u/--user
2183 override these.
2183 override these.
2184
2184
2185 If --exact is specified, import will set the working directory to
2185 If --exact is specified, import will set the working directory to
2186 the parent of each patch before applying it, and will abort if the
2186 the parent of each patch before applying it, and will abort if the
2187 resulting changeset has a different ID than the one recorded in
2187 resulting changeset has a different ID than the one recorded in
2188 the patch. This may happen due to character set problems or other
2188 the patch. This may happen due to character set problems or other
2189 deficiencies in the text patch format.
2189 deficiencies in the text patch format.
2190
2190
2191 With -s/--similarity, hg will attempt to discover renames and
2191 With -s/--similarity, hg will attempt to discover renames and
2192 copies in the patch in the same way as 'addremove'.
2192 copies in the patch in the same way as 'addremove'.
2193
2193
2194 To read a patch from standard input, use "-" as the patch name. If
2194 To read a patch from standard input, use "-" as the patch name. If
2195 a URL is specified, the patch will be downloaded from it.
2195 a URL is specified, the patch will be downloaded from it.
2196 See :hg:`help dates` for a list of formats valid for -d/--date.
2196 See :hg:`help dates` for a list of formats valid for -d/--date.
2197
2197
2198 Returns 0 on success.
2198 Returns 0 on success.
2199 """
2199 """
2200 patches = (patch1,) + patches
2200 patches = (patch1,) + patches
2201
2201
2202 date = opts.get('date')
2202 date = opts.get('date')
2203 if date:
2203 if date:
2204 opts['date'] = util.parsedate(date)
2204 opts['date'] = util.parsedate(date)
2205
2205
2206 try:
2206 try:
2207 sim = float(opts.get('similarity') or 0)
2207 sim = float(opts.get('similarity') or 0)
2208 except ValueError:
2208 except ValueError:
2209 raise util.Abort(_('similarity must be a number'))
2209 raise util.Abort(_('similarity must be a number'))
2210 if sim < 0 or sim > 100:
2210 if sim < 0 or sim > 100:
2211 raise util.Abort(_('similarity must be between 0 and 100'))
2211 raise util.Abort(_('similarity must be between 0 and 100'))
2212
2212
2213 if opts.get('exact') or not opts.get('force'):
2213 if opts.get('exact') or not opts.get('force'):
2214 cmdutil.bail_if_changed(repo)
2214 cmdutil.bail_if_changed(repo)
2215
2215
2216 d = opts["base"]
2216 d = opts["base"]
2217 strip = opts["strip"]
2217 strip = opts["strip"]
2218 wlock = lock = None
2218 wlock = lock = None
2219
2219
2220 def tryone(ui, hunk):
2220 def tryone(ui, hunk):
2221 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2221 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2222 patch.extract(ui, hunk)
2222 patch.extract(ui, hunk)
2223
2223
2224 if not tmpname:
2224 if not tmpname:
2225 return None
2225 return None
2226 commitid = _('to working directory')
2226 commitid = _('to working directory')
2227
2227
2228 try:
2228 try:
2229 cmdline_message = cmdutil.logmessage(opts)
2229 cmdline_message = cmdutil.logmessage(opts)
2230 if cmdline_message:
2230 if cmdline_message:
2231 # pickup the cmdline msg
2231 # pickup the cmdline msg
2232 message = cmdline_message
2232 message = cmdline_message
2233 elif message:
2233 elif message:
2234 # pickup the patch msg
2234 # pickup the patch msg
2235 message = message.strip()
2235 message = message.strip()
2236 else:
2236 else:
2237 # launch the editor
2237 # launch the editor
2238 message = None
2238 message = None
2239 ui.debug('message:\n%s\n' % message)
2239 ui.debug('message:\n%s\n' % message)
2240
2240
2241 wp = repo.parents()
2241 wp = repo.parents()
2242 if opts.get('exact'):
2242 if opts.get('exact'):
2243 if not nodeid or not p1:
2243 if not nodeid or not p1:
2244 raise util.Abort(_('not a Mercurial patch'))
2244 raise util.Abort(_('not a Mercurial patch'))
2245 p1 = repo.lookup(p1)
2245 p1 = repo.lookup(p1)
2246 p2 = repo.lookup(p2 or hex(nullid))
2246 p2 = repo.lookup(p2 or hex(nullid))
2247
2247
2248 if p1 != wp[0].node():
2248 if p1 != wp[0].node():
2249 hg.clean(repo, p1)
2249 hg.clean(repo, p1)
2250 repo.dirstate.setparents(p1, p2)
2250 repo.dirstate.setparents(p1, p2)
2251 elif p2:
2251 elif p2:
2252 try:
2252 try:
2253 p1 = repo.lookup(p1)
2253 p1 = repo.lookup(p1)
2254 p2 = repo.lookup(p2)
2254 p2 = repo.lookup(p2)
2255 if p1 == wp[0].node():
2255 if p1 == wp[0].node():
2256 repo.dirstate.setparents(p1, p2)
2256 repo.dirstate.setparents(p1, p2)
2257 except error.RepoError:
2257 except error.RepoError:
2258 pass
2258 pass
2259 if opts.get('exact') or opts.get('import_branch'):
2259 if opts.get('exact') or opts.get('import_branch'):
2260 repo.dirstate.setbranch(branch or 'default')
2260 repo.dirstate.setbranch(branch or 'default')
2261
2261
2262 files = {}
2262 files = {}
2263 try:
2263 try:
2264 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2264 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2265 files=files, eolmode=None)
2265 files=files, eolmode=None)
2266 finally:
2266 finally:
2267 files = patch.updatedir(ui, repo, files,
2267 files = patch.updatedir(ui, repo, files,
2268 similarity=sim / 100.0)
2268 similarity=sim / 100.0)
2269 if not opts.get('no_commit'):
2269 if not opts.get('no_commit'):
2270 if opts.get('exact'):
2270 if opts.get('exact'):
2271 m = None
2271 m = None
2272 else:
2272 else:
2273 m = cmdutil.matchfiles(repo, files or [])
2273 m = cmdutil.matchfiles(repo, files or [])
2274 n = repo.commit(message, opts.get('user') or user,
2274 n = repo.commit(message, opts.get('user') or user,
2275 opts.get('date') or date, match=m,
2275 opts.get('date') or date, match=m,
2276 editor=cmdutil.commiteditor)
2276 editor=cmdutil.commiteditor)
2277 if opts.get('exact'):
2277 if opts.get('exact'):
2278 if hex(n) != nodeid:
2278 if hex(n) != nodeid:
2279 repo.rollback()
2279 repo.rollback()
2280 raise util.Abort(_('patch is damaged'
2280 raise util.Abort(_('patch is damaged'
2281 ' or loses information'))
2281 ' or loses information'))
2282 # Force a dirstate write so that the next transaction
2282 # Force a dirstate write so that the next transaction
2283 # backups an up-do-date file.
2283 # backups an up-do-date file.
2284 repo.dirstate.write()
2284 repo.dirstate.write()
2285 if n:
2285 if n:
2286 commitid = short(n)
2286 commitid = short(n)
2287
2287
2288 return commitid
2288 return commitid
2289 finally:
2289 finally:
2290 os.unlink(tmpname)
2290 os.unlink(tmpname)
2291
2291
2292 try:
2292 try:
2293 wlock = repo.wlock()
2293 wlock = repo.wlock()
2294 lock = repo.lock()
2294 lock = repo.lock()
2295 lastcommit = None
2295 lastcommit = None
2296 for p in patches:
2296 for p in patches:
2297 pf = os.path.join(d, p)
2297 pf = os.path.join(d, p)
2298
2298
2299 if pf == '-':
2299 if pf == '-':
2300 ui.status(_("applying patch from stdin\n"))
2300 ui.status(_("applying patch from stdin\n"))
2301 pf = sys.stdin
2301 pf = sys.stdin
2302 else:
2302 else:
2303 ui.status(_("applying %s\n") % p)
2303 ui.status(_("applying %s\n") % p)
2304 pf = url.open(ui, pf)
2304 pf = url.open(ui, pf)
2305
2305
2306 haspatch = False
2306 haspatch = False
2307 for hunk in patch.split(pf):
2307 for hunk in patch.split(pf):
2308 commitid = tryone(ui, hunk)
2308 commitid = tryone(ui, hunk)
2309 if commitid:
2309 if commitid:
2310 haspatch = True
2310 haspatch = True
2311 if lastcommit:
2311 if lastcommit:
2312 ui.status(_('applied %s\n') % lastcommit)
2312 ui.status(_('applied %s\n') % lastcommit)
2313 lastcommit = commitid
2313 lastcommit = commitid
2314
2314
2315 if not haspatch:
2315 if not haspatch:
2316 raise util.Abort(_('no diffs found'))
2316 raise util.Abort(_('no diffs found'))
2317
2317
2318 finally:
2318 finally:
2319 release(lock, wlock)
2319 release(lock, wlock)
2320
2320
2321 def incoming(ui, repo, source="default", **opts):
2321 def incoming(ui, repo, source="default", **opts):
2322 """show new changesets found in source
2322 """show new changesets found in source
2323
2323
2324 Show new changesets found in the specified path/URL or the default
2324 Show new changesets found in the specified path/URL or the default
2325 pull location. These are the changesets that would have been pulled
2325 pull location. These are the changesets that would have been pulled
2326 if a pull at the time you issued this command.
2326 if a pull at the time you issued this command.
2327
2327
2328 For remote repository, using --bundle avoids downloading the
2328 For remote repository, using --bundle avoids downloading the
2329 changesets twice if the incoming is followed by a pull.
2329 changesets twice if the incoming is followed by a pull.
2330
2330
2331 See pull for valid source format details.
2331 See pull for valid source format details.
2332
2332
2333 Returns 0 if there are incoming changes, 1 otherwise.
2333 Returns 0 if there are incoming changes, 1 otherwise.
2334 """
2334 """
2335 limit = cmdutil.loglimit(opts)
2335 limit = cmdutil.loglimit(opts)
2336 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2336 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2337 other = hg.repository(hg.remoteui(repo, opts), source)
2337 other = hg.repository(hg.remoteui(repo, opts), source)
2338 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2338 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2339 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2339 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2340 if revs:
2340 if revs:
2341 revs = [other.lookup(rev) for rev in revs]
2341 revs = [other.lookup(rev) for rev in revs]
2342
2342
2343 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2343 tmp = discovery.findcommonincoming(repo, other, heads=revs,
2344 force=opts.get('force'))
2344 force=opts.get('force'))
2345 common, incoming, rheads = tmp
2345 common, incoming, rheads = tmp
2346 if not incoming:
2346 if not incoming:
2347 try:
2347 try:
2348 os.unlink(opts["bundle"])
2348 os.unlink(opts["bundle"])
2349 except:
2349 except:
2350 pass
2350 pass
2351 ui.status(_("no changes found\n"))
2351 ui.status(_("no changes found\n"))
2352 return 1
2352 return 1
2353
2353
2354 cleanup = None
2354 cleanup = None
2355 try:
2355 try:
2356 fname = opts["bundle"]
2356 fname = opts["bundle"]
2357 if fname or not other.local():
2357 if fname or not other.local():
2358 # create a bundle (uncompressed if other repo is not local)
2358 # create a bundle (uncompressed if other repo is not local)
2359
2359
2360 if revs is None and other.capable('changegroupsubset'):
2360 if revs is None and other.capable('changegroupsubset'):
2361 revs = rheads
2361 revs = rheads
2362
2362
2363 if revs is None:
2363 if revs is None:
2364 cg = other.changegroup(incoming, "incoming")
2364 cg = other.changegroup(incoming, "incoming")
2365 else:
2365 else:
2366 cg = other.changegroupsubset(incoming, revs, 'incoming')
2366 cg = other.changegroupsubset(incoming, revs, 'incoming')
2367 bundletype = other.local() and "HG10BZ" or "HG10UN"
2367 bundletype = other.local() and "HG10BZ" or "HG10UN"
2368 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2368 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2369 # keep written bundle?
2369 # keep written bundle?
2370 if opts["bundle"]:
2370 if opts["bundle"]:
2371 cleanup = None
2371 cleanup = None
2372 if not other.local():
2372 if not other.local():
2373 # use the created uncompressed bundlerepo
2373 # use the created uncompressed bundlerepo
2374 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2374 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2375
2375
2376 o = other.changelog.nodesbetween(incoming, revs)[0]
2376 o = other.changelog.nodesbetween(incoming, revs)[0]
2377 if opts.get('newest_first'):
2377 if opts.get('newest_first'):
2378 o.reverse()
2378 o.reverse()
2379 displayer = cmdutil.show_changeset(ui, other, opts)
2379 displayer = cmdutil.show_changeset(ui, other, opts)
2380 count = 0
2380 count = 0
2381 for n in o:
2381 for n in o:
2382 if limit is not None and count >= limit:
2382 if limit is not None and count >= limit:
2383 break
2383 break
2384 parents = [p for p in other.changelog.parents(n) if p != nullid]
2384 parents = [p for p in other.changelog.parents(n) if p != nullid]
2385 if opts.get('no_merges') and len(parents) == 2:
2385 if opts.get('no_merges') and len(parents) == 2:
2386 continue
2386 continue
2387 count += 1
2387 count += 1
2388 displayer.show(other[n])
2388 displayer.show(other[n])
2389 displayer.close()
2389 displayer.close()
2390 finally:
2390 finally:
2391 if hasattr(other, 'close'):
2391 if hasattr(other, 'close'):
2392 other.close()
2392 other.close()
2393 if cleanup:
2393 if cleanup:
2394 os.unlink(cleanup)
2394 os.unlink(cleanup)
2395
2395
2396 def init(ui, dest=".", **opts):
2396 def init(ui, dest=".", **opts):
2397 """create a new repository in the given directory
2397 """create a new repository in the given directory
2398
2398
2399 Initialize a new repository in the given directory. If the given
2399 Initialize a new repository in the given directory. If the given
2400 directory does not exist, it will be created.
2400 directory does not exist, it will be created.
2401
2401
2402 If no directory is given, the current directory is used.
2402 If no directory is given, the current directory is used.
2403
2403
2404 It is possible to specify an ``ssh://`` URL as the destination.
2404 It is possible to specify an ``ssh://`` URL as the destination.
2405 See :hg:`help urls` for more information.
2405 See :hg:`help urls` for more information.
2406
2406
2407 Returns 0 on success.
2407 Returns 0 on success.
2408 """
2408 """
2409 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2409 hg.repository(hg.remoteui(ui, opts), dest, create=1)
2410
2410
2411 def locate(ui, repo, *pats, **opts):
2411 def locate(ui, repo, *pats, **opts):
2412 """locate files matching specific patterns
2412 """locate files matching specific patterns
2413
2413
2414 Print files under Mercurial control in the working directory whose
2414 Print files under Mercurial control in the working directory whose
2415 names match the given patterns.
2415 names match the given patterns.
2416
2416
2417 By default, this command searches all directories in the working
2417 By default, this command searches all directories in the working
2418 directory. To search just the current directory and its
2418 directory. To search just the current directory and its
2419 subdirectories, use "--include .".
2419 subdirectories, use "--include .".
2420
2420
2421 If no patterns are given to match, this command prints the names
2421 If no patterns are given to match, this command prints the names
2422 of all files under Mercurial control in the working directory.
2422 of all files under Mercurial control in the working directory.
2423
2423
2424 If you want to feed the output of this command into the "xargs"
2424 If you want to feed the output of this command into the "xargs"
2425 command, use the -0 option to both this command and "xargs". This
2425 command, use the -0 option to both this command and "xargs". This
2426 will avoid the problem of "xargs" treating single filenames that
2426 will avoid the problem of "xargs" treating single filenames that
2427 contain whitespace as multiple filenames.
2427 contain whitespace as multiple filenames.
2428
2428
2429 Returns 0 if a match is found, 1 otherwise.
2429 Returns 0 if a match is found, 1 otherwise.
2430 """
2430 """
2431 end = opts.get('print0') and '\0' or '\n'
2431 end = opts.get('print0') and '\0' or '\n'
2432 rev = opts.get('rev') or None
2432 rev = opts.get('rev') or None
2433
2433
2434 ret = 1
2434 ret = 1
2435 m = cmdutil.match(repo, pats, opts, default='relglob')
2435 m = cmdutil.match(repo, pats, opts, default='relglob')
2436 m.bad = lambda x, y: False
2436 m.bad = lambda x, y: False
2437 for abs in repo[rev].walk(m):
2437 for abs in repo[rev].walk(m):
2438 if not rev and abs not in repo.dirstate:
2438 if not rev and abs not in repo.dirstate:
2439 continue
2439 continue
2440 if opts.get('fullpath'):
2440 if opts.get('fullpath'):
2441 ui.write(repo.wjoin(abs), end)
2441 ui.write(repo.wjoin(abs), end)
2442 else:
2442 else:
2443 ui.write(((pats and m.rel(abs)) or abs), end)
2443 ui.write(((pats and m.rel(abs)) or abs), end)
2444 ret = 0
2444 ret = 0
2445
2445
2446 return ret
2446 return ret
2447
2447
2448 def log(ui, repo, *pats, **opts):
2448 def log(ui, repo, *pats, **opts):
2449 """show revision history of entire repository or files
2449 """show revision history of entire repository or files
2450
2450
2451 Print the revision history of the specified files or the entire
2451 Print the revision history of the specified files or the entire
2452 project.
2452 project.
2453
2453
2454 File history is shown without following rename or copy history of
2454 File history is shown without following rename or copy history of
2455 files. Use -f/--follow with a filename to follow history across
2455 files. Use -f/--follow with a filename to follow history across
2456 renames and copies. --follow without a filename will only show
2456 renames and copies. --follow without a filename will only show
2457 ancestors or descendants of the starting revision. --follow-first
2457 ancestors or descendants of the starting revision. --follow-first
2458 only follows the first parent of merge revisions.
2458 only follows the first parent of merge revisions.
2459
2459
2460 If no revision range is specified, the default is tip:0 unless
2460 If no revision range is specified, the default is tip:0 unless
2461 --follow is set, in which case the working directory parent is
2461 --follow is set, in which case the working directory parent is
2462 used as the starting revision. You can specify a revision set for
2462 used as the starting revision. You can specify a revision set for
2463 log, see :hg:`help revsets` for more information.
2463 log, see :hg:`help revsets` for more information.
2464
2464
2465 See :hg:`help dates` for a list of formats valid for -d/--date.
2465 See :hg:`help dates` for a list of formats valid for -d/--date.
2466
2466
2467 By default this command prints revision number and changeset id,
2467 By default this command prints revision number and changeset id,
2468 tags, non-trivial parents, user, date and time, and a summary for
2468 tags, non-trivial parents, user, date and time, and a summary for
2469 each commit. When the -v/--verbose switch is used, the list of
2469 each commit. When the -v/--verbose switch is used, the list of
2470 changed files and full commit message are shown.
2470 changed files and full commit message are shown.
2471
2471
2472 NOTE: log -p/--patch may generate unexpected diff output for merge
2472 NOTE: log -p/--patch may generate unexpected diff output for merge
2473 changesets, as it will only compare the merge changeset against
2473 changesets, as it will only compare the merge changeset against
2474 its first parent. Also, only files different from BOTH parents
2474 its first parent. Also, only files different from BOTH parents
2475 will appear in files:.
2475 will appear in files:.
2476
2476
2477 Returns 0 on success.
2477 Returns 0 on success.
2478 """
2478 """
2479
2479
2480 matchfn = cmdutil.match(repo, pats, opts)
2480 matchfn = cmdutil.match(repo, pats, opts)
2481 limit = cmdutil.loglimit(opts)
2481 limit = cmdutil.loglimit(opts)
2482 count = 0
2482 count = 0
2483
2483
2484 endrev = None
2484 endrev = None
2485 if opts.get('copies') and opts.get('rev'):
2485 if opts.get('copies') and opts.get('rev'):
2486 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2486 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2487
2487
2488 df = False
2488 df = False
2489 if opts["date"]:
2489 if opts["date"]:
2490 df = util.matchdate(opts["date"])
2490 df = util.matchdate(opts["date"])
2491
2491
2492 branches = opts.get('branch', []) + opts.get('only_branch', [])
2492 branches = opts.get('branch', []) + opts.get('only_branch', [])
2493 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2493 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2494
2494
2495 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2495 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2496 def prep(ctx, fns):
2496 def prep(ctx, fns):
2497 rev = ctx.rev()
2497 rev = ctx.rev()
2498 parents = [p for p in repo.changelog.parentrevs(rev)
2498 parents = [p for p in repo.changelog.parentrevs(rev)
2499 if p != nullrev]
2499 if p != nullrev]
2500 if opts.get('no_merges') and len(parents) == 2:
2500 if opts.get('no_merges') and len(parents) == 2:
2501 return
2501 return
2502 if opts.get('only_merges') and len(parents) != 2:
2502 if opts.get('only_merges') and len(parents) != 2:
2503 return
2503 return
2504 if opts.get('branch') and ctx.branch() not in opts['branch']:
2504 if opts.get('branch') and ctx.branch() not in opts['branch']:
2505 return
2505 return
2506 if df and not df(ctx.date()[0]):
2506 if df and not df(ctx.date()[0]):
2507 return
2507 return
2508 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2508 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2509 return
2509 return
2510 if opts.get('keyword'):
2510 if opts.get('keyword'):
2511 for k in [kw.lower() for kw in opts['keyword']]:
2511 for k in [kw.lower() for kw in opts['keyword']]:
2512 if (k in ctx.user().lower() or
2512 if (k in ctx.user().lower() or
2513 k in ctx.description().lower() or
2513 k in ctx.description().lower() or
2514 k in " ".join(ctx.files()).lower()):
2514 k in " ".join(ctx.files()).lower()):
2515 break
2515 break
2516 else:
2516 else:
2517 return
2517 return
2518
2518
2519 copies = None
2519 copies = None
2520 if opts.get('copies') and rev:
2520 if opts.get('copies') and rev:
2521 copies = []
2521 copies = []
2522 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2522 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2523 for fn in ctx.files():
2523 for fn in ctx.files():
2524 rename = getrenamed(fn, rev)
2524 rename = getrenamed(fn, rev)
2525 if rename:
2525 if rename:
2526 copies.append((fn, rename[0]))
2526 copies.append((fn, rename[0]))
2527
2527
2528 revmatchfn = None
2528 revmatchfn = None
2529 if opts.get('patch') or opts.get('stat'):
2529 if opts.get('patch') or opts.get('stat'):
2530 revmatchfn = cmdutil.match(repo, fns, default='path')
2530 revmatchfn = cmdutil.match(repo, fns, default='path')
2531
2531
2532 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2532 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2533
2533
2534 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2534 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2535 if count == limit:
2535 if count == limit:
2536 break
2536 break
2537 if displayer.flush(ctx.rev()):
2537 if displayer.flush(ctx.rev()):
2538 count += 1
2538 count += 1
2539 displayer.close()
2539 displayer.close()
2540
2540
2541 def manifest(ui, repo, node=None, rev=None):
2541 def manifest(ui, repo, node=None, rev=None):
2542 """output the current or given revision of the project manifest
2542 """output the current or given revision of the project manifest
2543
2543
2544 Print a list of version controlled files for the given revision.
2544 Print a list of version controlled files for the given revision.
2545 If no revision is given, the first parent of the working directory
2545 If no revision is given, the first parent of the working directory
2546 is used, or the null revision if no revision is checked out.
2546 is used, or the null revision if no revision is checked out.
2547
2547
2548 With -v, print file permissions, symlink and executable bits.
2548 With -v, print file permissions, symlink and executable bits.
2549 With --debug, print file revision hashes.
2549 With --debug, print file revision hashes.
2550
2550
2551 Returns 0 on success.
2551 Returns 0 on success.
2552 """
2552 """
2553
2553
2554 if rev and node:
2554 if rev and node:
2555 raise util.Abort(_("please specify just one revision"))
2555 raise util.Abort(_("please specify just one revision"))
2556
2556
2557 if not node:
2557 if not node:
2558 node = rev
2558 node = rev
2559
2559
2560 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2560 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2561 ctx = repo[node]
2561 ctx = repo[node]
2562 for f in ctx:
2562 for f in ctx:
2563 if ui.debugflag:
2563 if ui.debugflag:
2564 ui.write("%40s " % hex(ctx.manifest()[f]))
2564 ui.write("%40s " % hex(ctx.manifest()[f]))
2565 if ui.verbose:
2565 if ui.verbose:
2566 ui.write(decor[ctx.flags(f)])
2566 ui.write(decor[ctx.flags(f)])
2567 ui.write("%s\n" % f)
2567 ui.write("%s\n" % f)
2568
2568
2569 def merge(ui, repo, node=None, **opts):
2569 def merge(ui, repo, node=None, **opts):
2570 """merge working directory with another revision
2570 """merge working directory with another revision
2571
2571
2572 The current working directory is updated with all changes made in
2572 The current working directory is updated with all changes made in
2573 the requested revision since the last common predecessor revision.
2573 the requested revision since the last common predecessor revision.
2574
2574
2575 Files that changed between either parent are marked as changed for
2575 Files that changed between either parent are marked as changed for
2576 the next commit and a commit must be performed before any further
2576 the next commit and a commit must be performed before any further
2577 updates to the repository are allowed. The next commit will have
2577 updates to the repository are allowed. The next commit will have
2578 two parents.
2578 two parents.
2579
2579
2580 If no revision is specified, the working directory's parent is a
2580 If no revision is specified, the working directory's parent is a
2581 head revision, and the current branch contains exactly one other
2581 head revision, and the current branch contains exactly one other
2582 head, the other head is merged with by default. Otherwise, an
2582 head, the other head is merged with by default. Otherwise, an
2583 explicit revision with which to merge with must be provided.
2583 explicit revision with which to merge with must be provided.
2584
2584
2585 To undo an uncommitted merge, use :hg:`update --clean .` which
2585 To undo an uncommitted merge, use :hg:`update --clean .` which
2586 will check out a clean copy of the original merge parent, losing
2586 will check out a clean copy of the original merge parent, losing
2587 all changes.
2587 all changes.
2588
2588
2589 Returns 0 on success, 1 if there are unresolved files.
2589 Returns 0 on success, 1 if there are unresolved files.
2590 """
2590 """
2591
2591
2592 if opts.get('rev') and node:
2592 if opts.get('rev') and node:
2593 raise util.Abort(_("please specify just one revision"))
2593 raise util.Abort(_("please specify just one revision"))
2594 if not node:
2594 if not node:
2595 node = opts.get('rev')
2595 node = opts.get('rev')
2596
2596
2597 if not node:
2597 if not node:
2598 branch = repo.changectx(None).branch()
2598 branch = repo.changectx(None).branch()
2599 bheads = repo.branchheads(branch)
2599 bheads = repo.branchheads(branch)
2600 if len(bheads) > 2:
2600 if len(bheads) > 2:
2601 raise util.Abort(_(
2601 raise util.Abort(_(
2602 'branch \'%s\' has %d heads - '
2602 'branch \'%s\' has %d heads - '
2603 'please merge with an explicit rev\n'
2603 'please merge with an explicit rev\n'
2604 '(run \'hg heads .\' to see heads)')
2604 '(run \'hg heads .\' to see heads)')
2605 % (branch, len(bheads)))
2605 % (branch, len(bheads)))
2606
2606
2607 parent = repo.dirstate.parents()[0]
2607 parent = repo.dirstate.parents()[0]
2608 if len(bheads) == 1:
2608 if len(bheads) == 1:
2609 if len(repo.heads()) > 1:
2609 if len(repo.heads()) > 1:
2610 raise util.Abort(_(
2610 raise util.Abort(_(
2611 'branch \'%s\' has one head - '
2611 'branch \'%s\' has one head - '
2612 'please merge with an explicit rev\n'
2612 'please merge with an explicit rev\n'
2613 '(run \'hg heads\' to see all heads)')
2613 '(run \'hg heads\' to see all heads)')
2614 % branch)
2614 % branch)
2615 msg = _('there is nothing to merge')
2615 msg = _('there is nothing to merge')
2616 if parent != repo.lookup(repo[None].branch()):
2616 if parent != repo.lookup(repo[None].branch()):
2617 msg = _('%s - use "hg update" instead') % msg
2617 msg = _('%s - use "hg update" instead') % msg
2618 raise util.Abort(msg)
2618 raise util.Abort(msg)
2619
2619
2620 if parent not in bheads:
2620 if parent not in bheads:
2621 raise util.Abort(_('working dir not at a head rev - '
2621 raise util.Abort(_('working dir not at a head rev - '
2622 'use "hg update" or merge with an explicit rev'))
2622 'use "hg update" or merge with an explicit rev'))
2623 node = parent == bheads[0] and bheads[-1] or bheads[0]
2623 node = parent == bheads[0] and bheads[-1] or bheads[0]
2624
2624
2625 if opts.get('preview'):
2625 if opts.get('preview'):
2626 # find nodes that are ancestors of p2 but not of p1
2626 # find nodes that are ancestors of p2 but not of p1
2627 p1 = repo.lookup('.')
2627 p1 = repo.lookup('.')
2628 p2 = repo.lookup(node)
2628 p2 = repo.lookup(node)
2629 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2629 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2630
2630
2631 displayer = cmdutil.show_changeset(ui, repo, opts)
2631 displayer = cmdutil.show_changeset(ui, repo, opts)
2632 for node in nodes:
2632 for node in nodes:
2633 displayer.show(repo[node])
2633 displayer.show(repo[node])
2634 displayer.close()
2634 displayer.close()
2635 return 0
2635 return 0
2636
2636
2637 return hg.merge(repo, node, force=opts.get('force'))
2637 return hg.merge(repo, node, force=opts.get('force'))
2638
2638
2639 def outgoing(ui, repo, dest=None, **opts):
2639 def outgoing(ui, repo, dest=None, **opts):
2640 """show changesets not found in the destination
2640 """show changesets not found in the destination
2641
2641
2642 Show changesets not found in the specified destination repository
2642 Show changesets not found in the specified destination repository
2643 or the default push location. These are the changesets that would
2643 or the default push location. These are the changesets that would
2644 be pushed if a push was requested.
2644 be pushed if a push was requested.
2645
2645
2646 See pull for details of valid destination formats.
2646 See pull for details of valid destination formats.
2647
2647
2648 Returns 0 if there are outgoing changes, 1 otherwise.
2648 Returns 0 if there are outgoing changes, 1 otherwise.
2649 """
2649 """
2650 limit = cmdutil.loglimit(opts)
2650 limit = cmdutil.loglimit(opts)
2651 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2651 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2652 dest, branches = hg.parseurl(dest, opts.get('branch'))
2652 dest, branches = hg.parseurl(dest, opts.get('branch'))
2653 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2653 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2654 if revs:
2654 if revs:
2655 revs = [repo.lookup(rev) for rev in revs]
2655 revs = [repo.lookup(rev) for rev in revs]
2656
2656
2657 other = hg.repository(hg.remoteui(repo, opts), dest)
2657 other = hg.repository(hg.remoteui(repo, opts), dest)
2658 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2658 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2659 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2659 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
2660 if not o:
2660 if not o:
2661 ui.status(_("no changes found\n"))
2661 ui.status(_("no changes found\n"))
2662 return 1
2662 return 1
2663 o = repo.changelog.nodesbetween(o, revs)[0]
2663 o = repo.changelog.nodesbetween(o, revs)[0]
2664 if opts.get('newest_first'):
2664 if opts.get('newest_first'):
2665 o.reverse()
2665 o.reverse()
2666 displayer = cmdutil.show_changeset(ui, repo, opts)
2666 displayer = cmdutil.show_changeset(ui, repo, opts)
2667 count = 0
2667 count = 0
2668 for n in o:
2668 for n in o:
2669 if limit is not None and count >= limit:
2669 if limit is not None and count >= limit:
2670 break
2670 break
2671 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2671 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2672 if opts.get('no_merges') and len(parents) == 2:
2672 if opts.get('no_merges') and len(parents) == 2:
2673 continue
2673 continue
2674 count += 1
2674 count += 1
2675 displayer.show(repo[n])
2675 displayer.show(repo[n])
2676 displayer.close()
2676 displayer.close()
2677
2677
2678 def parents(ui, repo, file_=None, **opts):
2678 def parents(ui, repo, file_=None, **opts):
2679 """show the parents of the working directory or revision
2679 """show the parents of the working directory or revision
2680
2680
2681 Print the working directory's parent revisions. If a revision is
2681 Print the working directory's parent revisions. If a revision is
2682 given via -r/--rev, the parent of that revision will be printed.
2682 given via -r/--rev, the parent of that revision will be printed.
2683 If a file argument is given, the revision in which the file was
2683 If a file argument is given, the revision in which the file was
2684 last changed (before the working directory revision or the
2684 last changed (before the working directory revision or the
2685 argument to --rev if given) is printed.
2685 argument to --rev if given) is printed.
2686
2686
2687 Returns 0 on success.
2687 Returns 0 on success.
2688 """
2688 """
2689 rev = opts.get('rev')
2689 rev = opts.get('rev')
2690 if rev:
2690 if rev:
2691 ctx = repo[rev]
2691 ctx = repo[rev]
2692 else:
2692 else:
2693 ctx = repo[None]
2693 ctx = repo[None]
2694
2694
2695 if file_:
2695 if file_:
2696 m = cmdutil.match(repo, (file_,), opts)
2696 m = cmdutil.match(repo, (file_,), opts)
2697 if m.anypats() or len(m.files()) != 1:
2697 if m.anypats() or len(m.files()) != 1:
2698 raise util.Abort(_('can only specify an explicit filename'))
2698 raise util.Abort(_('can only specify an explicit filename'))
2699 file_ = m.files()[0]
2699 file_ = m.files()[0]
2700 filenodes = []
2700 filenodes = []
2701 for cp in ctx.parents():
2701 for cp in ctx.parents():
2702 if not cp:
2702 if not cp:
2703 continue
2703 continue
2704 try:
2704 try:
2705 filenodes.append(cp.filenode(file_))
2705 filenodes.append(cp.filenode(file_))
2706 except error.LookupError:
2706 except error.LookupError:
2707 pass
2707 pass
2708 if not filenodes:
2708 if not filenodes:
2709 raise util.Abort(_("'%s' not found in manifest!") % file_)
2709 raise util.Abort(_("'%s' not found in manifest!") % file_)
2710 fl = repo.file(file_)
2710 fl = repo.file(file_)
2711 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2711 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2712 else:
2712 else:
2713 p = [cp.node() for cp in ctx.parents()]
2713 p = [cp.node() for cp in ctx.parents()]
2714
2714
2715 displayer = cmdutil.show_changeset(ui, repo, opts)
2715 displayer = cmdutil.show_changeset(ui, repo, opts)
2716 for n in p:
2716 for n in p:
2717 if n != nullid:
2717 if n != nullid:
2718 displayer.show(repo[n])
2718 displayer.show(repo[n])
2719 displayer.close()
2719 displayer.close()
2720
2720
2721 def paths(ui, repo, search=None):
2721 def paths(ui, repo, search=None):
2722 """show aliases for remote repositories
2722 """show aliases for remote repositories
2723
2723
2724 Show definition of symbolic path name NAME. If no name is given,
2724 Show definition of symbolic path name NAME. If no name is given,
2725 show definition of all available names.
2725 show definition of all available names.
2726
2726
2727 Path names are defined in the [paths] section of
2727 Path names are defined in the [paths] section of
2728 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2728 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2729 repository, ``.hg/hgrc`` is used, too.
2729 repository, ``.hg/hgrc`` is used, too.
2730
2730
2731 The path names ``default`` and ``default-push`` have a special
2731 The path names ``default`` and ``default-push`` have a special
2732 meaning. When performing a push or pull operation, they are used
2732 meaning. When performing a push or pull operation, they are used
2733 as fallbacks if no location is specified on the command-line.
2733 as fallbacks if no location is specified on the command-line.
2734 When ``default-push`` is set, it will be used for push and
2734 When ``default-push`` is set, it will be used for push and
2735 ``default`` will be used for pull; otherwise ``default`` is used
2735 ``default`` will be used for pull; otherwise ``default`` is used
2736 as the fallback for both. When cloning a repository, the clone
2736 as the fallback for both. When cloning a repository, the clone
2737 source is written as ``default`` in ``.hg/hgrc``. Note that
2737 source is written as ``default`` in ``.hg/hgrc``. Note that
2738 ``default`` and ``default-push`` apply to all inbound (e.g.
2738 ``default`` and ``default-push`` apply to all inbound (e.g.
2739 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2739 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2740 :hg:`bundle`) operations.
2740 :hg:`bundle`) operations.
2741
2741
2742 See :hg:`help urls` for more information.
2742 See :hg:`help urls` for more information.
2743
2743
2744 Returns 0 on success.
2744 Returns 0 on success.
2745 """
2745 """
2746 if search:
2746 if search:
2747 for name, path in ui.configitems("paths"):
2747 for name, path in ui.configitems("paths"):
2748 if name == search:
2748 if name == search:
2749 ui.write("%s\n" % url.hidepassword(path))
2749 ui.write("%s\n" % url.hidepassword(path))
2750 return
2750 return
2751 ui.warn(_("not found!\n"))
2751 ui.warn(_("not found!\n"))
2752 return 1
2752 return 1
2753 else:
2753 else:
2754 for name, path in ui.configitems("paths"):
2754 for name, path in ui.configitems("paths"):
2755 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2755 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2756
2756
2757 def postincoming(ui, repo, modheads, optupdate, checkout):
2757 def postincoming(ui, repo, modheads, optupdate, checkout):
2758 if modheads == 0:
2758 if modheads == 0:
2759 return
2759 return
2760 if optupdate:
2760 if optupdate:
2761 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2761 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2762 return hg.update(repo, checkout)
2762 return hg.update(repo, checkout)
2763 else:
2763 else:
2764 ui.status(_("not updating, since new heads added\n"))
2764 ui.status(_("not updating, since new heads added\n"))
2765 if modheads > 1:
2765 if modheads > 1:
2766 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2766 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2767 else:
2767 else:
2768 ui.status(_("(run 'hg update' to get a working copy)\n"))
2768 ui.status(_("(run 'hg update' to get a working copy)\n"))
2769
2769
2770 def pull(ui, repo, source="default", **opts):
2770 def pull(ui, repo, source="default", **opts):
2771 """pull changes from the specified source
2771 """pull changes from the specified source
2772
2772
2773 Pull changes from a remote repository to a local one.
2773 Pull changes from a remote repository to a local one.
2774
2774
2775 This finds all changes from the repository at the specified path
2775 This finds all changes from the repository at the specified path
2776 or URL and adds them to a local repository (the current one unless
2776 or URL and adds them to a local repository (the current one unless
2777 -R is specified). By default, this does not update the copy of the
2777 -R is specified). By default, this does not update the copy of the
2778 project in the working directory.
2778 project in the working directory.
2779
2779
2780 Use :hg:`incoming` if you want to see what would have been added
2780 Use :hg:`incoming` if you want to see what would have been added
2781 by a pull at the time you issued this command. If you then decide
2781 by a pull at the time you issued this command. If you then decide
2782 to add those changes to the repository, you should use :hg:`pull
2782 to add those changes to the repository, you should use :hg:`pull
2783 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2783 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2784
2784
2785 If SOURCE is omitted, the 'default' path will be used.
2785 If SOURCE is omitted, the 'default' path will be used.
2786 See :hg:`help urls` for more information.
2786 See :hg:`help urls` for more information.
2787
2787
2788 Returns 0 on success, 1 if an update had unresolved files.
2788 Returns 0 on success, 1 if an update had unresolved files.
2789 """
2789 """
2790 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2790 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2791 other = hg.repository(hg.remoteui(repo, opts), source)
2791 other = hg.repository(hg.remoteui(repo, opts), source)
2792 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2792 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2793 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2793 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2794 if revs:
2794 if revs:
2795 try:
2795 try:
2796 revs = [other.lookup(rev) for rev in revs]
2796 revs = [other.lookup(rev) for rev in revs]
2797 except error.CapabilityError:
2797 except error.CapabilityError:
2798 err = _("Other repository doesn't support revision lookup, "
2798 err = _("Other repository doesn't support revision lookup, "
2799 "so a rev cannot be specified.")
2799 "so a rev cannot be specified.")
2800 raise util.Abort(err)
2800 raise util.Abort(err)
2801
2801
2802 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2802 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2803 if checkout:
2803 if checkout:
2804 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2804 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2805 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2805 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2806
2806
2807 def push(ui, repo, dest=None, **opts):
2807 def push(ui, repo, dest=None, **opts):
2808 """push changes to the specified destination
2808 """push changes to the specified destination
2809
2809
2810 Push changesets from the local repository to the specified
2810 Push changesets from the local repository to the specified
2811 destination.
2811 destination.
2812
2812
2813 This operation is symmetrical to pull: it is identical to a pull
2813 This operation is symmetrical to pull: it is identical to a pull
2814 in the destination repository from the current one.
2814 in the destination repository from the current one.
2815
2815
2816 By default, push will not allow creation of new heads at the
2816 By default, push will not allow creation of new heads at the
2817 destination, since multiple heads would make it unclear which head
2817 destination, since multiple heads would make it unclear which head
2818 to use. In this situation, it is recommended to pull and merge
2818 to use. In this situation, it is recommended to pull and merge
2819 before pushing.
2819 before pushing.
2820
2820
2821 Use --new-branch if you want to allow push to create a new named
2821 Use --new-branch if you want to allow push to create a new named
2822 branch that is not present at the destination. This allows you to
2822 branch that is not present at the destination. This allows you to
2823 only create a new branch without forcing other changes.
2823 only create a new branch without forcing other changes.
2824
2824
2825 Use -f/--force to override the default behavior and push all
2825 Use -f/--force to override the default behavior and push all
2826 changesets on all branches.
2826 changesets on all branches.
2827
2827
2828 If -r/--rev is used, the specified revision and all its ancestors
2828 If -r/--rev is used, the specified revision and all its ancestors
2829 will be pushed to the remote repository.
2829 will be pushed to the remote repository.
2830
2830
2831 Please see :hg:`help urls` for important details about ``ssh://``
2831 Please see :hg:`help urls` for important details about ``ssh://``
2832 URLs. If DESTINATION is omitted, a default path will be used.
2832 URLs. If DESTINATION is omitted, a default path will be used.
2833
2833
2834 Returns 0 if push was successful, 1 if nothing to push.
2834 Returns 0 if push was successful, 1 if nothing to push.
2835 """
2835 """
2836 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2836 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2837 dest, branches = hg.parseurl(dest, opts.get('branch'))
2837 dest, branches = hg.parseurl(dest, opts.get('branch'))
2838 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2838 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2839 other = hg.repository(hg.remoteui(repo, opts), dest)
2839 other = hg.repository(hg.remoteui(repo, opts), dest)
2840 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2840 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2841 if revs:
2841 if revs:
2842 revs = [repo.lookup(rev) for rev in revs]
2842 revs = [repo.lookup(rev) for rev in revs]
2843
2843
2844 # push subrepos depth-first for coherent ordering
2844 # push subrepos depth-first for coherent ordering
2845 c = repo['']
2845 c = repo['']
2846 subs = c.substate # only repos that are committed
2846 subs = c.substate # only repos that are committed
2847 for s in sorted(subs):
2847 for s in sorted(subs):
2848 if not c.sub(s).push(opts.get('force')):
2848 if not c.sub(s).push(opts.get('force')):
2849 return False
2849 return False
2850
2850
2851 r = repo.push(other, opts.get('force'), revs=revs,
2851 r = repo.push(other, opts.get('force'), revs=revs,
2852 newbranch=opts.get('new_branch'))
2852 newbranch=opts.get('new_branch'))
2853 return r == 0
2853 return r == 0
2854
2854
2855 def recover(ui, repo):
2855 def recover(ui, repo):
2856 """roll back an interrupted transaction
2856 """roll back an interrupted transaction
2857
2857
2858 Recover from an interrupted commit or pull.
2858 Recover from an interrupted commit or pull.
2859
2859
2860 This command tries to fix the repository status after an
2860 This command tries to fix the repository status after an
2861 interrupted operation. It should only be necessary when Mercurial
2861 interrupted operation. It should only be necessary when Mercurial
2862 suggests it.
2862 suggests it.
2863
2863
2864 Returns 0 if successful, 1 if nothing to recover or verify fails.
2864 Returns 0 if successful, 1 if nothing to recover or verify fails.
2865 """
2865 """
2866 if repo.recover():
2866 if repo.recover():
2867 return hg.verify(repo)
2867 return hg.verify(repo)
2868 return 1
2868 return 1
2869
2869
2870 def remove(ui, repo, *pats, **opts):
2870 def remove(ui, repo, *pats, **opts):
2871 """remove the specified files on the next commit
2871 """remove the specified files on the next commit
2872
2872
2873 Schedule the indicated files for removal from the repository.
2873 Schedule the indicated files for removal from the repository.
2874
2874
2875 This only removes files from the current branch, not from the
2875 This only removes files from the current branch, not from the
2876 entire project history. -A/--after can be used to remove only
2876 entire project history. -A/--after can be used to remove only
2877 files that have already been deleted, -f/--force can be used to
2877 files that have already been deleted, -f/--force can be used to
2878 force deletion, and -Af can be used to remove files from the next
2878 force deletion, and -Af can be used to remove files from the next
2879 revision without deleting them from the working directory.
2879 revision without deleting them from the working directory.
2880
2880
2881 The following table details the behavior of remove for different
2881 The following table details the behavior of remove for different
2882 file states (columns) and option combinations (rows). The file
2882 file states (columns) and option combinations (rows). The file
2883 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2883 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2884 reported by :hg:`status`). The actions are Warn, Remove (from
2884 reported by :hg:`status`). The actions are Warn, Remove (from
2885 branch) and Delete (from disk)::
2885 branch) and Delete (from disk)::
2886
2886
2887 A C M !
2887 A C M !
2888 none W RD W R
2888 none W RD W R
2889 -f R RD RD R
2889 -f R RD RD R
2890 -A W W W R
2890 -A W W W R
2891 -Af R R R R
2891 -Af R R R R
2892
2892
2893 This command schedules the files to be removed at the next commit.
2893 This command schedules the files to be removed at the next commit.
2894 To undo a remove before that, see :hg:`revert`.
2894 To undo a remove before that, see :hg:`revert`.
2895
2895
2896 Returns 0 on success, 1 if any warnings encountered.
2896 Returns 0 on success, 1 if any warnings encountered.
2897 """
2897 """
2898
2898
2899 ret = 0
2899 ret = 0
2900 after, force = opts.get('after'), opts.get('force')
2900 after, force = opts.get('after'), opts.get('force')
2901 if not pats and not after:
2901 if not pats and not after:
2902 raise util.Abort(_('no files specified'))
2902 raise util.Abort(_('no files specified'))
2903
2903
2904 m = cmdutil.match(repo, pats, opts)
2904 m = cmdutil.match(repo, pats, opts)
2905 s = repo.status(match=m, clean=True)
2905 s = repo.status(match=m, clean=True)
2906 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2906 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2907
2907
2908 for f in m.files():
2908 for f in m.files():
2909 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2909 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2910 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2910 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2911 ret = 1
2911 ret = 1
2912
2912
2913 def warn(files, reason):
2913 def warn(files, reason):
2914 for f in files:
2914 for f in files:
2915 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2915 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2916 % (m.rel(f), reason))
2916 % (m.rel(f), reason))
2917 ret = 1
2917 ret = 1
2918
2918
2919 if force:
2919 if force:
2920 remove, forget = modified + deleted + clean, added
2920 remove, forget = modified + deleted + clean, added
2921 elif after:
2921 elif after:
2922 remove, forget = deleted, []
2922 remove, forget = deleted, []
2923 warn(modified + added + clean, _('still exists'))
2923 warn(modified + added + clean, _('still exists'))
2924 else:
2924 else:
2925 remove, forget = deleted + clean, []
2925 remove, forget = deleted + clean, []
2926 warn(modified, _('is modified'))
2926 warn(modified, _('is modified'))
2927 warn(added, _('has been marked for add'))
2927 warn(added, _('has been marked for add'))
2928
2928
2929 for f in sorted(remove + forget):
2929 for f in sorted(remove + forget):
2930 if ui.verbose or not m.exact(f):
2930 if ui.verbose or not m.exact(f):
2931 ui.status(_('removing %s\n') % m.rel(f))
2931 ui.status(_('removing %s\n') % m.rel(f))
2932
2932
2933 repo[None].forget(forget)
2933 repo[None].forget(forget)
2934 repo[None].remove(remove, unlink=not after)
2934 repo[None].remove(remove, unlink=not after)
2935 return ret
2935 return ret
2936
2936
2937 def rename(ui, repo, *pats, **opts):
2937 def rename(ui, repo, *pats, **opts):
2938 """rename files; equivalent of copy + remove
2938 """rename files; equivalent of copy + remove
2939
2939
2940 Mark dest as copies of sources; mark sources for deletion. If dest
2940 Mark dest as copies of sources; mark sources for deletion. If dest
2941 is a directory, copies are put in that directory. If dest is a
2941 is a directory, copies are put in that directory. If dest is a
2942 file, there can only be one source.
2942 file, there can only be one source.
2943
2943
2944 By default, this command copies the contents of files as they
2944 By default, this command copies the contents of files as they
2945 exist in the working directory. If invoked with -A/--after, the
2945 exist in the working directory. If invoked with -A/--after, the
2946 operation is recorded, but no copying is performed.
2946 operation is recorded, but no copying is performed.
2947
2947
2948 This command takes effect at the next commit. To undo a rename
2948 This command takes effect at the next commit. To undo a rename
2949 before that, see :hg:`revert`.
2949 before that, see :hg:`revert`.
2950
2950
2951 Returns 0 on success, 1 if errors are encountered.
2951 Returns 0 on success, 1 if errors are encountered.
2952 """
2952 """
2953 wlock = repo.wlock(False)
2953 wlock = repo.wlock(False)
2954 try:
2954 try:
2955 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2955 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2956 finally:
2956 finally:
2957 wlock.release()
2957 wlock.release()
2958
2958
2959 def resolve(ui, repo, *pats, **opts):
2959 def resolve(ui, repo, *pats, **opts):
2960 """redo merges or set/view the merge status of files
2960 """redo merges or set/view the merge status of files
2961
2961
2962 Merges with unresolved conflicts are often the result of
2962 Merges with unresolved conflicts are often the result of
2963 non-interactive merging using the ``internal:merge`` hgrc setting,
2963 non-interactive merging using the ``internal:merge`` hgrc setting,
2964 or a command-line merge tool like ``diff3``. The resolve command
2964 or a command-line merge tool like ``diff3``. The resolve command
2965 is used to manage the files involved in a merge, after :hg:`merge`
2965 is used to manage the files involved in a merge, after :hg:`merge`
2966 has been run, and before :hg:`commit` is run (i.e. the working
2966 has been run, and before :hg:`commit` is run (i.e. the working
2967 directory must have two parents).
2967 directory must have two parents).
2968
2968
2969 The resolve command can be used in the following ways:
2969 The resolve command can be used in the following ways:
2970
2970
2971 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2971 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2972 discarding any previous merge attempts. Re-merging is not
2972 discarding any previous merge attempts. Re-merging is not
2973 performed for files already marked as resolved. Use ``--all/-a``
2973 performed for files already marked as resolved. Use ``--all/-a``
2974 to selects all unresolved files.
2974 to selects all unresolved files.
2975
2975
2976 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2976 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2977 (e.g. after having manually fixed-up the files). The default is
2977 (e.g. after having manually fixed-up the files). The default is
2978 to mark all unresolved files.
2978 to mark all unresolved files.
2979
2979
2980 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2980 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2981 default is to mark all resolved files.
2981 default is to mark all resolved files.
2982
2982
2983 - :hg:`resolve -l`: list files which had or still have conflicts.
2983 - :hg:`resolve -l`: list files which had or still have conflicts.
2984 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2984 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2985
2985
2986 Note that Mercurial will not let you commit files with unresolved
2986 Note that Mercurial will not let you commit files with unresolved
2987 merge conflicts. You must use :hg:`resolve -m ...` before you can
2987 merge conflicts. You must use :hg:`resolve -m ...` before you can
2988 commit after a conflicting merge.
2988 commit after a conflicting merge.
2989
2989
2990 Returns 0 on success, 1 if any files fail a resolve attempt.
2990 Returns 0 on success, 1 if any files fail a resolve attempt.
2991 """
2991 """
2992
2992
2993 all, mark, unmark, show, nostatus = \
2993 all, mark, unmark, show, nostatus = \
2994 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2994 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2995
2995
2996 if (show and (mark or unmark)) or (mark and unmark):
2996 if (show and (mark or unmark)) or (mark and unmark):
2997 raise util.Abort(_("too many options specified"))
2997 raise util.Abort(_("too many options specified"))
2998 if pats and all:
2998 if pats and all:
2999 raise util.Abort(_("can't specify --all and patterns"))
2999 raise util.Abort(_("can't specify --all and patterns"))
3000 if not (all or pats or show or mark or unmark):
3000 if not (all or pats or show or mark or unmark):
3001 raise util.Abort(_('no files or directories specified; '
3001 raise util.Abort(_('no files or directories specified; '
3002 'use --all to remerge all files'))
3002 'use --all to remerge all files'))
3003
3003
3004 ms = mergemod.mergestate(repo)
3004 ms = mergemod.mergestate(repo)
3005 m = cmdutil.match(repo, pats, opts)
3005 m = cmdutil.match(repo, pats, opts)
3006 ret = 0
3006 ret = 0
3007
3007
3008 for f in ms:
3008 for f in ms:
3009 if m(f):
3009 if m(f):
3010 if show:
3010 if show:
3011 if nostatus:
3011 if nostatus:
3012 ui.write("%s\n" % f)
3012 ui.write("%s\n" % f)
3013 else:
3013 else:
3014 ui.write("%s %s\n" % (ms[f].upper(), f),
3014 ui.write("%s %s\n" % (ms[f].upper(), f),
3015 label='resolve.' +
3015 label='resolve.' +
3016 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3016 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3017 elif mark:
3017 elif mark:
3018 ms.mark(f, "r")
3018 ms.mark(f, "r")
3019 elif unmark:
3019 elif unmark:
3020 ms.mark(f, "u")
3020 ms.mark(f, "u")
3021 else:
3021 else:
3022 wctx = repo[None]
3022 wctx = repo[None]
3023 mctx = wctx.parents()[-1]
3023 mctx = wctx.parents()[-1]
3024
3024
3025 # backup pre-resolve (merge uses .orig for its own purposes)
3025 # backup pre-resolve (merge uses .orig for its own purposes)
3026 a = repo.wjoin(f)
3026 a = repo.wjoin(f)
3027 util.copyfile(a, a + ".resolve")
3027 util.copyfile(a, a + ".resolve")
3028
3028
3029 # resolve file
3029 # resolve file
3030 if ms.resolve(f, wctx, mctx):
3030 if ms.resolve(f, wctx, mctx):
3031 ret = 1
3031 ret = 1
3032
3032
3033 # replace filemerge's .orig file with our resolve file
3033 # replace filemerge's .orig file with our resolve file
3034 util.rename(a + ".resolve", a + ".orig")
3034 util.rename(a + ".resolve", a + ".orig")
3035 return ret
3035 return ret
3036
3036
3037 def revert(ui, repo, *pats, **opts):
3037 def revert(ui, repo, *pats, **opts):
3038 """restore individual files or directories to an earlier state
3038 """restore individual files or directories to an earlier state
3039
3039
3040 NOTE: This command is most likely not what you are looking for. revert
3040 NOTE: This command is most likely not what you are looking for. revert
3041 will partially overwrite content in the working directory without changing
3041 will partially overwrite content in the working directory without changing
3042 the working directory parents. Use :hg:`update -r rev` to check out earlier
3042 the working directory parents. Use :hg:`update -r rev` to check out earlier
3043 revisions, or :hg:`update --clean .` to undo a merge which has added
3043 revisions, or :hg:`update --clean .` to undo a merge which has added
3044 another parent.
3044 another parent.
3045
3045
3046 With no revision specified, revert the named files or directories
3046 With no revision specified, revert the named files or directories
3047 to the contents they had in the parent of the working directory.
3047 to the contents they had in the parent of the working directory.
3048 This restores the contents of the affected files to an unmodified
3048 This restores the contents of the affected files to an unmodified
3049 state and unschedules adds, removes, copies, and renames. If the
3049 state and unschedules adds, removes, copies, and renames. If the
3050 working directory has two parents, you must explicitly specify a
3050 working directory has two parents, you must explicitly specify a
3051 revision.
3051 revision.
3052
3052
3053 Using the -r/--rev option, revert the given files or directories
3053 Using the -r/--rev option, revert the given files or directories
3054 to their contents as of a specific revision. This can be helpful
3054 to their contents as of a specific revision. This can be helpful
3055 to "roll back" some or all of an earlier change. See :hg:`help
3055 to "roll back" some or all of an earlier change. See :hg:`help
3056 dates` for a list of formats valid for -d/--date.
3056 dates` for a list of formats valid for -d/--date.
3057
3057
3058 Revert modifies the working directory. It does not commit any
3058 Revert modifies the working directory. It does not commit any
3059 changes, or change the parent of the working directory. If you
3059 changes, or change the parent of the working directory. If you
3060 revert to a revision other than the parent of the working
3060 revert to a revision other than the parent of the working
3061 directory, the reverted files will thus appear modified
3061 directory, the reverted files will thus appear modified
3062 afterwards.
3062 afterwards.
3063
3063
3064 If a file has been deleted, it is restored. If the executable mode
3064 If a file has been deleted, it is restored. If the executable mode
3065 of a file was changed, it is reset.
3065 of a file was changed, it is reset.
3066
3066
3067 If names are given, all files matching the names are reverted.
3067 If names are given, all files matching the names are reverted.
3068 If no arguments are given, no files are reverted.
3068 If no arguments are given, no files are reverted.
3069
3069
3070 Modified files are saved with a .orig suffix before reverting.
3070 Modified files are saved with a .orig suffix before reverting.
3071 To disable these backups, use --no-backup.
3071 To disable these backups, use --no-backup.
3072
3072
3073 Returns 0 on success.
3073 Returns 0 on success.
3074 """
3074 """
3075
3075
3076 if opts.get("date"):
3076 if opts.get("date"):
3077 if opts.get("rev"):
3077 if opts.get("rev"):
3078 raise util.Abort(_("you can't specify a revision and a date"))
3078 raise util.Abort(_("you can't specify a revision and a date"))
3079 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3079 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3080
3080
3081 if not pats and not opts.get('all'):
3081 if not pats and not opts.get('all'):
3082 raise util.Abort(_('no files or directories specified; '
3082 raise util.Abort(_('no files or directories specified; '
3083 'use --all to revert the whole repo'))
3083 'use --all to revert the whole repo'))
3084
3084
3085 parent, p2 = repo.dirstate.parents()
3085 parent, p2 = repo.dirstate.parents()
3086 if not opts.get('rev') and p2 != nullid:
3086 if not opts.get('rev') and p2 != nullid:
3087 raise util.Abort(_('uncommitted merge - please provide a '
3087 raise util.Abort(_('uncommitted merge - please provide a '
3088 'specific revision'))
3088 'specific revision'))
3089 ctx = repo[opts.get('rev')]
3089 ctx = repo[opts.get('rev')]
3090 node = ctx.node()
3090 node = ctx.node()
3091 mf = ctx.manifest()
3091 mf = ctx.manifest()
3092 if node == parent:
3092 if node == parent:
3093 pmf = mf
3093 pmf = mf
3094 else:
3094 else:
3095 pmf = None
3095 pmf = None
3096
3096
3097 # need all matching names in dirstate and manifest of target rev,
3097 # need all matching names in dirstate and manifest of target rev,
3098 # so have to walk both. do not print errors if files exist in one
3098 # so have to walk both. do not print errors if files exist in one
3099 # but not other.
3099 # but not other.
3100
3100
3101 names = {}
3101 names = {}
3102
3102
3103 wlock = repo.wlock()
3103 wlock = repo.wlock()
3104 try:
3104 try:
3105 # walk dirstate.
3105 # walk dirstate.
3106
3106
3107 m = cmdutil.match(repo, pats, opts)
3107 m = cmdutil.match(repo, pats, opts)
3108 m.bad = lambda x, y: False
3108 m.bad = lambda x, y: False
3109 for abs in repo.walk(m):
3109 for abs in repo.walk(m):
3110 names[abs] = m.rel(abs), m.exact(abs)
3110 names[abs] = m.rel(abs), m.exact(abs)
3111
3111
3112 # walk target manifest.
3112 # walk target manifest.
3113
3113
3114 def badfn(path, msg):
3114 def badfn(path, msg):
3115 if path in names:
3115 if path in names:
3116 return
3116 return
3117 path_ = path + '/'
3117 path_ = path + '/'
3118 for f in names:
3118 for f in names:
3119 if f.startswith(path_):
3119 if f.startswith(path_):
3120 return
3120 return
3121 ui.warn("%s: %s\n" % (m.rel(path), msg))
3121 ui.warn("%s: %s\n" % (m.rel(path), msg))
3122
3122
3123 m = cmdutil.match(repo, pats, opts)
3123 m = cmdutil.match(repo, pats, opts)
3124 m.bad = badfn
3124 m.bad = badfn
3125 for abs in repo[node].walk(m):
3125 for abs in repo[node].walk(m):
3126 if abs not in names:
3126 if abs not in names:
3127 names[abs] = m.rel(abs), m.exact(abs)
3127 names[abs] = m.rel(abs), m.exact(abs)
3128
3128
3129 m = cmdutil.matchfiles(repo, names)
3129 m = cmdutil.matchfiles(repo, names)
3130 changes = repo.status(match=m)[:4]
3130 changes = repo.status(match=m)[:4]
3131 modified, added, removed, deleted = map(set, changes)
3131 modified, added, removed, deleted = map(set, changes)
3132
3132
3133 # if f is a rename, also revert the source
3133 # if f is a rename, also revert the source
3134 cwd = repo.getcwd()
3134 cwd = repo.getcwd()
3135 for f in added:
3135 for f in added:
3136 src = repo.dirstate.copied(f)
3136 src = repo.dirstate.copied(f)
3137 if src and src not in names and repo.dirstate[src] == 'r':
3137 if src and src not in names and repo.dirstate[src] == 'r':
3138 removed.add(src)
3138 removed.add(src)
3139 names[src] = (repo.pathto(src, cwd), True)
3139 names[src] = (repo.pathto(src, cwd), True)
3140
3140
3141 def removeforget(abs):
3141 def removeforget(abs):
3142 if repo.dirstate[abs] == 'a':
3142 if repo.dirstate[abs] == 'a':
3143 return _('forgetting %s\n')
3143 return _('forgetting %s\n')
3144 return _('removing %s\n')
3144 return _('removing %s\n')
3145
3145
3146 revert = ([], _('reverting %s\n'))
3146 revert = ([], _('reverting %s\n'))
3147 add = ([], _('adding %s\n'))
3147 add = ([], _('adding %s\n'))
3148 remove = ([], removeforget)
3148 remove = ([], removeforget)
3149 undelete = ([], _('undeleting %s\n'))
3149 undelete = ([], _('undeleting %s\n'))
3150
3150
3151 disptable = (
3151 disptable = (
3152 # dispatch table:
3152 # dispatch table:
3153 # file state
3153 # file state
3154 # action if in target manifest
3154 # action if in target manifest
3155 # action if not in target manifest
3155 # action if not in target manifest
3156 # make backup if in target manifest
3156 # make backup if in target manifest
3157 # make backup if not in target manifest
3157 # make backup if not in target manifest
3158 (modified, revert, remove, True, True),
3158 (modified, revert, remove, True, True),
3159 (added, revert, remove, True, False),
3159 (added, revert, remove, True, False),
3160 (removed, undelete, None, False, False),
3160 (removed, undelete, None, False, False),
3161 (deleted, revert, remove, False, False),
3161 (deleted, revert, remove, False, False),
3162 )
3162 )
3163
3163
3164 for abs, (rel, exact) in sorted(names.items()):
3164 for abs, (rel, exact) in sorted(names.items()):
3165 mfentry = mf.get(abs)
3165 mfentry = mf.get(abs)
3166 target = repo.wjoin(abs)
3166 target = repo.wjoin(abs)
3167 def handle(xlist, dobackup):
3167 def handle(xlist, dobackup):
3168 xlist[0].append(abs)
3168 xlist[0].append(abs)
3169 if (dobackup and not opts.get('no_backup') and
3169 if (dobackup and not opts.get('no_backup') and
3170 os.path.lexists(target)):
3170 os.path.lexists(target)):
3171 bakname = "%s.orig" % rel
3171 bakname = "%s.orig" % rel
3172 ui.note(_('saving current version of %s as %s\n') %
3172 ui.note(_('saving current version of %s as %s\n') %
3173 (rel, bakname))
3173 (rel, bakname))
3174 if not opts.get('dry_run'):
3174 if not opts.get('dry_run'):
3175 util.rename(target, bakname)
3175 util.rename(target, bakname)
3176 if ui.verbose or not exact:
3176 if ui.verbose or not exact:
3177 msg = xlist[1]
3177 msg = xlist[1]
3178 if not isinstance(msg, basestring):
3178 if not isinstance(msg, basestring):
3179 msg = msg(abs)
3179 msg = msg(abs)
3180 ui.status(msg % rel)
3180 ui.status(msg % rel)
3181 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3181 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3182 if abs not in table:
3182 if abs not in table:
3183 continue
3183 continue
3184 # file has changed in dirstate
3184 # file has changed in dirstate
3185 if mfentry:
3185 if mfentry:
3186 handle(hitlist, backuphit)
3186 handle(hitlist, backuphit)
3187 elif misslist is not None:
3187 elif misslist is not None:
3188 handle(misslist, backupmiss)
3188 handle(misslist, backupmiss)
3189 break
3189 break
3190 else:
3190 else:
3191 if abs not in repo.dirstate:
3191 if abs not in repo.dirstate:
3192 if mfentry:
3192 if mfentry:
3193 handle(add, True)
3193 handle(add, True)
3194 elif exact:
3194 elif exact:
3195 ui.warn(_('file not managed: %s\n') % rel)
3195 ui.warn(_('file not managed: %s\n') % rel)
3196 continue
3196 continue
3197 # file has not changed in dirstate
3197 # file has not changed in dirstate
3198 if node == parent:
3198 if node == parent:
3199 if exact:
3199 if exact:
3200 ui.warn(_('no changes needed to %s\n') % rel)
3200 ui.warn(_('no changes needed to %s\n') % rel)
3201 continue
3201 continue
3202 if pmf is None:
3202 if pmf is None:
3203 # only need parent manifest in this unlikely case,
3203 # only need parent manifest in this unlikely case,
3204 # so do not read by default
3204 # so do not read by default
3205 pmf = repo[parent].manifest()
3205 pmf = repo[parent].manifest()
3206 if abs in pmf:
3206 if abs in pmf:
3207 if mfentry:
3207 if mfentry:
3208 # if version of file is same in parent and target
3208 # if version of file is same in parent and target
3209 # manifests, do nothing
3209 # manifests, do nothing
3210 if (pmf[abs] != mfentry or
3210 if (pmf[abs] != mfentry or
3211 pmf.flags(abs) != mf.flags(abs)):
3211 pmf.flags(abs) != mf.flags(abs)):
3212 handle(revert, False)
3212 handle(revert, False)
3213 else:
3213 else:
3214 handle(remove, False)
3214 handle(remove, False)
3215
3215
3216 if not opts.get('dry_run'):
3216 if not opts.get('dry_run'):
3217 def checkout(f):
3217 def checkout(f):
3218 fc = ctx[f]
3218 fc = ctx[f]
3219 repo.wwrite(f, fc.data(), fc.flags())
3219 repo.wwrite(f, fc.data(), fc.flags())
3220
3220
3221 audit_path = util.path_auditor(repo.root)
3221 audit_path = util.path_auditor(repo.root)
3222 for f in remove[0]:
3222 for f in remove[0]:
3223 if repo.dirstate[f] == 'a':
3223 if repo.dirstate[f] == 'a':
3224 repo.dirstate.forget(f)
3224 repo.dirstate.forget(f)
3225 continue
3225 continue
3226 audit_path(f)
3226 audit_path(f)
3227 try:
3227 try:
3228 util.unlink(repo.wjoin(f))
3228 util.unlink(repo.wjoin(f))
3229 except OSError:
3229 except OSError:
3230 pass
3230 pass
3231 repo.dirstate.remove(f)
3231 repo.dirstate.remove(f)
3232
3232
3233 normal = None
3233 normal = None
3234 if node == parent:
3234 if node == parent:
3235 # We're reverting to our parent. If possible, we'd like status
3235 # We're reverting to our parent. If possible, we'd like status
3236 # to report the file as clean. We have to use normallookup for
3236 # to report the file as clean. We have to use normallookup for
3237 # merges to avoid losing information about merged/dirty files.
3237 # merges to avoid losing information about merged/dirty files.
3238 if p2 != nullid:
3238 if p2 != nullid:
3239 normal = repo.dirstate.normallookup
3239 normal = repo.dirstate.normallookup
3240 else:
3240 else:
3241 normal = repo.dirstate.normal
3241 normal = repo.dirstate.normal
3242 for f in revert[0]:
3242 for f in revert[0]:
3243 checkout(f)
3243 checkout(f)
3244 if normal:
3244 if normal:
3245 normal(f)
3245 normal(f)
3246
3246
3247 for f in add[0]:
3247 for f in add[0]:
3248 checkout(f)
3248 checkout(f)
3249 repo.dirstate.add(f)
3249 repo.dirstate.add(f)
3250
3250
3251 normal = repo.dirstate.normallookup
3251 normal = repo.dirstate.normallookup
3252 if node == parent and p2 == nullid:
3252 if node == parent and p2 == nullid:
3253 normal = repo.dirstate.normal
3253 normal = repo.dirstate.normal
3254 for f in undelete[0]:
3254 for f in undelete[0]:
3255 checkout(f)
3255 checkout(f)
3256 normal(f)
3256 normal(f)
3257
3257
3258 finally:
3258 finally:
3259 wlock.release()
3259 wlock.release()
3260
3260
3261 def rollback(ui, repo, **opts):
3261 def rollback(ui, repo, **opts):
3262 """roll back the last transaction (dangerous)
3262 """roll back the last transaction (dangerous)
3263
3263
3264 This command should be used with care. There is only one level of
3264 This command should be used with care. There is only one level of
3265 rollback, and there is no way to undo a rollback. It will also
3265 rollback, and there is no way to undo a rollback. It will also
3266 restore the dirstate at the time of the last transaction, losing
3266 restore the dirstate at the time of the last transaction, losing
3267 any dirstate changes since that time. This command does not alter
3267 any dirstate changes since that time. This command does not alter
3268 the working directory.
3268 the working directory.
3269
3269
3270 Transactions are used to encapsulate the effects of all commands
3270 Transactions are used to encapsulate the effects of all commands
3271 that create new changesets or propagate existing changesets into a
3271 that create new changesets or propagate existing changesets into a
3272 repository. For example, the following commands are transactional,
3272 repository. For example, the following commands are transactional,
3273 and their effects can be rolled back:
3273 and their effects can be rolled back:
3274
3274
3275 - commit
3275 - commit
3276 - import
3276 - import
3277 - pull
3277 - pull
3278 - push (with this repository as the destination)
3278 - push (with this repository as the destination)
3279 - unbundle
3279 - unbundle
3280
3280
3281 This command is not intended for use on public repositories. Once
3281 This command is not intended for use on public repositories. Once
3282 changes are visible for pull by other users, rolling a transaction
3282 changes are visible for pull by other users, rolling a transaction
3283 back locally is ineffective (someone else may already have pulled
3283 back locally is ineffective (someone else may already have pulled
3284 the changes). Furthermore, a race is possible with readers of the
3284 the changes). Furthermore, a race is possible with readers of the
3285 repository; for example an in-progress pull from the repository
3285 repository; for example an in-progress pull from the repository
3286 may fail if a rollback is performed.
3286 may fail if a rollback is performed.
3287
3287
3288 Returns 0 on success, 1 if no rollback data is available.
3288 Returns 0 on success, 1 if no rollback data is available.
3289 """
3289 """
3290 return repo.rollback(opts.get('dry_run'))
3290 return repo.rollback(opts.get('dry_run'))
3291
3291
3292 def root(ui, repo):
3292 def root(ui, repo):
3293 """print the root (top) of the current working directory
3293 """print the root (top) of the current working directory
3294
3294
3295 Print the root directory of the current repository.
3295 Print the root directory of the current repository.
3296
3296
3297 Returns 0 on success.
3297 Returns 0 on success.
3298 """
3298 """
3299 ui.write(repo.root + "\n")
3299 ui.write(repo.root + "\n")
3300
3300
3301 def serve(ui, repo, **opts):
3301 def serve(ui, repo, **opts):
3302 """start stand-alone webserver
3302 """start stand-alone webserver
3303
3303
3304 Start a local HTTP repository browser and pull server. You can use
3304 Start a local HTTP repository browser and pull server. You can use
3305 this for ad-hoc sharing and browing of repositories. It is
3305 this for ad-hoc sharing and browing of repositories. It is
3306 recommended to use a real web server to serve a repository for
3306 recommended to use a real web server to serve a repository for
3307 longer periods of time.
3307 longer periods of time.
3308
3308
3309 Please note that the server does not implement access control.
3309 Please note that the server does not implement access control.
3310 This means that, by default, anybody can read from the server and
3310 This means that, by default, anybody can read from the server and
3311 nobody can write to it by default. Set the ``web.allow_push``
3311 nobody can write to it by default. Set the ``web.allow_push``
3312 option to ``*`` to allow everybody to push to the server. You
3312 option to ``*`` to allow everybody to push to the server. You
3313 should use a real web server if you need to authenticate users.
3313 should use a real web server if you need to authenticate users.
3314
3314
3315 By default, the server logs accesses to stdout and errors to
3315 By default, the server logs accesses to stdout and errors to
3316 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3316 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3317 files.
3317 files.
3318
3318
3319 To have the server choose a free port number to listen on, specify
3319 To have the server choose a free port number to listen on, specify
3320 a port number of 0; in this case, the server will print the port
3320 a port number of 0; in this case, the server will print the port
3321 number it uses.
3321 number it uses.
3322
3322
3323 Returns 0 on success.
3323 Returns 0 on success.
3324 """
3324 """
3325
3325
3326 if opts["stdio"]:
3326 if opts["stdio"]:
3327 if repo is None:
3327 if repo is None:
3328 raise error.RepoError(_("There is no Mercurial repository here"
3328 raise error.RepoError(_("There is no Mercurial repository here"
3329 " (.hg not found)"))
3329 " (.hg not found)"))
3330 s = sshserver.sshserver(ui, repo)
3330 s = sshserver.sshserver(ui, repo)
3331 s.serve_forever()
3331 s.serve_forever()
3332
3332
3333 # this way we can check if something was given in the command-line
3333 # this way we can check if something was given in the command-line
3334 if opts.get('port'):
3334 if opts.get('port'):
3335 opts['port'] = int(opts.get('port'))
3335 opts['port'] = util.getport(opts.get('port'))
3336
3336
3337 baseui = repo and repo.baseui or ui
3337 baseui = repo and repo.baseui or ui
3338 optlist = ("name templates style address port prefix ipv6"
3338 optlist = ("name templates style address port prefix ipv6"
3339 " accesslog errorlog certificate encoding")
3339 " accesslog errorlog certificate encoding")
3340 for o in optlist.split():
3340 for o in optlist.split():
3341 val = opts.get(o, '')
3341 val = opts.get(o, '')
3342 if val in (None, ''): # should check against default options instead
3342 if val in (None, ''): # should check against default options instead
3343 continue
3343 continue
3344 baseui.setconfig("web", o, val)
3344 baseui.setconfig("web", o, val)
3345 if repo and repo.ui != baseui:
3345 if repo and repo.ui != baseui:
3346 repo.ui.setconfig("web", o, val)
3346 repo.ui.setconfig("web", o, val)
3347
3347
3348 o = opts.get('web_conf') or opts.get('webdir_conf')
3348 o = opts.get('web_conf') or opts.get('webdir_conf')
3349 if not o:
3349 if not o:
3350 if not repo:
3350 if not repo:
3351 raise error.RepoError(_("There is no Mercurial repository"
3351 raise error.RepoError(_("There is no Mercurial repository"
3352 " here (.hg not found)"))
3352 " here (.hg not found)"))
3353 o = repo.root
3353 o = repo.root
3354
3354
3355 app = hgweb.hgweb(o, baseui=ui)
3355 app = hgweb.hgweb(o, baseui=ui)
3356
3356
3357 class service(object):
3357 class service(object):
3358 def init(self):
3358 def init(self):
3359 util.set_signal_handler()
3359 util.set_signal_handler()
3360 self.httpd = hgweb.server.create_server(ui, app)
3360 self.httpd = hgweb.server.create_server(ui, app)
3361
3361
3362 if opts['port'] and not ui.verbose:
3362 if opts['port'] and not ui.verbose:
3363 return
3363 return
3364
3364
3365 if self.httpd.prefix:
3365 if self.httpd.prefix:
3366 prefix = self.httpd.prefix.strip('/') + '/'
3366 prefix = self.httpd.prefix.strip('/') + '/'
3367 else:
3367 else:
3368 prefix = ''
3368 prefix = ''
3369
3369
3370 port = ':%d' % self.httpd.port
3370 port = ':%d' % self.httpd.port
3371 if port == ':80':
3371 if port == ':80':
3372 port = ''
3372 port = ''
3373
3373
3374 bindaddr = self.httpd.addr
3374 bindaddr = self.httpd.addr
3375 if bindaddr == '0.0.0.0':
3375 if bindaddr == '0.0.0.0':
3376 bindaddr = '*'
3376 bindaddr = '*'
3377 elif ':' in bindaddr: # IPv6
3377 elif ':' in bindaddr: # IPv6
3378 bindaddr = '[%s]' % bindaddr
3378 bindaddr = '[%s]' % bindaddr
3379
3379
3380 fqaddr = self.httpd.fqaddr
3380 fqaddr = self.httpd.fqaddr
3381 if ':' in fqaddr:
3381 if ':' in fqaddr:
3382 fqaddr = '[%s]' % fqaddr
3382 fqaddr = '[%s]' % fqaddr
3383 if opts['port']:
3383 if opts['port']:
3384 write = ui.status
3384 write = ui.status
3385 else:
3385 else:
3386 write = ui.write
3386 write = ui.write
3387 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3387 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3388 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3388 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3389
3389
3390 def run(self):
3390 def run(self):
3391 self.httpd.serve_forever()
3391 self.httpd.serve_forever()
3392
3392
3393 service = service()
3393 service = service()
3394
3394
3395 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3395 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3396
3396
3397 def status(ui, repo, *pats, **opts):
3397 def status(ui, repo, *pats, **opts):
3398 """show changed files in the working directory
3398 """show changed files in the working directory
3399
3399
3400 Show status of files in the repository. If names are given, only
3400 Show status of files in the repository. If names are given, only
3401 files that match are shown. Files that are clean or ignored or
3401 files that match are shown. Files that are clean or ignored or
3402 the source of a copy/move operation, are not listed unless
3402 the source of a copy/move operation, are not listed unless
3403 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3403 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3404 Unless options described with "show only ..." are given, the
3404 Unless options described with "show only ..." are given, the
3405 options -mardu are used.
3405 options -mardu are used.
3406
3406
3407 Option -q/--quiet hides untracked (unknown and ignored) files
3407 Option -q/--quiet hides untracked (unknown and ignored) files
3408 unless explicitly requested with -u/--unknown or -i/--ignored.
3408 unless explicitly requested with -u/--unknown or -i/--ignored.
3409
3409
3410 NOTE: status may appear to disagree with diff if permissions have
3410 NOTE: status may appear to disagree with diff if permissions have
3411 changed or a merge has occurred. The standard diff format does not
3411 changed or a merge has occurred. The standard diff format does not
3412 report permission changes and diff only reports changes relative
3412 report permission changes and diff only reports changes relative
3413 to one merge parent.
3413 to one merge parent.
3414
3414
3415 If one revision is given, it is used as the base revision.
3415 If one revision is given, it is used as the base revision.
3416 If two revisions are given, the differences between them are
3416 If two revisions are given, the differences between them are
3417 shown. The --change option can also be used as a shortcut to list
3417 shown. The --change option can also be used as a shortcut to list
3418 the changed files of a revision from its first parent.
3418 the changed files of a revision from its first parent.
3419
3419
3420 The codes used to show the status of files are::
3420 The codes used to show the status of files are::
3421
3421
3422 M = modified
3422 M = modified
3423 A = added
3423 A = added
3424 R = removed
3424 R = removed
3425 C = clean
3425 C = clean
3426 ! = missing (deleted by non-hg command, but still tracked)
3426 ! = missing (deleted by non-hg command, but still tracked)
3427 ? = not tracked
3427 ? = not tracked
3428 I = ignored
3428 I = ignored
3429 = origin of the previous file listed as A (added)
3429 = origin of the previous file listed as A (added)
3430
3430
3431 Returns 0 on success.
3431 Returns 0 on success.
3432 """
3432 """
3433
3433
3434 revs = opts.get('rev')
3434 revs = opts.get('rev')
3435 change = opts.get('change')
3435 change = opts.get('change')
3436
3436
3437 if revs and change:
3437 if revs and change:
3438 msg = _('cannot specify --rev and --change at the same time')
3438 msg = _('cannot specify --rev and --change at the same time')
3439 raise util.Abort(msg)
3439 raise util.Abort(msg)
3440 elif change:
3440 elif change:
3441 node2 = repo.lookup(change)
3441 node2 = repo.lookup(change)
3442 node1 = repo[node2].parents()[0].node()
3442 node1 = repo[node2].parents()[0].node()
3443 else:
3443 else:
3444 node1, node2 = cmdutil.revpair(repo, revs)
3444 node1, node2 = cmdutil.revpair(repo, revs)
3445
3445
3446 cwd = (pats and repo.getcwd()) or ''
3446 cwd = (pats and repo.getcwd()) or ''
3447 end = opts.get('print0') and '\0' or '\n'
3447 end = opts.get('print0') and '\0' or '\n'
3448 copy = {}
3448 copy = {}
3449 states = 'modified added removed deleted unknown ignored clean'.split()
3449 states = 'modified added removed deleted unknown ignored clean'.split()
3450 show = [k for k in states if opts.get(k)]
3450 show = [k for k in states if opts.get(k)]
3451 if opts.get('all'):
3451 if opts.get('all'):
3452 show += ui.quiet and (states[:4] + ['clean']) or states
3452 show += ui.quiet and (states[:4] + ['clean']) or states
3453 if not show:
3453 if not show:
3454 show = ui.quiet and states[:4] or states[:5]
3454 show = ui.quiet and states[:4] or states[:5]
3455
3455
3456 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3456 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3457 'ignored' in show, 'clean' in show, 'unknown' in show)
3457 'ignored' in show, 'clean' in show, 'unknown' in show)
3458 changestates = zip(states, 'MAR!?IC', stat)
3458 changestates = zip(states, 'MAR!?IC', stat)
3459
3459
3460 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3460 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3461 ctxn = repo[nullid]
3461 ctxn = repo[nullid]
3462 ctx1 = repo[node1]
3462 ctx1 = repo[node1]
3463 ctx2 = repo[node2]
3463 ctx2 = repo[node2]
3464 added = stat[1]
3464 added = stat[1]
3465 if node2 is None:
3465 if node2 is None:
3466 added = stat[0] + stat[1] # merged?
3466 added = stat[0] + stat[1] # merged?
3467
3467
3468 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3468 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3469 if k in added:
3469 if k in added:
3470 copy[k] = v
3470 copy[k] = v
3471 elif v in added:
3471 elif v in added:
3472 copy[v] = k
3472 copy[v] = k
3473
3473
3474 for state, char, files in changestates:
3474 for state, char, files in changestates:
3475 if state in show:
3475 if state in show:
3476 format = "%s %%s%s" % (char, end)
3476 format = "%s %%s%s" % (char, end)
3477 if opts.get('no_status'):
3477 if opts.get('no_status'):
3478 format = "%%s%s" % end
3478 format = "%%s%s" % end
3479
3479
3480 for f in files:
3480 for f in files:
3481 ui.write(format % repo.pathto(f, cwd),
3481 ui.write(format % repo.pathto(f, cwd),
3482 label='status.' + state)
3482 label='status.' + state)
3483 if f in copy:
3483 if f in copy:
3484 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3484 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3485 label='status.copied')
3485 label='status.copied')
3486
3486
3487 def summary(ui, repo, **opts):
3487 def summary(ui, repo, **opts):
3488 """summarize working directory state
3488 """summarize working directory state
3489
3489
3490 This generates a brief summary of the working directory state,
3490 This generates a brief summary of the working directory state,
3491 including parents, branch, commit status, and available updates.
3491 including parents, branch, commit status, and available updates.
3492
3492
3493 With the --remote option, this will check the default paths for
3493 With the --remote option, this will check the default paths for
3494 incoming and outgoing changes. This can be time-consuming.
3494 incoming and outgoing changes. This can be time-consuming.
3495
3495
3496 Returns 0 on success.
3496 Returns 0 on success.
3497 """
3497 """
3498
3498
3499 ctx = repo[None]
3499 ctx = repo[None]
3500 parents = ctx.parents()
3500 parents = ctx.parents()
3501 pnode = parents[0].node()
3501 pnode = parents[0].node()
3502
3502
3503 for p in parents:
3503 for p in parents:
3504 # label with log.changeset (instead of log.parent) since this
3504 # label with log.changeset (instead of log.parent) since this
3505 # shows a working directory parent *changeset*:
3505 # shows a working directory parent *changeset*:
3506 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3506 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3507 label='log.changeset')
3507 label='log.changeset')
3508 ui.write(' '.join(p.tags()), label='log.tag')
3508 ui.write(' '.join(p.tags()), label='log.tag')
3509 if p.rev() == -1:
3509 if p.rev() == -1:
3510 if not len(repo):
3510 if not len(repo):
3511 ui.write(_(' (empty repository)'))
3511 ui.write(_(' (empty repository)'))
3512 else:
3512 else:
3513 ui.write(_(' (no revision checked out)'))
3513 ui.write(_(' (no revision checked out)'))
3514 ui.write('\n')
3514 ui.write('\n')
3515 if p.description():
3515 if p.description():
3516 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3516 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3517 label='log.summary')
3517 label='log.summary')
3518
3518
3519 branch = ctx.branch()
3519 branch = ctx.branch()
3520 bheads = repo.branchheads(branch)
3520 bheads = repo.branchheads(branch)
3521 m = _('branch: %s\n') % branch
3521 m = _('branch: %s\n') % branch
3522 if branch != 'default':
3522 if branch != 'default':
3523 ui.write(m, label='log.branch')
3523 ui.write(m, label='log.branch')
3524 else:
3524 else:
3525 ui.status(m, label='log.branch')
3525 ui.status(m, label='log.branch')
3526
3526
3527 st = list(repo.status(unknown=True))[:6]
3527 st = list(repo.status(unknown=True))[:6]
3528
3528
3529 c = repo.dirstate.copies()
3529 c = repo.dirstate.copies()
3530 copied, renamed = [], []
3530 copied, renamed = [], []
3531 for d, s in c.iteritems():
3531 for d, s in c.iteritems():
3532 if s in st[2]:
3532 if s in st[2]:
3533 st[2].remove(s)
3533 st[2].remove(s)
3534 renamed.append(d)
3534 renamed.append(d)
3535 else:
3535 else:
3536 copied.append(d)
3536 copied.append(d)
3537 if d in st[1]:
3537 if d in st[1]:
3538 st[1].remove(d)
3538 st[1].remove(d)
3539 st.insert(3, renamed)
3539 st.insert(3, renamed)
3540 st.insert(4, copied)
3540 st.insert(4, copied)
3541
3541
3542 ms = mergemod.mergestate(repo)
3542 ms = mergemod.mergestate(repo)
3543 st.append([f for f in ms if ms[f] == 'u'])
3543 st.append([f for f in ms if ms[f] == 'u'])
3544
3544
3545 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3545 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3546 st.append(subs)
3546 st.append(subs)
3547
3547
3548 labels = [ui.label(_('%d modified'), 'status.modified'),
3548 labels = [ui.label(_('%d modified'), 'status.modified'),
3549 ui.label(_('%d added'), 'status.added'),
3549 ui.label(_('%d added'), 'status.added'),
3550 ui.label(_('%d removed'), 'status.removed'),
3550 ui.label(_('%d removed'), 'status.removed'),
3551 ui.label(_('%d renamed'), 'status.copied'),
3551 ui.label(_('%d renamed'), 'status.copied'),
3552 ui.label(_('%d copied'), 'status.copied'),
3552 ui.label(_('%d copied'), 'status.copied'),
3553 ui.label(_('%d deleted'), 'status.deleted'),
3553 ui.label(_('%d deleted'), 'status.deleted'),
3554 ui.label(_('%d unknown'), 'status.unknown'),
3554 ui.label(_('%d unknown'), 'status.unknown'),
3555 ui.label(_('%d ignored'), 'status.ignored'),
3555 ui.label(_('%d ignored'), 'status.ignored'),
3556 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3556 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3557 ui.label(_('%d subrepos'), 'status.modified')]
3557 ui.label(_('%d subrepos'), 'status.modified')]
3558 t = []
3558 t = []
3559 for s, l in zip(st, labels):
3559 for s, l in zip(st, labels):
3560 if s:
3560 if s:
3561 t.append(l % len(s))
3561 t.append(l % len(s))
3562
3562
3563 t = ', '.join(t)
3563 t = ', '.join(t)
3564 cleanworkdir = False
3564 cleanworkdir = False
3565
3565
3566 if len(parents) > 1:
3566 if len(parents) > 1:
3567 t += _(' (merge)')
3567 t += _(' (merge)')
3568 elif branch != parents[0].branch():
3568 elif branch != parents[0].branch():
3569 t += _(' (new branch)')
3569 t += _(' (new branch)')
3570 elif (parents[0].extra().get('close') and
3570 elif (parents[0].extra().get('close') and
3571 pnode in repo.branchheads(branch, closed=True)):
3571 pnode in repo.branchheads(branch, closed=True)):
3572 t += _(' (head closed)')
3572 t += _(' (head closed)')
3573 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3573 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3574 t += _(' (clean)')
3574 t += _(' (clean)')
3575 cleanworkdir = True
3575 cleanworkdir = True
3576 elif pnode not in bheads:
3576 elif pnode not in bheads:
3577 t += _(' (new branch head)')
3577 t += _(' (new branch head)')
3578
3578
3579 if cleanworkdir:
3579 if cleanworkdir:
3580 ui.status(_('commit: %s\n') % t.strip())
3580 ui.status(_('commit: %s\n') % t.strip())
3581 else:
3581 else:
3582 ui.write(_('commit: %s\n') % t.strip())
3582 ui.write(_('commit: %s\n') % t.strip())
3583
3583
3584 # all ancestors of branch heads - all ancestors of parent = new csets
3584 # all ancestors of branch heads - all ancestors of parent = new csets
3585 new = [0] * len(repo)
3585 new = [0] * len(repo)
3586 cl = repo.changelog
3586 cl = repo.changelog
3587 for a in [cl.rev(n) for n in bheads]:
3587 for a in [cl.rev(n) for n in bheads]:
3588 new[a] = 1
3588 new[a] = 1
3589 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3589 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3590 new[a] = 1
3590 new[a] = 1
3591 for a in [p.rev() for p in parents]:
3591 for a in [p.rev() for p in parents]:
3592 if a >= 0:
3592 if a >= 0:
3593 new[a] = 0
3593 new[a] = 0
3594 for a in cl.ancestors(*[p.rev() for p in parents]):
3594 for a in cl.ancestors(*[p.rev() for p in parents]):
3595 new[a] = 0
3595 new[a] = 0
3596 new = sum(new)
3596 new = sum(new)
3597
3597
3598 if new == 0:
3598 if new == 0:
3599 ui.status(_('update: (current)\n'))
3599 ui.status(_('update: (current)\n'))
3600 elif pnode not in bheads:
3600 elif pnode not in bheads:
3601 ui.write(_('update: %d new changesets (update)\n') % new)
3601 ui.write(_('update: %d new changesets (update)\n') % new)
3602 else:
3602 else:
3603 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3603 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3604 (new, len(bheads)))
3604 (new, len(bheads)))
3605
3605
3606 if opts.get('remote'):
3606 if opts.get('remote'):
3607 t = []
3607 t = []
3608 source, branches = hg.parseurl(ui.expandpath('default'))
3608 source, branches = hg.parseurl(ui.expandpath('default'))
3609 other = hg.repository(hg.remoteui(repo, {}), source)
3609 other = hg.repository(hg.remoteui(repo, {}), source)
3610 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3610 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3611 ui.debug('comparing with %s\n' % url.hidepassword(source))
3611 ui.debug('comparing with %s\n' % url.hidepassword(source))
3612 repo.ui.pushbuffer()
3612 repo.ui.pushbuffer()
3613 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3613 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3614 repo.ui.popbuffer()
3614 repo.ui.popbuffer()
3615 if incoming:
3615 if incoming:
3616 t.append(_('1 or more incoming'))
3616 t.append(_('1 or more incoming'))
3617
3617
3618 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3618 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3619 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3619 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3620 other = hg.repository(hg.remoteui(repo, {}), dest)
3620 other = hg.repository(hg.remoteui(repo, {}), dest)
3621 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3621 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3622 repo.ui.pushbuffer()
3622 repo.ui.pushbuffer()
3623 o = discovery.findoutgoing(repo, other)
3623 o = discovery.findoutgoing(repo, other)
3624 repo.ui.popbuffer()
3624 repo.ui.popbuffer()
3625 o = repo.changelog.nodesbetween(o, None)[0]
3625 o = repo.changelog.nodesbetween(o, None)[0]
3626 if o:
3626 if o:
3627 t.append(_('%d outgoing') % len(o))
3627 t.append(_('%d outgoing') % len(o))
3628
3628
3629 if t:
3629 if t:
3630 ui.write(_('remote: %s\n') % (', '.join(t)))
3630 ui.write(_('remote: %s\n') % (', '.join(t)))
3631 else:
3631 else:
3632 ui.status(_('remote: (synced)\n'))
3632 ui.status(_('remote: (synced)\n'))
3633
3633
3634 def tag(ui, repo, name1, *names, **opts):
3634 def tag(ui, repo, name1, *names, **opts):
3635 """add one or more tags for the current or given revision
3635 """add one or more tags for the current or given revision
3636
3636
3637 Name a particular revision using <name>.
3637 Name a particular revision using <name>.
3638
3638
3639 Tags are used to name particular revisions of the repository and are
3639 Tags are used to name particular revisions of the repository and are
3640 very useful to compare different revisions, to go back to significant
3640 very useful to compare different revisions, to go back to significant
3641 earlier versions or to mark branch points as releases, etc.
3641 earlier versions or to mark branch points as releases, etc.
3642
3642
3643 If no revision is given, the parent of the working directory is
3643 If no revision is given, the parent of the working directory is
3644 used, or tip if no revision is checked out.
3644 used, or tip if no revision is checked out.
3645
3645
3646 To facilitate version control, distribution, and merging of tags,
3646 To facilitate version control, distribution, and merging of tags,
3647 they are stored as a file named ".hgtags" which is managed
3647 they are stored as a file named ".hgtags" which is managed
3648 similarly to other project files and can be hand-edited if
3648 similarly to other project files and can be hand-edited if
3649 necessary. The file '.hg/localtags' is used for local tags (not
3649 necessary. The file '.hg/localtags' is used for local tags (not
3650 shared among repositories).
3650 shared among repositories).
3651
3651
3652 See :hg:`help dates` for a list of formats valid for -d/--date.
3652 See :hg:`help dates` for a list of formats valid for -d/--date.
3653
3653
3654 Since tag names have priority over branch names during revision
3654 Since tag names have priority over branch names during revision
3655 lookup, using an existing branch name as a tag name is discouraged.
3655 lookup, using an existing branch name as a tag name is discouraged.
3656
3656
3657 Returns 0 on success.
3657 Returns 0 on success.
3658 """
3658 """
3659
3659
3660 rev_ = "."
3660 rev_ = "."
3661 names = [t.strip() for t in (name1,) + names]
3661 names = [t.strip() for t in (name1,) + names]
3662 if len(names) != len(set(names)):
3662 if len(names) != len(set(names)):
3663 raise util.Abort(_('tag names must be unique'))
3663 raise util.Abort(_('tag names must be unique'))
3664 for n in names:
3664 for n in names:
3665 if n in ['tip', '.', 'null']:
3665 if n in ['tip', '.', 'null']:
3666 raise util.Abort(_('the name \'%s\' is reserved') % n)
3666 raise util.Abort(_('the name \'%s\' is reserved') % n)
3667 if not n:
3667 if not n:
3668 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3668 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3669 if opts.get('rev') and opts.get('remove'):
3669 if opts.get('rev') and opts.get('remove'):
3670 raise util.Abort(_("--rev and --remove are incompatible"))
3670 raise util.Abort(_("--rev and --remove are incompatible"))
3671 if opts.get('rev'):
3671 if opts.get('rev'):
3672 rev_ = opts['rev']
3672 rev_ = opts['rev']
3673 message = opts.get('message')
3673 message = opts.get('message')
3674 if opts.get('remove'):
3674 if opts.get('remove'):
3675 expectedtype = opts.get('local') and 'local' or 'global'
3675 expectedtype = opts.get('local') and 'local' or 'global'
3676 for n in names:
3676 for n in names:
3677 if not repo.tagtype(n):
3677 if not repo.tagtype(n):
3678 raise util.Abort(_('tag \'%s\' does not exist') % n)
3678 raise util.Abort(_('tag \'%s\' does not exist') % n)
3679 if repo.tagtype(n) != expectedtype:
3679 if repo.tagtype(n) != expectedtype:
3680 if expectedtype == 'global':
3680 if expectedtype == 'global':
3681 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3681 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3682 else:
3682 else:
3683 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3683 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3684 rev_ = nullid
3684 rev_ = nullid
3685 if not message:
3685 if not message:
3686 # we don't translate commit messages
3686 # we don't translate commit messages
3687 message = 'Removed tag %s' % ', '.join(names)
3687 message = 'Removed tag %s' % ', '.join(names)
3688 elif not opts.get('force'):
3688 elif not opts.get('force'):
3689 for n in names:
3689 for n in names:
3690 if n in repo.tags():
3690 if n in repo.tags():
3691 raise util.Abort(_('tag \'%s\' already exists '
3691 raise util.Abort(_('tag \'%s\' already exists '
3692 '(use -f to force)') % n)
3692 '(use -f to force)') % n)
3693 if not rev_ and repo.dirstate.parents()[1] != nullid:
3693 if not rev_ and repo.dirstate.parents()[1] != nullid:
3694 raise util.Abort(_('uncommitted merge - please provide a '
3694 raise util.Abort(_('uncommitted merge - please provide a '
3695 'specific revision'))
3695 'specific revision'))
3696 r = repo[rev_].node()
3696 r = repo[rev_].node()
3697
3697
3698 if not message:
3698 if not message:
3699 # we don't translate commit messages
3699 # we don't translate commit messages
3700 message = ('Added tag %s for changeset %s' %
3700 message = ('Added tag %s for changeset %s' %
3701 (', '.join(names), short(r)))
3701 (', '.join(names), short(r)))
3702
3702
3703 date = opts.get('date')
3703 date = opts.get('date')
3704 if date:
3704 if date:
3705 date = util.parsedate(date)
3705 date = util.parsedate(date)
3706
3706
3707 if opts.get('edit'):
3707 if opts.get('edit'):
3708 message = ui.edit(message, ui.username())
3708 message = ui.edit(message, ui.username())
3709
3709
3710 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3710 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3711
3711
3712 def tags(ui, repo):
3712 def tags(ui, repo):
3713 """list repository tags
3713 """list repository tags
3714
3714
3715 This lists both regular and local tags. When the -v/--verbose
3715 This lists both regular and local tags. When the -v/--verbose
3716 switch is used, a third column "local" is printed for local tags.
3716 switch is used, a third column "local" is printed for local tags.
3717
3717
3718 Returns 0 on success.
3718 Returns 0 on success.
3719 """
3719 """
3720
3720
3721 hexfunc = ui.debugflag and hex or short
3721 hexfunc = ui.debugflag and hex or short
3722 tagtype = ""
3722 tagtype = ""
3723
3723
3724 for t, n in reversed(repo.tagslist()):
3724 for t, n in reversed(repo.tagslist()):
3725 if ui.quiet:
3725 if ui.quiet:
3726 ui.write("%s\n" % t)
3726 ui.write("%s\n" % t)
3727 continue
3727 continue
3728
3728
3729 try:
3729 try:
3730 hn = hexfunc(n)
3730 hn = hexfunc(n)
3731 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3731 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3732 except error.LookupError:
3732 except error.LookupError:
3733 r = " ?:%s" % hn
3733 r = " ?:%s" % hn
3734 else:
3734 else:
3735 spaces = " " * (30 - encoding.colwidth(t))
3735 spaces = " " * (30 - encoding.colwidth(t))
3736 if ui.verbose:
3736 if ui.verbose:
3737 if repo.tagtype(t) == 'local':
3737 if repo.tagtype(t) == 'local':
3738 tagtype = " local"
3738 tagtype = " local"
3739 else:
3739 else:
3740 tagtype = ""
3740 tagtype = ""
3741 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3741 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3742
3742
3743 def tip(ui, repo, **opts):
3743 def tip(ui, repo, **opts):
3744 """show the tip revision
3744 """show the tip revision
3745
3745
3746 The tip revision (usually just called the tip) is the changeset
3746 The tip revision (usually just called the tip) is the changeset
3747 most recently added to the repository (and therefore the most
3747 most recently added to the repository (and therefore the most
3748 recently changed head).
3748 recently changed head).
3749
3749
3750 If you have just made a commit, that commit will be the tip. If
3750 If you have just made a commit, that commit will be the tip. If
3751 you have just pulled changes from another repository, the tip of
3751 you have just pulled changes from another repository, the tip of
3752 that repository becomes the current tip. The "tip" tag is special
3752 that repository becomes the current tip. The "tip" tag is special
3753 and cannot be renamed or assigned to a different changeset.
3753 and cannot be renamed or assigned to a different changeset.
3754
3754
3755 Returns 0 on success.
3755 Returns 0 on success.
3756 """
3756 """
3757 displayer = cmdutil.show_changeset(ui, repo, opts)
3757 displayer = cmdutil.show_changeset(ui, repo, opts)
3758 displayer.show(repo[len(repo) - 1])
3758 displayer.show(repo[len(repo) - 1])
3759 displayer.close()
3759 displayer.close()
3760
3760
3761 def unbundle(ui, repo, fname1, *fnames, **opts):
3761 def unbundle(ui, repo, fname1, *fnames, **opts):
3762 """apply one or more changegroup files
3762 """apply one or more changegroup files
3763
3763
3764 Apply one or more compressed changegroup files generated by the
3764 Apply one or more compressed changegroup files generated by the
3765 bundle command.
3765 bundle command.
3766
3766
3767 Returns 0 on success, 1 if an update has unresolved files.
3767 Returns 0 on success, 1 if an update has unresolved files.
3768 """
3768 """
3769 fnames = (fname1,) + fnames
3769 fnames = (fname1,) + fnames
3770
3770
3771 lock = repo.lock()
3771 lock = repo.lock()
3772 try:
3772 try:
3773 for fname in fnames:
3773 for fname in fnames:
3774 f = url.open(ui, fname)
3774 f = url.open(ui, fname)
3775 gen = changegroup.readbundle(f, fname)
3775 gen = changegroup.readbundle(f, fname)
3776 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3776 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3777 lock=lock)
3777 lock=lock)
3778 finally:
3778 finally:
3779 lock.release()
3779 lock.release()
3780
3780
3781 return postincoming(ui, repo, modheads, opts.get('update'), None)
3781 return postincoming(ui, repo, modheads, opts.get('update'), None)
3782
3782
3783 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3783 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3784 """update working directory (or switch revisions)
3784 """update working directory (or switch revisions)
3785
3785
3786 Update the repository's working directory to the specified
3786 Update the repository's working directory to the specified
3787 changeset.
3787 changeset.
3788
3788
3789 If no changeset is specified, attempt to update to the tip of the
3789 If no changeset is specified, attempt to update to the tip of the
3790 current branch. If this changeset is a descendant of the working
3790 current branch. If this changeset is a descendant of the working
3791 directory's parent, update to it, otherwise abort.
3791 directory's parent, update to it, otherwise abort.
3792
3792
3793 The following rules apply when the working directory contains
3793 The following rules apply when the working directory contains
3794 uncommitted changes:
3794 uncommitted changes:
3795
3795
3796 1. If neither -c/--check nor -C/--clean is specified, and if
3796 1. If neither -c/--check nor -C/--clean is specified, and if
3797 the requested changeset is an ancestor or descendant of
3797 the requested changeset is an ancestor or descendant of
3798 the working directory's parent, the uncommitted changes
3798 the working directory's parent, the uncommitted changes
3799 are merged into the requested changeset and the merged
3799 are merged into the requested changeset and the merged
3800 result is left uncommitted. If the requested changeset is
3800 result is left uncommitted. If the requested changeset is
3801 not an ancestor or descendant (that is, it is on another
3801 not an ancestor or descendant (that is, it is on another
3802 branch), the update is aborted and the uncommitted changes
3802 branch), the update is aborted and the uncommitted changes
3803 are preserved.
3803 are preserved.
3804
3804
3805 2. With the -c/--check option, the update is aborted and the
3805 2. With the -c/--check option, the update is aborted and the
3806 uncommitted changes are preserved.
3806 uncommitted changes are preserved.
3807
3807
3808 3. With the -C/--clean option, uncommitted changes are discarded and
3808 3. With the -C/--clean option, uncommitted changes are discarded and
3809 the working directory is updated to the requested changeset.
3809 the working directory is updated to the requested changeset.
3810
3810
3811 Use null as the changeset to remove the working directory (like
3811 Use null as the changeset to remove the working directory (like
3812 :hg:`clone -U`).
3812 :hg:`clone -U`).
3813
3813
3814 If you want to update just one file to an older changeset, use :hg:`revert`.
3814 If you want to update just one file to an older changeset, use :hg:`revert`.
3815
3815
3816 See :hg:`help dates` for a list of formats valid for -d/--date.
3816 See :hg:`help dates` for a list of formats valid for -d/--date.
3817
3817
3818 Returns 0 on success, 1 if there are unresolved files.
3818 Returns 0 on success, 1 if there are unresolved files.
3819 """
3819 """
3820 if rev and node:
3820 if rev and node:
3821 raise util.Abort(_("please specify just one revision"))
3821 raise util.Abort(_("please specify just one revision"))
3822
3822
3823 if not rev:
3823 if not rev:
3824 rev = node
3824 rev = node
3825
3825
3826 if check and clean:
3826 if check and clean:
3827 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3827 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3828
3828
3829 if check:
3829 if check:
3830 # we could use dirty() but we can ignore merge and branch trivia
3830 # we could use dirty() but we can ignore merge and branch trivia
3831 c = repo[None]
3831 c = repo[None]
3832 if c.modified() or c.added() or c.removed():
3832 if c.modified() or c.added() or c.removed():
3833 raise util.Abort(_("uncommitted local changes"))
3833 raise util.Abort(_("uncommitted local changes"))
3834
3834
3835 if date:
3835 if date:
3836 if rev:
3836 if rev:
3837 raise util.Abort(_("you can't specify a revision and a date"))
3837 raise util.Abort(_("you can't specify a revision and a date"))
3838 rev = cmdutil.finddate(ui, repo, date)
3838 rev = cmdutil.finddate(ui, repo, date)
3839
3839
3840 if clean or check:
3840 if clean or check:
3841 return hg.clean(repo, rev)
3841 return hg.clean(repo, rev)
3842 else:
3842 else:
3843 return hg.update(repo, rev)
3843 return hg.update(repo, rev)
3844
3844
3845 def verify(ui, repo):
3845 def verify(ui, repo):
3846 """verify the integrity of the repository
3846 """verify the integrity of the repository
3847
3847
3848 Verify the integrity of the current repository.
3848 Verify the integrity of the current repository.
3849
3849
3850 This will perform an extensive check of the repository's
3850 This will perform an extensive check of the repository's
3851 integrity, validating the hashes and checksums of each entry in
3851 integrity, validating the hashes and checksums of each entry in
3852 the changelog, manifest, and tracked files, as well as the
3852 the changelog, manifest, and tracked files, as well as the
3853 integrity of their crosslinks and indices.
3853 integrity of their crosslinks and indices.
3854
3854
3855 Returns 0 on success, 1 if errors are encountered.
3855 Returns 0 on success, 1 if errors are encountered.
3856 """
3856 """
3857 return hg.verify(repo)
3857 return hg.verify(repo)
3858
3858
3859 def version_(ui):
3859 def version_(ui):
3860 """output version and copyright information"""
3860 """output version and copyright information"""
3861 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3861 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3862 % util.version())
3862 % util.version())
3863 ui.status(_(
3863 ui.status(_(
3864 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3864 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3865 "This is free software; see the source for copying conditions. "
3865 "This is free software; see the source for copying conditions. "
3866 "There is NO\nwarranty; "
3866 "There is NO\nwarranty; "
3867 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3867 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3868 ))
3868 ))
3869
3869
3870 # Command options and aliases are listed here, alphabetically
3870 # Command options and aliases are listed here, alphabetically
3871
3871
3872 globalopts = [
3872 globalopts = [
3873 ('R', 'repository', '',
3873 ('R', 'repository', '',
3874 _('repository root directory or name of overlay bundle file'),
3874 _('repository root directory or name of overlay bundle file'),
3875 _('REPO')),
3875 _('REPO')),
3876 ('', 'cwd', '',
3876 ('', 'cwd', '',
3877 _('change working directory'), _('DIR')),
3877 _('change working directory'), _('DIR')),
3878 ('y', 'noninteractive', None,
3878 ('y', 'noninteractive', None,
3879 _('do not prompt, assume \'yes\' for any required answers')),
3879 _('do not prompt, assume \'yes\' for any required answers')),
3880 ('q', 'quiet', None, _('suppress output')),
3880 ('q', 'quiet', None, _('suppress output')),
3881 ('v', 'verbose', None, _('enable additional output')),
3881 ('v', 'verbose', None, _('enable additional output')),
3882 ('', 'config', [],
3882 ('', 'config', [],
3883 _('set/override config option (use \'section.name=value\')'),
3883 _('set/override config option (use \'section.name=value\')'),
3884 _('CONFIG')),
3884 _('CONFIG')),
3885 ('', 'debug', None, _('enable debugging output')),
3885 ('', 'debug', None, _('enable debugging output')),
3886 ('', 'debugger', None, _('start debugger')),
3886 ('', 'debugger', None, _('start debugger')),
3887 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3887 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3888 _('ENCODE')),
3888 _('ENCODE')),
3889 ('', 'encodingmode', encoding.encodingmode,
3889 ('', 'encodingmode', encoding.encodingmode,
3890 _('set the charset encoding mode'), _('MODE')),
3890 _('set the charset encoding mode'), _('MODE')),
3891 ('', 'traceback', None, _('always print a traceback on exception')),
3891 ('', 'traceback', None, _('always print a traceback on exception')),
3892 ('', 'time', None, _('time how long the command takes')),
3892 ('', 'time', None, _('time how long the command takes')),
3893 ('', 'profile', None, _('print command execution profile')),
3893 ('', 'profile', None, _('print command execution profile')),
3894 ('', 'version', None, _('output version information and exit')),
3894 ('', 'version', None, _('output version information and exit')),
3895 ('h', 'help', None, _('display help and exit')),
3895 ('h', 'help', None, _('display help and exit')),
3896 ]
3896 ]
3897
3897
3898 dryrunopts = [('n', 'dry-run', None,
3898 dryrunopts = [('n', 'dry-run', None,
3899 _('do not perform actions, just print output'))]
3899 _('do not perform actions, just print output'))]
3900
3900
3901 remoteopts = [
3901 remoteopts = [
3902 ('e', 'ssh', '',
3902 ('e', 'ssh', '',
3903 _('specify ssh command to use'), _('CMD')),
3903 _('specify ssh command to use'), _('CMD')),
3904 ('', 'remotecmd', '',
3904 ('', 'remotecmd', '',
3905 _('specify hg command to run on the remote side'), _('CMD')),
3905 _('specify hg command to run on the remote side'), _('CMD')),
3906 ]
3906 ]
3907
3907
3908 walkopts = [
3908 walkopts = [
3909 ('I', 'include', [],
3909 ('I', 'include', [],
3910 _('include names matching the given patterns'), _('PATTERN')),
3910 _('include names matching the given patterns'), _('PATTERN')),
3911 ('X', 'exclude', [],
3911 ('X', 'exclude', [],
3912 _('exclude names matching the given patterns'), _('PATTERN')),
3912 _('exclude names matching the given patterns'), _('PATTERN')),
3913 ]
3913 ]
3914
3914
3915 commitopts = [
3915 commitopts = [
3916 ('m', 'message', '',
3916 ('m', 'message', '',
3917 _('use text as commit message'), _('TEXT')),
3917 _('use text as commit message'), _('TEXT')),
3918 ('l', 'logfile', '',
3918 ('l', 'logfile', '',
3919 _('read commit message from file'), _('FILE')),
3919 _('read commit message from file'), _('FILE')),
3920 ]
3920 ]
3921
3921
3922 commitopts2 = [
3922 commitopts2 = [
3923 ('d', 'date', '',
3923 ('d', 'date', '',
3924 _('record datecode as commit date'), _('DATE')),
3924 _('record datecode as commit date'), _('DATE')),
3925 ('u', 'user', '',
3925 ('u', 'user', '',
3926 _('record the specified user as committer'), _('USER')),
3926 _('record the specified user as committer'), _('USER')),
3927 ]
3927 ]
3928
3928
3929 templateopts = [
3929 templateopts = [
3930 ('', 'style', '',
3930 ('', 'style', '',
3931 _('display using template map file'), _('STYLE')),
3931 _('display using template map file'), _('STYLE')),
3932 ('', 'template', '',
3932 ('', 'template', '',
3933 _('display with template'), _('TEMPLATE')),
3933 _('display with template'), _('TEMPLATE')),
3934 ]
3934 ]
3935
3935
3936 logopts = [
3936 logopts = [
3937 ('p', 'patch', None, _('show patch')),
3937 ('p', 'patch', None, _('show patch')),
3938 ('g', 'git', None, _('use git extended diff format')),
3938 ('g', 'git', None, _('use git extended diff format')),
3939 ('l', 'limit', '',
3939 ('l', 'limit', '',
3940 _('limit number of changes displayed'), _('NUM')),
3940 _('limit number of changes displayed'), _('NUM')),
3941 ('M', 'no-merges', None, _('do not show merges')),
3941 ('M', 'no-merges', None, _('do not show merges')),
3942 ('', 'stat', None, _('output diffstat-style summary of changes')),
3942 ('', 'stat', None, _('output diffstat-style summary of changes')),
3943 ] + templateopts
3943 ] + templateopts
3944
3944
3945 diffopts = [
3945 diffopts = [
3946 ('a', 'text', None, _('treat all files as text')),
3946 ('a', 'text', None, _('treat all files as text')),
3947 ('g', 'git', None, _('use git extended diff format')),
3947 ('g', 'git', None, _('use git extended diff format')),
3948 ('', 'nodates', None, _('omit dates from diff headers'))
3948 ('', 'nodates', None, _('omit dates from diff headers'))
3949 ]
3949 ]
3950
3950
3951 diffopts2 = [
3951 diffopts2 = [
3952 ('p', 'show-function', None, _('show which function each change is in')),
3952 ('p', 'show-function', None, _('show which function each change is in')),
3953 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3953 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3954 ('w', 'ignore-all-space', None,
3954 ('w', 'ignore-all-space', None,
3955 _('ignore white space when comparing lines')),
3955 _('ignore white space when comparing lines')),
3956 ('b', 'ignore-space-change', None,
3956 ('b', 'ignore-space-change', None,
3957 _('ignore changes in the amount of white space')),
3957 _('ignore changes in the amount of white space')),
3958 ('B', 'ignore-blank-lines', None,
3958 ('B', 'ignore-blank-lines', None,
3959 _('ignore changes whose lines are all blank')),
3959 _('ignore changes whose lines are all blank')),
3960 ('U', 'unified', '',
3960 ('U', 'unified', '',
3961 _('number of lines of context to show'), _('NUM')),
3961 _('number of lines of context to show'), _('NUM')),
3962 ('', 'stat', None, _('output diffstat-style summary of changes')),
3962 ('', 'stat', None, _('output diffstat-style summary of changes')),
3963 ]
3963 ]
3964
3964
3965 similarityopts = [
3965 similarityopts = [
3966 ('s', 'similarity', '',
3966 ('s', 'similarity', '',
3967 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3967 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3968 ]
3968 ]
3969
3969
3970 table = {
3970 table = {
3971 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3971 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3972 "addremove":
3972 "addremove":
3973 (addremove, similarityopts + walkopts + dryrunopts,
3973 (addremove, similarityopts + walkopts + dryrunopts,
3974 _('[OPTION]... [FILE]...')),
3974 _('[OPTION]... [FILE]...')),
3975 "^annotate|blame":
3975 "^annotate|blame":
3976 (annotate,
3976 (annotate,
3977 [('r', 'rev', '',
3977 [('r', 'rev', '',
3978 _('annotate the specified revision'), _('REV')),
3978 _('annotate the specified revision'), _('REV')),
3979 ('', 'follow', None,
3979 ('', 'follow', None,
3980 _('follow copies/renames and list the filename (DEPRECATED)')),
3980 _('follow copies/renames and list the filename (DEPRECATED)')),
3981 ('', 'no-follow', None, _("don't follow copies and renames")),
3981 ('', 'no-follow', None, _("don't follow copies and renames")),
3982 ('a', 'text', None, _('treat all files as text')),
3982 ('a', 'text', None, _('treat all files as text')),
3983 ('u', 'user', None, _('list the author (long with -v)')),
3983 ('u', 'user', None, _('list the author (long with -v)')),
3984 ('f', 'file', None, _('list the filename')),
3984 ('f', 'file', None, _('list the filename')),
3985 ('d', 'date', None, _('list the date (short with -q)')),
3985 ('d', 'date', None, _('list the date (short with -q)')),
3986 ('n', 'number', None, _('list the revision number (default)')),
3986 ('n', 'number', None, _('list the revision number (default)')),
3987 ('c', 'changeset', None, _('list the changeset')),
3987 ('c', 'changeset', None, _('list the changeset')),
3988 ('l', 'line-number', None,
3988 ('l', 'line-number', None,
3989 _('show line number at the first appearance'))
3989 _('show line number at the first appearance'))
3990 ] + walkopts,
3990 ] + walkopts,
3991 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3991 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3992 "archive":
3992 "archive":
3993 (archive,
3993 (archive,
3994 [('', 'no-decode', None, _('do not pass files through decoders')),
3994 [('', 'no-decode', None, _('do not pass files through decoders')),
3995 ('p', 'prefix', '',
3995 ('p', 'prefix', '',
3996 _('directory prefix for files in archive'), _('PREFIX')),
3996 _('directory prefix for files in archive'), _('PREFIX')),
3997 ('r', 'rev', '',
3997 ('r', 'rev', '',
3998 _('revision to distribute'), _('REV')),
3998 _('revision to distribute'), _('REV')),
3999 ('t', 'type', '',
3999 ('t', 'type', '',
4000 _('type of distribution to create'), _('TYPE')),
4000 _('type of distribution to create'), _('TYPE')),
4001 ] + walkopts,
4001 ] + walkopts,
4002 _('[OPTION]... DEST')),
4002 _('[OPTION]... DEST')),
4003 "backout":
4003 "backout":
4004 (backout,
4004 (backout,
4005 [('', 'merge', None,
4005 [('', 'merge', None,
4006 _('merge with old dirstate parent after backout')),
4006 _('merge with old dirstate parent after backout')),
4007 ('', 'parent', '',
4007 ('', 'parent', '',
4008 _('parent to choose when backing out merge'), _('REV')),
4008 _('parent to choose when backing out merge'), _('REV')),
4009 ('r', 'rev', '',
4009 ('r', 'rev', '',
4010 _('revision to backout'), _('REV')),
4010 _('revision to backout'), _('REV')),
4011 ] + walkopts + commitopts + commitopts2,
4011 ] + walkopts + commitopts + commitopts2,
4012 _('[OPTION]... [-r] REV')),
4012 _('[OPTION]... [-r] REV')),
4013 "bisect":
4013 "bisect":
4014 (bisect,
4014 (bisect,
4015 [('r', 'reset', False, _('reset bisect state')),
4015 [('r', 'reset', False, _('reset bisect state')),
4016 ('g', 'good', False, _('mark changeset good')),
4016 ('g', 'good', False, _('mark changeset good')),
4017 ('b', 'bad', False, _('mark changeset bad')),
4017 ('b', 'bad', False, _('mark changeset bad')),
4018 ('s', 'skip', False, _('skip testing changeset')),
4018 ('s', 'skip', False, _('skip testing changeset')),
4019 ('c', 'command', '',
4019 ('c', 'command', '',
4020 _('use command to check changeset state'), _('CMD')),
4020 _('use command to check changeset state'), _('CMD')),
4021 ('U', 'noupdate', False, _('do not update to target'))],
4021 ('U', 'noupdate', False, _('do not update to target'))],
4022 _("[-gbsr] [-U] [-c CMD] [REV]")),
4022 _("[-gbsr] [-U] [-c CMD] [REV]")),
4023 "branch":
4023 "branch":
4024 (branch,
4024 (branch,
4025 [('f', 'force', None,
4025 [('f', 'force', None,
4026 _('set branch name even if it shadows an existing branch')),
4026 _('set branch name even if it shadows an existing branch')),
4027 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4027 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4028 _('[-fC] [NAME]')),
4028 _('[-fC] [NAME]')),
4029 "branches":
4029 "branches":
4030 (branches,
4030 (branches,
4031 [('a', 'active', False,
4031 [('a', 'active', False,
4032 _('show only branches that have unmerged heads')),
4032 _('show only branches that have unmerged heads')),
4033 ('c', 'closed', False,
4033 ('c', 'closed', False,
4034 _('show normal and closed branches'))],
4034 _('show normal and closed branches'))],
4035 _('[-ac]')),
4035 _('[-ac]')),
4036 "bundle":
4036 "bundle":
4037 (bundle,
4037 (bundle,
4038 [('f', 'force', None,
4038 [('f', 'force', None,
4039 _('run even when the destination is unrelated')),
4039 _('run even when the destination is unrelated')),
4040 ('r', 'rev', [],
4040 ('r', 'rev', [],
4041 _('a changeset intended to be added to the destination'),
4041 _('a changeset intended to be added to the destination'),
4042 _('REV')),
4042 _('REV')),
4043 ('b', 'branch', [],
4043 ('b', 'branch', [],
4044 _('a specific branch you would like to bundle'),
4044 _('a specific branch you would like to bundle'),
4045 _('BRANCH')),
4045 _('BRANCH')),
4046 ('', 'base', [],
4046 ('', 'base', [],
4047 _('a base changeset assumed to be available at the destination'),
4047 _('a base changeset assumed to be available at the destination'),
4048 _('REV')),
4048 _('REV')),
4049 ('a', 'all', None, _('bundle all changesets in the repository')),
4049 ('a', 'all', None, _('bundle all changesets in the repository')),
4050 ('t', 'type', 'bzip2',
4050 ('t', 'type', 'bzip2',
4051 _('bundle compression type to use'), _('TYPE')),
4051 _('bundle compression type to use'), _('TYPE')),
4052 ] + remoteopts,
4052 ] + remoteopts,
4053 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4053 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4054 "cat":
4054 "cat":
4055 (cat,
4055 (cat,
4056 [('o', 'output', '',
4056 [('o', 'output', '',
4057 _('print output to file with formatted name'), _('FORMAT')),
4057 _('print output to file with formatted name'), _('FORMAT')),
4058 ('r', 'rev', '',
4058 ('r', 'rev', '',
4059 _('print the given revision'), _('REV')),
4059 _('print the given revision'), _('REV')),
4060 ('', 'decode', None, _('apply any matching decode filter')),
4060 ('', 'decode', None, _('apply any matching decode filter')),
4061 ] + walkopts,
4061 ] + walkopts,
4062 _('[OPTION]... FILE...')),
4062 _('[OPTION]... FILE...')),
4063 "^clone":
4063 "^clone":
4064 (clone,
4064 (clone,
4065 [('U', 'noupdate', None,
4065 [('U', 'noupdate', None,
4066 _('the clone will include an empty working copy (only a repository)')),
4066 _('the clone will include an empty working copy (only a repository)')),
4067 ('u', 'updaterev', '',
4067 ('u', 'updaterev', '',
4068 _('revision, tag or branch to check out'), _('REV')),
4068 _('revision, tag or branch to check out'), _('REV')),
4069 ('r', 'rev', [],
4069 ('r', 'rev', [],
4070 _('include the specified changeset'), _('REV')),
4070 _('include the specified changeset'), _('REV')),
4071 ('b', 'branch', [],
4071 ('b', 'branch', [],
4072 _('clone only the specified branch'), _('BRANCH')),
4072 _('clone only the specified branch'), _('BRANCH')),
4073 ('', 'pull', None, _('use pull protocol to copy metadata')),
4073 ('', 'pull', None, _('use pull protocol to copy metadata')),
4074 ('', 'uncompressed', None,
4074 ('', 'uncompressed', None,
4075 _('use uncompressed transfer (fast over LAN)')),
4075 _('use uncompressed transfer (fast over LAN)')),
4076 ] + remoteopts,
4076 ] + remoteopts,
4077 _('[OPTION]... SOURCE [DEST]')),
4077 _('[OPTION]... SOURCE [DEST]')),
4078 "^commit|ci":
4078 "^commit|ci":
4079 (commit,
4079 (commit,
4080 [('A', 'addremove', None,
4080 [('A', 'addremove', None,
4081 _('mark new/missing files as added/removed before committing')),
4081 _('mark new/missing files as added/removed before committing')),
4082 ('', 'close-branch', None,
4082 ('', 'close-branch', None,
4083 _('mark a branch as closed, hiding it from the branch list')),
4083 _('mark a branch as closed, hiding it from the branch list')),
4084 ] + walkopts + commitopts + commitopts2,
4084 ] + walkopts + commitopts + commitopts2,
4085 _('[OPTION]... [FILE]...')),
4085 _('[OPTION]... [FILE]...')),
4086 "copy|cp":
4086 "copy|cp":
4087 (copy,
4087 (copy,
4088 [('A', 'after', None, _('record a copy that has already occurred')),
4088 [('A', 'after', None, _('record a copy that has already occurred')),
4089 ('f', 'force', None,
4089 ('f', 'force', None,
4090 _('forcibly copy over an existing managed file')),
4090 _('forcibly copy over an existing managed file')),
4091 ] + walkopts + dryrunopts,
4091 ] + walkopts + dryrunopts,
4092 _('[OPTION]... [SOURCE]... DEST')),
4092 _('[OPTION]... [SOURCE]... DEST')),
4093 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4093 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4094 "debugbuilddag":
4094 "debugbuilddag":
4095 (debugbuilddag,
4095 (debugbuilddag,
4096 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4096 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4097 ('a', 'appended-file', None, _('add single file all revs append to')),
4097 ('a', 'appended-file', None, _('add single file all revs append to')),
4098 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4098 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4099 ('n', 'new-file', None, _('add new file at each rev')),
4099 ('n', 'new-file', None, _('add new file at each rev')),
4100 ],
4100 ],
4101 _('[OPTION]... TEXT')),
4101 _('[OPTION]... TEXT')),
4102 "debugcheckstate": (debugcheckstate, [], ''),
4102 "debugcheckstate": (debugcheckstate, [], ''),
4103 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4103 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4104 "debugcomplete":
4104 "debugcomplete":
4105 (debugcomplete,
4105 (debugcomplete,
4106 [('o', 'options', None, _('show the command options'))],
4106 [('o', 'options', None, _('show the command options'))],
4107 _('[-o] CMD')),
4107 _('[-o] CMD')),
4108 "debugdag":
4108 "debugdag":
4109 (debugdag,
4109 (debugdag,
4110 [('t', 'tags', None, _('use tags as labels')),
4110 [('t', 'tags', None, _('use tags as labels')),
4111 ('b', 'branches', None, _('annotate with branch names')),
4111 ('b', 'branches', None, _('annotate with branch names')),
4112 ('', 'dots', None, _('use dots for runs')),
4112 ('', 'dots', None, _('use dots for runs')),
4113 ('s', 'spaces', None, _('separate elements by spaces')),
4113 ('s', 'spaces', None, _('separate elements by spaces')),
4114 ],
4114 ],
4115 _('[OPTION]... [FILE [REV]...]')),
4115 _('[OPTION]... [FILE [REV]...]')),
4116 "debugdate":
4116 "debugdate":
4117 (debugdate,
4117 (debugdate,
4118 [('e', 'extended', None, _('try extended date formats'))],
4118 [('e', 'extended', None, _('try extended date formats'))],
4119 _('[-e] DATE [RANGE]')),
4119 _('[-e] DATE [RANGE]')),
4120 "debugdata": (debugdata, [], _('FILE REV')),
4120 "debugdata": (debugdata, [], _('FILE REV')),
4121 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4121 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4122 "debugindex": (debugindex, [], _('FILE')),
4122 "debugindex": (debugindex, [], _('FILE')),
4123 "debugindexdot": (debugindexdot, [], _('FILE')),
4123 "debugindexdot": (debugindexdot, [], _('FILE')),
4124 "debuginstall": (debuginstall, [], ''),
4124 "debuginstall": (debuginstall, [], ''),
4125 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4125 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4126 "debugrebuildstate":
4126 "debugrebuildstate":
4127 (debugrebuildstate,
4127 (debugrebuildstate,
4128 [('r', 'rev', '',
4128 [('r', 'rev', '',
4129 _('revision to rebuild to'), _('REV'))],
4129 _('revision to rebuild to'), _('REV'))],
4130 _('[-r REV] [REV]')),
4130 _('[-r REV] [REV]')),
4131 "debugrename":
4131 "debugrename":
4132 (debugrename,
4132 (debugrename,
4133 [('r', 'rev', '',
4133 [('r', 'rev', '',
4134 _('revision to debug'), _('REV'))],
4134 _('revision to debug'), _('REV'))],
4135 _('[-r REV] FILE')),
4135 _('[-r REV] FILE')),
4136 "debugrevspec":
4136 "debugrevspec":
4137 (debugrevspec, [], ('REVSPEC')),
4137 (debugrevspec, [], ('REVSPEC')),
4138 "debugsetparents":
4138 "debugsetparents":
4139 (debugsetparents, [], _('REV1 [REV2]')),
4139 (debugsetparents, [], _('REV1 [REV2]')),
4140 "debugstate":
4140 "debugstate":
4141 (debugstate,
4141 (debugstate,
4142 [('', 'nodates', None, _('do not display the saved mtime'))],
4142 [('', 'nodates', None, _('do not display the saved mtime'))],
4143 _('[OPTION]...')),
4143 _('[OPTION]...')),
4144 "debugsub":
4144 "debugsub":
4145 (debugsub,
4145 (debugsub,
4146 [('r', 'rev', '',
4146 [('r', 'rev', '',
4147 _('revision to check'), _('REV'))],
4147 _('revision to check'), _('REV'))],
4148 _('[-r REV] [REV]')),
4148 _('[-r REV] [REV]')),
4149 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4149 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4150 "^diff":
4150 "^diff":
4151 (diff,
4151 (diff,
4152 [('r', 'rev', [],
4152 [('r', 'rev', [],
4153 _('revision'), _('REV')),
4153 _('revision'), _('REV')),
4154 ('c', 'change', '',
4154 ('c', 'change', '',
4155 _('change made by revision'), _('REV'))
4155 _('change made by revision'), _('REV'))
4156 ] + diffopts + diffopts2 + walkopts,
4156 ] + diffopts + diffopts2 + walkopts,
4157 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4157 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4158 "^export":
4158 "^export":
4159 (export,
4159 (export,
4160 [('o', 'output', '',
4160 [('o', 'output', '',
4161 _('print output to file with formatted name'), _('FORMAT')),
4161 _('print output to file with formatted name'), _('FORMAT')),
4162 ('', 'switch-parent', None, _('diff against the second parent')),
4162 ('', 'switch-parent', None, _('diff against the second parent')),
4163 ('r', 'rev', [],
4163 ('r', 'rev', [],
4164 _('revisions to export'), _('REV')),
4164 _('revisions to export'), _('REV')),
4165 ] + diffopts,
4165 ] + diffopts,
4166 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4166 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4167 "^forget":
4167 "^forget":
4168 (forget,
4168 (forget,
4169 [] + walkopts,
4169 [] + walkopts,
4170 _('[OPTION]... FILE...')),
4170 _('[OPTION]... FILE...')),
4171 "grep":
4171 "grep":
4172 (grep,
4172 (grep,
4173 [('0', 'print0', None, _('end fields with NUL')),
4173 [('0', 'print0', None, _('end fields with NUL')),
4174 ('', 'all', None, _('print all revisions that match')),
4174 ('', 'all', None, _('print all revisions that match')),
4175 ('f', 'follow', None,
4175 ('f', 'follow', None,
4176 _('follow changeset history,'
4176 _('follow changeset history,'
4177 ' or file history across copies and renames')),
4177 ' or file history across copies and renames')),
4178 ('i', 'ignore-case', None, _('ignore case when matching')),
4178 ('i', 'ignore-case', None, _('ignore case when matching')),
4179 ('l', 'files-with-matches', None,
4179 ('l', 'files-with-matches', None,
4180 _('print only filenames and revisions that match')),
4180 _('print only filenames and revisions that match')),
4181 ('n', 'line-number', None, _('print matching line numbers')),
4181 ('n', 'line-number', None, _('print matching line numbers')),
4182 ('r', 'rev', [],
4182 ('r', 'rev', [],
4183 _('only search files changed within revision range'), _('REV')),
4183 _('only search files changed within revision range'), _('REV')),
4184 ('u', 'user', None, _('list the author (long with -v)')),
4184 ('u', 'user', None, _('list the author (long with -v)')),
4185 ('d', 'date', None, _('list the date (short with -q)')),
4185 ('d', 'date', None, _('list the date (short with -q)')),
4186 ] + walkopts,
4186 ] + walkopts,
4187 _('[OPTION]... PATTERN [FILE]...')),
4187 _('[OPTION]... PATTERN [FILE]...')),
4188 "heads":
4188 "heads":
4189 (heads,
4189 (heads,
4190 [('r', 'rev', '',
4190 [('r', 'rev', '',
4191 _('show only heads which are descendants of REV'), _('REV')),
4191 _('show only heads which are descendants of REV'), _('REV')),
4192 ('t', 'topo', False, _('show topological heads only')),
4192 ('t', 'topo', False, _('show topological heads only')),
4193 ('a', 'active', False,
4193 ('a', 'active', False,
4194 _('show active branchheads only (DEPRECATED)')),
4194 _('show active branchheads only (DEPRECATED)')),
4195 ('c', 'closed', False,
4195 ('c', 'closed', False,
4196 _('show normal and closed branch heads')),
4196 _('show normal and closed branch heads')),
4197 ] + templateopts,
4197 ] + templateopts,
4198 _('[-ac] [-r REV] [REV]...')),
4198 _('[-ac] [-r REV] [REV]...')),
4199 "help": (help_, [], _('[TOPIC]')),
4199 "help": (help_, [], _('[TOPIC]')),
4200 "identify|id":
4200 "identify|id":
4201 (identify,
4201 (identify,
4202 [('r', 'rev', '',
4202 [('r', 'rev', '',
4203 _('identify the specified revision'), _('REV')),
4203 _('identify the specified revision'), _('REV')),
4204 ('n', 'num', None, _('show local revision number')),
4204 ('n', 'num', None, _('show local revision number')),
4205 ('i', 'id', None, _('show global revision id')),
4205 ('i', 'id', None, _('show global revision id')),
4206 ('b', 'branch', None, _('show branch')),
4206 ('b', 'branch', None, _('show branch')),
4207 ('t', 'tags', None, _('show tags'))],
4207 ('t', 'tags', None, _('show tags'))],
4208 _('[-nibt] [-r REV] [SOURCE]')),
4208 _('[-nibt] [-r REV] [SOURCE]')),
4209 "import|patch":
4209 "import|patch":
4210 (import_,
4210 (import_,
4211 [('p', 'strip', 1,
4211 [('p', 'strip', 1,
4212 _('directory strip option for patch. This has the same '
4212 _('directory strip option for patch. This has the same '
4213 'meaning as the corresponding patch option'),
4213 'meaning as the corresponding patch option'),
4214 _('NUM')),
4214 _('NUM')),
4215 ('b', 'base', '',
4215 ('b', 'base', '',
4216 _('base path'), _('PATH')),
4216 _('base path'), _('PATH')),
4217 ('f', 'force', None,
4217 ('f', 'force', None,
4218 _('skip check for outstanding uncommitted changes')),
4218 _('skip check for outstanding uncommitted changes')),
4219 ('', 'no-commit', None,
4219 ('', 'no-commit', None,
4220 _("don't commit, just update the working directory")),
4220 _("don't commit, just update the working directory")),
4221 ('', 'exact', None,
4221 ('', 'exact', None,
4222 _('apply patch to the nodes from which it was generated')),
4222 _('apply patch to the nodes from which it was generated')),
4223 ('', 'import-branch', None,
4223 ('', 'import-branch', None,
4224 _('use any branch information in patch (implied by --exact)'))] +
4224 _('use any branch information in patch (implied by --exact)'))] +
4225 commitopts + commitopts2 + similarityopts,
4225 commitopts + commitopts2 + similarityopts,
4226 _('[OPTION]... PATCH...')),
4226 _('[OPTION]... PATCH...')),
4227 "incoming|in":
4227 "incoming|in":
4228 (incoming,
4228 (incoming,
4229 [('f', 'force', None,
4229 [('f', 'force', None,
4230 _('run even if remote repository is unrelated')),
4230 _('run even if remote repository is unrelated')),
4231 ('n', 'newest-first', None, _('show newest record first')),
4231 ('n', 'newest-first', None, _('show newest record first')),
4232 ('', 'bundle', '',
4232 ('', 'bundle', '',
4233 _('file to store the bundles into'), _('FILE')),
4233 _('file to store the bundles into'), _('FILE')),
4234 ('r', 'rev', [],
4234 ('r', 'rev', [],
4235 _('a remote changeset intended to be added'), _('REV')),
4235 _('a remote changeset intended to be added'), _('REV')),
4236 ('b', 'branch', [],
4236 ('b', 'branch', [],
4237 _('a specific branch you would like to pull'), _('BRANCH')),
4237 _('a specific branch you would like to pull'), _('BRANCH')),
4238 ] + logopts + remoteopts,
4238 ] + logopts + remoteopts,
4239 _('[-p] [-n] [-M] [-f] [-r REV]...'
4239 _('[-p] [-n] [-M] [-f] [-r REV]...'
4240 ' [--bundle FILENAME] [SOURCE]')),
4240 ' [--bundle FILENAME] [SOURCE]')),
4241 "^init":
4241 "^init":
4242 (init,
4242 (init,
4243 remoteopts,
4243 remoteopts,
4244 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4244 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4245 "locate":
4245 "locate":
4246 (locate,
4246 (locate,
4247 [('r', 'rev', '',
4247 [('r', 'rev', '',
4248 _('search the repository as it is in REV'), _('REV')),
4248 _('search the repository as it is in REV'), _('REV')),
4249 ('0', 'print0', None,
4249 ('0', 'print0', None,
4250 _('end filenames with NUL, for use with xargs')),
4250 _('end filenames with NUL, for use with xargs')),
4251 ('f', 'fullpath', None,
4251 ('f', 'fullpath', None,
4252 _('print complete paths from the filesystem root')),
4252 _('print complete paths from the filesystem root')),
4253 ] + walkopts,
4253 ] + walkopts,
4254 _('[OPTION]... [PATTERN]...')),
4254 _('[OPTION]... [PATTERN]...')),
4255 "^log|history":
4255 "^log|history":
4256 (log,
4256 (log,
4257 [('f', 'follow', None,
4257 [('f', 'follow', None,
4258 _('follow changeset history,'
4258 _('follow changeset history,'
4259 ' or file history across copies and renames')),
4259 ' or file history across copies and renames')),
4260 ('', 'follow-first', None,
4260 ('', 'follow-first', None,
4261 _('only follow the first parent of merge changesets')),
4261 _('only follow the first parent of merge changesets')),
4262 ('d', 'date', '',
4262 ('d', 'date', '',
4263 _('show revisions matching date spec'), _('DATE')),
4263 _('show revisions matching date spec'), _('DATE')),
4264 ('C', 'copies', None, _('show copied files')),
4264 ('C', 'copies', None, _('show copied files')),
4265 ('k', 'keyword', [],
4265 ('k', 'keyword', [],
4266 _('do case-insensitive search for a given text'), _('TEXT')),
4266 _('do case-insensitive search for a given text'), _('TEXT')),
4267 ('r', 'rev', [],
4267 ('r', 'rev', [],
4268 _('show the specified revision or range'), _('REV')),
4268 _('show the specified revision or range'), _('REV')),
4269 ('', 'removed', None, _('include revisions where files were removed')),
4269 ('', 'removed', None, _('include revisions where files were removed')),
4270 ('m', 'only-merges', None, _('show only merges')),
4270 ('m', 'only-merges', None, _('show only merges')),
4271 ('u', 'user', [],
4271 ('u', 'user', [],
4272 _('revisions committed by user'), _('USER')),
4272 _('revisions committed by user'), _('USER')),
4273 ('', 'only-branch', [],
4273 ('', 'only-branch', [],
4274 _('show only changesets within the given named branch (DEPRECATED)'),
4274 _('show only changesets within the given named branch (DEPRECATED)'),
4275 _('BRANCH')),
4275 _('BRANCH')),
4276 ('b', 'branch', [],
4276 ('b', 'branch', [],
4277 _('show changesets within the given named branch'), _('BRANCH')),
4277 _('show changesets within the given named branch'), _('BRANCH')),
4278 ('P', 'prune', [],
4278 ('P', 'prune', [],
4279 _('do not display revision or any of its ancestors'), _('REV')),
4279 _('do not display revision or any of its ancestors'), _('REV')),
4280 ] + logopts + walkopts,
4280 ] + logopts + walkopts,
4281 _('[OPTION]... [FILE]')),
4281 _('[OPTION]... [FILE]')),
4282 "manifest":
4282 "manifest":
4283 (manifest,
4283 (manifest,
4284 [('r', 'rev', '',
4284 [('r', 'rev', '',
4285 _('revision to display'), _('REV'))],
4285 _('revision to display'), _('REV'))],
4286 _('[-r REV]')),
4286 _('[-r REV]')),
4287 "^merge":
4287 "^merge":
4288 (merge,
4288 (merge,
4289 [('f', 'force', None, _('force a merge with outstanding changes')),
4289 [('f', 'force', None, _('force a merge with outstanding changes')),
4290 ('r', 'rev', '',
4290 ('r', 'rev', '',
4291 _('revision to merge'), _('REV')),
4291 _('revision to merge'), _('REV')),
4292 ('P', 'preview', None,
4292 ('P', 'preview', None,
4293 _('review revisions to merge (no merge is performed)'))],
4293 _('review revisions to merge (no merge is performed)'))],
4294 _('[-P] [-f] [[-r] REV]')),
4294 _('[-P] [-f] [[-r] REV]')),
4295 "outgoing|out":
4295 "outgoing|out":
4296 (outgoing,
4296 (outgoing,
4297 [('f', 'force', None,
4297 [('f', 'force', None,
4298 _('run even when the destination is unrelated')),
4298 _('run even when the destination is unrelated')),
4299 ('r', 'rev', [],
4299 ('r', 'rev', [],
4300 _('a changeset intended to be included in the destination'),
4300 _('a changeset intended to be included in the destination'),
4301 _('REV')),
4301 _('REV')),
4302 ('n', 'newest-first', None, _('show newest record first')),
4302 ('n', 'newest-first', None, _('show newest record first')),
4303 ('b', 'branch', [],
4303 ('b', 'branch', [],
4304 _('a specific branch you would like to push'), _('BRANCH')),
4304 _('a specific branch you would like to push'), _('BRANCH')),
4305 ] + logopts + remoteopts,
4305 ] + logopts + remoteopts,
4306 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4306 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4307 "parents":
4307 "parents":
4308 (parents,
4308 (parents,
4309 [('r', 'rev', '',
4309 [('r', 'rev', '',
4310 _('show parents of the specified revision'), _('REV')),
4310 _('show parents of the specified revision'), _('REV')),
4311 ] + templateopts,
4311 ] + templateopts,
4312 _('[-r REV] [FILE]')),
4312 _('[-r REV] [FILE]')),
4313 "paths": (paths, [], _('[NAME]')),
4313 "paths": (paths, [], _('[NAME]')),
4314 "^pull":
4314 "^pull":
4315 (pull,
4315 (pull,
4316 [('u', 'update', None,
4316 [('u', 'update', None,
4317 _('update to new branch head if changesets were pulled')),
4317 _('update to new branch head if changesets were pulled')),
4318 ('f', 'force', None,
4318 ('f', 'force', None,
4319 _('run even when remote repository is unrelated')),
4319 _('run even when remote repository is unrelated')),
4320 ('r', 'rev', [],
4320 ('r', 'rev', [],
4321 _('a remote changeset intended to be added'), _('REV')),
4321 _('a remote changeset intended to be added'), _('REV')),
4322 ('b', 'branch', [],
4322 ('b', 'branch', [],
4323 _('a specific branch you would like to pull'), _('BRANCH')),
4323 _('a specific branch you would like to pull'), _('BRANCH')),
4324 ] + remoteopts,
4324 ] + remoteopts,
4325 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4325 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4326 "^push":
4326 "^push":
4327 (push,
4327 (push,
4328 [('f', 'force', None, _('force push')),
4328 [('f', 'force', None, _('force push')),
4329 ('r', 'rev', [],
4329 ('r', 'rev', [],
4330 _('a changeset intended to be included in the destination'),
4330 _('a changeset intended to be included in the destination'),
4331 _('REV')),
4331 _('REV')),
4332 ('b', 'branch', [],
4332 ('b', 'branch', [],
4333 _('a specific branch you would like to push'), _('BRANCH')),
4333 _('a specific branch you would like to push'), _('BRANCH')),
4334 ('', 'new-branch', False, _('allow pushing a new branch')),
4334 ('', 'new-branch', False, _('allow pushing a new branch')),
4335 ] + remoteopts,
4335 ] + remoteopts,
4336 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4336 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4337 "recover": (recover, []),
4337 "recover": (recover, []),
4338 "^remove|rm":
4338 "^remove|rm":
4339 (remove,
4339 (remove,
4340 [('A', 'after', None, _('record delete for missing files')),
4340 [('A', 'after', None, _('record delete for missing files')),
4341 ('f', 'force', None,
4341 ('f', 'force', None,
4342 _('remove (and delete) file even if added or modified')),
4342 _('remove (and delete) file even if added or modified')),
4343 ] + walkopts,
4343 ] + walkopts,
4344 _('[OPTION]... FILE...')),
4344 _('[OPTION]... FILE...')),
4345 "rename|mv":
4345 "rename|mv":
4346 (rename,
4346 (rename,
4347 [('A', 'after', None, _('record a rename that has already occurred')),
4347 [('A', 'after', None, _('record a rename that has already occurred')),
4348 ('f', 'force', None,
4348 ('f', 'force', None,
4349 _('forcibly copy over an existing managed file')),
4349 _('forcibly copy over an existing managed file')),
4350 ] + walkopts + dryrunopts,
4350 ] + walkopts + dryrunopts,
4351 _('[OPTION]... SOURCE... DEST')),
4351 _('[OPTION]... SOURCE... DEST')),
4352 "resolve":
4352 "resolve":
4353 (resolve,
4353 (resolve,
4354 [('a', 'all', None, _('select all unresolved files')),
4354 [('a', 'all', None, _('select all unresolved files')),
4355 ('l', 'list', None, _('list state of files needing merge')),
4355 ('l', 'list', None, _('list state of files needing merge')),
4356 ('m', 'mark', None, _('mark files as resolved')),
4356 ('m', 'mark', None, _('mark files as resolved')),
4357 ('u', 'unmark', None, _('mark files as unresolved')),
4357 ('u', 'unmark', None, _('mark files as unresolved')),
4358 ('n', 'no-status', None, _('hide status prefix'))]
4358 ('n', 'no-status', None, _('hide status prefix'))]
4359 + walkopts,
4359 + walkopts,
4360 _('[OPTION]... [FILE]...')),
4360 _('[OPTION]... [FILE]...')),
4361 "revert":
4361 "revert":
4362 (revert,
4362 (revert,
4363 [('a', 'all', None, _('revert all changes when no arguments given')),
4363 [('a', 'all', None, _('revert all changes when no arguments given')),
4364 ('d', 'date', '',
4364 ('d', 'date', '',
4365 _('tipmost revision matching date'), _('DATE')),
4365 _('tipmost revision matching date'), _('DATE')),
4366 ('r', 'rev', '',
4366 ('r', 'rev', '',
4367 _('revert to the specified revision'), _('REV')),
4367 _('revert to the specified revision'), _('REV')),
4368 ('', 'no-backup', None, _('do not save backup copies of files')),
4368 ('', 'no-backup', None, _('do not save backup copies of files')),
4369 ] + walkopts + dryrunopts,
4369 ] + walkopts + dryrunopts,
4370 _('[OPTION]... [-r REV] [NAME]...')),
4370 _('[OPTION]... [-r REV] [NAME]...')),
4371 "rollback": (rollback, dryrunopts),
4371 "rollback": (rollback, dryrunopts),
4372 "root": (root, []),
4372 "root": (root, []),
4373 "^serve":
4373 "^serve":
4374 (serve,
4374 (serve,
4375 [('A', 'accesslog', '',
4375 [('A', 'accesslog', '',
4376 _('name of access log file to write to'), _('FILE')),
4376 _('name of access log file to write to'), _('FILE')),
4377 ('d', 'daemon', None, _('run server in background')),
4377 ('d', 'daemon', None, _('run server in background')),
4378 ('', 'daemon-pipefds', '',
4378 ('', 'daemon-pipefds', '',
4379 _('used internally by daemon mode'), _('NUM')),
4379 _('used internally by daemon mode'), _('NUM')),
4380 ('E', 'errorlog', '',
4380 ('E', 'errorlog', '',
4381 _('name of error log file to write to'), _('FILE')),
4381 _('name of error log file to write to'), _('FILE')),
4382 # use string type, then we can check if something was passed
4382 # use string type, then we can check if something was passed
4383 ('p', 'port', '',
4383 ('p', 'port', '',
4384 _('port to listen on (default: 8000)'), _('PORT')),
4384 _('port to listen on (default: 8000)'), _('PORT')),
4385 ('a', 'address', '',
4385 ('a', 'address', '',
4386 _('address to listen on (default: all interfaces)'), _('ADDR')),
4386 _('address to listen on (default: all interfaces)'), _('ADDR')),
4387 ('', 'prefix', '',
4387 ('', 'prefix', '',
4388 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4388 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4389 ('n', 'name', '',
4389 ('n', 'name', '',
4390 _('name to show in web pages (default: working directory)'),
4390 _('name to show in web pages (default: working directory)'),
4391 _('NAME')),
4391 _('NAME')),
4392 ('', 'web-conf', '',
4392 ('', 'web-conf', '',
4393 _('name of the hgweb config file (serve more than one repository)'),
4393 _('name of the hgweb config file (serve more than one repository)'),
4394 _('FILE')),
4394 _('FILE')),
4395 ('', 'webdir-conf', '',
4395 ('', 'webdir-conf', '',
4396 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4396 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4397 ('', 'pid-file', '',
4397 ('', 'pid-file', '',
4398 _('name of file to write process ID to'), _('FILE')),
4398 _('name of file to write process ID to'), _('FILE')),
4399 ('', 'stdio', None, _('for remote clients')),
4399 ('', 'stdio', None, _('for remote clients')),
4400 ('t', 'templates', '',
4400 ('t', 'templates', '',
4401 _('web templates to use'), _('TEMPLATE')),
4401 _('web templates to use'), _('TEMPLATE')),
4402 ('', 'style', '',
4402 ('', 'style', '',
4403 _('template style to use'), _('STYLE')),
4403 _('template style to use'), _('STYLE')),
4404 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4404 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4405 ('', 'certificate', '',
4405 ('', 'certificate', '',
4406 _('SSL certificate file'), _('FILE'))],
4406 _('SSL certificate file'), _('FILE'))],
4407 _('[OPTION]...')),
4407 _('[OPTION]...')),
4408 "showconfig|debugconfig":
4408 "showconfig|debugconfig":
4409 (showconfig,
4409 (showconfig,
4410 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4410 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4411 _('[-u] [NAME]...')),
4411 _('[-u] [NAME]...')),
4412 "^summary|sum":
4412 "^summary|sum":
4413 (summary,
4413 (summary,
4414 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4414 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4415 "^status|st":
4415 "^status|st":
4416 (status,
4416 (status,
4417 [('A', 'all', None, _('show status of all files')),
4417 [('A', 'all', None, _('show status of all files')),
4418 ('m', 'modified', None, _('show only modified files')),
4418 ('m', 'modified', None, _('show only modified files')),
4419 ('a', 'added', None, _('show only added files')),
4419 ('a', 'added', None, _('show only added files')),
4420 ('r', 'removed', None, _('show only removed files')),
4420 ('r', 'removed', None, _('show only removed files')),
4421 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4421 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4422 ('c', 'clean', None, _('show only files without changes')),
4422 ('c', 'clean', None, _('show only files without changes')),
4423 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4423 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4424 ('i', 'ignored', None, _('show only ignored files')),
4424 ('i', 'ignored', None, _('show only ignored files')),
4425 ('n', 'no-status', None, _('hide status prefix')),
4425 ('n', 'no-status', None, _('hide status prefix')),
4426 ('C', 'copies', None, _('show source of copied files')),
4426 ('C', 'copies', None, _('show source of copied files')),
4427 ('0', 'print0', None,
4427 ('0', 'print0', None,
4428 _('end filenames with NUL, for use with xargs')),
4428 _('end filenames with NUL, for use with xargs')),
4429 ('', 'rev', [],
4429 ('', 'rev', [],
4430 _('show difference from revision'), _('REV')),
4430 _('show difference from revision'), _('REV')),
4431 ('', 'change', '',
4431 ('', 'change', '',
4432 _('list the changed files of a revision'), _('REV')),
4432 _('list the changed files of a revision'), _('REV')),
4433 ] + walkopts,
4433 ] + walkopts,
4434 _('[OPTION]... [FILE]...')),
4434 _('[OPTION]... [FILE]...')),
4435 "tag":
4435 "tag":
4436 (tag,
4436 (tag,
4437 [('f', 'force', None, _('replace existing tag')),
4437 [('f', 'force', None, _('replace existing tag')),
4438 ('l', 'local', None, _('make the tag local')),
4438 ('l', 'local', None, _('make the tag local')),
4439 ('r', 'rev', '',
4439 ('r', 'rev', '',
4440 _('revision to tag'), _('REV')),
4440 _('revision to tag'), _('REV')),
4441 ('', 'remove', None, _('remove a tag')),
4441 ('', 'remove', None, _('remove a tag')),
4442 # -l/--local is already there, commitopts cannot be used
4442 # -l/--local is already there, commitopts cannot be used
4443 ('e', 'edit', None, _('edit commit message')),
4443 ('e', 'edit', None, _('edit commit message')),
4444 ('m', 'message', '',
4444 ('m', 'message', '',
4445 _('use <text> as commit message'), _('TEXT')),
4445 _('use <text> as commit message'), _('TEXT')),
4446 ] + commitopts2,
4446 ] + commitopts2,
4447 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4447 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4448 "tags": (tags, [], ''),
4448 "tags": (tags, [], ''),
4449 "tip":
4449 "tip":
4450 (tip,
4450 (tip,
4451 [('p', 'patch', None, _('show patch')),
4451 [('p', 'patch', None, _('show patch')),
4452 ('g', 'git', None, _('use git extended diff format')),
4452 ('g', 'git', None, _('use git extended diff format')),
4453 ] + templateopts,
4453 ] + templateopts,
4454 _('[-p] [-g]')),
4454 _('[-p] [-g]')),
4455 "unbundle":
4455 "unbundle":
4456 (unbundle,
4456 (unbundle,
4457 [('u', 'update', None,
4457 [('u', 'update', None,
4458 _('update to new branch head if changesets were unbundled'))],
4458 _('update to new branch head if changesets were unbundled'))],
4459 _('[-u] FILE...')),
4459 _('[-u] FILE...')),
4460 "^update|up|checkout|co":
4460 "^update|up|checkout|co":
4461 (update,
4461 (update,
4462 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4462 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4463 ('c', 'check', None, _('check for uncommitted changes')),
4463 ('c', 'check', None, _('check for uncommitted changes')),
4464 ('d', 'date', '',
4464 ('d', 'date', '',
4465 _('tipmost revision matching date'), _('DATE')),
4465 _('tipmost revision matching date'), _('DATE')),
4466 ('r', 'rev', '',
4466 ('r', 'rev', '',
4467 _('revision'), _('REV'))],
4467 _('revision'), _('REV'))],
4468 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4468 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4469 "verify": (verify, []),
4469 "verify": (verify, []),
4470 "version": (version_, []),
4470 "version": (version_, []),
4471 }
4471 }
4472
4472
4473 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4473 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4474 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4474 " debugindex debugindexdot debugdate debuginstall debugfsinfo"
4475 " debugpushkey")
4475 " debugpushkey")
4476 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
4476 optionalrepo = ("identify paths serve showconfig debugancestor debugdag")
@@ -1,277 +1,277 b''
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 from mercurial import util, error
10 from mercurial import util, error
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 def _splitURI(uri):
13 def _splitURI(uri):
14 """ Return path and query splited from uri
14 """ Return path and query splited from uri
15
15
16 Just like CGI environment, the path is unquoted, the query is
16 Just like CGI environment, the path is unquoted, the query is
17 not.
17 not.
18 """
18 """
19 if '?' in uri:
19 if '?' in uri:
20 path, query = uri.split('?', 1)
20 path, query = uri.split('?', 1)
21 else:
21 else:
22 path, query = uri, ''
22 path, query = uri, ''
23 return urllib.unquote(path), query
23 return urllib.unquote(path), query
24
24
25 class _error_logger(object):
25 class _error_logger(object):
26 def __init__(self, handler):
26 def __init__(self, handler):
27 self.handler = handler
27 self.handler = handler
28 def flush(self):
28 def flush(self):
29 pass
29 pass
30 def write(self, str):
30 def write(self, str):
31 self.writelines(str.split('\n'))
31 self.writelines(str.split('\n'))
32 def writelines(self, seq):
32 def writelines(self, seq):
33 for msg in seq:
33 for msg in seq:
34 self.handler.log_error("HG error: %s", msg)
34 self.handler.log_error("HG error: %s", msg)
35
35
36 class _hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
36 class _hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
37
37
38 url_scheme = 'http'
38 url_scheme = 'http'
39
39
40 def __init__(self, *args, **kargs):
40 def __init__(self, *args, **kargs):
41 self.protocol_version = 'HTTP/1.1'
41 self.protocol_version = 'HTTP/1.1'
42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
43
43
44 def _log_any(self, fp, format, *args):
44 def _log_any(self, fp, format, *args):
45 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
45 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
46 self.log_date_time_string(),
46 self.log_date_time_string(),
47 format % args))
47 format % args))
48 fp.flush()
48 fp.flush()
49
49
50 def log_error(self, format, *args):
50 def log_error(self, format, *args):
51 self._log_any(self.server.errorlog, format, *args)
51 self._log_any(self.server.errorlog, format, *args)
52
52
53 def log_message(self, format, *args):
53 def log_message(self, format, *args):
54 self._log_any(self.server.accesslog, format, *args)
54 self._log_any(self.server.accesslog, format, *args)
55
55
56 def do_write(self):
56 def do_write(self):
57 try:
57 try:
58 self.do_hgweb()
58 self.do_hgweb()
59 except socket.error, inst:
59 except socket.error, inst:
60 if inst[0] != errno.EPIPE:
60 if inst[0] != errno.EPIPE:
61 raise
61 raise
62
62
63 def do_POST(self):
63 def do_POST(self):
64 try:
64 try:
65 self.do_write()
65 self.do_write()
66 except StandardError:
66 except StandardError:
67 self._start_response("500 Internal Server Error", [])
67 self._start_response("500 Internal Server Error", [])
68 self._write("Internal Server Error")
68 self._write("Internal Server Error")
69 tb = "".join(traceback.format_exception(*sys.exc_info()))
69 tb = "".join(traceback.format_exception(*sys.exc_info()))
70 self.log_error("Exception happened during processing "
70 self.log_error("Exception happened during processing "
71 "request '%s':\n%s", self.path, tb)
71 "request '%s':\n%s", self.path, tb)
72
72
73 def do_GET(self):
73 def do_GET(self):
74 self.do_POST()
74 self.do_POST()
75
75
76 def do_hgweb(self):
76 def do_hgweb(self):
77 path, query = _splitURI(self.path)
77 path, query = _splitURI(self.path)
78
78
79 env = {}
79 env = {}
80 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
80 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
81 env['REQUEST_METHOD'] = self.command
81 env['REQUEST_METHOD'] = self.command
82 env['SERVER_NAME'] = self.server.server_name
82 env['SERVER_NAME'] = self.server.server_name
83 env['SERVER_PORT'] = str(self.server.server_port)
83 env['SERVER_PORT'] = str(self.server.server_port)
84 env['REQUEST_URI'] = self.path
84 env['REQUEST_URI'] = self.path
85 env['SCRIPT_NAME'] = self.server.prefix
85 env['SCRIPT_NAME'] = self.server.prefix
86 env['PATH_INFO'] = path[len(self.server.prefix):]
86 env['PATH_INFO'] = path[len(self.server.prefix):]
87 env['REMOTE_HOST'] = self.client_address[0]
87 env['REMOTE_HOST'] = self.client_address[0]
88 env['REMOTE_ADDR'] = self.client_address[0]
88 env['REMOTE_ADDR'] = self.client_address[0]
89 if query:
89 if query:
90 env['QUERY_STRING'] = query
90 env['QUERY_STRING'] = query
91
91
92 if self.headers.typeheader is None:
92 if self.headers.typeheader is None:
93 env['CONTENT_TYPE'] = self.headers.type
93 env['CONTENT_TYPE'] = self.headers.type
94 else:
94 else:
95 env['CONTENT_TYPE'] = self.headers.typeheader
95 env['CONTENT_TYPE'] = self.headers.typeheader
96 length = self.headers.getheader('content-length')
96 length = self.headers.getheader('content-length')
97 if length:
97 if length:
98 env['CONTENT_LENGTH'] = length
98 env['CONTENT_LENGTH'] = length
99 for header in [h for h in self.headers.keys()
99 for header in [h for h in self.headers.keys()
100 if h not in ('content-type', 'content-length')]:
100 if h not in ('content-type', 'content-length')]:
101 hkey = 'HTTP_' + header.replace('-', '_').upper()
101 hkey = 'HTTP_' + header.replace('-', '_').upper()
102 hval = self.headers.getheader(header)
102 hval = self.headers.getheader(header)
103 hval = hval.replace('\n', '').strip()
103 hval = hval.replace('\n', '').strip()
104 if hval:
104 if hval:
105 env[hkey] = hval
105 env[hkey] = hval
106 env['SERVER_PROTOCOL'] = self.request_version
106 env['SERVER_PROTOCOL'] = self.request_version
107 env['wsgi.version'] = (1, 0)
107 env['wsgi.version'] = (1, 0)
108 env['wsgi.url_scheme'] = self.url_scheme
108 env['wsgi.url_scheme'] = self.url_scheme
109 env['wsgi.input'] = self.rfile
109 env['wsgi.input'] = self.rfile
110 env['wsgi.errors'] = _error_logger(self)
110 env['wsgi.errors'] = _error_logger(self)
111 env['wsgi.multithread'] = isinstance(self.server,
111 env['wsgi.multithread'] = isinstance(self.server,
112 SocketServer.ThreadingMixIn)
112 SocketServer.ThreadingMixIn)
113 env['wsgi.multiprocess'] = isinstance(self.server,
113 env['wsgi.multiprocess'] = isinstance(self.server,
114 SocketServer.ForkingMixIn)
114 SocketServer.ForkingMixIn)
115 env['wsgi.run_once'] = 0
115 env['wsgi.run_once'] = 0
116
116
117 self.close_connection = True
117 self.close_connection = True
118 self.saved_status = None
118 self.saved_status = None
119 self.saved_headers = []
119 self.saved_headers = []
120 self.sent_headers = False
120 self.sent_headers = False
121 self.length = None
121 self.length = None
122 for chunk in self.server.application(env, self._start_response):
122 for chunk in self.server.application(env, self._start_response):
123 self._write(chunk)
123 self._write(chunk)
124
124
125 def send_headers(self):
125 def send_headers(self):
126 if not self.saved_status:
126 if not self.saved_status:
127 raise AssertionError("Sending headers before "
127 raise AssertionError("Sending headers before "
128 "start_response() called")
128 "start_response() called")
129 saved_status = self.saved_status.split(None, 1)
129 saved_status = self.saved_status.split(None, 1)
130 saved_status[0] = int(saved_status[0])
130 saved_status[0] = int(saved_status[0])
131 self.send_response(*saved_status)
131 self.send_response(*saved_status)
132 should_close = True
132 should_close = True
133 for h in self.saved_headers:
133 for h in self.saved_headers:
134 self.send_header(*h)
134 self.send_header(*h)
135 if h[0].lower() == 'content-length':
135 if h[0].lower() == 'content-length':
136 should_close = False
136 should_close = False
137 self.length = int(h[1])
137 self.length = int(h[1])
138 # The value of the Connection header is a list of case-insensitive
138 # The value of the Connection header is a list of case-insensitive
139 # tokens separated by commas and optional whitespace.
139 # tokens separated by commas and optional whitespace.
140 if 'close' in [token.strip().lower() for token in
140 if 'close' in [token.strip().lower() for token in
141 self.headers.get('connection', '').split(',')]:
141 self.headers.get('connection', '').split(',')]:
142 should_close = True
142 should_close = True
143 if should_close:
143 if should_close:
144 self.send_header('Connection', 'close')
144 self.send_header('Connection', 'close')
145 self.close_connection = should_close
145 self.close_connection = should_close
146 self.end_headers()
146 self.end_headers()
147 self.sent_headers = True
147 self.sent_headers = True
148
148
149 def _start_response(self, http_status, headers, exc_info=None):
149 def _start_response(self, http_status, headers, exc_info=None):
150 code, msg = http_status.split(None, 1)
150 code, msg = http_status.split(None, 1)
151 code = int(code)
151 code = int(code)
152 self.saved_status = http_status
152 self.saved_status = http_status
153 bad_headers = ('connection', 'transfer-encoding')
153 bad_headers = ('connection', 'transfer-encoding')
154 self.saved_headers = [h for h in headers
154 self.saved_headers = [h for h in headers
155 if h[0].lower() not in bad_headers]
155 if h[0].lower() not in bad_headers]
156 return self._write
156 return self._write
157
157
158 def _write(self, data):
158 def _write(self, data):
159 if not self.saved_status:
159 if not self.saved_status:
160 raise AssertionError("data written before start_response() called")
160 raise AssertionError("data written before start_response() called")
161 elif not self.sent_headers:
161 elif not self.sent_headers:
162 self.send_headers()
162 self.send_headers()
163 if self.length is not None:
163 if self.length is not None:
164 if len(data) > self.length:
164 if len(data) > self.length:
165 raise AssertionError("Content-length header sent, but more "
165 raise AssertionError("Content-length header sent, but more "
166 "bytes than specified are being written.")
166 "bytes than specified are being written.")
167 self.length = self.length - len(data)
167 self.length = self.length - len(data)
168 self.wfile.write(data)
168 self.wfile.write(data)
169 self.wfile.flush()
169 self.wfile.flush()
170
170
171 class _shgwebhandler(_hgwebhandler):
171 class _shgwebhandler(_hgwebhandler):
172
172
173 url_scheme = 'https'
173 url_scheme = 'https'
174
174
175 def setup(self):
175 def setup(self):
176 self.connection = self.request
176 self.connection = self.request
177 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
177 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
178 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
178 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
179
179
180 def do_write(self):
180 def do_write(self):
181 from OpenSSL.SSL import SysCallError
181 from OpenSSL.SSL import SysCallError
182 try:
182 try:
183 super(_shgwebhandler, self).do_write()
183 super(_shgwebhandler, self).do_write()
184 except SysCallError, inst:
184 except SysCallError, inst:
185 if inst.args[0] != errno.EPIPE:
185 if inst.args[0] != errno.EPIPE:
186 raise
186 raise
187
187
188 def handle_one_request(self):
188 def handle_one_request(self):
189 from OpenSSL.SSL import SysCallError, ZeroReturnError
189 from OpenSSL.SSL import SysCallError, ZeroReturnError
190 try:
190 try:
191 super(_shgwebhandler, self).handle_one_request()
191 super(_shgwebhandler, self).handle_one_request()
192 except (SysCallError, ZeroReturnError):
192 except (SysCallError, ZeroReturnError):
193 self.close_connection = True
193 self.close_connection = True
194 pass
194 pass
195
195
196 try:
196 try:
197 from threading import activeCount
197 from threading import activeCount
198 _mixin = SocketServer.ThreadingMixIn
198 _mixin = SocketServer.ThreadingMixIn
199 except ImportError:
199 except ImportError:
200 if hasattr(os, "fork"):
200 if hasattr(os, "fork"):
201 _mixin = SocketServer.ForkingMixIn
201 _mixin = SocketServer.ForkingMixIn
202 else:
202 else:
203 class _mixin:
203 class _mixin:
204 pass
204 pass
205
205
206 def openlog(opt, default):
206 def openlog(opt, default):
207 if opt and opt != '-':
207 if opt and opt != '-':
208 return open(opt, 'a')
208 return open(opt, 'a')
209 return default
209 return default
210
210
211 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
211 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
212
212
213 # SO_REUSEADDR has broken semantics on windows
213 # SO_REUSEADDR has broken semantics on windows
214 if os.name == 'nt':
214 if os.name == 'nt':
215 allow_reuse_address = 0
215 allow_reuse_address = 0
216
216
217 def __init__(self, ui, app, addr, handler, **kwargs):
217 def __init__(self, ui, app, addr, handler, **kwargs):
218 BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
218 BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
219 self.daemon_threads = True
219 self.daemon_threads = True
220 self.application = app
220 self.application = app
221
221
222 ssl_cert = ui.config('web', 'certificate')
222 ssl_cert = ui.config('web', 'certificate')
223 if ssl_cert:
223 if ssl_cert:
224 try:
224 try:
225 from OpenSSL import SSL
225 from OpenSSL import SSL
226 ctx = SSL.Context(SSL.SSLv23_METHOD)
226 ctx = SSL.Context(SSL.SSLv23_METHOD)
227 except ImportError:
227 except ImportError:
228 raise util.Abort(_("SSL support is unavailable"))
228 raise util.Abort(_("SSL support is unavailable"))
229 ctx.use_privatekey_file(ssl_cert)
229 ctx.use_privatekey_file(ssl_cert)
230 ctx.use_certificate_file(ssl_cert)
230 ctx.use_certificate_file(ssl_cert)
231 sock = socket.socket(self.address_family, self.socket_type)
231 sock = socket.socket(self.address_family, self.socket_type)
232 self.socket = SSL.Connection(ctx, sock)
232 self.socket = SSL.Connection(ctx, sock)
233 self.server_bind()
233 self.server_bind()
234 self.server_activate()
234 self.server_activate()
235
235
236 prefix = ui.config('web', 'prefix', '')
236 prefix = ui.config('web', 'prefix', '')
237 if prefix:
237 if prefix:
238 prefix = '/' + prefix.strip('/')
238 prefix = '/' + prefix.strip('/')
239 self.prefix = prefix
239 self.prefix = prefix
240
240
241 alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
241 alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
242 elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
242 elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
243 self.accesslog = alog
243 self.accesslog = alog
244 self.errorlog = elog
244 self.errorlog = elog
245
245
246 self.addr, self.port = self.socket.getsockname()[0:2]
246 self.addr, self.port = self.socket.getsockname()[0:2]
247 self.fqaddr = socket.getfqdn(addr[0])
247 self.fqaddr = socket.getfqdn(addr[0])
248
248
249 class IPv6HTTPServer(MercurialHTTPServer):
249 class IPv6HTTPServer(MercurialHTTPServer):
250 address_family = getattr(socket, 'AF_INET6', None)
250 address_family = getattr(socket, 'AF_INET6', None)
251 def __init__(self, *args, **kwargs):
251 def __init__(self, *args, **kwargs):
252 if self.address_family is None:
252 if self.address_family is None:
253 raise error.RepoError(_('IPv6 is not available on this system'))
253 raise error.RepoError(_('IPv6 is not available on this system'))
254 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
254 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
255
255
256 def create_server(ui, app):
256 def create_server(ui, app):
257
257
258 if ui.config('web', 'certificate'):
258 if ui.config('web', 'certificate'):
259 handler = _shgwebhandler
259 handler = _shgwebhandler
260 else:
260 else:
261 handler = _hgwebhandler
261 handler = _hgwebhandler
262
262
263 if ui.configbool('web', 'ipv6'):
263 if ui.configbool('web', 'ipv6'):
264 cls = IPv6HTTPServer
264 cls = IPv6HTTPServer
265 else:
265 else:
266 cls = MercurialHTTPServer
266 cls = MercurialHTTPServer
267
267
268 # ugly hack due to python issue5853 (for threaded use)
268 # ugly hack due to python issue5853 (for threaded use)
269 import mimetypes; mimetypes.init()
269 import mimetypes; mimetypes.init()
270
270
271 address = ui.config('web', 'address', '')
271 address = ui.config('web', 'address', '')
272 port = int(ui.config('web', 'port', 8000))
272 port = util.getport(ui.config('web', 'port', 8000))
273 try:
273 try:
274 return cls(ui, app, (address, port), handler)
274 return cls(ui, app, (address, port), handler)
275 except socket.error, inst:
275 except socket.error, inst:
276 raise util.Abort(_("cannot start server at '%s:%d': %s")
276 raise util.Abort(_("cannot start server at '%s:%d': %s")
277 % (address, port, inst.args[1]))
277 % (address, port, inst.args[1]))
@@ -1,226 +1,226 b''
1 # mail.py - mail sending bits for mercurial
1 # mail.py - mail sending bits for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import util, encoding
9 import util, encoding
10 import os, smtplib, socket, quopri
10 import os, smtplib, socket, quopri
11 import email.Header, email.MIMEText, email.Utils
11 import email.Header, email.MIMEText, email.Utils
12
12
13 _oldheaderinit = email.Header.Header.__init__
13 _oldheaderinit = email.Header.Header.__init__
14 def _unifiedheaderinit(self, *args, **kw):
14 def _unifiedheaderinit(self, *args, **kw):
15 """
15 """
16 Python2.7 introduces a backwards incompatible change
16 Python2.7 introduces a backwards incompatible change
17 (Python issue1974, r70772) in email.Generator.Generator code:
17 (Python issue1974, r70772) in email.Generator.Generator code:
18 pre-2.7 code passed "continuation_ws='\t'" to the Header
18 pre-2.7 code passed "continuation_ws='\t'" to the Header
19 constructor, and 2.7 removed this parameter.
19 constructor, and 2.7 removed this parameter.
20
20
21 Default argument is continuation_ws=' ', which means that the
21 Default argument is continuation_ws=' ', which means that the
22 behaviour is different in <2.7 and 2.7
22 behaviour is different in <2.7 and 2.7
23
23
24 We consider the 2.7 behaviour to be preferable, but need
24 We consider the 2.7 behaviour to be preferable, but need
25 to have an unified behaviour for versions 2.4 to 2.7
25 to have an unified behaviour for versions 2.4 to 2.7
26 """
26 """
27 # override continuation_ws
27 # override continuation_ws
28 kw['continuation_ws'] = ' '
28 kw['continuation_ws'] = ' '
29 _oldheaderinit(self, *args, **kw)
29 _oldheaderinit(self, *args, **kw)
30
30
31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
32
32
33 def _smtp(ui):
33 def _smtp(ui):
34 '''build an smtp connection and return a function to send mail'''
34 '''build an smtp connection and return a function to send mail'''
35 local_hostname = ui.config('smtp', 'local_hostname')
35 local_hostname = ui.config('smtp', 'local_hostname')
36 s = smtplib.SMTP(local_hostname=local_hostname)
36 s = smtplib.SMTP(local_hostname=local_hostname)
37 mailhost = ui.config('smtp', 'host')
37 mailhost = ui.config('smtp', 'host')
38 if not mailhost:
38 if not mailhost:
39 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
39 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
40 mailport = int(ui.config('smtp', 'port', 25))
40 mailport = util.getport(ui.config('smtp', 'port', 25))
41 ui.note(_('sending mail: smtp host %s, port %s\n') %
41 ui.note(_('sending mail: smtp host %s, port %s\n') %
42 (mailhost, mailport))
42 (mailhost, mailport))
43 s.connect(host=mailhost, port=mailport)
43 s.connect(host=mailhost, port=mailport)
44 if ui.configbool('smtp', 'tls'):
44 if ui.configbool('smtp', 'tls'):
45 if not hasattr(socket, 'ssl'):
45 if not hasattr(socket, 'ssl'):
46 raise util.Abort(_("can't use TLS: Python SSL support "
46 raise util.Abort(_("can't use TLS: Python SSL support "
47 "not installed"))
47 "not installed"))
48 ui.note(_('(using tls)\n'))
48 ui.note(_('(using tls)\n'))
49 s.ehlo()
49 s.ehlo()
50 s.starttls()
50 s.starttls()
51 s.ehlo()
51 s.ehlo()
52 username = ui.config('smtp', 'username')
52 username = ui.config('smtp', 'username')
53 password = ui.config('smtp', 'password')
53 password = ui.config('smtp', 'password')
54 if username and not password:
54 if username and not password:
55 password = ui.getpass()
55 password = ui.getpass()
56 if username and password:
56 if username and password:
57 ui.note(_('(authenticating to mail server as %s)\n') %
57 ui.note(_('(authenticating to mail server as %s)\n') %
58 (username))
58 (username))
59 try:
59 try:
60 s.login(username, password)
60 s.login(username, password)
61 except smtplib.SMTPException, inst:
61 except smtplib.SMTPException, inst:
62 raise util.Abort(inst)
62 raise util.Abort(inst)
63
63
64 def send(sender, recipients, msg):
64 def send(sender, recipients, msg):
65 try:
65 try:
66 return s.sendmail(sender, recipients, msg)
66 return s.sendmail(sender, recipients, msg)
67 except smtplib.SMTPRecipientsRefused, inst:
67 except smtplib.SMTPRecipientsRefused, inst:
68 recipients = [r[1] for r in inst.recipients.values()]
68 recipients = [r[1] for r in inst.recipients.values()]
69 raise util.Abort('\n' + '\n'.join(recipients))
69 raise util.Abort('\n' + '\n'.join(recipients))
70 except smtplib.SMTPException, inst:
70 except smtplib.SMTPException, inst:
71 raise util.Abort(inst)
71 raise util.Abort(inst)
72
72
73 return send
73 return send
74
74
75 def _sendmail(ui, sender, recipients, msg):
75 def _sendmail(ui, sender, recipients, msg):
76 '''send mail using sendmail.'''
76 '''send mail using sendmail.'''
77 program = ui.config('email', 'method')
77 program = ui.config('email', 'method')
78 cmdline = '%s -f %s %s' % (program, util.email(sender),
78 cmdline = '%s -f %s %s' % (program, util.email(sender),
79 ' '.join(map(util.email, recipients)))
79 ' '.join(map(util.email, recipients)))
80 ui.note(_('sending mail: %s\n') % cmdline)
80 ui.note(_('sending mail: %s\n') % cmdline)
81 fp = util.popen(cmdline, 'w')
81 fp = util.popen(cmdline, 'w')
82 fp.write(msg)
82 fp.write(msg)
83 ret = fp.close()
83 ret = fp.close()
84 if ret:
84 if ret:
85 raise util.Abort('%s %s' % (
85 raise util.Abort('%s %s' % (
86 os.path.basename(program.split(None, 1)[0]),
86 os.path.basename(program.split(None, 1)[0]),
87 util.explain_exit(ret)[0]))
87 util.explain_exit(ret)[0]))
88
88
89 def connect(ui):
89 def connect(ui):
90 '''make a mail connection. return a function to send mail.
90 '''make a mail connection. return a function to send mail.
91 call as sendmail(sender, list-of-recipients, msg).'''
91 call as sendmail(sender, list-of-recipients, msg).'''
92 if ui.config('email', 'method', 'smtp') == 'smtp':
92 if ui.config('email', 'method', 'smtp') == 'smtp':
93 return _smtp(ui)
93 return _smtp(ui)
94 return lambda s, r, m: _sendmail(ui, s, r, m)
94 return lambda s, r, m: _sendmail(ui, s, r, m)
95
95
96 def sendmail(ui, sender, recipients, msg):
96 def sendmail(ui, sender, recipients, msg):
97 send = connect(ui)
97 send = connect(ui)
98 return send(sender, recipients, msg)
98 return send(sender, recipients, msg)
99
99
100 def validateconfig(ui):
100 def validateconfig(ui):
101 '''determine if we have enough config data to try sending email.'''
101 '''determine if we have enough config data to try sending email.'''
102 method = ui.config('email', 'method', 'smtp')
102 method = ui.config('email', 'method', 'smtp')
103 if method == 'smtp':
103 if method == 'smtp':
104 if not ui.config('smtp', 'host'):
104 if not ui.config('smtp', 'host'):
105 raise util.Abort(_('smtp specified as email transport, '
105 raise util.Abort(_('smtp specified as email transport, '
106 'but no smtp host configured'))
106 'but no smtp host configured'))
107 else:
107 else:
108 if not util.find_exe(method):
108 if not util.find_exe(method):
109 raise util.Abort(_('%r specified as email transport, '
109 raise util.Abort(_('%r specified as email transport, '
110 'but not in PATH') % method)
110 'but not in PATH') % method)
111
111
112 def mimetextpatch(s, subtype='plain', display=False):
112 def mimetextpatch(s, subtype='plain', display=False):
113 '''If patch in utf-8 transfer-encode it.'''
113 '''If patch in utf-8 transfer-encode it.'''
114
114
115 enc = None
115 enc = None
116 for line in s.splitlines():
116 for line in s.splitlines():
117 if len(line) > 950:
117 if len(line) > 950:
118 s = quopri.encodestring(s)
118 s = quopri.encodestring(s)
119 enc = "quoted-printable"
119 enc = "quoted-printable"
120 break
120 break
121
121
122 cs = 'us-ascii'
122 cs = 'us-ascii'
123 if not display:
123 if not display:
124 try:
124 try:
125 s.decode('us-ascii')
125 s.decode('us-ascii')
126 except UnicodeDecodeError:
126 except UnicodeDecodeError:
127 try:
127 try:
128 s.decode('utf-8')
128 s.decode('utf-8')
129 cs = 'utf-8'
129 cs = 'utf-8'
130 except UnicodeDecodeError:
130 except UnicodeDecodeError:
131 # We'll go with us-ascii as a fallback.
131 # We'll go with us-ascii as a fallback.
132 pass
132 pass
133
133
134 msg = email.MIMEText.MIMEText(s, subtype, cs)
134 msg = email.MIMEText.MIMEText(s, subtype, cs)
135 if enc:
135 if enc:
136 del msg['Content-Transfer-Encoding']
136 del msg['Content-Transfer-Encoding']
137 msg['Content-Transfer-Encoding'] = enc
137 msg['Content-Transfer-Encoding'] = enc
138 return msg
138 return msg
139
139
140 def _charsets(ui):
140 def _charsets(ui):
141 '''Obtains charsets to send mail parts not containing patches.'''
141 '''Obtains charsets to send mail parts not containing patches.'''
142 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
142 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
143 fallbacks = [encoding.fallbackencoding.lower(),
143 fallbacks = [encoding.fallbackencoding.lower(),
144 encoding.encoding.lower(), 'utf-8']
144 encoding.encoding.lower(), 'utf-8']
145 for cs in fallbacks: # find unique charsets while keeping order
145 for cs in fallbacks: # find unique charsets while keeping order
146 if cs not in charsets:
146 if cs not in charsets:
147 charsets.append(cs)
147 charsets.append(cs)
148 return [cs for cs in charsets if not cs.endswith('ascii')]
148 return [cs for cs in charsets if not cs.endswith('ascii')]
149
149
150 def _encode(ui, s, charsets):
150 def _encode(ui, s, charsets):
151 '''Returns (converted) string, charset tuple.
151 '''Returns (converted) string, charset tuple.
152 Finds out best charset by cycling through sendcharsets in descending
152 Finds out best charset by cycling through sendcharsets in descending
153 order. Tries both encoding and fallbackencoding for input. Only as
153 order. Tries both encoding and fallbackencoding for input. Only as
154 last resort send as is in fake ascii.
154 last resort send as is in fake ascii.
155 Caveat: Do not use for mail parts containing patches!'''
155 Caveat: Do not use for mail parts containing patches!'''
156 try:
156 try:
157 s.decode('ascii')
157 s.decode('ascii')
158 except UnicodeDecodeError:
158 except UnicodeDecodeError:
159 sendcharsets = charsets or _charsets(ui)
159 sendcharsets = charsets or _charsets(ui)
160 for ics in (encoding.encoding, encoding.fallbackencoding):
160 for ics in (encoding.encoding, encoding.fallbackencoding):
161 try:
161 try:
162 u = s.decode(ics)
162 u = s.decode(ics)
163 except UnicodeDecodeError:
163 except UnicodeDecodeError:
164 continue
164 continue
165 for ocs in sendcharsets:
165 for ocs in sendcharsets:
166 try:
166 try:
167 return u.encode(ocs), ocs
167 return u.encode(ocs), ocs
168 except UnicodeEncodeError:
168 except UnicodeEncodeError:
169 pass
169 pass
170 except LookupError:
170 except LookupError:
171 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
171 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
172 # if ascii, or all conversion attempts fail, send (broken) ascii
172 # if ascii, or all conversion attempts fail, send (broken) ascii
173 return s, 'us-ascii'
173 return s, 'us-ascii'
174
174
175 def headencode(ui, s, charsets=None, display=False):
175 def headencode(ui, s, charsets=None, display=False):
176 '''Returns RFC-2047 compliant header from given string.'''
176 '''Returns RFC-2047 compliant header from given string.'''
177 if not display:
177 if not display:
178 # split into words?
178 # split into words?
179 s, cs = _encode(ui, s, charsets)
179 s, cs = _encode(ui, s, charsets)
180 return str(email.Header.Header(s, cs))
180 return str(email.Header.Header(s, cs))
181 return s
181 return s
182
182
183 def _addressencode(ui, name, addr, charsets=None):
183 def _addressencode(ui, name, addr, charsets=None):
184 name = headencode(ui, name, charsets)
184 name = headencode(ui, name, charsets)
185 try:
185 try:
186 acc, dom = addr.split('@')
186 acc, dom = addr.split('@')
187 acc = acc.encode('ascii')
187 acc = acc.encode('ascii')
188 dom = dom.decode(encoding.encoding).encode('idna')
188 dom = dom.decode(encoding.encoding).encode('idna')
189 addr = '%s@%s' % (acc, dom)
189 addr = '%s@%s' % (acc, dom)
190 except UnicodeDecodeError:
190 except UnicodeDecodeError:
191 raise util.Abort(_('invalid email address: %s') % addr)
191 raise util.Abort(_('invalid email address: %s') % addr)
192 except ValueError:
192 except ValueError:
193 try:
193 try:
194 # too strict?
194 # too strict?
195 addr = addr.encode('ascii')
195 addr = addr.encode('ascii')
196 except UnicodeDecodeError:
196 except UnicodeDecodeError:
197 raise util.Abort(_('invalid local address: %s') % addr)
197 raise util.Abort(_('invalid local address: %s') % addr)
198 return email.Utils.formataddr((name, addr))
198 return email.Utils.formataddr((name, addr))
199
199
200 def addressencode(ui, address, charsets=None, display=False):
200 def addressencode(ui, address, charsets=None, display=False):
201 '''Turns address into RFC-2047 compliant header.'''
201 '''Turns address into RFC-2047 compliant header.'''
202 if display or not address:
202 if display or not address:
203 return address or ''
203 return address or ''
204 name, addr = email.Utils.parseaddr(address)
204 name, addr = email.Utils.parseaddr(address)
205 return _addressencode(ui, name, addr, charsets)
205 return _addressencode(ui, name, addr, charsets)
206
206
207 def addrlistencode(ui, addrs, charsets=None, display=False):
207 def addrlistencode(ui, addrs, charsets=None, display=False):
208 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
208 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
209 A single element of input list may contain multiple addresses, but output
209 A single element of input list may contain multiple addresses, but output
210 always has one address per item'''
210 always has one address per item'''
211 if display:
211 if display:
212 return [a.strip() for a in addrs if a.strip()]
212 return [a.strip() for a in addrs if a.strip()]
213
213
214 result = []
214 result = []
215 for name, addr in email.Utils.getaddresses(addrs):
215 for name, addr in email.Utils.getaddresses(addrs):
216 if name or addr:
216 if name or addr:
217 result.append(_addressencode(ui, name, addr, charsets))
217 result.append(_addressencode(ui, name, addr, charsets))
218 return result
218 return result
219
219
220 def mimeencode(ui, s, charsets=None, display=False):
220 def mimeencode(ui, s, charsets=None, display=False):
221 '''creates mime text object, encodes it if needed, and sets
221 '''creates mime text object, encodes it if needed, and sets
222 charset and transfer-encoding accordingly.'''
222 charset and transfer-encoding accordingly.'''
223 cs = 'us-ascii'
223 cs = 'us-ascii'
224 if not display:
224 if not display:
225 s, cs = _encode(ui, s, charsets)
225 s, cs = _encode(ui, s, charsets)
226 return email.MIMEText.MIMEText(s, 'plain', cs)
226 return email.MIMEText.MIMEText(s, 'plain', cs)
@@ -1,1416 +1,1432 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp
20 import imp, socket
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 if sys.version_info[0] < 3:
41 if sys.version_info[0] < 3:
42 def fakebuffer(sliceable, offset=0):
42 def fakebuffer(sliceable, offset=0):
43 return sliceable[offset:]
43 return sliceable[offset:]
44 else:
44 else:
45 def fakebuffer(sliceable, offset=0):
45 def fakebuffer(sliceable, offset=0):
46 return memoryview(sliceable)[offset:]
46 return memoryview(sliceable)[offset:]
47 try:
47 try:
48 buffer
48 buffer
49 except NameError:
49 except NameError:
50 __builtin__.buffer = fakebuffer
50 __builtin__.buffer = fakebuffer
51
51
52 import subprocess
52 import subprocess
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54
54
55 def popen2(cmd, env=None, newlines=False):
55 def popen2(cmd, env=None, newlines=False):
56 # Setting bufsize to -1 lets the system decide the buffer size.
56 # Setting bufsize to -1 lets the system decide the buffer size.
57 # The default for bufsize is 0, meaning unbuffered. This leads to
57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 close_fds=closefds,
60 close_fds=closefds,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 universal_newlines=newlines,
62 universal_newlines=newlines,
63 env=env)
63 env=env)
64 return p.stdin, p.stdout
64 return p.stdin, p.stdout
65
65
66 def popen3(cmd, env=None, newlines=False):
66 def popen3(cmd, env=None, newlines=False):
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 close_fds=closefds,
68 close_fds=closefds,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 stderr=subprocess.PIPE,
70 stderr=subprocess.PIPE,
71 universal_newlines=newlines,
71 universal_newlines=newlines,
72 env=env)
72 env=env)
73 return p.stdin, p.stdout, p.stderr
73 return p.stdin, p.stdout, p.stderr
74
74
75 def version():
75 def version():
76 """Return version information if available."""
76 """Return version information if available."""
77 try:
77 try:
78 import __version__
78 import __version__
79 return __version__.version
79 return __version__.version
80 except ImportError:
80 except ImportError:
81 return 'unknown'
81 return 'unknown'
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 '%b %d %H:%M:%S %Y',
97 '%b %d %H:%M:%S %Y',
98 '%b %d %I:%M:%S%p %Y',
98 '%b %d %I:%M:%S%p %Y',
99 '%b %d %H:%M:%S',
99 '%b %d %H:%M:%S',
100 '%b %d %I:%M:%S%p',
100 '%b %d %I:%M:%S%p',
101 '%b %d %H:%M',
101 '%b %d %H:%M',
102 '%b %d %I:%M%p',
102 '%b %d %I:%M%p',
103 '%b %d %Y',
103 '%b %d %Y',
104 '%b %d',
104 '%b %d',
105 '%H:%M:%S',
105 '%H:%M:%S',
106 '%I:%M:%S%p',
106 '%I:%M:%S%p',
107 '%H:%M',
107 '%H:%M',
108 '%I:%M%p',
108 '%I:%M%p',
109 )
109 )
110
110
111 extendeddateformats = defaultdateformats + (
111 extendeddateformats = defaultdateformats + (
112 "%Y",
112 "%Y",
113 "%Y-%m",
113 "%Y-%m",
114 "%b",
114 "%b",
115 "%b %Y",
115 "%b %Y",
116 )
116 )
117
117
118 def cachefunc(func):
118 def cachefunc(func):
119 '''cache the result of function calls'''
119 '''cache the result of function calls'''
120 # XXX doesn't handle keywords args
120 # XXX doesn't handle keywords args
121 cache = {}
121 cache = {}
122 if func.func_code.co_argcount == 1:
122 if func.func_code.co_argcount == 1:
123 # we gain a small amount of time because
123 # we gain a small amount of time because
124 # we don't need to pack/unpack the list
124 # we don't need to pack/unpack the list
125 def f(arg):
125 def f(arg):
126 if arg not in cache:
126 if arg not in cache:
127 cache[arg] = func(arg)
127 cache[arg] = func(arg)
128 return cache[arg]
128 return cache[arg]
129 else:
129 else:
130 def f(*args):
130 def f(*args):
131 if args not in cache:
131 if args not in cache:
132 cache[args] = func(*args)
132 cache[args] = func(*args)
133 return cache[args]
133 return cache[args]
134
134
135 return f
135 return f
136
136
137 def lrucachefunc(func):
137 def lrucachefunc(func):
138 '''cache most recent results of function calls'''
138 '''cache most recent results of function calls'''
139 cache = {}
139 cache = {}
140 order = []
140 order = []
141 if func.func_code.co_argcount == 1:
141 if func.func_code.co_argcount == 1:
142 def f(arg):
142 def f(arg):
143 if arg not in cache:
143 if arg not in cache:
144 if len(cache) > 20:
144 if len(cache) > 20:
145 del cache[order.pop(0)]
145 del cache[order.pop(0)]
146 cache[arg] = func(arg)
146 cache[arg] = func(arg)
147 else:
147 else:
148 order.remove(arg)
148 order.remove(arg)
149 order.append(arg)
149 order.append(arg)
150 return cache[arg]
150 return cache[arg]
151 else:
151 else:
152 def f(*args):
152 def f(*args):
153 if args not in cache:
153 if args not in cache:
154 if len(cache) > 20:
154 if len(cache) > 20:
155 del cache[order.pop(0)]
155 del cache[order.pop(0)]
156 cache[args] = func(*args)
156 cache[args] = func(*args)
157 else:
157 else:
158 order.remove(args)
158 order.remove(args)
159 order.append(args)
159 order.append(args)
160 return cache[args]
160 return cache[args]
161
161
162 return f
162 return f
163
163
164 class propertycache(object):
164 class propertycache(object):
165 def __init__(self, func):
165 def __init__(self, func):
166 self.func = func
166 self.func = func
167 self.name = func.__name__
167 self.name = func.__name__
168 def __get__(self, obj, type=None):
168 def __get__(self, obj, type=None):
169 result = self.func(obj)
169 result = self.func(obj)
170 setattr(obj, self.name, result)
170 setattr(obj, self.name, result)
171 return result
171 return result
172
172
173 def pipefilter(s, cmd):
173 def pipefilter(s, cmd):
174 '''filter string S through command CMD, returning its output'''
174 '''filter string S through command CMD, returning its output'''
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 pout, perr = p.communicate(s)
177 pout, perr = p.communicate(s)
178 return pout
178 return pout
179
179
180 def tempfilter(s, cmd):
180 def tempfilter(s, cmd):
181 '''filter string S through a pair of temporary files with CMD.
181 '''filter string S through a pair of temporary files with CMD.
182 CMD is used as a template to create the real command to be run,
182 CMD is used as a template to create the real command to be run,
183 with the strings INFILE and OUTFILE replaced by the real names of
183 with the strings INFILE and OUTFILE replaced by the real names of
184 the temporary files generated.'''
184 the temporary files generated.'''
185 inname, outname = None, None
185 inname, outname = None, None
186 try:
186 try:
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 fp = os.fdopen(infd, 'wb')
188 fp = os.fdopen(infd, 'wb')
189 fp.write(s)
189 fp.write(s)
190 fp.close()
190 fp.close()
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 os.close(outfd)
192 os.close(outfd)
193 cmd = cmd.replace('INFILE', inname)
193 cmd = cmd.replace('INFILE', inname)
194 cmd = cmd.replace('OUTFILE', outname)
194 cmd = cmd.replace('OUTFILE', outname)
195 code = os.system(cmd)
195 code = os.system(cmd)
196 if sys.platform == 'OpenVMS' and code & 1:
196 if sys.platform == 'OpenVMS' and code & 1:
197 code = 0
197 code = 0
198 if code:
198 if code:
199 raise Abort(_("command '%s' failed: %s") %
199 raise Abort(_("command '%s' failed: %s") %
200 (cmd, explain_exit(code)))
200 (cmd, explain_exit(code)))
201 return open(outname, 'rb').read()
201 return open(outname, 'rb').read()
202 finally:
202 finally:
203 try:
203 try:
204 if inname:
204 if inname:
205 os.unlink(inname)
205 os.unlink(inname)
206 except:
206 except:
207 pass
207 pass
208 try:
208 try:
209 if outname:
209 if outname:
210 os.unlink(outname)
210 os.unlink(outname)
211 except:
211 except:
212 pass
212 pass
213
213
214 filtertable = {
214 filtertable = {
215 'tempfile:': tempfilter,
215 'tempfile:': tempfilter,
216 'pipe:': pipefilter,
216 'pipe:': pipefilter,
217 }
217 }
218
218
219 def filter(s, cmd):
219 def filter(s, cmd):
220 "filter a string through a command that transforms its input to its output"
220 "filter a string through a command that transforms its input to its output"
221 for name, fn in filtertable.iteritems():
221 for name, fn in filtertable.iteritems():
222 if cmd.startswith(name):
222 if cmd.startswith(name):
223 return fn(s, cmd[len(name):].lstrip())
223 return fn(s, cmd[len(name):].lstrip())
224 return pipefilter(s, cmd)
224 return pipefilter(s, cmd)
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data"""
227 """return true if a string is binary data"""
228 return bool(s and '\0' in s)
228 return bool(s and '\0' in s)
229
229
230 def increasingchunks(source, min=1024, max=65536):
230 def increasingchunks(source, min=1024, max=65536):
231 '''return no less than min bytes per chunk while data remains,
231 '''return no less than min bytes per chunk while data remains,
232 doubling min after each chunk until it reaches max'''
232 doubling min after each chunk until it reaches max'''
233 def log2(x):
233 def log2(x):
234 if not x:
234 if not x:
235 return 0
235 return 0
236 i = 0
236 i = 0
237 while x:
237 while x:
238 x >>= 1
238 x >>= 1
239 i += 1
239 i += 1
240 return i - 1
240 return i - 1
241
241
242 buf = []
242 buf = []
243 blen = 0
243 blen = 0
244 for chunk in source:
244 for chunk in source:
245 buf.append(chunk)
245 buf.append(chunk)
246 blen += len(chunk)
246 blen += len(chunk)
247 if blen >= min:
247 if blen >= min:
248 if min < max:
248 if min < max:
249 min = min << 1
249 min = min << 1
250 nmin = 1 << log2(blen)
250 nmin = 1 << log2(blen)
251 if nmin > min:
251 if nmin > min:
252 min = nmin
252 min = nmin
253 if min > max:
253 if min > max:
254 min = max
254 min = max
255 yield ''.join(buf)
255 yield ''.join(buf)
256 blen = 0
256 blen = 0
257 buf = []
257 buf = []
258 if buf:
258 if buf:
259 yield ''.join(buf)
259 yield ''.join(buf)
260
260
261 Abort = error.Abort
261 Abort = error.Abort
262
262
263 def always(fn):
263 def always(fn):
264 return True
264 return True
265
265
266 def never(fn):
266 def never(fn):
267 return False
267 return False
268
268
269 def pathto(root, n1, n2):
269 def pathto(root, n1, n2):
270 '''return the relative path from one place to another.
270 '''return the relative path from one place to another.
271 root should use os.sep to separate directories
271 root should use os.sep to separate directories
272 n1 should use os.sep to separate directories
272 n1 should use os.sep to separate directories
273 n2 should use "/" to separate directories
273 n2 should use "/" to separate directories
274 returns an os.sep-separated path.
274 returns an os.sep-separated path.
275
275
276 If n1 is a relative path, it's assumed it's
276 If n1 is a relative path, it's assumed it's
277 relative to root.
277 relative to root.
278 n2 should always be relative to root.
278 n2 should always be relative to root.
279 '''
279 '''
280 if not n1:
280 if not n1:
281 return localpath(n2)
281 return localpath(n2)
282 if os.path.isabs(n1):
282 if os.path.isabs(n1):
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 return os.path.join(root, localpath(n2))
284 return os.path.join(root, localpath(n2))
285 n2 = '/'.join((pconvert(root), n2))
285 n2 = '/'.join((pconvert(root), n2))
286 a, b = splitpath(n1), n2.split('/')
286 a, b = splitpath(n1), n2.split('/')
287 a.reverse()
287 a.reverse()
288 b.reverse()
288 b.reverse()
289 while a and b and a[-1] == b[-1]:
289 while a and b and a[-1] == b[-1]:
290 a.pop()
290 a.pop()
291 b.pop()
291 b.pop()
292 b.reverse()
292 b.reverse()
293 return os.sep.join((['..'] * len(a)) + b) or '.'
293 return os.sep.join((['..'] * len(a)) + b) or '.'
294
294
295 def canonpath(root, cwd, myname):
295 def canonpath(root, cwd, myname):
296 """return the canonical path of myname, given cwd and root"""
296 """return the canonical path of myname, given cwd and root"""
297 if endswithsep(root):
297 if endswithsep(root):
298 rootsep = root
298 rootsep = root
299 else:
299 else:
300 rootsep = root + os.sep
300 rootsep = root + os.sep
301 name = myname
301 name = myname
302 if not os.path.isabs(name):
302 if not os.path.isabs(name):
303 name = os.path.join(root, cwd, name)
303 name = os.path.join(root, cwd, name)
304 name = os.path.normpath(name)
304 name = os.path.normpath(name)
305 audit_path = path_auditor(root)
305 audit_path = path_auditor(root)
306 if name != rootsep and name.startswith(rootsep):
306 if name != rootsep and name.startswith(rootsep):
307 name = name[len(rootsep):]
307 name = name[len(rootsep):]
308 audit_path(name)
308 audit_path(name)
309 return pconvert(name)
309 return pconvert(name)
310 elif name == root:
310 elif name == root:
311 return ''
311 return ''
312 else:
312 else:
313 # Determine whether `name' is in the hierarchy at or beneath `root',
313 # Determine whether `name' is in the hierarchy at or beneath `root',
314 # by iterating name=dirname(name) until that causes no change (can't
314 # by iterating name=dirname(name) until that causes no change (can't
315 # check name == '/', because that doesn't work on windows). For each
315 # check name == '/', because that doesn't work on windows). For each
316 # `name', compare dev/inode numbers. If they match, the list `rel'
316 # `name', compare dev/inode numbers. If they match, the list `rel'
317 # holds the reversed list of components making up the relative file
317 # holds the reversed list of components making up the relative file
318 # name we want.
318 # name we want.
319 root_st = os.stat(root)
319 root_st = os.stat(root)
320 rel = []
320 rel = []
321 while True:
321 while True:
322 try:
322 try:
323 name_st = os.stat(name)
323 name_st = os.stat(name)
324 except OSError:
324 except OSError:
325 break
325 break
326 if samestat(name_st, root_st):
326 if samestat(name_st, root_st):
327 if not rel:
327 if not rel:
328 # name was actually the same as root (maybe a symlink)
328 # name was actually the same as root (maybe a symlink)
329 return ''
329 return ''
330 rel.reverse()
330 rel.reverse()
331 name = os.path.join(*rel)
331 name = os.path.join(*rel)
332 audit_path(name)
332 audit_path(name)
333 return pconvert(name)
333 return pconvert(name)
334 dirname, basename = os.path.split(name)
334 dirname, basename = os.path.split(name)
335 rel.append(basename)
335 rel.append(basename)
336 if dirname == name:
336 if dirname == name:
337 break
337 break
338 name = dirname
338 name = dirname
339
339
340 raise Abort('%s not under root' % myname)
340 raise Abort('%s not under root' % myname)
341
341
342 _hgexecutable = None
342 _hgexecutable = None
343
343
344 def main_is_frozen():
344 def main_is_frozen():
345 """return True if we are a frozen executable.
345 """return True if we are a frozen executable.
346
346
347 The code supports py2exe (most common, Windows only) and tools/freeze
347 The code supports py2exe (most common, Windows only) and tools/freeze
348 (portable, not much used).
348 (portable, not much used).
349 """
349 """
350 return (hasattr(sys, "frozen") or # new py2exe
350 return (hasattr(sys, "frozen") or # new py2exe
351 hasattr(sys, "importers") or # old py2exe
351 hasattr(sys, "importers") or # old py2exe
352 imp.is_frozen("__main__")) # tools/freeze
352 imp.is_frozen("__main__")) # tools/freeze
353
353
354 def hgexecutable():
354 def hgexecutable():
355 """return location of the 'hg' executable.
355 """return location of the 'hg' executable.
356
356
357 Defaults to $HG or 'hg' in the search path.
357 Defaults to $HG or 'hg' in the search path.
358 """
358 """
359 if _hgexecutable is None:
359 if _hgexecutable is None:
360 hg = os.environ.get('HG')
360 hg = os.environ.get('HG')
361 if hg:
361 if hg:
362 set_hgexecutable(hg)
362 set_hgexecutable(hg)
363 elif main_is_frozen():
363 elif main_is_frozen():
364 set_hgexecutable(sys.executable)
364 set_hgexecutable(sys.executable)
365 else:
365 else:
366 exe = find_exe('hg') or os.path.basename(sys.argv[0])
366 exe = find_exe('hg') or os.path.basename(sys.argv[0])
367 set_hgexecutable(exe)
367 set_hgexecutable(exe)
368 return _hgexecutable
368 return _hgexecutable
369
369
370 def set_hgexecutable(path):
370 def set_hgexecutable(path):
371 """set location of the 'hg' executable"""
371 """set location of the 'hg' executable"""
372 global _hgexecutable
372 global _hgexecutable
373 _hgexecutable = path
373 _hgexecutable = path
374
374
375 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
375 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
376 '''enhanced shell command execution.
376 '''enhanced shell command execution.
377 run with environment maybe modified, maybe in different dir.
377 run with environment maybe modified, maybe in different dir.
378
378
379 if command fails and onerr is None, return status. if ui object,
379 if command fails and onerr is None, return status. if ui object,
380 print error message and return status, else raise onerr object as
380 print error message and return status, else raise onerr object as
381 exception.
381 exception.
382
382
383 if out is specified, it is assumed to be a file-like object that has a
383 if out is specified, it is assumed to be a file-like object that has a
384 write() method. stdout and stderr will be redirected to out.'''
384 write() method. stdout and stderr will be redirected to out.'''
385 def py2shell(val):
385 def py2shell(val):
386 'convert python object into string that is useful to shell'
386 'convert python object into string that is useful to shell'
387 if val is None or val is False:
387 if val is None or val is False:
388 return '0'
388 return '0'
389 if val is True:
389 if val is True:
390 return '1'
390 return '1'
391 return str(val)
391 return str(val)
392 origcmd = cmd
392 origcmd = cmd
393 if os.name == 'nt':
393 if os.name == 'nt':
394 cmd = '"%s"' % cmd
394 cmd = '"%s"' % cmd
395 env = dict(os.environ)
395 env = dict(os.environ)
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 env['HG'] = hgexecutable()
397 env['HG'] = hgexecutable()
398 if out is None:
398 if out is None:
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 env=env, cwd=cwd)
400 env=env, cwd=cwd)
401 else:
401 else:
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 stderr=subprocess.STDOUT)
404 stderr=subprocess.STDOUT)
405 for line in proc.stdout:
405 for line in proc.stdout:
406 out.write(line)
406 out.write(line)
407 proc.wait()
407 proc.wait()
408 rc = proc.returncode
408 rc = proc.returncode
409 if sys.platform == 'OpenVMS' and rc & 1:
409 if sys.platform == 'OpenVMS' and rc & 1:
410 rc = 0
410 rc = 0
411 if rc and onerr:
411 if rc and onerr:
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 explain_exit(rc)[0])
413 explain_exit(rc)[0])
414 if errprefix:
414 if errprefix:
415 errmsg = '%s: %s' % (errprefix, errmsg)
415 errmsg = '%s: %s' % (errprefix, errmsg)
416 try:
416 try:
417 onerr.warn(errmsg + '\n')
417 onerr.warn(errmsg + '\n')
418 except AttributeError:
418 except AttributeError:
419 raise onerr(errmsg)
419 raise onerr(errmsg)
420 return rc
420 return rc
421
421
422 def checksignature(func):
422 def checksignature(func):
423 '''wrap a function with code to check for calling errors'''
423 '''wrap a function with code to check for calling errors'''
424 def check(*args, **kwargs):
424 def check(*args, **kwargs):
425 try:
425 try:
426 return func(*args, **kwargs)
426 return func(*args, **kwargs)
427 except TypeError:
427 except TypeError:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 raise error.SignatureError
429 raise error.SignatureError
430 raise
430 raise
431
431
432 return check
432 return check
433
433
434 def unlink(f):
434 def unlink(f):
435 """unlink and remove the directory if it is empty"""
435 """unlink and remove the directory if it is empty"""
436 os.unlink(f)
436 os.unlink(f)
437 # try removing directories that might now be empty
437 # try removing directories that might now be empty
438 try:
438 try:
439 os.removedirs(os.path.dirname(f))
439 os.removedirs(os.path.dirname(f))
440 except OSError:
440 except OSError:
441 pass
441 pass
442
442
443 def copyfile(src, dest):
443 def copyfile(src, dest):
444 "copy a file, preserving mode and atime/mtime"
444 "copy a file, preserving mode and atime/mtime"
445 if os.path.islink(src):
445 if os.path.islink(src):
446 try:
446 try:
447 os.unlink(dest)
447 os.unlink(dest)
448 except:
448 except:
449 pass
449 pass
450 os.symlink(os.readlink(src), dest)
450 os.symlink(os.readlink(src), dest)
451 else:
451 else:
452 try:
452 try:
453 shutil.copyfile(src, dest)
453 shutil.copyfile(src, dest)
454 shutil.copystat(src, dest)
454 shutil.copystat(src, dest)
455 except shutil.Error, inst:
455 except shutil.Error, inst:
456 raise Abort(str(inst))
456 raise Abort(str(inst))
457
457
458 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
459 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
460
460
461 if hardlink is None:
461 if hardlink is None:
462 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
463 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
464
464
465 num = 0
465 num = 0
466 if os.path.isdir(src):
466 if os.path.isdir(src):
467 os.mkdir(dst)
467 os.mkdir(dst)
468 for name, kind in osutil.listdir(src):
468 for name, kind in osutil.listdir(src):
469 srcname = os.path.join(src, name)
469 srcname = os.path.join(src, name)
470 dstname = os.path.join(dst, name)
470 dstname = os.path.join(dst, name)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 num += n
472 num += n
473 else:
473 else:
474 if hardlink:
474 if hardlink:
475 try:
475 try:
476 os_link(src, dst)
476 os_link(src, dst)
477 except (IOError, OSError):
477 except (IOError, OSError):
478 hardlink = False
478 hardlink = False
479 shutil.copy(src, dst)
479 shutil.copy(src, dst)
480 else:
480 else:
481 shutil.copy(src, dst)
481 shutil.copy(src, dst)
482 num += 1
482 num += 1
483
483
484 return hardlink, num
484 return hardlink, num
485
485
486 class path_auditor(object):
486 class path_auditor(object):
487 '''ensure that a filesystem path contains no banned components.
487 '''ensure that a filesystem path contains no banned components.
488 the following properties of a path are checked:
488 the following properties of a path are checked:
489
489
490 - under top-level .hg
490 - under top-level .hg
491 - starts at the root of a windows drive
491 - starts at the root of a windows drive
492 - contains ".."
492 - contains ".."
493 - traverses a symlink (e.g. a/symlink_here/b)
493 - traverses a symlink (e.g. a/symlink_here/b)
494 - inside a nested repository'''
494 - inside a nested repository'''
495
495
496 def __init__(self, root):
496 def __init__(self, root):
497 self.audited = set()
497 self.audited = set()
498 self.auditeddir = set()
498 self.auditeddir = set()
499 self.root = root
499 self.root = root
500
500
501 def __call__(self, path):
501 def __call__(self, path):
502 if path in self.audited:
502 if path in self.audited:
503 return
503 return
504 normpath = os.path.normcase(path)
504 normpath = os.path.normcase(path)
505 parts = splitpath(normpath)
505 parts = splitpath(normpath)
506 if (os.path.splitdrive(path)[0]
506 if (os.path.splitdrive(path)[0]
507 or parts[0].lower() in ('.hg', '.hg.', '')
507 or parts[0].lower() in ('.hg', '.hg.', '')
508 or os.pardir in parts):
508 or os.pardir in parts):
509 raise Abort(_("path contains illegal component: %s") % path)
509 raise Abort(_("path contains illegal component: %s") % path)
510 if '.hg' in path.lower():
510 if '.hg' in path.lower():
511 lparts = [p.lower() for p in parts]
511 lparts = [p.lower() for p in parts]
512 for p in '.hg', '.hg.':
512 for p in '.hg', '.hg.':
513 if p in lparts[1:]:
513 if p in lparts[1:]:
514 pos = lparts.index(p)
514 pos = lparts.index(p)
515 base = os.path.join(*parts[:pos])
515 base = os.path.join(*parts[:pos])
516 raise Abort(_('path %r is inside repo %r') % (path, base))
516 raise Abort(_('path %r is inside repo %r') % (path, base))
517 def check(prefix):
517 def check(prefix):
518 curpath = os.path.join(self.root, prefix)
518 curpath = os.path.join(self.root, prefix)
519 try:
519 try:
520 st = os.lstat(curpath)
520 st = os.lstat(curpath)
521 except OSError, err:
521 except OSError, err:
522 # EINVAL can be raised as invalid path syntax under win32.
522 # EINVAL can be raised as invalid path syntax under win32.
523 # They must be ignored for patterns can be checked too.
523 # They must be ignored for patterns can be checked too.
524 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
524 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
525 raise
525 raise
526 else:
526 else:
527 if stat.S_ISLNK(st.st_mode):
527 if stat.S_ISLNK(st.st_mode):
528 raise Abort(_('path %r traverses symbolic link %r') %
528 raise Abort(_('path %r traverses symbolic link %r') %
529 (path, prefix))
529 (path, prefix))
530 elif (stat.S_ISDIR(st.st_mode) and
530 elif (stat.S_ISDIR(st.st_mode) and
531 os.path.isdir(os.path.join(curpath, '.hg'))):
531 os.path.isdir(os.path.join(curpath, '.hg'))):
532 raise Abort(_('path %r is inside repo %r') %
532 raise Abort(_('path %r is inside repo %r') %
533 (path, prefix))
533 (path, prefix))
534 parts.pop()
534 parts.pop()
535 prefixes = []
535 prefixes = []
536 while parts:
536 while parts:
537 prefix = os.sep.join(parts)
537 prefix = os.sep.join(parts)
538 if prefix in self.auditeddir:
538 if prefix in self.auditeddir:
539 break
539 break
540 check(prefix)
540 check(prefix)
541 prefixes.append(prefix)
541 prefixes.append(prefix)
542 parts.pop()
542 parts.pop()
543
543
544 self.audited.add(path)
544 self.audited.add(path)
545 # only add prefixes to the cache after checking everything: we don't
545 # only add prefixes to the cache after checking everything: we don't
546 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
546 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
547 self.auditeddir.update(prefixes)
547 self.auditeddir.update(prefixes)
548
548
549 def nlinks(pathname):
549 def nlinks(pathname):
550 """Return number of hardlinks for the given file."""
550 """Return number of hardlinks for the given file."""
551 return os.lstat(pathname).st_nlink
551 return os.lstat(pathname).st_nlink
552
552
553 if hasattr(os, 'link'):
553 if hasattr(os, 'link'):
554 os_link = os.link
554 os_link = os.link
555 else:
555 else:
556 def os_link(src, dst):
556 def os_link(src, dst):
557 raise OSError(0, _("Hardlinks not supported"))
557 raise OSError(0, _("Hardlinks not supported"))
558
558
559 def lookup_reg(key, name=None, scope=None):
559 def lookup_reg(key, name=None, scope=None):
560 return None
560 return None
561
561
562 def hidewindow():
562 def hidewindow():
563 """Hide current shell window.
563 """Hide current shell window.
564
564
565 Used to hide the window opened when starting asynchronous
565 Used to hide the window opened when starting asynchronous
566 child process under Windows, unneeded on other systems.
566 child process under Windows, unneeded on other systems.
567 """
567 """
568 pass
568 pass
569
569
570 if os.name == 'nt':
570 if os.name == 'nt':
571 from windows import *
571 from windows import *
572 else:
572 else:
573 from posix import *
573 from posix import *
574
574
575 def makelock(info, pathname):
575 def makelock(info, pathname):
576 try:
576 try:
577 return os.symlink(info, pathname)
577 return os.symlink(info, pathname)
578 except OSError, why:
578 except OSError, why:
579 if why.errno == errno.EEXIST:
579 if why.errno == errno.EEXIST:
580 raise
580 raise
581 except AttributeError: # no symlink in os
581 except AttributeError: # no symlink in os
582 pass
582 pass
583
583
584 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
584 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
585 os.write(ld, info)
585 os.write(ld, info)
586 os.close(ld)
586 os.close(ld)
587
587
588 def readlock(pathname):
588 def readlock(pathname):
589 try:
589 try:
590 return os.readlink(pathname)
590 return os.readlink(pathname)
591 except OSError, why:
591 except OSError, why:
592 if why.errno not in (errno.EINVAL, errno.ENOSYS):
592 if why.errno not in (errno.EINVAL, errno.ENOSYS):
593 raise
593 raise
594 except AttributeError: # no symlink in os
594 except AttributeError: # no symlink in os
595 pass
595 pass
596 return posixfile(pathname).read()
596 return posixfile(pathname).read()
597
597
598 def fstat(fp):
598 def fstat(fp):
599 '''stat file object that may not have fileno method.'''
599 '''stat file object that may not have fileno method.'''
600 try:
600 try:
601 return os.fstat(fp.fileno())
601 return os.fstat(fp.fileno())
602 except AttributeError:
602 except AttributeError:
603 return os.stat(fp.name)
603 return os.stat(fp.name)
604
604
605 # File system features
605 # File system features
606
606
607 def checkcase(path):
607 def checkcase(path):
608 """
608 """
609 Check whether the given path is on a case-sensitive filesystem
609 Check whether the given path is on a case-sensitive filesystem
610
610
611 Requires a path (like /foo/.hg) ending with a foldable final
611 Requires a path (like /foo/.hg) ending with a foldable final
612 directory component.
612 directory component.
613 """
613 """
614 s1 = os.stat(path)
614 s1 = os.stat(path)
615 d, b = os.path.split(path)
615 d, b = os.path.split(path)
616 p2 = os.path.join(d, b.upper())
616 p2 = os.path.join(d, b.upper())
617 if path == p2:
617 if path == p2:
618 p2 = os.path.join(d, b.lower())
618 p2 = os.path.join(d, b.lower())
619 try:
619 try:
620 s2 = os.stat(p2)
620 s2 = os.stat(p2)
621 if s2 == s1:
621 if s2 == s1:
622 return False
622 return False
623 return True
623 return True
624 except:
624 except:
625 return True
625 return True
626
626
627 _fspathcache = {}
627 _fspathcache = {}
628 def fspath(name, root):
628 def fspath(name, root):
629 '''Get name in the case stored in the filesystem
629 '''Get name in the case stored in the filesystem
630
630
631 The name is either relative to root, or it is an absolute path starting
631 The name is either relative to root, or it is an absolute path starting
632 with root. Note that this function is unnecessary, and should not be
632 with root. Note that this function is unnecessary, and should not be
633 called, for case-sensitive filesystems (simply because it's expensive).
633 called, for case-sensitive filesystems (simply because it's expensive).
634 '''
634 '''
635 # If name is absolute, make it relative
635 # If name is absolute, make it relative
636 if name.lower().startswith(root.lower()):
636 if name.lower().startswith(root.lower()):
637 l = len(root)
637 l = len(root)
638 if name[l] == os.sep or name[l] == os.altsep:
638 if name[l] == os.sep or name[l] == os.altsep:
639 l = l + 1
639 l = l + 1
640 name = name[l:]
640 name = name[l:]
641
641
642 if not os.path.exists(os.path.join(root, name)):
642 if not os.path.exists(os.path.join(root, name)):
643 return None
643 return None
644
644
645 seps = os.sep
645 seps = os.sep
646 if os.altsep:
646 if os.altsep:
647 seps = seps + os.altsep
647 seps = seps + os.altsep
648 # Protect backslashes. This gets silly very quickly.
648 # Protect backslashes. This gets silly very quickly.
649 seps.replace('\\','\\\\')
649 seps.replace('\\','\\\\')
650 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
650 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
651 dir = os.path.normcase(os.path.normpath(root))
651 dir = os.path.normcase(os.path.normpath(root))
652 result = []
652 result = []
653 for part, sep in pattern.findall(name):
653 for part, sep in pattern.findall(name):
654 if sep:
654 if sep:
655 result.append(sep)
655 result.append(sep)
656 continue
656 continue
657
657
658 if dir not in _fspathcache:
658 if dir not in _fspathcache:
659 _fspathcache[dir] = os.listdir(dir)
659 _fspathcache[dir] = os.listdir(dir)
660 contents = _fspathcache[dir]
660 contents = _fspathcache[dir]
661
661
662 lpart = part.lower()
662 lpart = part.lower()
663 lenp = len(part)
663 lenp = len(part)
664 for n in contents:
664 for n in contents:
665 if lenp == len(n) and n.lower() == lpart:
665 if lenp == len(n) and n.lower() == lpart:
666 result.append(n)
666 result.append(n)
667 break
667 break
668 else:
668 else:
669 # Cannot happen, as the file exists!
669 # Cannot happen, as the file exists!
670 result.append(part)
670 result.append(part)
671 dir = os.path.join(dir, lpart)
671 dir = os.path.join(dir, lpart)
672
672
673 return ''.join(result)
673 return ''.join(result)
674
674
675 def checkexec(path):
675 def checkexec(path):
676 """
676 """
677 Check whether the given path is on a filesystem with UNIX-like exec flags
677 Check whether the given path is on a filesystem with UNIX-like exec flags
678
678
679 Requires a directory (like /foo/.hg)
679 Requires a directory (like /foo/.hg)
680 """
680 """
681
681
682 # VFAT on some Linux versions can flip mode but it doesn't persist
682 # VFAT on some Linux versions can flip mode but it doesn't persist
683 # a FS remount. Frequently we can detect it if files are created
683 # a FS remount. Frequently we can detect it if files are created
684 # with exec bit on.
684 # with exec bit on.
685
685
686 try:
686 try:
687 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
687 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
688 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
688 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
689 try:
689 try:
690 os.close(fh)
690 os.close(fh)
691 m = os.stat(fn).st_mode & 0777
691 m = os.stat(fn).st_mode & 0777
692 new_file_has_exec = m & EXECFLAGS
692 new_file_has_exec = m & EXECFLAGS
693 os.chmod(fn, m ^ EXECFLAGS)
693 os.chmod(fn, m ^ EXECFLAGS)
694 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
694 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
695 finally:
695 finally:
696 os.unlink(fn)
696 os.unlink(fn)
697 except (IOError, OSError):
697 except (IOError, OSError):
698 # we don't care, the user probably won't be able to commit anyway
698 # we don't care, the user probably won't be able to commit anyway
699 return False
699 return False
700 return not (new_file_has_exec or exec_flags_cannot_flip)
700 return not (new_file_has_exec or exec_flags_cannot_flip)
701
701
702 def checklink(path):
702 def checklink(path):
703 """check whether the given path is on a symlink-capable filesystem"""
703 """check whether the given path is on a symlink-capable filesystem"""
704 # mktemp is not racy because symlink creation will fail if the
704 # mktemp is not racy because symlink creation will fail if the
705 # file already exists
705 # file already exists
706 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
706 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
707 try:
707 try:
708 os.symlink(".", name)
708 os.symlink(".", name)
709 os.unlink(name)
709 os.unlink(name)
710 return True
710 return True
711 except (OSError, AttributeError):
711 except (OSError, AttributeError):
712 return False
712 return False
713
713
714 def needbinarypatch():
714 def needbinarypatch():
715 """return True if patches should be applied in binary mode by default."""
715 """return True if patches should be applied in binary mode by default."""
716 return os.name == 'nt'
716 return os.name == 'nt'
717
717
718 def endswithsep(path):
718 def endswithsep(path):
719 '''Check path ends with os.sep or os.altsep.'''
719 '''Check path ends with os.sep or os.altsep.'''
720 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
720 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
721
721
722 def splitpath(path):
722 def splitpath(path):
723 '''Split path by os.sep.
723 '''Split path by os.sep.
724 Note that this function does not use os.altsep because this is
724 Note that this function does not use os.altsep because this is
725 an alternative of simple "xxx.split(os.sep)".
725 an alternative of simple "xxx.split(os.sep)".
726 It is recommended to use os.path.normpath() before using this
726 It is recommended to use os.path.normpath() before using this
727 function if need.'''
727 function if need.'''
728 return path.split(os.sep)
728 return path.split(os.sep)
729
729
730 def gui():
730 def gui():
731 '''Are we running in a GUI?'''
731 '''Are we running in a GUI?'''
732 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
732 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
733
733
734 def mktempcopy(name, emptyok=False, createmode=None):
734 def mktempcopy(name, emptyok=False, createmode=None):
735 """Create a temporary file with the same contents from name
735 """Create a temporary file with the same contents from name
736
736
737 The permission bits are copied from the original file.
737 The permission bits are copied from the original file.
738
738
739 If the temporary file is going to be truncated immediately, you
739 If the temporary file is going to be truncated immediately, you
740 can use emptyok=True as an optimization.
740 can use emptyok=True as an optimization.
741
741
742 Returns the name of the temporary file.
742 Returns the name of the temporary file.
743 """
743 """
744 d, fn = os.path.split(name)
744 d, fn = os.path.split(name)
745 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
745 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
746 os.close(fd)
746 os.close(fd)
747 # Temporary files are created with mode 0600, which is usually not
747 # Temporary files are created with mode 0600, which is usually not
748 # what we want. If the original file already exists, just copy
748 # what we want. If the original file already exists, just copy
749 # its mode. Otherwise, manually obey umask.
749 # its mode. Otherwise, manually obey umask.
750 try:
750 try:
751 st_mode = os.lstat(name).st_mode & 0777
751 st_mode = os.lstat(name).st_mode & 0777
752 except OSError, inst:
752 except OSError, inst:
753 if inst.errno != errno.ENOENT:
753 if inst.errno != errno.ENOENT:
754 raise
754 raise
755 st_mode = createmode
755 st_mode = createmode
756 if st_mode is None:
756 if st_mode is None:
757 st_mode = ~umask
757 st_mode = ~umask
758 st_mode &= 0666
758 st_mode &= 0666
759 os.chmod(temp, st_mode)
759 os.chmod(temp, st_mode)
760 if emptyok:
760 if emptyok:
761 return temp
761 return temp
762 try:
762 try:
763 try:
763 try:
764 ifp = posixfile(name, "rb")
764 ifp = posixfile(name, "rb")
765 except IOError, inst:
765 except IOError, inst:
766 if inst.errno == errno.ENOENT:
766 if inst.errno == errno.ENOENT:
767 return temp
767 return temp
768 if not getattr(inst, 'filename', None):
768 if not getattr(inst, 'filename', None):
769 inst.filename = name
769 inst.filename = name
770 raise
770 raise
771 ofp = posixfile(temp, "wb")
771 ofp = posixfile(temp, "wb")
772 for chunk in filechunkiter(ifp):
772 for chunk in filechunkiter(ifp):
773 ofp.write(chunk)
773 ofp.write(chunk)
774 ifp.close()
774 ifp.close()
775 ofp.close()
775 ofp.close()
776 except:
776 except:
777 try: os.unlink(temp)
777 try: os.unlink(temp)
778 except: pass
778 except: pass
779 raise
779 raise
780 return temp
780 return temp
781
781
782 class atomictempfile(object):
782 class atomictempfile(object):
783 """file-like object that atomically updates a file
783 """file-like object that atomically updates a file
784
784
785 All writes will be redirected to a temporary copy of the original
785 All writes will be redirected to a temporary copy of the original
786 file. When rename is called, the copy is renamed to the original
786 file. When rename is called, the copy is renamed to the original
787 name, making the changes visible.
787 name, making the changes visible.
788 """
788 """
789 def __init__(self, name, mode='w+b', createmode=None):
789 def __init__(self, name, mode='w+b', createmode=None):
790 self.__name = name
790 self.__name = name
791 self._fp = None
791 self._fp = None
792 self.temp = mktempcopy(name, emptyok=('w' in mode),
792 self.temp = mktempcopy(name, emptyok=('w' in mode),
793 createmode=createmode)
793 createmode=createmode)
794 self._fp = posixfile(self.temp, mode)
794 self._fp = posixfile(self.temp, mode)
795
795
796 def __getattr__(self, name):
796 def __getattr__(self, name):
797 return getattr(self._fp, name)
797 return getattr(self._fp, name)
798
798
799 def rename(self):
799 def rename(self):
800 if not self._fp.closed:
800 if not self._fp.closed:
801 self._fp.close()
801 self._fp.close()
802 rename(self.temp, localpath(self.__name))
802 rename(self.temp, localpath(self.__name))
803
803
804 def __del__(self):
804 def __del__(self):
805 if not self._fp:
805 if not self._fp:
806 return
806 return
807 if not self._fp.closed:
807 if not self._fp.closed:
808 try:
808 try:
809 os.unlink(self.temp)
809 os.unlink(self.temp)
810 except: pass
810 except: pass
811 self._fp.close()
811 self._fp.close()
812
812
813 def makedirs(name, mode=None):
813 def makedirs(name, mode=None):
814 """recursive directory creation with parent mode inheritance"""
814 """recursive directory creation with parent mode inheritance"""
815 try:
815 try:
816 os.mkdir(name)
816 os.mkdir(name)
817 if mode is not None:
817 if mode is not None:
818 os.chmod(name, mode)
818 os.chmod(name, mode)
819 return
819 return
820 except OSError, err:
820 except OSError, err:
821 if err.errno == errno.EEXIST:
821 if err.errno == errno.EEXIST:
822 return
822 return
823 if err.errno != errno.ENOENT:
823 if err.errno != errno.ENOENT:
824 raise
824 raise
825 parent = os.path.abspath(os.path.dirname(name))
825 parent = os.path.abspath(os.path.dirname(name))
826 makedirs(parent, mode)
826 makedirs(parent, mode)
827 makedirs(name, mode)
827 makedirs(name, mode)
828
828
829 class opener(object):
829 class opener(object):
830 """Open files relative to a base directory
830 """Open files relative to a base directory
831
831
832 This class is used to hide the details of COW semantics and
832 This class is used to hide the details of COW semantics and
833 remote file access from higher level code.
833 remote file access from higher level code.
834 """
834 """
835 def __init__(self, base, audit=True):
835 def __init__(self, base, audit=True):
836 self.base = base
836 self.base = base
837 if audit:
837 if audit:
838 self.audit_path = path_auditor(base)
838 self.audit_path = path_auditor(base)
839 else:
839 else:
840 self.audit_path = always
840 self.audit_path = always
841 self.createmode = None
841 self.createmode = None
842
842
843 @propertycache
843 @propertycache
844 def _can_symlink(self):
844 def _can_symlink(self):
845 return checklink(self.base)
845 return checklink(self.base)
846
846
847 def _fixfilemode(self, name):
847 def _fixfilemode(self, name):
848 if self.createmode is None:
848 if self.createmode is None:
849 return
849 return
850 os.chmod(name, self.createmode & 0666)
850 os.chmod(name, self.createmode & 0666)
851
851
852 def __call__(self, path, mode="r", text=False, atomictemp=False):
852 def __call__(self, path, mode="r", text=False, atomictemp=False):
853 self.audit_path(path)
853 self.audit_path(path)
854 f = os.path.join(self.base, path)
854 f = os.path.join(self.base, path)
855
855
856 if not text and "b" not in mode:
856 if not text and "b" not in mode:
857 mode += "b" # for that other OS
857 mode += "b" # for that other OS
858
858
859 nlink = -1
859 nlink = -1
860 if mode not in ("r", "rb"):
860 if mode not in ("r", "rb"):
861 try:
861 try:
862 nlink = nlinks(f)
862 nlink = nlinks(f)
863 except OSError:
863 except OSError:
864 nlink = 0
864 nlink = 0
865 d = os.path.dirname(f)
865 d = os.path.dirname(f)
866 if not os.path.isdir(d):
866 if not os.path.isdir(d):
867 makedirs(d, self.createmode)
867 makedirs(d, self.createmode)
868 if atomictemp:
868 if atomictemp:
869 return atomictempfile(f, mode, self.createmode)
869 return atomictempfile(f, mode, self.createmode)
870 if nlink > 1:
870 if nlink > 1:
871 rename(mktempcopy(f), f)
871 rename(mktempcopy(f), f)
872 fp = posixfile(f, mode)
872 fp = posixfile(f, mode)
873 if nlink == 0:
873 if nlink == 0:
874 self._fixfilemode(f)
874 self._fixfilemode(f)
875 return fp
875 return fp
876
876
877 def symlink(self, src, dst):
877 def symlink(self, src, dst):
878 self.audit_path(dst)
878 self.audit_path(dst)
879 linkname = os.path.join(self.base, dst)
879 linkname = os.path.join(self.base, dst)
880 try:
880 try:
881 os.unlink(linkname)
881 os.unlink(linkname)
882 except OSError:
882 except OSError:
883 pass
883 pass
884
884
885 dirname = os.path.dirname(linkname)
885 dirname = os.path.dirname(linkname)
886 if not os.path.exists(dirname):
886 if not os.path.exists(dirname):
887 makedirs(dirname, self.createmode)
887 makedirs(dirname, self.createmode)
888
888
889 if self._can_symlink:
889 if self._can_symlink:
890 try:
890 try:
891 os.symlink(src, linkname)
891 os.symlink(src, linkname)
892 except OSError, err:
892 except OSError, err:
893 raise OSError(err.errno, _('could not symlink to %r: %s') %
893 raise OSError(err.errno, _('could not symlink to %r: %s') %
894 (src, err.strerror), linkname)
894 (src, err.strerror), linkname)
895 else:
895 else:
896 f = self(dst, "w")
896 f = self(dst, "w")
897 f.write(src)
897 f.write(src)
898 f.close()
898 f.close()
899 self._fixfilemode(dst)
899 self._fixfilemode(dst)
900
900
901 class chunkbuffer(object):
901 class chunkbuffer(object):
902 """Allow arbitrary sized chunks of data to be efficiently read from an
902 """Allow arbitrary sized chunks of data to be efficiently read from an
903 iterator over chunks of arbitrary size."""
903 iterator over chunks of arbitrary size."""
904
904
905 def __init__(self, in_iter):
905 def __init__(self, in_iter):
906 """in_iter is the iterator that's iterating over the input chunks.
906 """in_iter is the iterator that's iterating over the input chunks.
907 targetsize is how big a buffer to try to maintain."""
907 targetsize is how big a buffer to try to maintain."""
908 def splitbig(chunks):
908 def splitbig(chunks):
909 for chunk in chunks:
909 for chunk in chunks:
910 if len(chunk) > 2**20:
910 if len(chunk) > 2**20:
911 pos = 0
911 pos = 0
912 while pos < len(chunk):
912 while pos < len(chunk):
913 end = pos + 2 ** 18
913 end = pos + 2 ** 18
914 yield chunk[pos:end]
914 yield chunk[pos:end]
915 pos = end
915 pos = end
916 else:
916 else:
917 yield chunk
917 yield chunk
918 self.iter = splitbig(in_iter)
918 self.iter = splitbig(in_iter)
919 self._queue = []
919 self._queue = []
920
920
921 def read(self, l):
921 def read(self, l):
922 """Read L bytes of data from the iterator of chunks of data.
922 """Read L bytes of data from the iterator of chunks of data.
923 Returns less than L bytes if the iterator runs dry."""
923 Returns less than L bytes if the iterator runs dry."""
924 left = l
924 left = l
925 buf = ''
925 buf = ''
926 queue = self._queue
926 queue = self._queue
927 while left > 0:
927 while left > 0:
928 # refill the queue
928 # refill the queue
929 if not queue:
929 if not queue:
930 target = 2**18
930 target = 2**18
931 for chunk in self.iter:
931 for chunk in self.iter:
932 queue.append(chunk)
932 queue.append(chunk)
933 target -= len(chunk)
933 target -= len(chunk)
934 if target <= 0:
934 if target <= 0:
935 break
935 break
936 if not queue:
936 if not queue:
937 break
937 break
938
938
939 chunk = queue.pop(0)
939 chunk = queue.pop(0)
940 left -= len(chunk)
940 left -= len(chunk)
941 if left < 0:
941 if left < 0:
942 queue.insert(0, chunk[left:])
942 queue.insert(0, chunk[left:])
943 buf += chunk[:left]
943 buf += chunk[:left]
944 else:
944 else:
945 buf += chunk
945 buf += chunk
946
946
947 return buf
947 return buf
948
948
949 def filechunkiter(f, size=65536, limit=None):
949 def filechunkiter(f, size=65536, limit=None):
950 """Create a generator that produces the data in the file size
950 """Create a generator that produces the data in the file size
951 (default 65536) bytes at a time, up to optional limit (default is
951 (default 65536) bytes at a time, up to optional limit (default is
952 to read all data). Chunks may be less than size bytes if the
952 to read all data). Chunks may be less than size bytes if the
953 chunk is the last chunk in the file, or the file is a socket or
953 chunk is the last chunk in the file, or the file is a socket or
954 some other type of file that sometimes reads less data than is
954 some other type of file that sometimes reads less data than is
955 requested."""
955 requested."""
956 assert size >= 0
956 assert size >= 0
957 assert limit is None or limit >= 0
957 assert limit is None or limit >= 0
958 while True:
958 while True:
959 if limit is None:
959 if limit is None:
960 nbytes = size
960 nbytes = size
961 else:
961 else:
962 nbytes = min(limit, size)
962 nbytes = min(limit, size)
963 s = nbytes and f.read(nbytes)
963 s = nbytes and f.read(nbytes)
964 if not s:
964 if not s:
965 break
965 break
966 if limit:
966 if limit:
967 limit -= len(s)
967 limit -= len(s)
968 yield s
968 yield s
969
969
970 def makedate():
970 def makedate():
971 lt = time.localtime()
971 lt = time.localtime()
972 if lt[8] == 1 and time.daylight:
972 if lt[8] == 1 and time.daylight:
973 tz = time.altzone
973 tz = time.altzone
974 else:
974 else:
975 tz = time.timezone
975 tz = time.timezone
976 return time.mktime(lt), tz
976 return time.mktime(lt), tz
977
977
978 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
978 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
979 """represent a (unixtime, offset) tuple as a localized time.
979 """represent a (unixtime, offset) tuple as a localized time.
980 unixtime is seconds since the epoch, and offset is the time zone's
980 unixtime is seconds since the epoch, and offset is the time zone's
981 number of seconds away from UTC. if timezone is false, do not
981 number of seconds away from UTC. if timezone is false, do not
982 append time zone to string."""
982 append time zone to string."""
983 t, tz = date or makedate()
983 t, tz = date or makedate()
984 if "%1" in format or "%2" in format:
984 if "%1" in format or "%2" in format:
985 sign = (tz > 0) and "-" or "+"
985 sign = (tz > 0) and "-" or "+"
986 minutes = abs(tz) // 60
986 minutes = abs(tz) // 60
987 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
987 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
988 format = format.replace("%2", "%02d" % (minutes % 60))
988 format = format.replace("%2", "%02d" % (minutes % 60))
989 s = time.strftime(format, time.gmtime(float(t) - tz))
989 s = time.strftime(format, time.gmtime(float(t) - tz))
990 return s
990 return s
991
991
992 def shortdate(date=None):
992 def shortdate(date=None):
993 """turn (timestamp, tzoff) tuple into iso 8631 date."""
993 """turn (timestamp, tzoff) tuple into iso 8631 date."""
994 return datestr(date, format='%Y-%m-%d')
994 return datestr(date, format='%Y-%m-%d')
995
995
996 def strdate(string, format, defaults=[]):
996 def strdate(string, format, defaults=[]):
997 """parse a localized time string and return a (unixtime, offset) tuple.
997 """parse a localized time string and return a (unixtime, offset) tuple.
998 if the string cannot be parsed, ValueError is raised."""
998 if the string cannot be parsed, ValueError is raised."""
999 def timezone(string):
999 def timezone(string):
1000 tz = string.split()[-1]
1000 tz = string.split()[-1]
1001 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1001 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1002 sign = (tz[0] == "+") and 1 or -1
1002 sign = (tz[0] == "+") and 1 or -1
1003 hours = int(tz[1:3])
1003 hours = int(tz[1:3])
1004 minutes = int(tz[3:5])
1004 minutes = int(tz[3:5])
1005 return -sign * (hours * 60 + minutes) * 60
1005 return -sign * (hours * 60 + minutes) * 60
1006 if tz == "GMT" or tz == "UTC":
1006 if tz == "GMT" or tz == "UTC":
1007 return 0
1007 return 0
1008 return None
1008 return None
1009
1009
1010 # NOTE: unixtime = localunixtime + offset
1010 # NOTE: unixtime = localunixtime + offset
1011 offset, date = timezone(string), string
1011 offset, date = timezone(string), string
1012 if offset != None:
1012 if offset != None:
1013 date = " ".join(string.split()[:-1])
1013 date = " ".join(string.split()[:-1])
1014
1014
1015 # add missing elements from defaults
1015 # add missing elements from defaults
1016 for part in defaults:
1016 for part in defaults:
1017 found = [True for p in part if ("%"+p) in format]
1017 found = [True for p in part if ("%"+p) in format]
1018 if not found:
1018 if not found:
1019 date += "@" + defaults[part]
1019 date += "@" + defaults[part]
1020 format += "@%" + part[0]
1020 format += "@%" + part[0]
1021
1021
1022 timetuple = time.strptime(date, format)
1022 timetuple = time.strptime(date, format)
1023 localunixtime = int(calendar.timegm(timetuple))
1023 localunixtime = int(calendar.timegm(timetuple))
1024 if offset is None:
1024 if offset is None:
1025 # local timezone
1025 # local timezone
1026 unixtime = int(time.mktime(timetuple))
1026 unixtime = int(time.mktime(timetuple))
1027 offset = unixtime - localunixtime
1027 offset = unixtime - localunixtime
1028 else:
1028 else:
1029 unixtime = localunixtime + offset
1029 unixtime = localunixtime + offset
1030 return unixtime, offset
1030 return unixtime, offset
1031
1031
1032 def parsedate(date, formats=None, defaults=None):
1032 def parsedate(date, formats=None, defaults=None):
1033 """parse a localized date/time string and return a (unixtime, offset) tuple.
1033 """parse a localized date/time string and return a (unixtime, offset) tuple.
1034
1034
1035 The date may be a "unixtime offset" string or in one of the specified
1035 The date may be a "unixtime offset" string or in one of the specified
1036 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1036 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1037 """
1037 """
1038 if not date:
1038 if not date:
1039 return 0, 0
1039 return 0, 0
1040 if isinstance(date, tuple) and len(date) == 2:
1040 if isinstance(date, tuple) and len(date) == 2:
1041 return date
1041 return date
1042 if not formats:
1042 if not formats:
1043 formats = defaultdateformats
1043 formats = defaultdateformats
1044 date = date.strip()
1044 date = date.strip()
1045 try:
1045 try:
1046 when, offset = map(int, date.split(' '))
1046 when, offset = map(int, date.split(' '))
1047 except ValueError:
1047 except ValueError:
1048 # fill out defaults
1048 # fill out defaults
1049 if not defaults:
1049 if not defaults:
1050 defaults = {}
1050 defaults = {}
1051 now = makedate()
1051 now = makedate()
1052 for part in "d mb yY HI M S".split():
1052 for part in "d mb yY HI M S".split():
1053 if part not in defaults:
1053 if part not in defaults:
1054 if part[0] in "HMS":
1054 if part[0] in "HMS":
1055 defaults[part] = "00"
1055 defaults[part] = "00"
1056 else:
1056 else:
1057 defaults[part] = datestr(now, "%" + part[0])
1057 defaults[part] = datestr(now, "%" + part[0])
1058
1058
1059 for format in formats:
1059 for format in formats:
1060 try:
1060 try:
1061 when, offset = strdate(date, format, defaults)
1061 when, offset = strdate(date, format, defaults)
1062 except (ValueError, OverflowError):
1062 except (ValueError, OverflowError):
1063 pass
1063 pass
1064 else:
1064 else:
1065 break
1065 break
1066 else:
1066 else:
1067 raise Abort(_('invalid date: %r ') % date)
1067 raise Abort(_('invalid date: %r ') % date)
1068 # validate explicit (probably user-specified) date and
1068 # validate explicit (probably user-specified) date and
1069 # time zone offset. values must fit in signed 32 bits for
1069 # time zone offset. values must fit in signed 32 bits for
1070 # current 32-bit linux runtimes. timezones go from UTC-12
1070 # current 32-bit linux runtimes. timezones go from UTC-12
1071 # to UTC+14
1071 # to UTC+14
1072 if abs(when) > 0x7fffffff:
1072 if abs(when) > 0x7fffffff:
1073 raise Abort(_('date exceeds 32 bits: %d') % when)
1073 raise Abort(_('date exceeds 32 bits: %d') % when)
1074 if offset < -50400 or offset > 43200:
1074 if offset < -50400 or offset > 43200:
1075 raise Abort(_('impossible time zone offset: %d') % offset)
1075 raise Abort(_('impossible time zone offset: %d') % offset)
1076 return when, offset
1076 return when, offset
1077
1077
1078 def matchdate(date):
1078 def matchdate(date):
1079 """Return a function that matches a given date match specifier
1079 """Return a function that matches a given date match specifier
1080
1080
1081 Formats include:
1081 Formats include:
1082
1082
1083 '{date}' match a given date to the accuracy provided
1083 '{date}' match a given date to the accuracy provided
1084
1084
1085 '<{date}' on or before a given date
1085 '<{date}' on or before a given date
1086
1086
1087 '>{date}' on or after a given date
1087 '>{date}' on or after a given date
1088
1088
1089 """
1089 """
1090
1090
1091 def lower(date):
1091 def lower(date):
1092 d = dict(mb="1", d="1")
1092 d = dict(mb="1", d="1")
1093 return parsedate(date, extendeddateformats, d)[0]
1093 return parsedate(date, extendeddateformats, d)[0]
1094
1094
1095 def upper(date):
1095 def upper(date):
1096 d = dict(mb="12", HI="23", M="59", S="59")
1096 d = dict(mb="12", HI="23", M="59", S="59")
1097 for days in "31 30 29".split():
1097 for days in "31 30 29".split():
1098 try:
1098 try:
1099 d["d"] = days
1099 d["d"] = days
1100 return parsedate(date, extendeddateformats, d)[0]
1100 return parsedate(date, extendeddateformats, d)[0]
1101 except:
1101 except:
1102 pass
1102 pass
1103 d["d"] = "28"
1103 d["d"] = "28"
1104 return parsedate(date, extendeddateformats, d)[0]
1104 return parsedate(date, extendeddateformats, d)[0]
1105
1105
1106 date = date.strip()
1106 date = date.strip()
1107 if date[0] == "<":
1107 if date[0] == "<":
1108 when = upper(date[1:])
1108 when = upper(date[1:])
1109 return lambda x: x <= when
1109 return lambda x: x <= when
1110 elif date[0] == ">":
1110 elif date[0] == ">":
1111 when = lower(date[1:])
1111 when = lower(date[1:])
1112 return lambda x: x >= when
1112 return lambda x: x >= when
1113 elif date[0] == "-":
1113 elif date[0] == "-":
1114 try:
1114 try:
1115 days = int(date[1:])
1115 days = int(date[1:])
1116 except ValueError:
1116 except ValueError:
1117 raise Abort(_("invalid day spec: %s") % date[1:])
1117 raise Abort(_("invalid day spec: %s") % date[1:])
1118 when = makedate()[0] - days * 3600 * 24
1118 when = makedate()[0] - days * 3600 * 24
1119 return lambda x: x >= when
1119 return lambda x: x >= when
1120 elif " to " in date:
1120 elif " to " in date:
1121 a, b = date.split(" to ")
1121 a, b = date.split(" to ")
1122 start, stop = lower(a), upper(b)
1122 start, stop = lower(a), upper(b)
1123 return lambda x: x >= start and x <= stop
1123 return lambda x: x >= start and x <= stop
1124 else:
1124 else:
1125 start, stop = lower(date), upper(date)
1125 start, stop = lower(date), upper(date)
1126 return lambda x: x >= start and x <= stop
1126 return lambda x: x >= start and x <= stop
1127
1127
1128 def shortuser(user):
1128 def shortuser(user):
1129 """Return a short representation of a user name or email address."""
1129 """Return a short representation of a user name or email address."""
1130 f = user.find('@')
1130 f = user.find('@')
1131 if f >= 0:
1131 if f >= 0:
1132 user = user[:f]
1132 user = user[:f]
1133 f = user.find('<')
1133 f = user.find('<')
1134 if f >= 0:
1134 if f >= 0:
1135 user = user[f + 1:]
1135 user = user[f + 1:]
1136 f = user.find(' ')
1136 f = user.find(' ')
1137 if f >= 0:
1137 if f >= 0:
1138 user = user[:f]
1138 user = user[:f]
1139 f = user.find('.')
1139 f = user.find('.')
1140 if f >= 0:
1140 if f >= 0:
1141 user = user[:f]
1141 user = user[:f]
1142 return user
1142 return user
1143
1143
1144 def email(author):
1144 def email(author):
1145 '''get email of author.'''
1145 '''get email of author.'''
1146 r = author.find('>')
1146 r = author.find('>')
1147 if r == -1:
1147 if r == -1:
1148 r = None
1148 r = None
1149 return author[author.find('<') + 1:r]
1149 return author[author.find('<') + 1:r]
1150
1150
1151 def ellipsis(text, maxlength=400):
1151 def ellipsis(text, maxlength=400):
1152 """Trim string to at most maxlength (default: 400) characters."""
1152 """Trim string to at most maxlength (default: 400) characters."""
1153 if len(text) <= maxlength:
1153 if len(text) <= maxlength:
1154 return text
1154 return text
1155 else:
1155 else:
1156 return "%s..." % (text[:maxlength - 3])
1156 return "%s..." % (text[:maxlength - 3])
1157
1157
1158 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1158 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1159 '''yield every hg repository under path, recursively.'''
1159 '''yield every hg repository under path, recursively.'''
1160 def errhandler(err):
1160 def errhandler(err):
1161 if err.filename == path:
1161 if err.filename == path:
1162 raise err
1162 raise err
1163 if followsym and hasattr(os.path, 'samestat'):
1163 if followsym and hasattr(os.path, 'samestat'):
1164 def _add_dir_if_not_there(dirlst, dirname):
1164 def _add_dir_if_not_there(dirlst, dirname):
1165 match = False
1165 match = False
1166 samestat = os.path.samestat
1166 samestat = os.path.samestat
1167 dirstat = os.stat(dirname)
1167 dirstat = os.stat(dirname)
1168 for lstdirstat in dirlst:
1168 for lstdirstat in dirlst:
1169 if samestat(dirstat, lstdirstat):
1169 if samestat(dirstat, lstdirstat):
1170 match = True
1170 match = True
1171 break
1171 break
1172 if not match:
1172 if not match:
1173 dirlst.append(dirstat)
1173 dirlst.append(dirstat)
1174 return not match
1174 return not match
1175 else:
1175 else:
1176 followsym = False
1176 followsym = False
1177
1177
1178 if (seen_dirs is None) and followsym:
1178 if (seen_dirs is None) and followsym:
1179 seen_dirs = []
1179 seen_dirs = []
1180 _add_dir_if_not_there(seen_dirs, path)
1180 _add_dir_if_not_there(seen_dirs, path)
1181 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1181 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1182 dirs.sort()
1182 dirs.sort()
1183 if '.hg' in dirs:
1183 if '.hg' in dirs:
1184 yield root # found a repository
1184 yield root # found a repository
1185 qroot = os.path.join(root, '.hg', 'patches')
1185 qroot = os.path.join(root, '.hg', 'patches')
1186 if os.path.isdir(os.path.join(qroot, '.hg')):
1186 if os.path.isdir(os.path.join(qroot, '.hg')):
1187 yield qroot # we have a patch queue repo here
1187 yield qroot # we have a patch queue repo here
1188 if recurse:
1188 if recurse:
1189 # avoid recursing inside the .hg directory
1189 # avoid recursing inside the .hg directory
1190 dirs.remove('.hg')
1190 dirs.remove('.hg')
1191 else:
1191 else:
1192 dirs[:] = [] # don't descend further
1192 dirs[:] = [] # don't descend further
1193 elif followsym:
1193 elif followsym:
1194 newdirs = []
1194 newdirs = []
1195 for d in dirs:
1195 for d in dirs:
1196 fname = os.path.join(root, d)
1196 fname = os.path.join(root, d)
1197 if _add_dir_if_not_there(seen_dirs, fname):
1197 if _add_dir_if_not_there(seen_dirs, fname):
1198 if os.path.islink(fname):
1198 if os.path.islink(fname):
1199 for hgname in walkrepos(fname, True, seen_dirs):
1199 for hgname in walkrepos(fname, True, seen_dirs):
1200 yield hgname
1200 yield hgname
1201 else:
1201 else:
1202 newdirs.append(d)
1202 newdirs.append(d)
1203 dirs[:] = newdirs
1203 dirs[:] = newdirs
1204
1204
1205 _rcpath = None
1205 _rcpath = None
1206
1206
1207 def os_rcpath():
1207 def os_rcpath():
1208 '''return default os-specific hgrc search path'''
1208 '''return default os-specific hgrc search path'''
1209 path = system_rcpath()
1209 path = system_rcpath()
1210 path.extend(user_rcpath())
1210 path.extend(user_rcpath())
1211 path = [os.path.normpath(f) for f in path]
1211 path = [os.path.normpath(f) for f in path]
1212 return path
1212 return path
1213
1213
1214 def rcpath():
1214 def rcpath():
1215 '''return hgrc search path. if env var HGRCPATH is set, use it.
1215 '''return hgrc search path. if env var HGRCPATH is set, use it.
1216 for each item in path, if directory, use files ending in .rc,
1216 for each item in path, if directory, use files ending in .rc,
1217 else use item.
1217 else use item.
1218 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1218 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1219 if no HGRCPATH, use default os-specific path.'''
1219 if no HGRCPATH, use default os-specific path.'''
1220 global _rcpath
1220 global _rcpath
1221 if _rcpath is None:
1221 if _rcpath is None:
1222 if 'HGRCPATH' in os.environ:
1222 if 'HGRCPATH' in os.environ:
1223 _rcpath = []
1223 _rcpath = []
1224 for p in os.environ['HGRCPATH'].split(os.pathsep):
1224 for p in os.environ['HGRCPATH'].split(os.pathsep):
1225 if not p:
1225 if not p:
1226 continue
1226 continue
1227 p = expandpath(p)
1227 p = expandpath(p)
1228 if os.path.isdir(p):
1228 if os.path.isdir(p):
1229 for f, kind in osutil.listdir(p):
1229 for f, kind in osutil.listdir(p):
1230 if f.endswith('.rc'):
1230 if f.endswith('.rc'):
1231 _rcpath.append(os.path.join(p, f))
1231 _rcpath.append(os.path.join(p, f))
1232 else:
1232 else:
1233 _rcpath.append(p)
1233 _rcpath.append(p)
1234 else:
1234 else:
1235 _rcpath = os_rcpath()
1235 _rcpath = os_rcpath()
1236 return _rcpath
1236 return _rcpath
1237
1237
1238 def bytecount(nbytes):
1238 def bytecount(nbytes):
1239 '''return byte count formatted as readable string, with units'''
1239 '''return byte count formatted as readable string, with units'''
1240
1240
1241 units = (
1241 units = (
1242 (100, 1 << 30, _('%.0f GB')),
1242 (100, 1 << 30, _('%.0f GB')),
1243 (10, 1 << 30, _('%.1f GB')),
1243 (10, 1 << 30, _('%.1f GB')),
1244 (1, 1 << 30, _('%.2f GB')),
1244 (1, 1 << 30, _('%.2f GB')),
1245 (100, 1 << 20, _('%.0f MB')),
1245 (100, 1 << 20, _('%.0f MB')),
1246 (10, 1 << 20, _('%.1f MB')),
1246 (10, 1 << 20, _('%.1f MB')),
1247 (1, 1 << 20, _('%.2f MB')),
1247 (1, 1 << 20, _('%.2f MB')),
1248 (100, 1 << 10, _('%.0f KB')),
1248 (100, 1 << 10, _('%.0f KB')),
1249 (10, 1 << 10, _('%.1f KB')),
1249 (10, 1 << 10, _('%.1f KB')),
1250 (1, 1 << 10, _('%.2f KB')),
1250 (1, 1 << 10, _('%.2f KB')),
1251 (1, 1, _('%.0f bytes')),
1251 (1, 1, _('%.0f bytes')),
1252 )
1252 )
1253
1253
1254 for multiplier, divisor, format in units:
1254 for multiplier, divisor, format in units:
1255 if nbytes >= divisor * multiplier:
1255 if nbytes >= divisor * multiplier:
1256 return format % (nbytes / float(divisor))
1256 return format % (nbytes / float(divisor))
1257 return units[-1][2] % nbytes
1257 return units[-1][2] % nbytes
1258
1258
1259 def drop_scheme(scheme, path):
1259 def drop_scheme(scheme, path):
1260 sc = scheme + ':'
1260 sc = scheme + ':'
1261 if path.startswith(sc):
1261 if path.startswith(sc):
1262 path = path[len(sc):]
1262 path = path[len(sc):]
1263 if path.startswith('//'):
1263 if path.startswith('//'):
1264 if scheme == 'file':
1264 if scheme == 'file':
1265 i = path.find('/', 2)
1265 i = path.find('/', 2)
1266 if i == -1:
1266 if i == -1:
1267 return ''
1267 return ''
1268 # On Windows, absolute paths are rooted at the current drive
1268 # On Windows, absolute paths are rooted at the current drive
1269 # root. On POSIX they are rooted at the file system root.
1269 # root. On POSIX they are rooted at the file system root.
1270 if os.name == 'nt':
1270 if os.name == 'nt':
1271 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1271 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1272 path = os.path.join(droot, path[i + 1:])
1272 path = os.path.join(droot, path[i + 1:])
1273 else:
1273 else:
1274 path = path[i:]
1274 path = path[i:]
1275 else:
1275 else:
1276 path = path[2:]
1276 path = path[2:]
1277 return path
1277 return path
1278
1278
1279 def uirepr(s):
1279 def uirepr(s):
1280 # Avoid double backslash in Windows path repr()
1280 # Avoid double backslash in Windows path repr()
1281 return repr(s).replace('\\\\', '\\')
1281 return repr(s).replace('\\\\', '\\')
1282
1282
1283 #### naming convention of below implementation follows 'textwrap' module
1283 #### naming convention of below implementation follows 'textwrap' module
1284
1284
1285 class MBTextWrapper(textwrap.TextWrapper):
1285 class MBTextWrapper(textwrap.TextWrapper):
1286 def __init__(self, **kwargs):
1286 def __init__(self, **kwargs):
1287 textwrap.TextWrapper.__init__(self, **kwargs)
1287 textwrap.TextWrapper.__init__(self, **kwargs)
1288
1288
1289 def _cutdown(self, str, space_left):
1289 def _cutdown(self, str, space_left):
1290 l = 0
1290 l = 0
1291 ucstr = unicode(str, encoding.encoding)
1291 ucstr = unicode(str, encoding.encoding)
1292 w = unicodedata.east_asian_width
1292 w = unicodedata.east_asian_width
1293 for i in xrange(len(ucstr)):
1293 for i in xrange(len(ucstr)):
1294 l += w(ucstr[i]) in 'WFA' and 2 or 1
1294 l += w(ucstr[i]) in 'WFA' and 2 or 1
1295 if space_left < l:
1295 if space_left < l:
1296 return (ucstr[:i].encode(encoding.encoding),
1296 return (ucstr[:i].encode(encoding.encoding),
1297 ucstr[i:].encode(encoding.encoding))
1297 ucstr[i:].encode(encoding.encoding))
1298 return str, ''
1298 return str, ''
1299
1299
1300 # ----------------------------------------
1300 # ----------------------------------------
1301 # overriding of base class
1301 # overriding of base class
1302
1302
1303 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1303 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1304 space_left = max(width - cur_len, 1)
1304 space_left = max(width - cur_len, 1)
1305
1305
1306 if self.break_long_words:
1306 if self.break_long_words:
1307 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1307 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1308 cur_line.append(cut)
1308 cur_line.append(cut)
1309 reversed_chunks[-1] = res
1309 reversed_chunks[-1] = res
1310 elif not cur_line:
1310 elif not cur_line:
1311 cur_line.append(reversed_chunks.pop())
1311 cur_line.append(reversed_chunks.pop())
1312
1312
1313 #### naming convention of above implementation follows 'textwrap' module
1313 #### naming convention of above implementation follows 'textwrap' module
1314
1314
1315 def wrap(line, width=None, initindent='', hangindent=''):
1315 def wrap(line, width=None, initindent='', hangindent=''):
1316 if width is None:
1316 if width is None:
1317 width = termwidth() - 2
1317 width = termwidth() - 2
1318 maxindent = max(len(hangindent), len(initindent))
1318 maxindent = max(len(hangindent), len(initindent))
1319 if width <= maxindent:
1319 if width <= maxindent:
1320 # adjust for weird terminal size
1320 # adjust for weird terminal size
1321 width = max(78, maxindent + 1)
1321 width = max(78, maxindent + 1)
1322 wrapper = MBTextWrapper(width=width,
1322 wrapper = MBTextWrapper(width=width,
1323 initial_indent=initindent,
1323 initial_indent=initindent,
1324 subsequent_indent=hangindent)
1324 subsequent_indent=hangindent)
1325 return wrapper.fill(line)
1325 return wrapper.fill(line)
1326
1326
1327 def iterlines(iterator):
1327 def iterlines(iterator):
1328 for chunk in iterator:
1328 for chunk in iterator:
1329 for line in chunk.splitlines():
1329 for line in chunk.splitlines():
1330 yield line
1330 yield line
1331
1331
1332 def expandpath(path):
1332 def expandpath(path):
1333 return os.path.expanduser(os.path.expandvars(path))
1333 return os.path.expanduser(os.path.expandvars(path))
1334
1334
1335 def hgcmd():
1335 def hgcmd():
1336 """Return the command used to execute current hg
1336 """Return the command used to execute current hg
1337
1337
1338 This is different from hgexecutable() because on Windows we want
1338 This is different from hgexecutable() because on Windows we want
1339 to avoid things opening new shell windows like batch files, so we
1339 to avoid things opening new shell windows like batch files, so we
1340 get either the python call or current executable.
1340 get either the python call or current executable.
1341 """
1341 """
1342 if main_is_frozen():
1342 if main_is_frozen():
1343 return [sys.executable]
1343 return [sys.executable]
1344 return gethgcmd()
1344 return gethgcmd()
1345
1345
1346 def rundetached(args, condfn):
1346 def rundetached(args, condfn):
1347 """Execute the argument list in a detached process.
1347 """Execute the argument list in a detached process.
1348
1348
1349 condfn is a callable which is called repeatedly and should return
1349 condfn is a callable which is called repeatedly and should return
1350 True once the child process is known to have started successfully.
1350 True once the child process is known to have started successfully.
1351 At this point, the child process PID is returned. If the child
1351 At this point, the child process PID is returned. If the child
1352 process fails to start or finishes before condfn() evaluates to
1352 process fails to start or finishes before condfn() evaluates to
1353 True, return -1.
1353 True, return -1.
1354 """
1354 """
1355 # Windows case is easier because the child process is either
1355 # Windows case is easier because the child process is either
1356 # successfully starting and validating the condition or exiting
1356 # successfully starting and validating the condition or exiting
1357 # on failure. We just poll on its PID. On Unix, if the child
1357 # on failure. We just poll on its PID. On Unix, if the child
1358 # process fails to start, it will be left in a zombie state until
1358 # process fails to start, it will be left in a zombie state until
1359 # the parent wait on it, which we cannot do since we expect a long
1359 # the parent wait on it, which we cannot do since we expect a long
1360 # running process on success. Instead we listen for SIGCHLD telling
1360 # running process on success. Instead we listen for SIGCHLD telling
1361 # us our child process terminated.
1361 # us our child process terminated.
1362 terminated = set()
1362 terminated = set()
1363 def handler(signum, frame):
1363 def handler(signum, frame):
1364 terminated.add(os.wait())
1364 terminated.add(os.wait())
1365 prevhandler = None
1365 prevhandler = None
1366 if hasattr(signal, 'SIGCHLD'):
1366 if hasattr(signal, 'SIGCHLD'):
1367 prevhandler = signal.signal(signal.SIGCHLD, handler)
1367 prevhandler = signal.signal(signal.SIGCHLD, handler)
1368 try:
1368 try:
1369 pid = spawndetached(args)
1369 pid = spawndetached(args)
1370 while not condfn():
1370 while not condfn():
1371 if ((pid in terminated or not testpid(pid))
1371 if ((pid in terminated or not testpid(pid))
1372 and not condfn()):
1372 and not condfn()):
1373 return -1
1373 return -1
1374 time.sleep(0.1)
1374 time.sleep(0.1)
1375 return pid
1375 return pid
1376 finally:
1376 finally:
1377 if prevhandler is not None:
1377 if prevhandler is not None:
1378 signal.signal(signal.SIGCHLD, prevhandler)
1378 signal.signal(signal.SIGCHLD, prevhandler)
1379
1379
1380 try:
1380 try:
1381 any, all = any, all
1381 any, all = any, all
1382 except NameError:
1382 except NameError:
1383 def any(iterable):
1383 def any(iterable):
1384 for i in iterable:
1384 for i in iterable:
1385 if i:
1385 if i:
1386 return True
1386 return True
1387 return False
1387 return False
1388
1388
1389 def all(iterable):
1389 def all(iterable):
1390 for i in iterable:
1390 for i in iterable:
1391 if not i:
1391 if not i:
1392 return False
1392 return False
1393 return True
1393 return True
1394
1394
1395 def termwidth():
1395 def termwidth():
1396 if 'COLUMNS' in os.environ:
1396 if 'COLUMNS' in os.environ:
1397 try:
1397 try:
1398 return int(os.environ['COLUMNS'])
1398 return int(os.environ['COLUMNS'])
1399 except ValueError:
1399 except ValueError:
1400 pass
1400 pass
1401 return termwidth_()
1401 return termwidth_()
1402
1402
1403 def interpolate(prefix, mapping, s, fn=None):
1403 def interpolate(prefix, mapping, s, fn=None):
1404 """Return the result of interpolating items in the mapping into string s.
1404 """Return the result of interpolating items in the mapping into string s.
1405
1405
1406 prefix is a single character string, or a two character string with
1406 prefix is a single character string, or a two character string with
1407 a backslash as the first character if the prefix needs to be escaped in
1407 a backslash as the first character if the prefix needs to be escaped in
1408 a regular expression.
1408 a regular expression.
1409
1409
1410 fn is an optional function that will be applied to the replacement text
1410 fn is an optional function that will be applied to the replacement text
1411 just before replacement.
1411 just before replacement.
1412 """
1412 """
1413 fn = fn or (lambda s: s)
1413 fn = fn or (lambda s: s)
1414 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1414 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1415 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1415 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1416
1416
1417 def getport(port):
1418 """Return the port for a given network service.
1419
1420 If port is an integer, it's returned as is. If it's a string, it's
1421 looked up using socket.getservbyname(). If there's no matching
1422 service, util.Abort is raised.
1423 """
1424 try:
1425 return int(port)
1426 except ValueError:
1427 pass
1428
1429 try:
1430 return socket.getservbyname(port)
1431 except socket.error:
1432 raise Abort(_("no port number associated with service '%s'") % port)
@@ -1,49 +1,56 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hgserve()
3 hgserve()
4 {
4 {
5 hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
5 hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
6 | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
6 | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
7 -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
7 -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
8 -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
8 -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
9 cat hg.pid >> "$DAEMON_PIDS"
9 cat hg.pid >> "$DAEMON_PIDS"
10 echo % errors
10 echo % errors
11 cat errors.log
11 cat errors.log
12 sleep 1
12 sleep 1
13 kill `cat hg.pid`
13 if [ "$KILLQUIETLY" = "Y" ]; then
14 kill `cat hg.pid` 2>/dev/null
15 else
16 kill `cat hg.pid`
17 fi
14 sleep 1
18 sleep 1
15 }
19 }
16
20
17 hg init test
21 hg init test
18 cd test
22 cd test
19
23
20 echo '[web]' > .hg/hgrc
24 echo '[web]' > .hg/hgrc
21 echo 'accesslog = access.log' >> .hg/hgrc
25 echo 'accesslog = access.log' >> .hg/hgrc
22 echo "port = $HGPORT1" >> .hg/hgrc
26 echo "port = $HGPORT1" >> .hg/hgrc
23
27
24 echo % Without -v
28 echo % Without -v
25 hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
29 hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
26 cat hg.pid >> "$DAEMON_PIDS"
30 cat hg.pid >> "$DAEMON_PIDS"
27 if [ -f access.log ]; then
31 if [ -f access.log ]; then
28 echo 'access log created - .hg/hgrc respected'
32 echo 'access log created - .hg/hgrc respected'
29 fi
33 fi
30 echo % errors
34 echo % errors
31 cat errors.log
35 cat errors.log
32
36
33 echo % With -v
37 echo % With -v
34 hgserve
38 hgserve
35
39
36 echo % With -v and -p HGPORT2
40 echo % With -v and -p HGPORT2
37 hgserve -p "$HGPORT2"
41 hgserve -p "$HGPORT2"
38
42
43 echo '% With -v and -p http (should fail)'
44 KILLQUIETLY=Y hgserve -p http
45
39 echo % With --prefix foo
46 echo % With --prefix foo
40 hgserve --prefix foo
47 hgserve --prefix foo
41
48
42 echo % With --prefix /foo
49 echo % With --prefix /foo
43 hgserve --prefix /foo
50 hgserve --prefix /foo
44
51
45 echo % With --prefix foo/
52 echo % With --prefix foo/
46 hgserve --prefix foo/
53 hgserve --prefix foo/
47
54
48 echo % With --prefix /foo/
55 echo % With --prefix /foo/
49 hgserve --prefix /foo/
56 hgserve --prefix /foo/
@@ -1,21 +1,25 b''
1 % Without -v
1 % Without -v
2 access log created - .hg/hgrc respected
2 access log created - .hg/hgrc respected
3 % errors
3 % errors
4 % With -v
4 % With -v
5 listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
5 listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
6 % errors
6 % errors
7 % With -v and -p HGPORT2
7 % With -v and -p HGPORT2
8 listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
8 listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
9 % errors
9 % errors
10 % With -v and -p http (should fail)
11 abort: cannot start server at 'localhost:80': Permission denied
12 abort: child process failed to start
13 % errors
10 % With --prefix foo
14 % With --prefix foo
11 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
15 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
12 % errors
16 % errors
13 % With --prefix /foo
17 % With --prefix /foo
14 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
18 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
15 % errors
19 % errors
16 % With --prefix foo/
20 % With --prefix foo/
17 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
21 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
18 % errors
22 % errors
19 % With --prefix /foo/
23 % With --prefix /foo/
20 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
24 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
21 % errors
25 % errors
General Comments 0
You need to be logged in to leave comments. Login now