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