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