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