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