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