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