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