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