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