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