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