##// END OF EJS Templates
hgk: fix parent breakage
Matt Mackall -
r6768:e3bb0053 default
parent child Browse files
Show More
@@ -1,355 +1,355 b''
1 # Minimal support for git commands on an hg repository
1 # Minimal support for git commands on an hg repository
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 '''browsing the repository in a graphical way
7 '''browsing the repository in a graphical way
8
8
9 The hgk extension allows browsing the history of a repository in a
9 The hgk extension allows browsing the history of a repository in a
10 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is
10 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is
11 not distributed with Mercurial.)
11 not distributed with Mercurial.)
12
12
13 hgk consists of two parts: a Tcl script that does the displaying and
13 hgk consists of two parts: a Tcl script that does the displaying and
14 querying of information, and an extension to mercurial named hgk.py,
14 querying of information, and an extension to mercurial named hgk.py,
15 which provides hooks for hgk to get information. hgk can be found in
15 which provides hooks for hgk to get information. hgk can be found in
16 the contrib directory, and hgk.py can be found in the hgext directory.
16 the contrib directory, and hgk.py can be found in the hgext directory.
17
17
18 To load the hgext.py extension, add it to your .hgrc file (you have
18 To load the hgext.py extension, add it to your .hgrc file (you have
19 to use your global $HOME/.hgrc file, not one in a repository). You
19 to use your global $HOME/.hgrc file, not one in a repository). You
20 can specify an absolute path:
20 can specify an absolute path:
21
21
22 [extensions]
22 [extensions]
23 hgk=/usr/local/lib/hgk.py
23 hgk=/usr/local/lib/hgk.py
24
24
25 Mercurial can also scan the default python library path for a file
25 Mercurial can also scan the default python library path for a file
26 named 'hgk.py' if you set hgk empty:
26 named 'hgk.py' if you set hgk empty:
27
27
28 [extensions]
28 [extensions]
29 hgk=
29 hgk=
30
30
31 The hg view command will launch the hgk Tcl script. For this command
31 The hg view command will launch the hgk Tcl script. For this command
32 to work, hgk must be in your search path. Alternately, you can
32 to work, hgk must be in your search path. Alternately, you can
33 specify the path to hgk in your .hgrc file:
33 specify the path to hgk in your .hgrc file:
34
34
35 [hgk]
35 [hgk]
36 path=/location/of/hgk
36 path=/location/of/hgk
37
37
38 hgk can make use of the extdiff extension to visualize revisions.
38 hgk can make use of the extdiff extension to visualize revisions.
39 Assuming you had already configured extdiff vdiff command, just add:
39 Assuming you had already configured extdiff vdiff command, just add:
40
40
41 [hgk]
41 [hgk]
42 vdiff=vdiff
42 vdiff=vdiff
43
43
44 Revisions context menu will now display additional entries to fire
44 Revisions context menu will now display additional entries to fire
45 vdiff on hovered and selected revisions.'''
45 vdiff on hovered and selected revisions.'''
46
46
47 import os
47 import os
48 from mercurial import commands, util, patch, revlog, cmdutil
48 from mercurial import commands, util, patch, revlog, cmdutil
49 from mercurial.node import nullid, nullrev, short
49 from mercurial.node import nullid, nullrev, short
50
50
51 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
51 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
52 """diff trees from two commits"""
52 """diff trees from two commits"""
53 def __difftree(repo, node1, node2, files=[]):
53 def __difftree(repo, node1, node2, files=[]):
54 assert node2 is not None
54 assert node2 is not None
55 mmap = repo[node1].manifest()
55 mmap = repo[node1].manifest()
56 mmap2 = repo[node2].manifest()
56 mmap2 = repo[node2].manifest()
57 m = cmdutil.match(repo, files)
57 m = cmdutil.match(repo, files)
58 modified, added, removed = repo.status(node1, node2, m)[:3]
58 modified, added, removed = repo.status(node1, node2, m)[:3]
59 empty = short(nullid)
59 empty = short(nullid)
60
60
61 for f in modified:
61 for f in modified:
62 # TODO get file permissions
62 # TODO get file permissions
63 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
63 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
64 (short(mmap[f]), short(mmap2[f]), f, f))
64 (short(mmap[f]), short(mmap2[f]), f, f))
65 for f in added:
65 for f in added:
66 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
66 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
67 (empty, short(mmap2[f]), f, f))
67 (empty, short(mmap2[f]), f, f))
68 for f in removed:
68 for f in removed:
69 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
69 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
70 (short(mmap[f]), empty, f, f))
70 (short(mmap[f]), empty, f, f))
71 ##
71 ##
72
72
73 while True:
73 while True:
74 if opts['stdin']:
74 if opts['stdin']:
75 try:
75 try:
76 line = raw_input().split(' ')
76 line = raw_input().split(' ')
77 node1 = line[0]
77 node1 = line[0]
78 if len(line) > 1:
78 if len(line) > 1:
79 node2 = line[1]
79 node2 = line[1]
80 else:
80 else:
81 node2 = None
81 node2 = None
82 except EOFError:
82 except EOFError:
83 break
83 break
84 node1 = repo.lookup(node1)
84 node1 = repo.lookup(node1)
85 if node2:
85 if node2:
86 node2 = repo.lookup(node2)
86 node2 = repo.lookup(node2)
87 else:
87 else:
88 node2 = node1
88 node2 = node1
89 node1 = repo.changelog.parents(node1)[0]
89 node1 = repo.changelog.parents(node1)[0]
90 if opts['patch']:
90 if opts['patch']:
91 if opts['pretty']:
91 if opts['pretty']:
92 catcommit(ui, repo, node2, "")
92 catcommit(ui, repo, node2, "")
93 m = cmdutil.match(repo, files)
93 m = cmdutil.match(repo, files)
94 patch.diff(repo, node1, node2, match=m,
94 patch.diff(repo, node1, node2, match=m,
95 opts=patch.diffopts(ui, {'git': True}))
95 opts=patch.diffopts(ui, {'git': True}))
96 else:
96 else:
97 __difftree(repo, node1, node2, files=files)
97 __difftree(repo, node1, node2, files=files)
98 if not opts['stdin']:
98 if not opts['stdin']:
99 break
99 break
100
100
101 def catcommit(ui, repo, n, prefix, ctx=None):
101 def catcommit(ui, repo, n, prefix, ctx=None):
102 nlprefix = '\n' + prefix;
102 nlprefix = '\n' + prefix;
103 if ctx is None:
103 if ctx is None:
104 ctx = repo[n]
104 ctx = repo[n]
105 (p1, p2) = ctx.parents()
106 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
105 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
107 if p1: ui.write("parent %s\n" % short(p1.node()))
106 for p in ctx.parents():
108 if p2: ui.write("parent %s\n" % short(p2.node()))
107 ui.write("parent %s\n" % p)
108
109 date = ctx.date()
109 date = ctx.date()
110 description = ctx.description().replace("\0", "")
110 description = ctx.description().replace("\0", "")
111 lines = description.splitlines()
111 lines = description.splitlines()
112 if lines and lines[-1].startswith('committer:'):
112 if lines and lines[-1].startswith('committer:'):
113 committer = lines[-1].split(': ')[1].rstrip()
113 committer = lines[-1].split(': ')[1].rstrip()
114 else:
114 else:
115 committer = ctx.user()
115 committer = ctx.user()
116
116
117 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
117 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
118 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
118 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
119 ui.write("revision %d\n" % ctx.rev())
119 ui.write("revision %d\n" % ctx.rev())
120 ui.write("branch %s\n\n" % ctx.branch())
120 ui.write("branch %s\n\n" % ctx.branch())
121
121
122 if prefix != "":
122 if prefix != "":
123 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
123 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
124 else:
124 else:
125 ui.write(description + "\n")
125 ui.write(description + "\n")
126 if prefix:
126 if prefix:
127 ui.write('\0')
127 ui.write('\0')
128
128
129 def base(ui, repo, node1, node2):
129 def base(ui, repo, node1, node2):
130 """Output common ancestor information"""
130 """Output common ancestor information"""
131 node1 = repo.lookup(node1)
131 node1 = repo.lookup(node1)
132 node2 = repo.lookup(node2)
132 node2 = repo.lookup(node2)
133 n = repo.changelog.ancestor(node1, node2)
133 n = repo.changelog.ancestor(node1, node2)
134 ui.write(short(n) + "\n")
134 ui.write(short(n) + "\n")
135
135
136 def catfile(ui, repo, type=None, r=None, **opts):
136 def catfile(ui, repo, type=None, r=None, **opts):
137 """cat a specific revision"""
137 """cat a specific revision"""
138 # in stdin mode, every line except the commit is prefixed with two
138 # in stdin mode, every line except the commit is prefixed with two
139 # spaces. This way the our caller can find the commit without magic
139 # spaces. This way the our caller can find the commit without magic
140 # strings
140 # strings
141 #
141 #
142 prefix = ""
142 prefix = ""
143 if opts['stdin']:
143 if opts['stdin']:
144 try:
144 try:
145 (type, r) = raw_input().split(' ');
145 (type, r) = raw_input().split(' ');
146 prefix = " "
146 prefix = " "
147 except EOFError:
147 except EOFError:
148 return
148 return
149
149
150 else:
150 else:
151 if not type or not r:
151 if not type or not r:
152 ui.warn("cat-file: type or revision not supplied\n")
152 ui.warn("cat-file: type or revision not supplied\n")
153 commands.help_(ui, 'cat-file')
153 commands.help_(ui, 'cat-file')
154
154
155 while r:
155 while r:
156 if type != "commit":
156 if type != "commit":
157 ui.warn("aborting hg cat-file only understands commits\n")
157 ui.warn("aborting hg cat-file only understands commits\n")
158 return 1;
158 return 1;
159 n = repo.lookup(r)
159 n = repo.lookup(r)
160 catcommit(ui, repo, n, prefix)
160 catcommit(ui, repo, n, prefix)
161 if opts['stdin']:
161 if opts['stdin']:
162 try:
162 try:
163 (type, r) = raw_input().split(' ');
163 (type, r) = raw_input().split(' ');
164 except EOFError:
164 except EOFError:
165 break
165 break
166 else:
166 else:
167 break
167 break
168
168
169 # git rev-tree is a confusing thing. You can supply a number of
169 # git rev-tree is a confusing thing. You can supply a number of
170 # commit sha1s on the command line, and it walks the commit history
170 # commit sha1s on the command line, and it walks the commit history
171 # telling you which commits are reachable from the supplied ones via
171 # telling you which commits are reachable from the supplied ones via
172 # a bitmask based on arg position.
172 # a bitmask based on arg position.
173 # you can specify a commit to stop at by starting the sha1 with ^
173 # you can specify a commit to stop at by starting the sha1 with ^
174 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
174 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
175 def chlogwalk():
175 def chlogwalk():
176 count = len(repo)
176 count = len(repo)
177 i = count
177 i = count
178 l = [0] * 100
178 l = [0] * 100
179 chunk = 100
179 chunk = 100
180 while True:
180 while True:
181 if chunk > i:
181 if chunk > i:
182 chunk = i
182 chunk = i
183 i = 0
183 i = 0
184 else:
184 else:
185 i -= chunk
185 i -= chunk
186
186
187 for x in xrange(0, chunk):
187 for x in xrange(0, chunk):
188 if i + x >= count:
188 if i + x >= count:
189 l[chunk - x:] = [0] * (chunk - x)
189 l[chunk - x:] = [0] * (chunk - x)
190 break
190 break
191 if full != None:
191 if full != None:
192 l[x] = repo[i + x]
192 l[x] = repo[i + x]
193 l[x].changeset() # force reading
193 l[x].changeset() # force reading
194 else:
194 else:
195 l[x] = 1
195 l[x] = 1
196 for x in xrange(chunk-1, -1, -1):
196 for x in xrange(chunk-1, -1, -1):
197 if l[x] != 0:
197 if l[x] != 0:
198 yield (i + x, full != None and l[x] or None)
198 yield (i + x, full != None and l[x] or None)
199 if i == 0:
199 if i == 0:
200 break
200 break
201
201
202 # calculate and return the reachability bitmask for sha
202 # calculate and return the reachability bitmask for sha
203 def is_reachable(ar, reachable, sha):
203 def is_reachable(ar, reachable, sha):
204 if len(ar) == 0:
204 if len(ar) == 0:
205 return 1
205 return 1
206 mask = 0
206 mask = 0
207 for i in xrange(len(ar)):
207 for i in xrange(len(ar)):
208 if sha in reachable[i]:
208 if sha in reachable[i]:
209 mask |= 1 << i
209 mask |= 1 << i
210
210
211 return mask
211 return mask
212
212
213 reachable = []
213 reachable = []
214 stop_sha1 = []
214 stop_sha1 = []
215 want_sha1 = []
215 want_sha1 = []
216 count = 0
216 count = 0
217
217
218 # figure out which commits they are asking for and which ones they
218 # figure out which commits they are asking for and which ones they
219 # want us to stop on
219 # want us to stop on
220 for i in xrange(len(args)):
220 for i in xrange(len(args)):
221 if args[i].startswith('^'):
221 if args[i].startswith('^'):
222 s = repo.lookup(args[i][1:])
222 s = repo.lookup(args[i][1:])
223 stop_sha1.append(s)
223 stop_sha1.append(s)
224 want_sha1.append(s)
224 want_sha1.append(s)
225 elif args[i] != 'HEAD':
225 elif args[i] != 'HEAD':
226 want_sha1.append(repo.lookup(args[i]))
226 want_sha1.append(repo.lookup(args[i]))
227
227
228 # calculate the graph for the supplied commits
228 # calculate the graph for the supplied commits
229 for i in xrange(len(want_sha1)):
229 for i in xrange(len(want_sha1)):
230 reachable.append({});
230 reachable.append({});
231 n = want_sha1[i];
231 n = want_sha1[i];
232 visit = [n];
232 visit = [n];
233 reachable[i][n] = 1
233 reachable[i][n] = 1
234 while visit:
234 while visit:
235 n = visit.pop(0)
235 n = visit.pop(0)
236 if n in stop_sha1:
236 if n in stop_sha1:
237 continue
237 continue
238 for p in repo.changelog.parents(n):
238 for p in repo.changelog.parents(n):
239 if p not in reachable[i]:
239 if p not in reachable[i]:
240 reachable[i][p] = 1
240 reachable[i][p] = 1
241 visit.append(p)
241 visit.append(p)
242 if p in stop_sha1:
242 if p in stop_sha1:
243 continue
243 continue
244
244
245 # walk the repository looking for commits that are in our
245 # walk the repository looking for commits that are in our
246 # reachability graph
246 # reachability graph
247 for i, ctx in chlogwalk():
247 for i, ctx in chlogwalk():
248 n = repo.changelog.node(i)
248 n = repo.changelog.node(i)
249 mask = is_reachable(want_sha1, reachable, n)
249 mask = is_reachable(want_sha1, reachable, n)
250 if mask:
250 if mask:
251 parentstr = ""
251 parentstr = ""
252 if parents:
252 if parents:
253 pp = repo.changelog.parents(n)
253 pp = repo.changelog.parents(n)
254 if pp[0] != nullid:
254 if pp[0] != nullid:
255 parentstr += " " + short(pp[0])
255 parentstr += " " + short(pp[0])
256 if pp[1] != nullid:
256 if pp[1] != nullid:
257 parentstr += " " + short(pp[1])
257 parentstr += " " + short(pp[1])
258 if not full:
258 if not full:
259 ui.write("%s%s\n" % (short(n), parentstr))
259 ui.write("%s%s\n" % (short(n), parentstr))
260 elif full == "commit":
260 elif full == "commit":
261 ui.write("%s%s\n" % (short(n), parentstr))
261 ui.write("%s%s\n" % (short(n), parentstr))
262 catcommit(ui, repo, n, ' ', ctx)
262 catcommit(ui, repo, n, ' ', ctx)
263 else:
263 else:
264 (p1, p2) = repo.changelog.parents(n)
264 (p1, p2) = repo.changelog.parents(n)
265 (h, h1, h2) = map(short, (n, p1, p2))
265 (h, h1, h2) = map(short, (n, p1, p2))
266 (i1, i2) = map(repo.changelog.rev, (p1, p2))
266 (i1, i2) = map(repo.changelog.rev, (p1, p2))
267
267
268 date = ctx.date()[0]
268 date = ctx.date()[0]
269 ui.write("%s %s:%s" % (date, h, mask))
269 ui.write("%s %s:%s" % (date, h, mask))
270 mask = is_reachable(want_sha1, reachable, p1)
270 mask = is_reachable(want_sha1, reachable, p1)
271 if i1 != nullrev and mask > 0:
271 if i1 != nullrev and mask > 0:
272 ui.write("%s:%s " % (h1, mask)),
272 ui.write("%s:%s " % (h1, mask)),
273 mask = is_reachable(want_sha1, reachable, p2)
273 mask = is_reachable(want_sha1, reachable, p2)
274 if i2 != nullrev and mask > 0:
274 if i2 != nullrev and mask > 0:
275 ui.write("%s:%s " % (h2, mask))
275 ui.write("%s:%s " % (h2, mask))
276 ui.write("\n")
276 ui.write("\n")
277 if maxnr and count >= maxnr:
277 if maxnr and count >= maxnr:
278 break
278 break
279 count += 1
279 count += 1
280
280
281 def revparse(ui, repo, *revs, **opts):
281 def revparse(ui, repo, *revs, **opts):
282 """Parse given revisions"""
282 """Parse given revisions"""
283 def revstr(rev):
283 def revstr(rev):
284 if rev == 'HEAD':
284 if rev == 'HEAD':
285 rev = 'tip'
285 rev = 'tip'
286 return revlog.hex(repo.lookup(rev))
286 return revlog.hex(repo.lookup(rev))
287
287
288 for r in revs:
288 for r in revs:
289 revrange = r.split(':', 1)
289 revrange = r.split(':', 1)
290 ui.write('%s\n' % revstr(revrange[0]))
290 ui.write('%s\n' % revstr(revrange[0]))
291 if len(revrange) == 2:
291 if len(revrange) == 2:
292 ui.write('^%s\n' % revstr(revrange[1]))
292 ui.write('^%s\n' % revstr(revrange[1]))
293
293
294 # git rev-list tries to order things by date, and has the ability to stop
294 # git rev-list tries to order things by date, and has the ability to stop
295 # at a given commit without walking the whole repo. TODO add the stop
295 # at a given commit without walking the whole repo. TODO add the stop
296 # parameter
296 # parameter
297 def revlist(ui, repo, *revs, **opts):
297 def revlist(ui, repo, *revs, **opts):
298 """print revisions"""
298 """print revisions"""
299 if opts['header']:
299 if opts['header']:
300 full = "commit"
300 full = "commit"
301 else:
301 else:
302 full = None
302 full = None
303 copy = [x for x in revs]
303 copy = [x for x in revs]
304 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
304 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
305
305
306 def config(ui, repo, **opts):
306 def config(ui, repo, **opts):
307 """print extension options"""
307 """print extension options"""
308 def writeopt(name, value):
308 def writeopt(name, value):
309 ui.write('k=%s\nv=%s\n' % (name, value))
309 ui.write('k=%s\nv=%s\n' % (name, value))
310
310
311 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
311 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
312
312
313
313
314 def view(ui, repo, *etc, **opts):
314 def view(ui, repo, *etc, **opts):
315 "start interactive history viewer"
315 "start interactive history viewer"
316 os.chdir(repo.root)
316 os.chdir(repo.root)
317 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
317 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
318 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
318 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
319 ui.debug("running %s\n" % cmd)
319 ui.debug("running %s\n" % cmd)
320 util.system(cmd)
320 util.system(cmd)
321
321
322 cmdtable = {
322 cmdtable = {
323 "^view":
323 "^view":
324 (view,
324 (view,
325 [('l', 'limit', '', 'limit number of changes displayed')],
325 [('l', 'limit', '', 'limit number of changes displayed')],
326 'hg view [-l LIMIT] [REVRANGE]'),
326 'hg view [-l LIMIT] [REVRANGE]'),
327 "debug-diff-tree":
327 "debug-diff-tree":
328 (difftree,
328 (difftree,
329 [('p', 'patch', None, 'generate patch'),
329 [('p', 'patch', None, 'generate patch'),
330 ('r', 'recursive', None, 'recursive'),
330 ('r', 'recursive', None, 'recursive'),
331 ('P', 'pretty', None, 'pretty'),
331 ('P', 'pretty', None, 'pretty'),
332 ('s', 'stdin', None, 'stdin'),
332 ('s', 'stdin', None, 'stdin'),
333 ('C', 'copy', None, 'detect copies'),
333 ('C', 'copy', None, 'detect copies'),
334 ('S', 'search', "", 'search')],
334 ('S', 'search', "", 'search')],
335 'hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...'),
335 'hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...'),
336 "debug-cat-file":
336 "debug-cat-file":
337 (catfile,
337 (catfile,
338 [('s', 'stdin', None, 'stdin')],
338 [('s', 'stdin', None, 'stdin')],
339 'hg debug-cat-file [OPTION]... TYPE FILE'),
339 'hg debug-cat-file [OPTION]... TYPE FILE'),
340 "debug-config":
340 "debug-config":
341 (config, [], 'hg debug-config'),
341 (config, [], 'hg debug-config'),
342 "debug-merge-base":
342 "debug-merge-base":
343 (base, [], 'hg debug-merge-base node node'),
343 (base, [], 'hg debug-merge-base node node'),
344 "debug-rev-parse":
344 "debug-rev-parse":
345 (revparse,
345 (revparse,
346 [('', 'default', '', 'ignored')],
346 [('', 'default', '', 'ignored')],
347 'hg debug-rev-parse REV'),
347 'hg debug-rev-parse REV'),
348 "debug-rev-list":
348 "debug-rev-list":
349 (revlist,
349 (revlist,
350 [('H', 'header', None, 'header'),
350 [('H', 'header', None, 'header'),
351 ('t', 'topo-order', None, 'topo-order'),
351 ('t', 'topo-order', None, 'topo-order'),
352 ('p', 'parents', None, 'parents'),
352 ('p', 'parents', None, 'parents'),
353 ('n', 'max-count', 0, 'max-count')],
353 ('n', 'max-count', 0, 'max-count')],
354 'hg debug-rev-list [options] revs'),
354 'hg debug-rev-list [options] revs'),
355 }
355 }
General Comments 0
You need to be logged in to leave comments. Login now