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