##// END OF EJS Templates
merge with stable
Matt Mackall -
r16295:ba42eb72 merge default
parent child Browse files
Show More
@@ -1,215 +1,217
1 # git.py - git support for the convert extension
1 # git.py - git support for the convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
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 import os
8 import os
9 from mercurial import util
9 from mercurial import util
10 from mercurial.node import hex, nullid
10 from mercurial.node import hex, nullid
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 from common import NoRepo, commit, converter_source, checktool
13 from common import NoRepo, commit, converter_source, checktool
14
14
15 class convert_git(converter_source):
15 class convert_git(converter_source):
16 # Windows does not support GIT_DIR= construct while other systems
16 # Windows does not support GIT_DIR= construct while other systems
17 # cannot remove environment variable. Just assume none have
17 # cannot remove environment variable. Just assume none have
18 # both issues.
18 # both issues.
19 if util.safehasattr(os, 'unsetenv'):
19 if util.safehasattr(os, 'unsetenv'):
20 def gitopen(self, s, noerr=False):
20 def gitopen(self, s, noerr=False):
21 prevgitdir = os.environ.get('GIT_DIR')
21 prevgitdir = os.environ.get('GIT_DIR')
22 os.environ['GIT_DIR'] = self.path
22 os.environ['GIT_DIR'] = self.path
23 try:
23 try:
24 if noerr:
24 if noerr:
25 (stdin, stdout, stderr) = util.popen3(s)
25 (stdin, stdout, stderr) = util.popen3(s)
26 return stdout
26 return stdout
27 else:
27 else:
28 return util.popen(s, 'rb')
28 return util.popen(s, 'rb')
29 finally:
29 finally:
30 if prevgitdir is None:
30 if prevgitdir is None:
31 del os.environ['GIT_DIR']
31 del os.environ['GIT_DIR']
32 else:
32 else:
33 os.environ['GIT_DIR'] = prevgitdir
33 os.environ['GIT_DIR'] = prevgitdir
34 else:
34 else:
35 def gitopen(self, s, noerr=False):
35 def gitopen(self, s, noerr=False):
36 if noerr:
36 if noerr:
37 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
37 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
38 return so
38 return so
39 else:
39 else:
40 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
40 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
41
41
42 def gitread(self, s):
42 def gitread(self, s):
43 fh = self.gitopen(s)
43 fh = self.gitopen(s)
44 data = fh.read()
44 data = fh.read()
45 return data, fh.close()
45 return data, fh.close()
46
46
47 def __init__(self, ui, path, rev=None):
47 def __init__(self, ui, path, rev=None):
48 super(convert_git, self).__init__(ui, path, rev=rev)
48 super(convert_git, self).__init__(ui, path, rev=rev)
49
49
50 if os.path.isdir(path + "/.git"):
50 if os.path.isdir(path + "/.git"):
51 path += "/.git"
51 path += "/.git"
52 if not os.path.exists(path + "/objects"):
52 if not os.path.exists(path + "/objects"):
53 raise NoRepo(_("%s does not look like a Git repository") % path)
53 raise NoRepo(_("%s does not look like a Git repository") % path)
54
54
55 checktool('git', 'git')
55 checktool('git', 'git')
56
56
57 self.path = path
57 self.path = path
58
58
59 def getheads(self):
59 def getheads(self):
60 if not self.rev:
60 if not self.rev:
61 heads, ret = self.gitread('git rev-parse --branches --remotes')
61 heads, ret = self.gitread('git rev-parse --branches --remotes')
62 heads = heads.splitlines()
62 heads = heads.splitlines()
63 else:
63 else:
64 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
64 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
65 heads = [heads[:-1]]
65 heads = [heads[:-1]]
66 if ret:
66 if ret:
67 raise util.Abort(_('cannot retrieve git heads'))
67 raise util.Abort(_('cannot retrieve git heads'))
68 return heads
68 return heads
69
69
70 def catfile(self, rev, type):
70 def catfile(self, rev, type):
71 if rev == hex(nullid):
71 if rev == hex(nullid):
72 raise IOError()
72 raise IOError()
73 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
73 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
74 if ret:
74 if ret:
75 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
75 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
76 return data
76 return data
77
77
78 def getfile(self, name, rev):
78 def getfile(self, name, rev):
79 data = self.catfile(rev, "blob")
79 data = self.catfile(rev, "blob")
80 mode = self.modecache[(name, rev)]
80 mode = self.modecache[(name, rev)]
81 return data, mode
81 return data, mode
82
82
83 def getchanges(self, version):
83 def getchanges(self, version):
84 self.modecache = {}
84 self.modecache = {}
85 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
85 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
86 changes = []
86 changes = []
87 seen = set()
87 seen = set()
88 entry = None
88 entry = None
89 for l in fh.read().split('\x00'):
89 for l in fh.read().split('\x00'):
90 if not entry:
90 if not entry:
91 if not l.startswith(':'):
91 if not l.startswith(':'):
92 continue
92 continue
93 entry = l
93 entry = l
94 continue
94 continue
95 f = l
95 f = l
96 if f not in seen:
96 if f not in seen:
97 seen.add(f)
97 seen.add(f)
98 entry = entry.split()
98 entry = entry.split()
99 h = entry[3]
99 h = entry[3]
100 if entry[1] == '160000':
101 raise util.Abort('git submodules are not supported!')
100 p = (entry[1] == "100755")
102 p = (entry[1] == "100755")
101 s = (entry[1] == "120000")
103 s = (entry[1] == "120000")
102 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
104 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
103 changes.append((f, h))
105 changes.append((f, h))
104 entry = None
106 entry = None
105 if fh.close():
107 if fh.close():
106 raise util.Abort(_('cannot read changes in %s') % version)
108 raise util.Abort(_('cannot read changes in %s') % version)
107 return (changes, {})
109 return (changes, {})
108
110
109 def getcommit(self, version):
111 def getcommit(self, version):
110 c = self.catfile(version, "commit") # read the commit hash
112 c = self.catfile(version, "commit") # read the commit hash
111 end = c.find("\n\n")
113 end = c.find("\n\n")
112 message = c[end + 2:]
114 message = c[end + 2:]
113 message = self.recode(message)
115 message = self.recode(message)
114 l = c[:end].splitlines()
116 l = c[:end].splitlines()
115 parents = []
117 parents = []
116 author = committer = None
118 author = committer = None
117 for e in l[1:]:
119 for e in l[1:]:
118 n, v = e.split(" ", 1)
120 n, v = e.split(" ", 1)
119 if n == "author":
121 if n == "author":
120 p = v.split()
122 p = v.split()
121 tm, tz = p[-2:]
123 tm, tz = p[-2:]
122 author = " ".join(p[:-2])
124 author = " ".join(p[:-2])
123 if author[0] == "<": author = author[1:-1]
125 if author[0] == "<": author = author[1:-1]
124 author = self.recode(author)
126 author = self.recode(author)
125 if n == "committer":
127 if n == "committer":
126 p = v.split()
128 p = v.split()
127 tm, tz = p[-2:]
129 tm, tz = p[-2:]
128 committer = " ".join(p[:-2])
130 committer = " ".join(p[:-2])
129 if committer[0] == "<": committer = committer[1:-1]
131 if committer[0] == "<": committer = committer[1:-1]
130 committer = self.recode(committer)
132 committer = self.recode(committer)
131 if n == "parent":
133 if n == "parent":
132 parents.append(v)
134 parents.append(v)
133
135
134 if committer and committer != author:
136 if committer and committer != author:
135 message += "\ncommitter: %s\n" % committer
137 message += "\ncommitter: %s\n" % committer
136 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
138 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
137 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
139 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
138 date = tm + " " + str(tz)
140 date = tm + " " + str(tz)
139
141
140 c = commit(parents=parents, date=date, author=author, desc=message,
142 c = commit(parents=parents, date=date, author=author, desc=message,
141 rev=version)
143 rev=version)
142 return c
144 return c
143
145
144 def gettags(self):
146 def gettags(self):
145 tags = {}
147 tags = {}
146 alltags = {}
148 alltags = {}
147 fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
149 fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
148 prefix = 'refs/tags/'
150 prefix = 'refs/tags/'
149
151
150 # Build complete list of tags, both annotated and bare ones
152 # Build complete list of tags, both annotated and bare ones
151 for line in fh:
153 for line in fh:
152 line = line.strip()
154 line = line.strip()
153 node, tag = line.split(None, 1)
155 node, tag = line.split(None, 1)
154 if not tag.startswith(prefix):
156 if not tag.startswith(prefix):
155 continue
157 continue
156 alltags[tag[len(prefix):]] = node
158 alltags[tag[len(prefix):]] = node
157 if fh.close():
159 if fh.close():
158 raise util.Abort(_('cannot read tags from %s') % self.path)
160 raise util.Abort(_('cannot read tags from %s') % self.path)
159
161
160 # Filter out tag objects for annotated tag refs
162 # Filter out tag objects for annotated tag refs
161 for tag in alltags:
163 for tag in alltags:
162 if tag.endswith('^{}'):
164 if tag.endswith('^{}'):
163 tags[tag[:-3]] = alltags[tag]
165 tags[tag[:-3]] = alltags[tag]
164 else:
166 else:
165 if tag + '^{}' in alltags:
167 if tag + '^{}' in alltags:
166 continue
168 continue
167 else:
169 else:
168 tags[tag] = alltags[tag]
170 tags[tag] = alltags[tag]
169
171
170 return tags
172 return tags
171
173
172 def getchangedfiles(self, version, i):
174 def getchangedfiles(self, version, i):
173 changes = []
175 changes = []
174 if i is None:
176 if i is None:
175 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
177 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
176 for l in fh:
178 for l in fh:
177 if "\t" not in l:
179 if "\t" not in l:
178 continue
180 continue
179 m, f = l[:-1].split("\t")
181 m, f = l[:-1].split("\t")
180 changes.append(f)
182 changes.append(f)
181 else:
183 else:
182 fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
184 fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
183 % (version, version, i + 1))
185 % (version, version, i + 1))
184 changes = [f.rstrip('\n') for f in fh]
186 changes = [f.rstrip('\n') for f in fh]
185 if fh.close():
187 if fh.close():
186 raise util.Abort(_('cannot read changes in %s') % version)
188 raise util.Abort(_('cannot read changes in %s') % version)
187
189
188 return changes
190 return changes
189
191
190 def getbookmarks(self):
192 def getbookmarks(self):
191 bookmarks = {}
193 bookmarks = {}
192
194
193 # Interesting references in git are prefixed
195 # Interesting references in git are prefixed
194 prefix = 'refs/heads/'
196 prefix = 'refs/heads/'
195 prefixlen = len(prefix)
197 prefixlen = len(prefix)
196
198
197 # factor two commands
199 # factor two commands
198 gitcmd = { 'remote/': 'git ls-remote --heads origin',
200 gitcmd = { 'remote/': 'git ls-remote --heads origin',
199 '': 'git show-ref'}
201 '': 'git show-ref'}
200
202
201 # Origin heads
203 # Origin heads
202 for reftype in gitcmd:
204 for reftype in gitcmd:
203 try:
205 try:
204 fh = self.gitopen(gitcmd[reftype], noerr=True)
206 fh = self.gitopen(gitcmd[reftype], noerr=True)
205 for line in fh:
207 for line in fh:
206 line = line.strip()
208 line = line.strip()
207 rev, name = line.split(None, 1)
209 rev, name = line.split(None, 1)
208 if not name.startswith(prefix):
210 if not name.startswith(prefix):
209 continue
211 continue
210 name = '%s%s' % (reftype, name[prefixlen:])
212 name = '%s%s' % (reftype, name[prefixlen:])
211 bookmarks[name] = rev
213 bookmarks[name] = rev
212 except:
214 except:
213 pass
215 pass
214
216
215 return bookmarks
217 return bookmarks
@@ -1,741 +1,741
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching 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 i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
16 self.args = args
16 self.args = args
17 self.ui = ui
17 self.ui = ui
18 self.repo = repo
18 self.repo = repo
19
19
20 # input/output/error streams
20 # input/output/error streams
21 self.fin = fin
21 self.fin = fin
22 self.fout = fout
22 self.fout = fout
23 self.ferr = ferr
23 self.ferr = ferr
24
24
25 def run():
25 def run():
26 "run the command in sys.argv"
26 "run the command in sys.argv"
27 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
27 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28
28
29 def dispatch(req):
29 def dispatch(req):
30 "run the command specified in req.args"
30 "run the command specified in req.args"
31 if req.ferr:
31 if req.ferr:
32 ferr = req.ferr
32 ferr = req.ferr
33 elif req.ui:
33 elif req.ui:
34 ferr = req.ui.ferr
34 ferr = req.ui.ferr
35 else:
35 else:
36 ferr = sys.stderr
36 ferr = sys.stderr
37
37
38 try:
38 try:
39 if not req.ui:
39 if not req.ui:
40 req.ui = uimod.ui()
40 req.ui = uimod.ui()
41 if '--traceback' in req.args:
41 if '--traceback' in req.args:
42 req.ui.setconfig('ui', 'traceback', 'on')
42 req.ui.setconfig('ui', 'traceback', 'on')
43
43
44 # set ui streams from the request
44 # set ui streams from the request
45 if req.fin:
45 if req.fin:
46 req.ui.fin = req.fin
46 req.ui.fin = req.fin
47 if req.fout:
47 if req.fout:
48 req.ui.fout = req.fout
48 req.ui.fout = req.fout
49 if req.ferr:
49 if req.ferr:
50 req.ui.ferr = req.ferr
50 req.ui.ferr = req.ferr
51 except util.Abort, inst:
51 except util.Abort, inst:
52 ferr.write(_("abort: %s\n") % inst)
52 ferr.write(_("abort: %s\n") % inst)
53 if inst.hint:
53 if inst.hint:
54 ferr.write(_("(%s)\n") % inst.hint)
54 ferr.write(_("(%s)\n") % inst.hint)
55 return -1
55 return -1
56 except error.ParseError, inst:
56 except error.ParseError, inst:
57 if len(inst.args) > 1:
57 if len(inst.args) > 1:
58 ferr.write(_("hg: parse error at %s: %s\n") %
58 ferr.write(_("hg: parse error at %s: %s\n") %
59 (inst.args[1], inst.args[0]))
59 (inst.args[1], inst.args[0]))
60 else:
60 else:
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 return -1
62 return -1
63
63
64 return _runcatch(req)
64 return _runcatch(req)
65
65
66 def _runcatch(req):
66 def _runcatch(req):
67 def catchterm(*args):
67 def catchterm(*args):
68 raise error.SignalInterrupt
68 raise error.SignalInterrupt
69
69
70 ui = req.ui
70 ui = req.ui
71 try:
71 try:
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
73 num = getattr(signal, name, None)
73 num = getattr(signal, name, None)
74 if num:
74 if num:
75 signal.signal(num, catchterm)
75 signal.signal(num, catchterm)
76 except ValueError:
76 except ValueError:
77 pass # happens if called in a thread
77 pass # happens if called in a thread
78
78
79 try:
79 try:
80 try:
80 try:
81 # enter the debugger before command execution
81 # enter the debugger before command execution
82 if '--debugger' in req.args:
82 if '--debugger' in req.args:
83 ui.warn(_("entering debugger - "
83 ui.warn(_("entering debugger - "
84 "type c to continue starting hg or h for help\n"))
84 "type c to continue starting hg or h for help\n"))
85 pdb.set_trace()
85 pdb.set_trace()
86 try:
86 try:
87 return _dispatch(req)
87 return _dispatch(req)
88 finally:
88 finally:
89 ui.flush()
89 ui.flush()
90 except:
90 except:
91 # enter the debugger when we hit an exception
91 # enter the debugger when we hit an exception
92 if '--debugger' in req.args:
92 if '--debugger' in req.args:
93 traceback.print_exc()
93 traceback.print_exc()
94 pdb.post_mortem(sys.exc_info()[2])
94 pdb.post_mortem(sys.exc_info()[2])
95 ui.traceback()
95 ui.traceback()
96 raise
96 raise
97
97
98 # Global exception handling, alphabetically
98 # Global exception handling, alphabetically
99 # Mercurial-specific first, followed by built-in and library exceptions
99 # Mercurial-specific first, followed by built-in and library exceptions
100 except error.AmbiguousCommand, inst:
100 except error.AmbiguousCommand, inst:
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
102 (inst.args[0], " ".join(inst.args[1])))
102 (inst.args[0], " ".join(inst.args[1])))
103 except error.ParseError, inst:
103 except error.ParseError, inst:
104 if len(inst.args) > 1:
104 if len(inst.args) > 1:
105 ui.warn(_("hg: parse error at %s: %s\n") %
105 ui.warn(_("hg: parse error at %s: %s\n") %
106 (inst.args[1], inst.args[0]))
106 (inst.args[1], inst.args[0]))
107 else:
107 else:
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
109 return -1
109 return -1
110 except error.LockHeld, inst:
110 except error.LockHeld, inst:
111 if inst.errno == errno.ETIMEDOUT:
111 if inst.errno == errno.ETIMEDOUT:
112 reason = _('timed out waiting for lock held by %s') % inst.locker
112 reason = _('timed out waiting for lock held by %s') % inst.locker
113 else:
113 else:
114 reason = _('lock held by %s') % inst.locker
114 reason = _('lock held by %s') % inst.locker
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
116 except error.LockUnavailable, inst:
116 except error.LockUnavailable, inst:
117 ui.warn(_("abort: could not lock %s: %s\n") %
117 ui.warn(_("abort: could not lock %s: %s\n") %
118 (inst.desc or inst.filename, inst.strerror))
118 (inst.desc or inst.filename, inst.strerror))
119 except error.CommandError, inst:
119 except error.CommandError, inst:
120 if inst.args[0]:
120 if inst.args[0]:
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
122 commands.help_(ui, inst.args[0], full=False, command=True)
122 commands.help_(ui, inst.args[0], full=False, command=True)
123 else:
123 else:
124 ui.warn(_("hg: %s\n") % inst.args[1])
124 ui.warn(_("hg: %s\n") % inst.args[1])
125 commands.help_(ui, 'shortlist')
125 commands.help_(ui, 'shortlist')
126 except error.OutOfBandError, inst:
126 except error.OutOfBandError, inst:
127 ui.warn(_("abort: remote error:\n"))
127 ui.warn(_("abort: remote error:\n"))
128 ui.warn(''.join(inst.args))
128 ui.warn(''.join(inst.args))
129 except error.RepoError, inst:
129 except error.RepoError, inst:
130 ui.warn(_("abort: %s!\n") % inst)
130 ui.warn(_("abort: %s!\n") % inst)
131 if inst.hint:
131 if inst.hint:
132 ui.warn(_("(%s)\n") % inst.hint)
132 ui.warn(_("(%s)\n") % inst.hint)
133 except error.ResponseError, inst:
133 except error.ResponseError, inst:
134 ui.warn(_("abort: %s") % inst.args[0])
134 ui.warn(_("abort: %s") % inst.args[0])
135 if not isinstance(inst.args[1], basestring):
135 if not isinstance(inst.args[1], basestring):
136 ui.warn(" %r\n" % (inst.args[1],))
136 ui.warn(" %r\n" % (inst.args[1],))
137 elif not inst.args[1]:
137 elif not inst.args[1]:
138 ui.warn(_(" empty string\n"))
138 ui.warn(_(" empty string\n"))
139 else:
139 else:
140 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
140 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
141 except error.RevlogError, inst:
141 except error.RevlogError, inst:
142 ui.warn(_("abort: %s!\n") % inst)
142 ui.warn(_("abort: %s!\n") % inst)
143 except error.SignalInterrupt:
143 except error.SignalInterrupt:
144 ui.warn(_("killed!\n"))
144 ui.warn(_("killed!\n"))
145 except error.UnknownCommand, inst:
145 except error.UnknownCommand, inst:
146 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
146 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
147 try:
147 try:
148 # check if the command is in a disabled extension
148 # check if the command is in a disabled extension
149 # (but don't check for extensions themselves)
149 # (but don't check for extensions themselves)
150 commands.help_(ui, inst.args[0], unknowncmd=True)
150 commands.help_(ui, inst.args[0], unknowncmd=True)
151 except error.UnknownCommand:
151 except error.UnknownCommand:
152 commands.help_(ui, 'shortlist')
152 commands.help_(ui, 'shortlist')
153 except util.Abort, inst:
153 except util.Abort, inst:
154 ui.warn(_("abort: %s\n") % inst)
154 ui.warn(_("abort: %s\n") % inst)
155 if inst.hint:
155 if inst.hint:
156 ui.warn(_("(%s)\n") % inst.hint)
156 ui.warn(_("(%s)\n") % inst.hint)
157 except ImportError, inst:
157 except ImportError, inst:
158 ui.warn(_("abort: %s!\n") % inst)
158 ui.warn(_("abort: %s!\n") % inst)
159 m = str(inst).split()[-1]
159 m = str(inst).split()[-1]
160 if m in "mpatch bdiff".split():
160 if m in "mpatch bdiff".split():
161 ui.warn(_("(did you forget to compile extensions?)\n"))
161 ui.warn(_("(did you forget to compile extensions?)\n"))
162 elif m in "zlib".split():
162 elif m in "zlib".split():
163 ui.warn(_("(is your Python install correct?)\n"))
163 ui.warn(_("(is your Python install correct?)\n"))
164 except IOError, inst:
164 except IOError, inst:
165 if util.safehasattr(inst, "code"):
165 if util.safehasattr(inst, "code"):
166 ui.warn(_("abort: %s\n") % inst)
166 ui.warn(_("abort: %s\n") % inst)
167 elif util.safehasattr(inst, "reason"):
167 elif util.safehasattr(inst, "reason"):
168 try: # usually it is in the form (errno, strerror)
168 try: # usually it is in the form (errno, strerror)
169 reason = inst.reason.args[1]
169 reason = inst.reason.args[1]
170 except (AttributeError, IndexError):
170 except (AttributeError, IndexError):
171 # it might be anything, for example a string
171 # it might be anything, for example a string
172 reason = inst.reason
172 reason = inst.reason
173 ui.warn(_("abort: error: %s\n") % reason)
173 ui.warn(_("abort: error: %s\n") % reason)
174 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
174 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
175 if ui.debugflag:
175 if ui.debugflag:
176 ui.warn(_("broken pipe\n"))
176 ui.warn(_("broken pipe\n"))
177 elif getattr(inst, "strerror", None):
177 elif getattr(inst, "strerror", None):
178 if getattr(inst, "filename", None):
178 if getattr(inst, "filename", None):
179 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
179 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
180 else:
180 else:
181 ui.warn(_("abort: %s\n") % inst.strerror)
181 ui.warn(_("abort: %s\n") % inst.strerror)
182 else:
182 else:
183 raise
183 raise
184 except OSError, inst:
184 except OSError, inst:
185 if getattr(inst, "filename", None):
185 if getattr(inst, "filename", None):
186 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
186 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
187 else:
187 else:
188 ui.warn(_("abort: %s\n") % inst.strerror)
188 ui.warn(_("abort: %s\n") % inst.strerror)
189 except KeyboardInterrupt:
189 except KeyboardInterrupt:
190 try:
190 try:
191 ui.warn(_("interrupted!\n"))
191 ui.warn(_("interrupted!\n"))
192 except IOError, inst:
192 except IOError, inst:
193 if inst.errno == errno.EPIPE:
193 if inst.errno == errno.EPIPE:
194 if ui.debugflag:
194 if ui.debugflag:
195 ui.warn(_("\nbroken pipe\n"))
195 ui.warn(_("\nbroken pipe\n"))
196 else:
196 else:
197 raise
197 raise
198 except MemoryError:
198 except MemoryError:
199 ui.warn(_("abort: out of memory\n"))
199 ui.warn(_("abort: out of memory\n"))
200 except SystemExit, inst:
200 except SystemExit, inst:
201 # Commands shouldn't sys.exit directly, but give a return code.
201 # Commands shouldn't sys.exit directly, but give a return code.
202 # Just in case catch this and and pass exit code to caller.
202 # Just in case catch this and and pass exit code to caller.
203 return inst.code
203 return inst.code
204 except socket.error, inst:
204 except socket.error, inst:
205 ui.warn(_("abort: %s\n") % inst.args[-1])
205 ui.warn(_("abort: %s\n") % inst.args[-1])
206 except:
206 except:
207 ui.warn(_("** unknown exception encountered,"
207 ui.warn(_("** unknown exception encountered,"
208 " please report by visiting\n"))
208 " please report by visiting\n"))
209 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
209 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
210 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
210 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
211 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
211 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
212 % util.version())
212 % util.version())
213 ui.warn(_("** Extensions loaded: %s\n")
213 ui.warn(_("** Extensions loaded: %s\n")
214 % ", ".join([x[0] for x in extensions.extensions()]))
214 % ", ".join([x[0] for x in extensions.extensions()]))
215 raise
215 raise
216
216
217 return -1
217 return -1
218
218
219 def aliasargs(fn, givenargs):
219 def aliasargs(fn, givenargs):
220 args = getattr(fn, 'args', [])
220 args = getattr(fn, 'args', [])
221 if args and givenargs:
221 if args:
222 cmd = ' '.join(map(util.shellquote, args))
222 cmd = ' '.join(map(util.shellquote, args))
223
223
224 nums = []
224 nums = []
225 def replacer(m):
225 def replacer(m):
226 num = int(m.group(1)) - 1
226 num = int(m.group(1)) - 1
227 nums.append(num)
227 nums.append(num)
228 if num < len(givenargs):
228 if num < len(givenargs):
229 return givenargs[num]
229 return givenargs[num]
230 return ''
230 raise util.Abort(_('too few arguments for command alias'))
231 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
231 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
232 givenargs = [x for i, x in enumerate(givenargs)
232 givenargs = [x for i, x in enumerate(givenargs)
233 if i not in nums]
233 if i not in nums]
234 args = shlex.split(cmd)
234 args = shlex.split(cmd)
235 return args + givenargs
235 return args + givenargs
236
236
237 class cmdalias(object):
237 class cmdalias(object):
238 def __init__(self, name, definition, cmdtable):
238 def __init__(self, name, definition, cmdtable):
239 self.name = self.cmd = name
239 self.name = self.cmd = name
240 self.cmdname = ''
240 self.cmdname = ''
241 self.definition = definition
241 self.definition = definition
242 self.args = []
242 self.args = []
243 self.opts = []
243 self.opts = []
244 self.help = ''
244 self.help = ''
245 self.norepo = True
245 self.norepo = True
246 self.badalias = False
246 self.badalias = False
247
247
248 try:
248 try:
249 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
249 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
250 for alias, e in cmdtable.iteritems():
250 for alias, e in cmdtable.iteritems():
251 if e is entry:
251 if e is entry:
252 self.cmd = alias
252 self.cmd = alias
253 break
253 break
254 self.shadows = True
254 self.shadows = True
255 except error.UnknownCommand:
255 except error.UnknownCommand:
256 self.shadows = False
256 self.shadows = False
257
257
258 if not self.definition:
258 if not self.definition:
259 def fn(ui, *args):
259 def fn(ui, *args):
260 ui.warn(_("no definition for alias '%s'\n") % self.name)
260 ui.warn(_("no definition for alias '%s'\n") % self.name)
261 return 1
261 return 1
262 self.fn = fn
262 self.fn = fn
263 self.badalias = True
263 self.badalias = True
264 return
264 return
265
265
266 if self.definition.startswith('!'):
266 if self.definition.startswith('!'):
267 self.shell = True
267 self.shell = True
268 def fn(ui, *args):
268 def fn(ui, *args):
269 env = {'HG_ARGS': ' '.join((self.name,) + args)}
269 env = {'HG_ARGS': ' '.join((self.name,) + args)}
270 def _checkvar(m):
270 def _checkvar(m):
271 if m.groups()[0] == '$':
271 if m.groups()[0] == '$':
272 return m.group()
272 return m.group()
273 elif int(m.groups()[0]) <= len(args):
273 elif int(m.groups()[0]) <= len(args):
274 return m.group()
274 return m.group()
275 else:
275 else:
276 ui.debug("No argument found for substitution "
276 ui.debug("No argument found for substitution "
277 "of %i variable in alias '%s' definition."
277 "of %i variable in alias '%s' definition."
278 % (int(m.groups()[0]), self.name))
278 % (int(m.groups()[0]), self.name))
279 return ''
279 return ''
280 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
280 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
281 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
281 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
282 replace['0'] = self.name
282 replace['0'] = self.name
283 replace['@'] = ' '.join(args)
283 replace['@'] = ' '.join(args)
284 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
284 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
285 return util.system(cmd, environ=env, out=ui.fout)
285 return util.system(cmd, environ=env, out=ui.fout)
286 self.fn = fn
286 self.fn = fn
287 return
287 return
288
288
289 args = shlex.split(self.definition)
289 args = shlex.split(self.definition)
290 self.cmdname = cmd = args.pop(0)
290 self.cmdname = cmd = args.pop(0)
291 args = map(util.expandpath, args)
291 args = map(util.expandpath, args)
292
292
293 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
293 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
294 if _earlygetopt([invalidarg], args):
294 if _earlygetopt([invalidarg], args):
295 def fn(ui, *args):
295 def fn(ui, *args):
296 ui.warn(_("error in definition for alias '%s': %s may only "
296 ui.warn(_("error in definition for alias '%s': %s may only "
297 "be given on the command line\n")
297 "be given on the command line\n")
298 % (self.name, invalidarg))
298 % (self.name, invalidarg))
299 return 1
299 return 1
300
300
301 self.fn = fn
301 self.fn = fn
302 self.badalias = True
302 self.badalias = True
303 return
303 return
304
304
305 try:
305 try:
306 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
306 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
307 if len(tableentry) > 2:
307 if len(tableentry) > 2:
308 self.fn, self.opts, self.help = tableentry
308 self.fn, self.opts, self.help = tableentry
309 else:
309 else:
310 self.fn, self.opts = tableentry
310 self.fn, self.opts = tableentry
311
311
312 self.args = aliasargs(self.fn, args)
312 self.args = aliasargs(self.fn, args)
313 if cmd not in commands.norepo.split(' '):
313 if cmd not in commands.norepo.split(' '):
314 self.norepo = False
314 self.norepo = False
315 if self.help.startswith("hg " + cmd):
315 if self.help.startswith("hg " + cmd):
316 # drop prefix in old-style help lines so hg shows the alias
316 # drop prefix in old-style help lines so hg shows the alias
317 self.help = self.help[4 + len(cmd):]
317 self.help = self.help[4 + len(cmd):]
318 self.__doc__ = self.fn.__doc__
318 self.__doc__ = self.fn.__doc__
319
319
320 except error.UnknownCommand:
320 except error.UnknownCommand:
321 def fn(ui, *args):
321 def fn(ui, *args):
322 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
322 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
323 % (self.name, cmd))
323 % (self.name, cmd))
324 try:
324 try:
325 # check if the command is in a disabled extension
325 # check if the command is in a disabled extension
326 commands.help_(ui, cmd, unknowncmd=True)
326 commands.help_(ui, cmd, unknowncmd=True)
327 except error.UnknownCommand:
327 except error.UnknownCommand:
328 pass
328 pass
329 return 1
329 return 1
330 self.fn = fn
330 self.fn = fn
331 self.badalias = True
331 self.badalias = True
332 except error.AmbiguousCommand:
332 except error.AmbiguousCommand:
333 def fn(ui, *args):
333 def fn(ui, *args):
334 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
334 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
335 % (self.name, cmd))
335 % (self.name, cmd))
336 return 1
336 return 1
337 self.fn = fn
337 self.fn = fn
338 self.badalias = True
338 self.badalias = True
339
339
340 def __call__(self, ui, *args, **opts):
340 def __call__(self, ui, *args, **opts):
341 if self.shadows:
341 if self.shadows:
342 ui.debug("alias '%s' shadows command '%s'\n" %
342 ui.debug("alias '%s' shadows command '%s'\n" %
343 (self.name, self.cmdname))
343 (self.name, self.cmdname))
344
344
345 if util.safehasattr(self, 'shell'):
345 if util.safehasattr(self, 'shell'):
346 return self.fn(ui, *args, **opts)
346 return self.fn(ui, *args, **opts)
347 else:
347 else:
348 try:
348 try:
349 util.checksignature(self.fn)(ui, *args, **opts)
349 util.checksignature(self.fn)(ui, *args, **opts)
350 except error.SignatureError:
350 except error.SignatureError:
351 args = ' '.join([self.cmdname] + self.args)
351 args = ' '.join([self.cmdname] + self.args)
352 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
352 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
353 raise
353 raise
354
354
355 def addaliases(ui, cmdtable):
355 def addaliases(ui, cmdtable):
356 # aliases are processed after extensions have been loaded, so they
356 # aliases are processed after extensions have been loaded, so they
357 # may use extension commands. Aliases can also use other alias definitions,
357 # may use extension commands. Aliases can also use other alias definitions,
358 # but only if they have been defined prior to the current definition.
358 # but only if they have been defined prior to the current definition.
359 for alias, definition in ui.configitems('alias'):
359 for alias, definition in ui.configitems('alias'):
360 aliasdef = cmdalias(alias, definition, cmdtable)
360 aliasdef = cmdalias(alias, definition, cmdtable)
361
361
362 try:
362 try:
363 olddef = cmdtable[aliasdef.cmd][0]
363 olddef = cmdtable[aliasdef.cmd][0]
364 if olddef.definition == aliasdef.definition:
364 if olddef.definition == aliasdef.definition:
365 continue
365 continue
366 except (KeyError, AttributeError):
366 except (KeyError, AttributeError):
367 # definition might not exist or it might not be a cmdalias
367 # definition might not exist or it might not be a cmdalias
368 pass
368 pass
369
369
370 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
370 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
371 if aliasdef.norepo:
371 if aliasdef.norepo:
372 commands.norepo += ' %s' % alias
372 commands.norepo += ' %s' % alias
373
373
374 def _parse(ui, args):
374 def _parse(ui, args):
375 options = {}
375 options = {}
376 cmdoptions = {}
376 cmdoptions = {}
377
377
378 try:
378 try:
379 args = fancyopts.fancyopts(args, commands.globalopts, options)
379 args = fancyopts.fancyopts(args, commands.globalopts, options)
380 except fancyopts.getopt.GetoptError, inst:
380 except fancyopts.getopt.GetoptError, inst:
381 raise error.CommandError(None, inst)
381 raise error.CommandError(None, inst)
382
382
383 if args:
383 if args:
384 cmd, args = args[0], args[1:]
384 cmd, args = args[0], args[1:]
385 aliases, entry = cmdutil.findcmd(cmd, commands.table,
385 aliases, entry = cmdutil.findcmd(cmd, commands.table,
386 ui.config("ui", "strict"))
386 ui.config("ui", "strict"))
387 cmd = aliases[0]
387 cmd = aliases[0]
388 args = aliasargs(entry[0], args)
388 args = aliasargs(entry[0], args)
389 defaults = ui.config("defaults", cmd)
389 defaults = ui.config("defaults", cmd)
390 if defaults:
390 if defaults:
391 args = map(util.expandpath, shlex.split(defaults)) + args
391 args = map(util.expandpath, shlex.split(defaults)) + args
392 c = list(entry[1])
392 c = list(entry[1])
393 else:
393 else:
394 cmd = None
394 cmd = None
395 c = []
395 c = []
396
396
397 # combine global options into local
397 # combine global options into local
398 for o in commands.globalopts:
398 for o in commands.globalopts:
399 c.append((o[0], o[1], options[o[1]], o[3]))
399 c.append((o[0], o[1], options[o[1]], o[3]))
400
400
401 try:
401 try:
402 args = fancyopts.fancyopts(args, c, cmdoptions, True)
402 args = fancyopts.fancyopts(args, c, cmdoptions, True)
403 except fancyopts.getopt.GetoptError, inst:
403 except fancyopts.getopt.GetoptError, inst:
404 raise error.CommandError(cmd, inst)
404 raise error.CommandError(cmd, inst)
405
405
406 # separate global options back out
406 # separate global options back out
407 for o in commands.globalopts:
407 for o in commands.globalopts:
408 n = o[1]
408 n = o[1]
409 options[n] = cmdoptions[n]
409 options[n] = cmdoptions[n]
410 del cmdoptions[n]
410 del cmdoptions[n]
411
411
412 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
412 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
413
413
414 def _parseconfig(ui, config):
414 def _parseconfig(ui, config):
415 """parse the --config options from the command line"""
415 """parse the --config options from the command line"""
416 configs = []
416 configs = []
417
417
418 for cfg in config:
418 for cfg in config:
419 try:
419 try:
420 name, value = cfg.split('=', 1)
420 name, value = cfg.split('=', 1)
421 section, name = name.split('.', 1)
421 section, name = name.split('.', 1)
422 if not section or not name:
422 if not section or not name:
423 raise IndexError
423 raise IndexError
424 ui.setconfig(section, name, value)
424 ui.setconfig(section, name, value)
425 configs.append((section, name, value))
425 configs.append((section, name, value))
426 except (IndexError, ValueError):
426 except (IndexError, ValueError):
427 raise util.Abort(_('malformed --config option: %r '
427 raise util.Abort(_('malformed --config option: %r '
428 '(use --config section.name=value)') % cfg)
428 '(use --config section.name=value)') % cfg)
429
429
430 return configs
430 return configs
431
431
432 def _earlygetopt(aliases, args):
432 def _earlygetopt(aliases, args):
433 """Return list of values for an option (or aliases).
433 """Return list of values for an option (or aliases).
434
434
435 The values are listed in the order they appear in args.
435 The values are listed in the order they appear in args.
436 The options and values are removed from args.
436 The options and values are removed from args.
437 """
437 """
438 try:
438 try:
439 argcount = args.index("--")
439 argcount = args.index("--")
440 except ValueError:
440 except ValueError:
441 argcount = len(args)
441 argcount = len(args)
442 shortopts = [opt for opt in aliases if len(opt) == 2]
442 shortopts = [opt for opt in aliases if len(opt) == 2]
443 values = []
443 values = []
444 pos = 0
444 pos = 0
445 while pos < argcount:
445 while pos < argcount:
446 if args[pos] in aliases:
446 if args[pos] in aliases:
447 if pos + 1 >= argcount:
447 if pos + 1 >= argcount:
448 # ignore and let getopt report an error if there is no value
448 # ignore and let getopt report an error if there is no value
449 break
449 break
450 del args[pos]
450 del args[pos]
451 values.append(args.pop(pos))
451 values.append(args.pop(pos))
452 argcount -= 2
452 argcount -= 2
453 elif args[pos][:2] in shortopts:
453 elif args[pos][:2] in shortopts:
454 # short option can have no following space, e.g. hg log -Rfoo
454 # short option can have no following space, e.g. hg log -Rfoo
455 values.append(args.pop(pos)[2:])
455 values.append(args.pop(pos)[2:])
456 argcount -= 1
456 argcount -= 1
457 else:
457 else:
458 pos += 1
458 pos += 1
459 return values
459 return values
460
460
461 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
461 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
462 # run pre-hook, and abort if it fails
462 # run pre-hook, and abort if it fails
463 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
463 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
464 pats=cmdpats, opts=cmdoptions)
464 pats=cmdpats, opts=cmdoptions)
465 if ret:
465 if ret:
466 return ret
466 return ret
467 ret = _runcommand(ui, options, cmd, d)
467 ret = _runcommand(ui, options, cmd, d)
468 # run post-hook, passing command result
468 # run post-hook, passing command result
469 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
469 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
470 result=ret, pats=cmdpats, opts=cmdoptions)
470 result=ret, pats=cmdpats, opts=cmdoptions)
471 return ret
471 return ret
472
472
473 def _getlocal(ui, rpath):
473 def _getlocal(ui, rpath):
474 """Return (path, local ui object) for the given target path.
474 """Return (path, local ui object) for the given target path.
475
475
476 Takes paths in [cwd]/.hg/hgrc into account."
476 Takes paths in [cwd]/.hg/hgrc into account."
477 """
477 """
478 try:
478 try:
479 wd = os.getcwd()
479 wd = os.getcwd()
480 except OSError, e:
480 except OSError, e:
481 raise util.Abort(_("error getting current working directory: %s") %
481 raise util.Abort(_("error getting current working directory: %s") %
482 e.strerror)
482 e.strerror)
483 path = cmdutil.findrepo(wd) or ""
483 path = cmdutil.findrepo(wd) or ""
484 if not path:
484 if not path:
485 lui = ui
485 lui = ui
486 else:
486 else:
487 lui = ui.copy()
487 lui = ui.copy()
488 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
488 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
489
489
490 if rpath and rpath[-1]:
490 if rpath and rpath[-1]:
491 path = lui.expandpath(rpath[-1])
491 path = lui.expandpath(rpath[-1])
492 lui = ui.copy()
492 lui = ui.copy()
493 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
493 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
494
494
495 return path, lui
495 return path, lui
496
496
497 def _checkshellalias(lui, ui, args):
497 def _checkshellalias(lui, ui, args):
498 norepo = commands.norepo
498 norepo = commands.norepo
499 options = {}
499 options = {}
500
500
501 try:
501 try:
502 args = fancyopts.fancyopts(args, commands.globalopts, options)
502 args = fancyopts.fancyopts(args, commands.globalopts, options)
503 except fancyopts.getopt.GetoptError:
503 except fancyopts.getopt.GetoptError:
504 return
504 return
505
505
506 if not args:
506 if not args:
507 return
507 return
508
508
509 cmdtable = commands.table.copy()
509 cmdtable = commands.table.copy()
510 addaliases(lui, cmdtable)
510 addaliases(lui, cmdtable)
511
511
512 cmd = args[0]
512 cmd = args[0]
513 try:
513 try:
514 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
514 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
515 except (error.AmbiguousCommand, error.UnknownCommand):
515 except (error.AmbiguousCommand, error.UnknownCommand):
516 commands.norepo = norepo
516 commands.norepo = norepo
517 return
517 return
518
518
519 cmd = aliases[0]
519 cmd = aliases[0]
520 fn = entry[0]
520 fn = entry[0]
521
521
522 if cmd and util.safehasattr(fn, 'shell'):
522 if cmd and util.safehasattr(fn, 'shell'):
523 d = lambda: fn(ui, *args[1:])
523 d = lambda: fn(ui, *args[1:])
524 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
524 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
525
525
526 commands.norepo = norepo
526 commands.norepo = norepo
527
527
528 _loaded = set()
528 _loaded = set()
529 def _dispatch(req):
529 def _dispatch(req):
530 args = req.args
530 args = req.args
531 ui = req.ui
531 ui = req.ui
532
532
533 # read --config before doing anything else
533 # read --config before doing anything else
534 # (e.g. to change trust settings for reading .hg/hgrc)
534 # (e.g. to change trust settings for reading .hg/hgrc)
535 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
535 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
536
536
537 # check for cwd
537 # check for cwd
538 cwd = _earlygetopt(['--cwd'], args)
538 cwd = _earlygetopt(['--cwd'], args)
539 if cwd:
539 if cwd:
540 os.chdir(cwd[-1])
540 os.chdir(cwd[-1])
541
541
542 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
542 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
543 path, lui = _getlocal(ui, rpath)
543 path, lui = _getlocal(ui, rpath)
544
544
545 # Now that we're operating in the right directory/repository with
545 # Now that we're operating in the right directory/repository with
546 # the right config settings, check for shell aliases
546 # the right config settings, check for shell aliases
547 shellaliasfn = _checkshellalias(lui, ui, args)
547 shellaliasfn = _checkshellalias(lui, ui, args)
548 if shellaliasfn:
548 if shellaliasfn:
549 return shellaliasfn()
549 return shellaliasfn()
550
550
551 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
551 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
552 # reposetup. Programs like TortoiseHg will call _dispatch several
552 # reposetup. Programs like TortoiseHg will call _dispatch several
553 # times so we keep track of configured extensions in _loaded.
553 # times so we keep track of configured extensions in _loaded.
554 extensions.loadall(lui)
554 extensions.loadall(lui)
555 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
555 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
556 # Propagate any changes to lui.__class__ by extensions
556 # Propagate any changes to lui.__class__ by extensions
557 ui.__class__ = lui.__class__
557 ui.__class__ = lui.__class__
558
558
559 # (uisetup and extsetup are handled in extensions.loadall)
559 # (uisetup and extsetup are handled in extensions.loadall)
560
560
561 for name, module in exts:
561 for name, module in exts:
562 cmdtable = getattr(module, 'cmdtable', {})
562 cmdtable = getattr(module, 'cmdtable', {})
563 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
563 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
564 if overrides:
564 if overrides:
565 ui.warn(_("extension '%s' overrides commands: %s\n")
565 ui.warn(_("extension '%s' overrides commands: %s\n")
566 % (name, " ".join(overrides)))
566 % (name, " ".join(overrides)))
567 commands.table.update(cmdtable)
567 commands.table.update(cmdtable)
568 _loaded.add(name)
568 _loaded.add(name)
569
569
570 # (reposetup is handled in hg.repository)
570 # (reposetup is handled in hg.repository)
571
571
572 addaliases(lui, commands.table)
572 addaliases(lui, commands.table)
573
573
574 # check for fallback encoding
574 # check for fallback encoding
575 fallback = lui.config('ui', 'fallbackencoding')
575 fallback = lui.config('ui', 'fallbackencoding')
576 if fallback:
576 if fallback:
577 encoding.fallbackencoding = fallback
577 encoding.fallbackencoding = fallback
578
578
579 fullargs = args
579 fullargs = args
580 cmd, func, args, options, cmdoptions = _parse(lui, args)
580 cmd, func, args, options, cmdoptions = _parse(lui, args)
581
581
582 if options["config"]:
582 if options["config"]:
583 raise util.Abort(_("option --config may not be abbreviated!"))
583 raise util.Abort(_("option --config may not be abbreviated!"))
584 if options["cwd"]:
584 if options["cwd"]:
585 raise util.Abort(_("option --cwd may not be abbreviated!"))
585 raise util.Abort(_("option --cwd may not be abbreviated!"))
586 if options["repository"]:
586 if options["repository"]:
587 raise util.Abort(_(
587 raise util.Abort(_(
588 "option -R has to be separated from other options (e.g. not -qR) "
588 "option -R has to be separated from other options (e.g. not -qR) "
589 "and --repository may only be abbreviated as --repo!"))
589 "and --repository may only be abbreviated as --repo!"))
590
590
591 if options["encoding"]:
591 if options["encoding"]:
592 encoding.encoding = options["encoding"]
592 encoding.encoding = options["encoding"]
593 if options["encodingmode"]:
593 if options["encodingmode"]:
594 encoding.encodingmode = options["encodingmode"]
594 encoding.encodingmode = options["encodingmode"]
595 if options["time"]:
595 if options["time"]:
596 def get_times():
596 def get_times():
597 t = os.times()
597 t = os.times()
598 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
598 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
599 t = (t[0], t[1], t[2], t[3], time.clock())
599 t = (t[0], t[1], t[2], t[3], time.clock())
600 return t
600 return t
601 s = get_times()
601 s = get_times()
602 def print_time():
602 def print_time():
603 t = get_times()
603 t = get_times()
604 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
604 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
605 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
605 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
606 atexit.register(print_time)
606 atexit.register(print_time)
607
607
608 uis = set([ui, lui])
608 uis = set([ui, lui])
609
609
610 if req.repo:
610 if req.repo:
611 uis.add(req.repo.ui)
611 uis.add(req.repo.ui)
612
612
613 # copy configs that were passed on the cmdline (--config) to the repo ui
613 # copy configs that were passed on the cmdline (--config) to the repo ui
614 for cfg in cfgs:
614 for cfg in cfgs:
615 req.repo.ui.setconfig(*cfg)
615 req.repo.ui.setconfig(*cfg)
616
616
617 if options['verbose'] or options['debug'] or options['quiet']:
617 if options['verbose'] or options['debug'] or options['quiet']:
618 for opt in ('verbose', 'debug', 'quiet'):
618 for opt in ('verbose', 'debug', 'quiet'):
619 val = str(bool(options[opt]))
619 val = str(bool(options[opt]))
620 for ui_ in uis:
620 for ui_ in uis:
621 ui_.setconfig('ui', opt, val)
621 ui_.setconfig('ui', opt, val)
622
622
623 if options['traceback']:
623 if options['traceback']:
624 for ui_ in uis:
624 for ui_ in uis:
625 ui_.setconfig('ui', 'traceback', 'on')
625 ui_.setconfig('ui', 'traceback', 'on')
626
626
627 if options['noninteractive']:
627 if options['noninteractive']:
628 for ui_ in uis:
628 for ui_ in uis:
629 ui_.setconfig('ui', 'interactive', 'off')
629 ui_.setconfig('ui', 'interactive', 'off')
630
630
631 if cmdoptions.get('insecure', False):
631 if cmdoptions.get('insecure', False):
632 for ui_ in uis:
632 for ui_ in uis:
633 ui_.setconfig('web', 'cacerts', '')
633 ui_.setconfig('web', 'cacerts', '')
634
634
635 if options['version']:
635 if options['version']:
636 return commands.version_(ui)
636 return commands.version_(ui)
637 if options['help']:
637 if options['help']:
638 return commands.help_(ui, cmd)
638 return commands.help_(ui, cmd)
639 elif not cmd:
639 elif not cmd:
640 return commands.help_(ui, 'shortlist')
640 return commands.help_(ui, 'shortlist')
641
641
642 repo = None
642 repo = None
643 cmdpats = args[:]
643 cmdpats = args[:]
644 if cmd not in commands.norepo.split():
644 if cmd not in commands.norepo.split():
645 # use the repo from the request only if we don't have -R
645 # use the repo from the request only if we don't have -R
646 if not rpath and not cwd:
646 if not rpath and not cwd:
647 repo = req.repo
647 repo = req.repo
648
648
649 if repo:
649 if repo:
650 # set the descriptors of the repo ui to those of ui
650 # set the descriptors of the repo ui to those of ui
651 repo.ui.fin = ui.fin
651 repo.ui.fin = ui.fin
652 repo.ui.fout = ui.fout
652 repo.ui.fout = ui.fout
653 repo.ui.ferr = ui.ferr
653 repo.ui.ferr = ui.ferr
654 else:
654 else:
655 try:
655 try:
656 repo = hg.repository(ui, path=path)
656 repo = hg.repository(ui, path=path)
657 if not repo.local():
657 if not repo.local():
658 raise util.Abort(_("repository '%s' is not local") % path)
658 raise util.Abort(_("repository '%s' is not local") % path)
659 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
659 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
660 except error.RequirementError:
660 except error.RequirementError:
661 raise
661 raise
662 except error.RepoError:
662 except error.RepoError:
663 if cmd not in commands.optionalrepo.split():
663 if cmd not in commands.optionalrepo.split():
664 if args and not path: # try to infer -R from command args
664 if args and not path: # try to infer -R from command args
665 repos = map(cmdutil.findrepo, args)
665 repos = map(cmdutil.findrepo, args)
666 guess = repos[0]
666 guess = repos[0]
667 if guess and repos.count(guess) == len(repos):
667 if guess and repos.count(guess) == len(repos):
668 req.args = ['--repository', guess] + fullargs
668 req.args = ['--repository', guess] + fullargs
669 return _dispatch(req)
669 return _dispatch(req)
670 if not path:
670 if not path:
671 raise error.RepoError(_("no repository found in '%s'"
671 raise error.RepoError(_("no repository found in '%s'"
672 " (.hg not found)") % os.getcwd())
672 " (.hg not found)") % os.getcwd())
673 raise
673 raise
674 if repo:
674 if repo:
675 ui = repo.ui
675 ui = repo.ui
676 args.insert(0, repo)
676 args.insert(0, repo)
677 elif rpath:
677 elif rpath:
678 ui.warn(_("warning: --repository ignored\n"))
678 ui.warn(_("warning: --repository ignored\n"))
679
679
680 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
680 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
681 ui.log("command", msg + "\n")
681 ui.log("command", msg + "\n")
682 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
682 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
683 try:
683 try:
684 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
684 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
685 cmdpats, cmdoptions)
685 cmdpats, cmdoptions)
686 finally:
686 finally:
687 if repo and repo != req.repo:
687 if repo and repo != req.repo:
688 repo.close()
688 repo.close()
689
689
690 def _runcommand(ui, options, cmd, cmdfunc):
690 def _runcommand(ui, options, cmd, cmdfunc):
691 def checkargs():
691 def checkargs():
692 try:
692 try:
693 return cmdfunc()
693 return cmdfunc()
694 except error.SignatureError:
694 except error.SignatureError:
695 raise error.CommandError(cmd, _("invalid arguments"))
695 raise error.CommandError(cmd, _("invalid arguments"))
696
696
697 if options['profile']:
697 if options['profile']:
698 format = ui.config('profiling', 'format', default='text')
698 format = ui.config('profiling', 'format', default='text')
699 field = ui.config('profiling', 'sort', default='inlinetime')
699 field = ui.config('profiling', 'sort', default='inlinetime')
700 climit = ui.configint('profiling', 'nested', default=5)
700 climit = ui.configint('profiling', 'nested', default=5)
701
701
702 if not format in ['text', 'kcachegrind']:
702 if not format in ['text', 'kcachegrind']:
703 ui.warn(_("unrecognized profiling format '%s'"
703 ui.warn(_("unrecognized profiling format '%s'"
704 " - Ignored\n") % format)
704 " - Ignored\n") % format)
705 format = 'text'
705 format = 'text'
706
706
707 output = ui.config('profiling', 'output')
707 output = ui.config('profiling', 'output')
708
708
709 if output:
709 if output:
710 path = ui.expandpath(output)
710 path = ui.expandpath(output)
711 ostream = open(path, 'wb')
711 ostream = open(path, 'wb')
712 else:
712 else:
713 ostream = sys.stderr
713 ostream = sys.stderr
714
714
715 try:
715 try:
716 from mercurial import lsprof
716 from mercurial import lsprof
717 except ImportError:
717 except ImportError:
718 raise util.Abort(_(
718 raise util.Abort(_(
719 'lsprof not available - install from '
719 'lsprof not available - install from '
720 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
720 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
721 p = lsprof.Profiler()
721 p = lsprof.Profiler()
722 p.enable(subcalls=True)
722 p.enable(subcalls=True)
723 try:
723 try:
724 return checkargs()
724 return checkargs()
725 finally:
725 finally:
726 p.disable()
726 p.disable()
727
727
728 if format == 'kcachegrind':
728 if format == 'kcachegrind':
729 import lsprofcalltree
729 import lsprofcalltree
730 calltree = lsprofcalltree.KCacheGrind(p)
730 calltree = lsprofcalltree.KCacheGrind(p)
731 calltree.output(ostream)
731 calltree.output(ostream)
732 else:
732 else:
733 # format == 'text'
733 # format == 'text'
734 stats = lsprof.Stats(p.getstats())
734 stats = lsprof.Stats(p.getstats())
735 stats.sort(field)
735 stats.sort(field)
736 stats.pprint(limit=30, file=ostream, climit=climit)
736 stats.pprint(limit=30, file=ostream, climit=climit)
737
737
738 if output:
738 if output:
739 ostream.close()
739 ostream.close()
740 else:
740 else:
741 return checkargs()
741 return checkargs()
@@ -1,320 +1,321
1 """ Mercurial phases support code
1 """ Mercurial phases support code
2
2
3 ---
3 ---
4
4
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
6 Logilab SA <contact@logilab.fr>
6 Logilab SA <contact@logilab.fr>
7 Augie Fackler <durin42@gmail.com>
7 Augie Fackler <durin42@gmail.com>
8
8
9 This software may be used and distributed according to the terms of the
9 This software may be used and distributed according to the terms of the
10 GNU General Public License version 2 or any later version.
10 GNU General Public License version 2 or any later version.
11
11
12 ---
12 ---
13
13
14 This module implements most phase logic in mercurial.
14 This module implements most phase logic in mercurial.
15
15
16
16
17 Basic Concept
17 Basic Concept
18 =============
18 =============
19
19
20 A 'changeset phases' is an indicator that tells us how a changeset is
20 A 'changeset phases' is an indicator that tells us how a changeset is
21 manipulated and communicated. The details of each phase is described below,
21 manipulated and communicated. The details of each phase is described below,
22 here we describe the properties they have in common.
22 here we describe the properties they have in common.
23
23
24 Like bookmarks, phases are not stored in history and thus are not permanent and
24 Like bookmarks, phases are not stored in history and thus are not permanent and
25 leave no audit trail.
25 leave no audit trail.
26
26
27 First, no changeset can be in two phases at once. Phases are ordered, so they
27 First, no changeset can be in two phases at once. Phases are ordered, so they
28 can be considered from lowest to highest. The default, lowest phase is 'public'
28 can be considered from lowest to highest. The default, lowest phase is 'public'
29 - this is the normal phase of existing changesets. A child changeset can not be
29 - this is the normal phase of existing changesets. A child changeset can not be
30 in a lower phase than its parents.
30 in a lower phase than its parents.
31
31
32 These phases share a hierarchy of traits:
32 These phases share a hierarchy of traits:
33
33
34 immutable shared
34 immutable shared
35 public: X X
35 public: X X
36 draft: X
36 draft: X
37 secret:
37 secret:
38
38
39 local commits are draft by default
39 local commits are draft by default
40
40
41 Phase movement and exchange
41 Phase movement and exchange
42 ============================
42 ============================
43
43
44 Phase data are exchanged by pushkey on pull and push. Some server have a
44 Phase data are exchanged by pushkey on pull and push. Some server have a
45 publish option set, we call them publishing server. Pushing to such server make
45 publish option set, we call them publishing server. Pushing to such server make
46 draft changeset publish.
46 draft changeset publish.
47
47
48 A small list of fact/rules define the exchange of phase:
48 A small list of fact/rules define the exchange of phase:
49
49
50 * old client never changes server states
50 * old client never changes server states
51 * pull never changes server states
51 * pull never changes server states
52 * publish and old server csets are seen as public by client
52 * publish and old server csets are seen as public by client
53
53
54 * Any secret changeset seens in another repository is lowered to at least draft
54 * Any secret changeset seens in another repository is lowered to at least draft
55
55
56
56
57 Here is the final table summing up the 49 possible usecase of phase exchange:
57 Here is the final table summing up the 49 possible usecase of phase exchange:
58
58
59 server
59 server
60 old publish non-publish
60 old publish non-publish
61 N X N D P N D P
61 N X N D P N D P
62 old client
62 old client
63 pull
63 pull
64 N - X/X - X/D X/P - X/D X/P
64 N - X/X - X/D X/P - X/D X/P
65 X - X/X - X/D X/P - X/D X/P
65 X - X/X - X/D X/P - X/D X/P
66 push
66 push
67 X X/X X/X X/P X/P X/P X/D X/D X/P
67 X X/X X/X X/P X/P X/P X/D X/D X/P
68 new client
68 new client
69 pull
69 pull
70 N - P/X - P/D P/P - D/D P/P
70 N - P/X - P/D P/P - D/D P/P
71 D - P/X - P/D P/P - D/D P/P
71 D - P/X - P/D P/P - D/D P/P
72 P - P/X - P/D P/P - P/D P/P
72 P - P/X - P/D P/P - P/D P/P
73 push
73 push
74 D P/X P/X P/P P/P P/P D/D D/D P/P
74 D P/X P/X P/P P/P P/P D/D D/D P/P
75 P P/X P/X P/P P/P P/P P/P P/P P/P
75 P P/X P/X P/P P/P P/P P/P P/P P/P
76
76
77 Legend:
77 Legend:
78
78
79 A/B = final state on client / state on server
79 A/B = final state on client / state on server
80
80
81 * N = new/not present,
81 * N = new/not present,
82 * P = public,
82 * P = public,
83 * D = draft,
83 * D = draft,
84 * X = not tracked (ie: the old client or server has no internal way of
84 * X = not tracked (ie: the old client or server has no internal way of
85 recording the phase.)
85 recording the phase.)
86
86
87 passive = only pushes
87 passive = only pushes
88
88
89
89
90 A cell here can be read like this:
90 A cell here can be read like this:
91
91
92 "When a new client pushes a draft changeset (D) to a publishing server
92 "When a new client pushes a draft changeset (D) to a publishing server
93 where it's not present (N), it's marked public on both sides (P/P)."
93 where it's not present (N), it's marked public on both sides (P/P)."
94
94
95 Note: old client behave as publish server with Draft only content
95 Note: old client behave as publish server with Draft only content
96 - other people see it as public
96 - other people see it as public
97 - content is pushed as draft
97 - content is pushed as draft
98
98
99 """
99 """
100
100
101 import errno
101 import errno
102 from node import nullid, bin, hex, short
102 from node import nullid, bin, hex, short
103 from i18n import _
103 from i18n import _
104
104
105 allphases = public, draft, secret = range(3)
105 allphases = public, draft, secret = range(3)
106 trackedphases = allphases[1:]
106 trackedphases = allphases[1:]
107 phasenames = ['public', 'draft', 'secret']
107 phasenames = ['public', 'draft', 'secret']
108
108
109 def readroots(repo):
109 def readroots(repo):
110 """Read phase roots from disk"""
110 """Read phase roots from disk"""
111 roots = [set() for i in allphases]
111 roots = [set() for i in allphases]
112 try:
112 try:
113 f = repo.sopener('phaseroots')
113 f = repo.sopener('phaseroots')
114 try:
114 try:
115 for line in f:
115 for line in f:
116 phase, nh = line.strip().split()
116 phase, nh = line.strip().split()
117 roots[int(phase)].add(bin(nh))
117 roots[int(phase)].add(bin(nh))
118 finally:
118 finally:
119 f.close()
119 f.close()
120 except IOError, inst:
120 except IOError, inst:
121 if inst.errno != errno.ENOENT:
121 if inst.errno != errno.ENOENT:
122 raise
122 raise
123 for f in repo._phasedefaults:
123 for f in repo._phasedefaults:
124 roots = f(repo, roots)
124 roots = f(repo, roots)
125 repo._dirtyphases = True
125 repo._dirtyphases = True
126 return roots
126 return roots
127
127
128 def writeroots(repo):
128 def writeroots(repo):
129 """Write phase roots from disk"""
129 """Write phase roots from disk"""
130 f = repo.sopener('phaseroots', 'w', atomictemp=True)
130 f = repo.sopener('phaseroots', 'w', atomictemp=True)
131 try:
131 try:
132 for phase, roots in enumerate(repo._phaseroots):
132 for phase, roots in enumerate(repo._phaseroots):
133 for h in roots:
133 for h in roots:
134 f.write('%i %s\n' % (phase, hex(h)))
134 f.write('%i %s\n' % (phase, hex(h)))
135 repo._dirtyphases = False
135 repo._dirtyphases = False
136 finally:
136 finally:
137 f.close()
137 f.close()
138
138
139 def filterunknown(repo, phaseroots=None):
139 def filterunknown(repo, phaseroots=None):
140 """remove unknown nodes from the phase boundary
140 """remove unknown nodes from the phase boundary
141
141
142 no data is lost as unknown node only old data for their descentants
142 no data is lost as unknown node only old data for their descentants
143 """
143 """
144 if phaseroots is None:
144 if phaseroots is None:
145 phaseroots = repo._phaseroots
145 phaseroots = repo._phaseroots
146 nodemap = repo.changelog.nodemap # to filter unknown nodes
146 nodemap = repo.changelog.nodemap # to filter unknown nodes
147 for phase, nodes in enumerate(phaseroots):
147 for phase, nodes in enumerate(phaseroots):
148 missing = [node for node in nodes if node not in nodemap]
148 missing = [node for node in nodes if node not in nodemap]
149 if missing:
149 if missing:
150 for mnode in missing:
150 for mnode in missing:
151 msg = 'Removing unknown node %(n)s from %(p)i-phase boundary'
151 repo.ui.debug(
152 repo.ui.debug(msg, {'n': short(mnode), 'p': phase})
152 'removing unknown node %s from %i-phase boundary\n'
153 % (short(mnode), phase))
153 nodes.symmetric_difference_update(missing)
154 nodes.symmetric_difference_update(missing)
154 repo._dirtyphases = True
155 repo._dirtyphases = True
155
156
156 def advanceboundary(repo, targetphase, nodes):
157 def advanceboundary(repo, targetphase, nodes):
157 """Add nodes to a phase changing other nodes phases if necessary.
158 """Add nodes to a phase changing other nodes phases if necessary.
158
159
159 This function move boundary *forward* this means that all nodes are set
160 This function move boundary *forward* this means that all nodes are set
160 in the target phase or kept in a *lower* phase.
161 in the target phase or kept in a *lower* phase.
161
162
162 Simplify boundary to contains phase roots only."""
163 Simplify boundary to contains phase roots only."""
163 delroots = [] # set of root deleted by this path
164 delroots = [] # set of root deleted by this path
164 for phase in xrange(targetphase + 1, len(allphases)):
165 for phase in xrange(targetphase + 1, len(allphases)):
165 # filter nodes that are not in a compatible phase already
166 # filter nodes that are not in a compatible phase already
166 # XXX rev phase cache might have been invalidated by a previous loop
167 # XXX rev phase cache might have been invalidated by a previous loop
167 # XXX we need to be smarter here
168 # XXX we need to be smarter here
168 nodes = [n for n in nodes if repo[n].phase() >= phase]
169 nodes = [n for n in nodes if repo[n].phase() >= phase]
169 if not nodes:
170 if not nodes:
170 break # no roots to move anymore
171 break # no roots to move anymore
171 roots = repo._phaseroots[phase]
172 roots = repo._phaseroots[phase]
172 olds = roots.copy()
173 olds = roots.copy()
173 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
174 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
174 roots.clear()
175 roots.clear()
175 roots.update(ctx.node() for ctx in ctxs)
176 roots.update(ctx.node() for ctx in ctxs)
176 if olds != roots:
177 if olds != roots:
177 # invalidate cache (we probably could be smarter here
178 # invalidate cache (we probably could be smarter here
178 if '_phaserev' in vars(repo):
179 if '_phaserev' in vars(repo):
179 del repo._phaserev
180 del repo._phaserev
180 repo._dirtyphases = True
181 repo._dirtyphases = True
181 # some roots may need to be declared for lower phases
182 # some roots may need to be declared for lower phases
182 delroots.extend(olds - roots)
183 delroots.extend(olds - roots)
183 # declare deleted root in the target phase
184 # declare deleted root in the target phase
184 if targetphase != 0:
185 if targetphase != 0:
185 retractboundary(repo, targetphase, delroots)
186 retractboundary(repo, targetphase, delroots)
186
187
187
188
188 def retractboundary(repo, targetphase, nodes):
189 def retractboundary(repo, targetphase, nodes):
189 """Set nodes back to a phase changing other nodes phases if necessary.
190 """Set nodes back to a phase changing other nodes phases if necessary.
190
191
191 This function move boundary *backward* this means that all nodes are set
192 This function move boundary *backward* this means that all nodes are set
192 in the target phase or kept in a *higher* phase.
193 in the target phase or kept in a *higher* phase.
193
194
194 Simplify boundary to contains phase roots only."""
195 Simplify boundary to contains phase roots only."""
195 currentroots = repo._phaseroots[targetphase]
196 currentroots = repo._phaseroots[targetphase]
196 newroots = [n for n in nodes if repo[n].phase() < targetphase]
197 newroots = [n for n in nodes if repo[n].phase() < targetphase]
197 if newroots:
198 if newroots:
198 currentroots.update(newroots)
199 currentroots.update(newroots)
199 ctxs = repo.set('roots(%ln::)', currentroots)
200 ctxs = repo.set('roots(%ln::)', currentroots)
200 currentroots.intersection_update(ctx.node() for ctx in ctxs)
201 currentroots.intersection_update(ctx.node() for ctx in ctxs)
201 if '_phaserev' in vars(repo):
202 if '_phaserev' in vars(repo):
202 del repo._phaserev
203 del repo._phaserev
203 repo._dirtyphases = True
204 repo._dirtyphases = True
204
205
205
206
206 def listphases(repo):
207 def listphases(repo):
207 """List phases root for serialisation over pushkey"""
208 """List phases root for serialisation over pushkey"""
208 keys = {}
209 keys = {}
209 value = '%i' % draft
210 value = '%i' % draft
210 for root in repo._phaseroots[draft]:
211 for root in repo._phaseroots[draft]:
211 keys[hex(root)] = value
212 keys[hex(root)] = value
212
213
213 if repo.ui.configbool('phases', 'publish', True):
214 if repo.ui.configbool('phases', 'publish', True):
214 # Add an extra data to let remote know we are a publishing repo.
215 # Add an extra data to let remote know we are a publishing repo.
215 # Publishing repo can't just pretend they are old repo. When pushing to
216 # Publishing repo can't just pretend they are old repo. When pushing to
216 # a publishing repo, the client still need to push phase boundary
217 # a publishing repo, the client still need to push phase boundary
217 #
218 #
218 # Push do not only push changeset. It also push phase data. New
219 # Push do not only push changeset. It also push phase data. New
219 # phase data may apply to common changeset which won't be push (as they
220 # phase data may apply to common changeset which won't be push (as they
220 # are common). Here is a very simple example:
221 # are common). Here is a very simple example:
221 #
222 #
222 # 1) repo A push changeset X as draft to repo B
223 # 1) repo A push changeset X as draft to repo B
223 # 2) repo B make changeset X public
224 # 2) repo B make changeset X public
224 # 3) repo B push to repo A. X is not pushed but the data that X as now
225 # 3) repo B push to repo A. X is not pushed but the data that X as now
225 # public should
226 # public should
226 #
227 #
227 # The server can't handle it on it's own as it has no idea of client
228 # The server can't handle it on it's own as it has no idea of client
228 # phase data.
229 # phase data.
229 keys['publishing'] = 'True'
230 keys['publishing'] = 'True'
230 return keys
231 return keys
231
232
232 def pushphase(repo, nhex, oldphasestr, newphasestr):
233 def pushphase(repo, nhex, oldphasestr, newphasestr):
233 """List phases root for serialisation over pushkey"""
234 """List phases root for serialisation over pushkey"""
234 lock = repo.lock()
235 lock = repo.lock()
235 try:
236 try:
236 currentphase = repo[nhex].phase()
237 currentphase = repo[nhex].phase()
237 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
238 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
238 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
239 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
239 if currentphase == oldphase and newphase < oldphase:
240 if currentphase == oldphase and newphase < oldphase:
240 advanceboundary(repo, newphase, [bin(nhex)])
241 advanceboundary(repo, newphase, [bin(nhex)])
241 return 1
242 return 1
242 elif currentphase == newphase:
243 elif currentphase == newphase:
243 # raced, but got correct result
244 # raced, but got correct result
244 return 1
245 return 1
245 else:
246 else:
246 return 0
247 return 0
247 finally:
248 finally:
248 lock.release()
249 lock.release()
249
250
250 def visibleheads(repo):
251 def visibleheads(repo):
251 """return the set of visible head of this repo"""
252 """return the set of visible head of this repo"""
252 # XXX we want a cache on this
253 # XXX we want a cache on this
253 sroots = repo._phaseroots[secret]
254 sroots = repo._phaseroots[secret]
254 if sroots:
255 if sroots:
255 # XXX very slow revset. storing heads or secret "boundary" would help.
256 # XXX very slow revset. storing heads or secret "boundary" would help.
256 revset = repo.set('heads(not (%ln::))', sroots)
257 revset = repo.set('heads(not (%ln::))', sroots)
257
258
258 vheads = [ctx.node() for ctx in revset]
259 vheads = [ctx.node() for ctx in revset]
259 if not vheads:
260 if not vheads:
260 vheads.append(nullid)
261 vheads.append(nullid)
261 else:
262 else:
262 vheads = repo.heads()
263 vheads = repo.heads()
263 return vheads
264 return vheads
264
265
265 def analyzeremotephases(repo, subset, roots):
266 def analyzeremotephases(repo, subset, roots):
266 """Compute phases heads and root in a subset of node from root dict
267 """Compute phases heads and root in a subset of node from root dict
267
268
268 * subset is heads of the subset
269 * subset is heads of the subset
269 * roots is {<nodeid> => phase} mapping. key and value are string.
270 * roots is {<nodeid> => phase} mapping. key and value are string.
270
271
271 Accept unknown element input
272 Accept unknown element input
272 """
273 """
273 # build list from dictionary
274 # build list from dictionary
274 draftroots = []
275 draftroots = []
275 nodemap = repo.changelog.nodemap # to filter unknown nodes
276 nodemap = repo.changelog.nodemap # to filter unknown nodes
276 for nhex, phase in roots.iteritems():
277 for nhex, phase in roots.iteritems():
277 if nhex == 'publishing': # ignore data related to publish option
278 if nhex == 'publishing': # ignore data related to publish option
278 continue
279 continue
279 node = bin(nhex)
280 node = bin(nhex)
280 phase = int(phase)
281 phase = int(phase)
281 if phase == 0:
282 if phase == 0:
282 if node != nullid:
283 if node != nullid:
283 repo.ui.warn(_('ignoring inconsistent public root'
284 repo.ui.warn(_('ignoring inconsistent public root'
284 ' from remote: %s\n') % nhex)
285 ' from remote: %s\n') % nhex)
285 elif phase == 1:
286 elif phase == 1:
286 if node in nodemap:
287 if node in nodemap:
287 draftroots.append(node)
288 draftroots.append(node)
288 else:
289 else:
289 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
290 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n')
290 % (phase, nhex))
291 % (phase, nhex))
291 # compute heads
292 # compute heads
292 publicheads = newheads(repo, subset, draftroots)
293 publicheads = newheads(repo, subset, draftroots)
293 return publicheads, draftroots
294 return publicheads, draftroots
294
295
295 def newheads(repo, heads, roots):
296 def newheads(repo, heads, roots):
296 """compute new head of a subset minus another
297 """compute new head of a subset minus another
297
298
298 * `heads`: define the first subset
299 * `heads`: define the first subset
299 * `rroots`: define the second we substract to the first"""
300 * `rroots`: define the second we substract to the first"""
300 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
301 revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
301 heads, roots, roots, heads)
302 heads, roots, roots, heads)
302 return [c.node() for c in revset]
303 return [c.node() for c in revset]
303
304
304
305
305 def newcommitphase(ui):
306 def newcommitphase(ui):
306 """helper to get the target phase of new commit
307 """helper to get the target phase of new commit
307
308
308 Handle all possible values for the phases.new-commit options.
309 Handle all possible values for the phases.new-commit options.
309
310
310 """
311 """
311 v = ui.config('phases', 'new-commit', draft)
312 v = ui.config('phases', 'new-commit', draft)
312 try:
313 try:
313 return phasenames.index(v)
314 return phasenames.index(v)
314 except ValueError:
315 except ValueError:
315 try:
316 try:
316 return int(v)
317 return int(v)
317 except ValueError:
318 except ValueError:
318 msg = _("phases.new-commit: not a valid phase name ('%s')")
319 msg = _("phases.new-commit: not a valid phase name ('%s')")
319 raise error.ConfigError(msg % v)
320 raise error.ConfigError(msg % v)
320
321
@@ -1,410 +1,416
1 $ "$TESTDIR/hghave" system-sh || exit 80
1 $ "$TESTDIR/hghave" system-sh || exit 80
2
2
3 $ HGFOO=BAR; export HGFOO
3 $ HGFOO=BAR; export HGFOO
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [extensions]
5 > [extensions]
6 > graphlog=
6 > graphlog=
7 >
7 >
8 > [alias]
8 > [alias]
9 > # should clobber ci but not commit (issue2993)
9 > # should clobber ci but not commit (issue2993)
10 > ci = version
10 > ci = version
11 > myinit = init
11 > myinit = init
12 > cleanstatus = status -c
12 > cleanstatus = status -c
13 > unknown = bargle
13 > unknown = bargle
14 > ambiguous = s
14 > ambiguous = s
15 > recursive = recursive
15 > recursive = recursive
16 > nodefinition =
16 > nodefinition =
17 > no--cwd = status --cwd elsewhere
17 > no--cwd = status --cwd elsewhere
18 > no-R = status -R elsewhere
18 > no-R = status -R elsewhere
19 > no--repo = status --repo elsewhere
19 > no--repo = status --repo elsewhere
20 > no--repository = status --repository elsewhere
20 > no--repository = status --repository elsewhere
21 > mylog = log
21 > mylog = log
22 > lognull = log -r null
22 > lognull = log -r null
23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
25 > dln = lognull --debug
25 > dln = lognull --debug
26 > nousage = rollback
26 > nousage = rollback
27 > put = export -r 0 -o "\$FOO/%R.diff"
27 > put = export -r 0 -o "\$FOO/%R.diff"
28 > blank = !echo
28 > blank = !echo
29 > self = !echo '\$0'
29 > self = !echo '\$0'
30 > echo = !echo '\$@'
30 > echo = !echo '\$@'
31 > echo1 = !echo '\$1'
31 > echo1 = !echo '\$1'
32 > echo2 = !echo '\$2'
32 > echo2 = !echo '\$2'
33 > echo13 = !echo '\$1' '\$3'
33 > echo13 = !echo '\$1' '\$3'
34 > count = !hg log -r '\$@' --template='.' | wc -c | sed -e 's/ //g'
34 > count = !hg log -r '\$@' --template='.' | wc -c | sed -e 's/ //g'
35 > mcount = !hg log \$@ --template='.' | wc -c | sed -e 's/ //g'
35 > mcount = !hg log \$@ --template='.' | wc -c | sed -e 's/ //g'
36 > rt = root
36 > rt = root
37 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
37 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
38 > idalias = id
38 > idalias = id
39 > idaliaslong = id
39 > idaliaslong = id
40 > idaliasshell = !echo test
40 > idaliasshell = !echo test
41 > parentsshell1 = !echo one
41 > parentsshell1 = !echo one
42 > parentsshell2 = !echo two
42 > parentsshell2 = !echo two
43 > escaped1 = !echo 'test\$\$test'
43 > escaped1 = !echo 'test\$\$test'
44 > escaped2 = !echo "HGFOO is \$\$HGFOO"
44 > escaped2 = !echo "HGFOO is \$\$HGFOO"
45 > escaped3 = !echo "\$1 is \$\$\$1"
45 > escaped3 = !echo "\$1 is \$\$\$1"
46 > escaped4 = !echo '\$\$0' '\$\$@'
46 > escaped4 = !echo '\$\$0' '\$\$@'
47 >
47 >
48 > [defaults]
48 > [defaults]
49 > mylog = -q
49 > mylog = -q
50 > lognull = -q
50 > lognull = -q
51 > log = -v
51 > log = -v
52 > EOF
52 > EOF
53
53
54
54
55 basic
55 basic
56
56
57 $ hg myinit alias
57 $ hg myinit alias
58
58
59
59
60 unknown
60 unknown
61
61
62 $ hg unknown
62 $ hg unknown
63 alias 'unknown' resolves to unknown command 'bargle'
63 alias 'unknown' resolves to unknown command 'bargle'
64 $ hg help unknown
64 $ hg help unknown
65 alias 'unknown' resolves to unknown command 'bargle'
65 alias 'unknown' resolves to unknown command 'bargle'
66
66
67
67
68 ambiguous
68 ambiguous
69
69
70 $ hg ambiguous
70 $ hg ambiguous
71 alias 'ambiguous' resolves to ambiguous command 's'
71 alias 'ambiguous' resolves to ambiguous command 's'
72 $ hg help ambiguous
72 $ hg help ambiguous
73 alias 'ambiguous' resolves to ambiguous command 's'
73 alias 'ambiguous' resolves to ambiguous command 's'
74
74
75
75
76 recursive
76 recursive
77
77
78 $ hg recursive
78 $ hg recursive
79 alias 'recursive' resolves to unknown command 'recursive'
79 alias 'recursive' resolves to unknown command 'recursive'
80 $ hg help recursive
80 $ hg help recursive
81 alias 'recursive' resolves to unknown command 'recursive'
81 alias 'recursive' resolves to unknown command 'recursive'
82
82
83
83
84 no definition
84 no definition
85
85
86 $ hg nodef
86 $ hg nodef
87 no definition for alias 'nodefinition'
87 no definition for alias 'nodefinition'
88 $ hg help nodef
88 $ hg help nodef
89 no definition for alias 'nodefinition'
89 no definition for alias 'nodefinition'
90
90
91
91
92 invalid options
92 invalid options
93
93
94 $ hg no--cwd
94 $ hg no--cwd
95 error in definition for alias 'no--cwd': --cwd may only be given on the command line
95 error in definition for alias 'no--cwd': --cwd may only be given on the command line
96 $ hg help no--cwd
96 $ hg help no--cwd
97 error in definition for alias 'no--cwd': --cwd may only be given on the command line
97 error in definition for alias 'no--cwd': --cwd may only be given on the command line
98 $ hg no-R
98 $ hg no-R
99 error in definition for alias 'no-R': -R may only be given on the command line
99 error in definition for alias 'no-R': -R may only be given on the command line
100 $ hg help no-R
100 $ hg help no-R
101 error in definition for alias 'no-R': -R may only be given on the command line
101 error in definition for alias 'no-R': -R may only be given on the command line
102 $ hg no--repo
102 $ hg no--repo
103 error in definition for alias 'no--repo': --repo may only be given on the command line
103 error in definition for alias 'no--repo': --repo may only be given on the command line
104 $ hg help no--repo
104 $ hg help no--repo
105 error in definition for alias 'no--repo': --repo may only be given on the command line
105 error in definition for alias 'no--repo': --repo may only be given on the command line
106 $ hg no--repository
106 $ hg no--repository
107 error in definition for alias 'no--repository': --repository may only be given on the command line
107 error in definition for alias 'no--repository': --repository may only be given on the command line
108 $ hg help no--repository
108 $ hg help no--repository
109 error in definition for alias 'no--repository': --repository may only be given on the command line
109 error in definition for alias 'no--repository': --repository may only be given on the command line
110
110
111 $ cd alias
111 $ cd alias
112
112
113
113
114 no usage
114 no usage
115
115
116 $ hg nousage
116 $ hg nousage
117 no rollback information available
117 no rollback information available
118
118
119 $ echo foo > foo
119 $ echo foo > foo
120 $ hg commit -Amfoo
120 $ hg commit -Amfoo
121 adding foo
121 adding foo
122
122
123
123
124 with opts
124 with opts
125
125
126 $ hg cleanst
126 $ hg cleanst
127 C foo
127 C foo
128
128
129
129
130 with opts and whitespace
130 with opts and whitespace
131
131
132 $ hg shortlog
132 $ hg shortlog
133 0 e63c23eaa88a | 1970-01-01 00:00 +0000
133 0 e63c23eaa88a | 1970-01-01 00:00 +0000
134
134
135 positional arguments
135 positional arguments
136
136
137 $ hg positional
138 abort: too few arguments for command alias
139 [255]
140 $ hg positional a
141 abort: too few arguments for command alias
142 [255]
137 $ hg positional 'node|short' rev
143 $ hg positional 'node|short' rev
138 0 e63c23eaa88a | 1970-01-01 00:00 +0000
144 0 e63c23eaa88a | 1970-01-01 00:00 +0000
139
145
140 interaction with defaults
146 interaction with defaults
141
147
142 $ hg mylog
148 $ hg mylog
143 0:e63c23eaa88a
149 0:e63c23eaa88a
144 $ hg lognull
150 $ hg lognull
145 -1:000000000000
151 -1:000000000000
146
152
147
153
148 properly recursive
154 properly recursive
149
155
150 $ hg dln
156 $ hg dln
151 changeset: -1:0000000000000000000000000000000000000000
157 changeset: -1:0000000000000000000000000000000000000000
152 parent: -1:0000000000000000000000000000000000000000
158 parent: -1:0000000000000000000000000000000000000000
153 parent: -1:0000000000000000000000000000000000000000
159 parent: -1:0000000000000000000000000000000000000000
154 manifest: -1:0000000000000000000000000000000000000000
160 manifest: -1:0000000000000000000000000000000000000000
155 user:
161 user:
156 date: Thu Jan 01 00:00:00 1970 +0000
162 date: Thu Jan 01 00:00:00 1970 +0000
157 extra: branch=default
163 extra: branch=default
158
164
159
165
160
166
161 path expanding
167 path expanding
162
168
163 $ FOO=`pwd` hg put
169 $ FOO=`pwd` hg put
164 $ cat 0.diff
170 $ cat 0.diff
165 # HG changeset patch
171 # HG changeset patch
166 # User test
172 # User test
167 # Date 0 0
173 # Date 0 0
168 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
174 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
169 # Parent 0000000000000000000000000000000000000000
175 # Parent 0000000000000000000000000000000000000000
170 foo
176 foo
171
177
172 diff -r 000000000000 -r e63c23eaa88a foo
178 diff -r 000000000000 -r e63c23eaa88a foo
173 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
179 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
174 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
180 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
175 @@ -0,0 +1,1 @@
181 @@ -0,0 +1,1 @@
176 +foo
182 +foo
177
183
178
184
179 simple shell aliases
185 simple shell aliases
180
186
181 $ hg blank
187 $ hg blank
182
188
183 $ hg blank foo
189 $ hg blank foo
184
190
185 $ hg self
191 $ hg self
186 self
192 self
187 $ hg echo
193 $ hg echo
188
194
189 $ hg echo foo
195 $ hg echo foo
190 foo
196 foo
191 $ hg echo 'test $2' foo
197 $ hg echo 'test $2' foo
192 test $2 foo
198 test $2 foo
193 $ hg echo1 foo bar baz
199 $ hg echo1 foo bar baz
194 foo
200 foo
195 $ hg echo2 foo bar baz
201 $ hg echo2 foo bar baz
196 bar
202 bar
197 $ hg echo13 foo bar baz test
203 $ hg echo13 foo bar baz test
198 foo baz
204 foo baz
199 $ hg echo2 foo
205 $ hg echo2 foo
200
206
201 $ echo bar > bar
207 $ echo bar > bar
202 $ hg commit -qA -m bar
208 $ hg commit -qA -m bar
203 $ hg count .
209 $ hg count .
204 1
210 1
205 $ hg count 'branch(default)'
211 $ hg count 'branch(default)'
206 2
212 2
207 $ hg mcount -r '"branch(default)"'
213 $ hg mcount -r '"branch(default)"'
208 2
214 2
209
215
210 $ hg tglog
216 $ hg tglog
211 @ 1:7e7f92de180e: 'bar'
217 @ 1:7e7f92de180e: 'bar'
212 |
218 |
213 o 0:e63c23eaa88a: 'foo'
219 o 0:e63c23eaa88a: 'foo'
214
220
215
221
216
222
217 shadowing
223 shadowing
218
224
219 $ hg i
225 $ hg i
220 hg: command 'i' is ambiguous:
226 hg: command 'i' is ambiguous:
221 idalias idaliaslong idaliasshell identify import incoming init
227 idalias idaliaslong idaliasshell identify import incoming init
222 [255]
228 [255]
223 $ hg id
229 $ hg id
224 7e7f92de180e tip
230 7e7f92de180e tip
225 $ hg ida
231 $ hg ida
226 hg: command 'ida' is ambiguous:
232 hg: command 'ida' is ambiguous:
227 idalias idaliaslong idaliasshell
233 idalias idaliaslong idaliasshell
228 [255]
234 [255]
229 $ hg idalias
235 $ hg idalias
230 7e7f92de180e tip
236 7e7f92de180e tip
231 $ hg idaliasl
237 $ hg idaliasl
232 7e7f92de180e tip
238 7e7f92de180e tip
233 $ hg idaliass
239 $ hg idaliass
234 test
240 test
235 $ hg parentsshell
241 $ hg parentsshell
236 hg: command 'parentsshell' is ambiguous:
242 hg: command 'parentsshell' is ambiguous:
237 parentsshell1 parentsshell2
243 parentsshell1 parentsshell2
238 [255]
244 [255]
239 $ hg parentsshell1
245 $ hg parentsshell1
240 one
246 one
241 $ hg parentsshell2
247 $ hg parentsshell2
242 two
248 two
243
249
244
250
245 shell aliases with global options
251 shell aliases with global options
246
252
247 $ hg init sub
253 $ hg init sub
248 $ cd sub
254 $ cd sub
249 $ hg count 'branch(default)'
255 $ hg count 'branch(default)'
250 0
256 0
251 $ hg -v count 'branch(default)'
257 $ hg -v count 'branch(default)'
252 0
258 0
253 $ hg -R .. count 'branch(default)'
259 $ hg -R .. count 'branch(default)'
254 0
260 0
255 $ hg --cwd .. count 'branch(default)'
261 $ hg --cwd .. count 'branch(default)'
256 2
262 2
257 $ hg echo --cwd ..
263 $ hg echo --cwd ..
258
264
259
265
260
266
261 repo specific shell aliases
267 repo specific shell aliases
262
268
263 $ cat >> .hg/hgrc <<EOF
269 $ cat >> .hg/hgrc <<EOF
264 > [alias]
270 > [alias]
265 > subalias = !echo sub \$@
271 > subalias = !echo sub \$@
266 > EOF
272 > EOF
267 $ cat >> ../.hg/hgrc <<EOF
273 $ cat >> ../.hg/hgrc <<EOF
268 > [alias]
274 > [alias]
269 > mainalias = !echo main \$@
275 > mainalias = !echo main \$@
270 > EOF
276 > EOF
271
277
272
278
273 shell alias defined in current repo
279 shell alias defined in current repo
274
280
275 $ hg subalias
281 $ hg subalias
276 sub
282 sub
277 $ hg --cwd .. subalias > /dev/null
283 $ hg --cwd .. subalias > /dev/null
278 hg: unknown command 'subalias'
284 hg: unknown command 'subalias'
279 [255]
285 [255]
280 $ hg -R .. subalias > /dev/null
286 $ hg -R .. subalias > /dev/null
281 hg: unknown command 'subalias'
287 hg: unknown command 'subalias'
282 [255]
288 [255]
283
289
284
290
285 shell alias defined in other repo
291 shell alias defined in other repo
286
292
287 $ hg mainalias > /dev/null
293 $ hg mainalias > /dev/null
288 hg: unknown command 'mainalias'
294 hg: unknown command 'mainalias'
289 [255]
295 [255]
290 $ hg -R .. mainalias
296 $ hg -R .. mainalias
291 main
297 main
292 $ hg --cwd .. mainalias
298 $ hg --cwd .. mainalias
293 main
299 main
294
300
295
301
296 shell aliases with escaped $ chars
302 shell aliases with escaped $ chars
297
303
298 $ hg escaped1
304 $ hg escaped1
299 test$test
305 test$test
300 $ hg escaped2
306 $ hg escaped2
301 HGFOO is BAR
307 HGFOO is BAR
302 $ hg escaped3 HGFOO
308 $ hg escaped3 HGFOO
303 HGFOO is BAR
309 HGFOO is BAR
304 $ hg escaped4 test
310 $ hg escaped4 test
305 $0 $@
311 $0 $@
306
312
307
313
308 invalid arguments
314 invalid arguments
309
315
310 $ hg rt foo
316 $ hg rt foo
311 hg rt: invalid arguments
317 hg rt: invalid arguments
312 hg rt
318 hg rt
313
319
314 alias for: hg root
320 alias for: hg root
315
321
316 use "hg help rt" to show the full help text
322 use "hg help rt" to show the full help text
317 [255]
323 [255]
318
324
319 invalid global arguments for normal commands, aliases, and shell aliases
325 invalid global arguments for normal commands, aliases, and shell aliases
320
326
321 $ hg --invalid root
327 $ hg --invalid root
322 hg: option --invalid not recognized
328 hg: option --invalid not recognized
323 Mercurial Distributed SCM
329 Mercurial Distributed SCM
324
330
325 basic commands:
331 basic commands:
326
332
327 add add the specified files on the next commit
333 add add the specified files on the next commit
328 annotate show changeset information by line for each file
334 annotate show changeset information by line for each file
329 clone make a copy of an existing repository
335 clone make a copy of an existing repository
330 commit commit the specified files or all outstanding changes
336 commit commit the specified files or all outstanding changes
331 diff diff repository (or selected files)
337 diff diff repository (or selected files)
332 export dump the header and diffs for one or more changesets
338 export dump the header and diffs for one or more changesets
333 forget forget the specified files on the next commit
339 forget forget the specified files on the next commit
334 init create a new repository in the given directory
340 init create a new repository in the given directory
335 log show revision history of entire repository or files
341 log show revision history of entire repository or files
336 merge merge working directory with another revision
342 merge merge working directory with another revision
337 phase set or show the current phase name
343 phase set or show the current phase name
338 pull pull changes from the specified source
344 pull pull changes from the specified source
339 push push changes to the specified destination
345 push push changes to the specified destination
340 remove remove the specified files on the next commit
346 remove remove the specified files on the next commit
341 serve start stand-alone webserver
347 serve start stand-alone webserver
342 status show changed files in the working directory
348 status show changed files in the working directory
343 summary summarize working directory state
349 summary summarize working directory state
344 update update working directory (or switch revisions)
350 update update working directory (or switch revisions)
345
351
346 use "hg help" for the full list of commands or "hg -v" for details
352 use "hg help" for the full list of commands or "hg -v" for details
347 [255]
353 [255]
348 $ hg --invalid mylog
354 $ hg --invalid mylog
349 hg: option --invalid not recognized
355 hg: option --invalid not recognized
350 Mercurial Distributed SCM
356 Mercurial Distributed SCM
351
357
352 basic commands:
358 basic commands:
353
359
354 add add the specified files on the next commit
360 add add the specified files on the next commit
355 annotate show changeset information by line for each file
361 annotate show changeset information by line for each file
356 clone make a copy of an existing repository
362 clone make a copy of an existing repository
357 commit commit the specified files or all outstanding changes
363 commit commit the specified files or all outstanding changes
358 diff diff repository (or selected files)
364 diff diff repository (or selected files)
359 export dump the header and diffs for one or more changesets
365 export dump the header and diffs for one or more changesets
360 forget forget the specified files on the next commit
366 forget forget the specified files on the next commit
361 init create a new repository in the given directory
367 init create a new repository in the given directory
362 log show revision history of entire repository or files
368 log show revision history of entire repository or files
363 merge merge working directory with another revision
369 merge merge working directory with another revision
364 phase set or show the current phase name
370 phase set or show the current phase name
365 pull pull changes from the specified source
371 pull pull changes from the specified source
366 push push changes to the specified destination
372 push push changes to the specified destination
367 remove remove the specified files on the next commit
373 remove remove the specified files on the next commit
368 serve start stand-alone webserver
374 serve start stand-alone webserver
369 status show changed files in the working directory
375 status show changed files in the working directory
370 summary summarize working directory state
376 summary summarize working directory state
371 update update working directory (or switch revisions)
377 update update working directory (or switch revisions)
372
378
373 use "hg help" for the full list of commands or "hg -v" for details
379 use "hg help" for the full list of commands or "hg -v" for details
374 [255]
380 [255]
375 $ hg --invalid blank
381 $ hg --invalid blank
376 hg: option --invalid not recognized
382 hg: option --invalid not recognized
377 Mercurial Distributed SCM
383 Mercurial Distributed SCM
378
384
379 basic commands:
385 basic commands:
380
386
381 add add the specified files on the next commit
387 add add the specified files on the next commit
382 annotate show changeset information by line for each file
388 annotate show changeset information by line for each file
383 clone make a copy of an existing repository
389 clone make a copy of an existing repository
384 commit commit the specified files or all outstanding changes
390 commit commit the specified files or all outstanding changes
385 diff diff repository (or selected files)
391 diff diff repository (or selected files)
386 export dump the header and diffs for one or more changesets
392 export dump the header and diffs for one or more changesets
387 forget forget the specified files on the next commit
393 forget forget the specified files on the next commit
388 init create a new repository in the given directory
394 init create a new repository in the given directory
389 log show revision history of entire repository or files
395 log show revision history of entire repository or files
390 merge merge working directory with another revision
396 merge merge working directory with another revision
391 phase set or show the current phase name
397 phase set or show the current phase name
392 pull pull changes from the specified source
398 pull pull changes from the specified source
393 push push changes to the specified destination
399 push push changes to the specified destination
394 remove remove the specified files on the next commit
400 remove remove the specified files on the next commit
395 serve start stand-alone webserver
401 serve start stand-alone webserver
396 status show changed files in the working directory
402 status show changed files in the working directory
397 summary summarize working directory state
403 summary summarize working directory state
398 update update working directory (or switch revisions)
404 update update working directory (or switch revisions)
399
405
400 use "hg help" for the full list of commands or "hg -v" for details
406 use "hg help" for the full list of commands or "hg -v" for details
401 [255]
407 [255]
402
408
403 This should show id:
409 This should show id:
404
410
405 $ hg --config alias.log='id' log
411 $ hg --config alias.log='id' log
406 000000000000 tip
412 000000000000 tip
407
413
408 This shouldn't:
414 This shouldn't:
409
415
410 $ hg --config alias.log='id' history
416 $ hg --config alias.log='id' history
General Comments 0
You need to be logged in to leave comments. Login now