comparison hgext/hgk.py @ 2432:6877064c72f7

move hgk.py into hgext. now to enable "hg view" is one less step. edit ~/.hgrc: [extensions] hgext.hgk = cp contrib/hgk ~/bin
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Wed, 14 Jun 2006 15:41:06 -0700
parents contrib/hgk.py@5f581f337b05
children c49dc6f38a19
comparison
equal deleted inserted replaced
2430:4ccd71b83d5e 2432:6877064c72f7
1 # Minimal support for git commands on an hg repository
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 import time, sys, signal, os
9 from mercurial import hg, mdiff, fancyopts, commands, ui, util
10
11 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
12 changes=None, text=False):
13 def date(c):
14 return time.asctime(time.gmtime(c[2][0]))
15
16 if not changes:
17 changes = repo.changes(node1, node2, files, match=match)
18 modified, added, removed, deleted, unknown = changes
19 if files:
20 modified, added, removed = map(lambda x: filterfiles(files, x),
21 (modified, added, removed))
22
23 if not modified and not added and not removed:
24 return
25
26 if node2:
27 change = repo.changelog.read(node2)
28 mmap2 = repo.manifest.read(change[0])
29 date2 = date(change)
30 def read(f):
31 return repo.file(f).read(mmap2[f])
32 else:
33 date2 = time.asctime()
34 if not node1:
35 node1 = repo.dirstate.parents()[0]
36 def read(f):
37 return repo.wfile(f).read()
38
39 change = repo.changelog.read(node1)
40 mmap = repo.manifest.read(change[0])
41 date1 = date(change)
42
43 for f in modified:
44 to = None
45 if f in mmap:
46 to = repo.file(f).read(mmap[f])
47 tn = read(f)
48 fp.write("diff --git a/%s b/%s\n" % (f, f))
49 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
50 for f in added:
51 to = None
52 tn = read(f)
53 fp.write("diff --git /dev/null b/%s\n" % (f))
54 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
55 for f in removed:
56 to = repo.file(f).read(mmap[f])
57 tn = None
58 fp.write("diff --git a/%s /dev/null\n" % (f))
59 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
60
61 def difftree(ui, repo, node1=None, node2=None, **opts):
62 """diff trees from two commits"""
63 def __difftree(repo, node1, node2):
64 def date(c):
65 return time.asctime(time.gmtime(c[2][0]))
66
67 if node2:
68 change = repo.changelog.read(node2)
69 mmap2 = repo.manifest.read(change[0])
70 modified, added, removed, deleted, unknown = repo.changes(node1, node2)
71 def read(f): return repo.file(f).read(mmap2[f])
72 date2 = date(change)
73 else:
74 date2 = time.asctime()
75 modified, added, removed, deleted, unknown = repo.changes(node1)
76 if not node1:
77 node1 = repo.dirstate.parents()[0]
78 def read(f): return file(os.path.join(repo.root, f)).read()
79
80 change = repo.changelog.read(node1)
81 mmap = repo.manifest.read(change[0])
82 date1 = date(change)
83 empty = "0" * 40;
84
85 for f in modified:
86 # TODO get file permissions
87 print ":100664 100664 %s %s M\t%s\t%s" % (hg.hex(mmap[f]),
88 hg.hex(mmap2[f]), f, f)
89 for f in added:
90 print ":000000 100664 %s %s N\t%s\t%s" % (empty, hg.hex(mmap2[f]), f, f)
91 for f in removed:
92 print ":100664 000000 %s %s D\t%s\t%s" % (hg.hex(mmap[f]), empty, f, f)
93 ##
94
95 while True:
96 if opts['stdin']:
97 try:
98 line = raw_input().split(' ')
99 node1 = line[0]
100 if len(line) > 1:
101 node2 = line[1]
102 else:
103 node2 = None
104 except EOFError:
105 break
106 node1 = repo.lookup(node1)
107 if node2:
108 node2 = repo.lookup(node2)
109 else:
110 node2 = node1
111 node1 = repo.changelog.parents(node1)[0]
112 if opts['patch']:
113 if opts['pretty']:
114 catcommit(repo, node2, "")
115 dodiff(sys.stdout, ui, repo, node1, node2)
116 else:
117 __difftree(repo, node1, node2)
118 if not opts['stdin']:
119 break
120
121 def catcommit(repo, n, prefix, changes=None):
122 nlprefix = '\n' + prefix;
123 (p1, p2) = repo.changelog.parents(n)
124 (h, h1, h2) = map(hg.hex, (n, p1, p2))
125 (i1, i2) = map(repo.changelog.rev, (p1, p2))
126 if not changes:
127 changes = repo.changelog.read(n)
128 print "tree %s" % (hg.hex(changes[0]))
129 if i1 != -1: print "parent %s" % (h1)
130 if i2 != -1: print "parent %s" % (h2)
131 date_ar = changes[2]
132 date = int(float(date_ar[0]))
133 lines = changes[4].splitlines()
134 if lines[-1].startswith('committer:'):
135 committer = lines[-1].split(': ')[1].rstrip()
136 else:
137 committer = changes[1]
138
139 print "author %s %s %s" % (changes[1], date, date_ar[1])
140 print "committer %s %s %s" % (committer, date, date_ar[1])
141 print ""
142 if prefix != "":
143 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
144 else:
145 print changes[4]
146 if prefix:
147 sys.stdout.write('\0')
148
149 def base(ui, repo, node1, node2):
150 """Output common ancestor information"""
151 node1 = repo.lookup(node1)
152 node2 = repo.lookup(node2)
153 n = repo.changelog.ancestor(node1, node2)
154 print hg.hex(n)
155
156 def catfile(ui, repo, type=None, r=None, **opts):
157 """cat a specific revision"""
158 # in stdin mode, every line except the commit is prefixed with two
159 # spaces. This way the our caller can find the commit without magic
160 # strings
161 #
162 prefix = ""
163 if opts['stdin']:
164 try:
165 (type, r) = raw_input().split(' ');
166 prefix = " "
167 except EOFError:
168 return
169
170 else:
171 if not type or not r:
172 ui.warn("cat-file: type or revision not supplied\n")
173 commands.help_(ui, 'cat-file')
174
175 while r:
176 if type != "commit":
177 sys.stderr.write("aborting hg cat-file only understands commits\n")
178 sys.exit(1);
179 n = repo.lookup(r)
180 catcommit(repo, n, prefix)
181 if opts['stdin']:
182 try:
183 (type, r) = raw_input().split(' ');
184 except EOFError:
185 break
186 else:
187 break
188
189 # git rev-tree is a confusing thing. You can supply a number of
190 # commit sha1s on the command line, and it walks the commit history
191 # telling you which commits are reachable from the supplied ones via
192 # a bitmask based on arg position.
193 # you can specify a commit to stop at by starting the sha1 with ^
194 def revtree(args, repo, full="tree", maxnr=0, parents=False):
195 def chlogwalk():
196 ch = repo.changelog
197 count = ch.count()
198 i = count
199 l = [0] * 100
200 chunk = 100
201 while True:
202 if chunk > i:
203 chunk = i
204 i = 0
205 else:
206 i -= chunk
207
208 for x in xrange(0, chunk):
209 if i + x >= count:
210 l[chunk - x:] = [0] * (chunk - x)
211 break
212 if full != None:
213 l[x] = ch.read(ch.node(i + x))
214 else:
215 l[x] = 1
216 for x in xrange(chunk-1, -1, -1):
217 if l[x] != 0:
218 yield (i + x, full != None and l[x] or None)
219 if i == 0:
220 break
221
222 # calculate and return the reachability bitmask for sha
223 def is_reachable(ar, reachable, sha):
224 if len(ar) == 0:
225 return 1
226 mask = 0
227 for i in range(len(ar)):
228 if sha in reachable[i]:
229 mask |= 1 << i
230
231 return mask
232
233 reachable = []
234 stop_sha1 = []
235 want_sha1 = []
236 count = 0
237
238 # figure out which commits they are asking for and which ones they
239 # want us to stop on
240 for i in range(len(args)):
241 if args[i].startswith('^'):
242 s = repo.lookup(args[i][1:])
243 stop_sha1.append(s)
244 want_sha1.append(s)
245 elif args[i] != 'HEAD':
246 want_sha1.append(repo.lookup(args[i]))
247
248 # calculate the graph for the supplied commits
249 for i in range(len(want_sha1)):
250 reachable.append({});
251 n = want_sha1[i];
252 visit = [n];
253 reachable[i][n] = 1
254 while visit:
255 n = visit.pop(0)
256 if n in stop_sha1:
257 continue
258 for p in repo.changelog.parents(n):
259 if p not in reachable[i]:
260 reachable[i][p] = 1
261 visit.append(p)
262 if p in stop_sha1:
263 continue
264
265 # walk the repository looking for commits that are in our
266 # reachability graph
267 #for i in range(repo.changelog.count()-1, -1, -1):
268 for i, changes in chlogwalk():
269 n = repo.changelog.node(i)
270 mask = is_reachable(want_sha1, reachable, n)
271 if mask:
272 parentstr = ""
273 if parents:
274 pp = repo.changelog.parents(n)
275 if pp[0] != hg.nullid:
276 parentstr += " " + hg.hex(pp[0])
277 if pp[1] != hg.nullid:
278 parentstr += " " + hg.hex(pp[1])
279 if not full:
280 print hg.hex(n) + parentstr
281 elif full is "commit":
282 print hg.hex(n) + parentstr
283 catcommit(repo, n, ' ', changes)
284 else:
285 (p1, p2) = repo.changelog.parents(n)
286 (h, h1, h2) = map(hg.hex, (n, p1, p2))
287 (i1, i2) = map(repo.changelog.rev, (p1, p2))
288
289 date = changes[2][0]
290 print "%s %s:%s" % (date, h, mask),
291 mask = is_reachable(want_sha1, reachable, p1)
292 if i1 != -1 and mask > 0:
293 print "%s:%s " % (h1, mask),
294 mask = is_reachable(want_sha1, reachable, p2)
295 if i2 != -1 and mask > 0:
296 print "%s:%s " % (h2, mask),
297 print ""
298 if maxnr and count >= maxnr:
299 break
300 count += 1
301
302 # git rev-list tries to order things by date, and has the ability to stop
303 # at a given commit without walking the whole repo. TODO add the stop
304 # parameter
305 def revlist(ui, repo, *revs, **opts):
306 """print revisions"""
307 if opts['header']:
308 full = "commit"
309 else:
310 full = None
311 copy = [x for x in revs]
312 revtree(copy, repo, full, opts['max_count'], opts['parents'])
313
314 def view(ui, repo, *etc):
315 "start interactive history viewer"
316 os.chdir(repo.root)
317 os.system(ui.config("hgk", "path", "hgk") + " " + " ".join(etc))
318
319 cmdtable = {
320 "view": (view, [], 'hg view'),
321 "debug-diff-tree": (difftree, [('p', 'patch', None, 'generate patch'),
322 ('r', 'recursive', None, 'recursive'),
323 ('P', 'pretty', None, 'pretty'),
324 ('s', 'stdin', None, 'stdin'),
325 ('C', 'copy', None, 'detect copies'),
326 ('S', 'search', "", 'search')],
327 "hg git-diff-tree [options] node1 node2"),
328 "debug-cat-file": (catfile, [('s', 'stdin', None, 'stdin')],
329 "hg debug-cat-file [options] type file"),
330 "debug-merge-base": (base, [], "hg debug-merge-base node node"),
331 "debug-rev-list": (revlist, [('H', 'header', None, 'header'),
332 ('t', 'topo-order', None, 'topo-order'),
333 ('p', 'parents', None, 'parents'),
334 ('n', 'max-count', 0, 'max-count')],
335 "hg debug-rev-list [options] revs"),
336 }
337
338 def reposetup(ui, repo):
339 pass