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