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