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