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