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