Mercurial > hg-stable
annotate contrib/hgit @ 737:8db4d406b3d3
Merge with Matt's tip.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Wed, 20 Jul 2005 03:52:06 -0800 |
parents | 688d03d6997a 095dd8c757e0 |
children | d2422f10c136 |
rev | line source |
---|---|
267 | 1 #!/usr/bin/env python |
2 # | |
3 # Minimal support for git commands on an hg repository | |
4 # | |
5 # Copyright 2005 Chris Mason <mason@suse.com> | |
6 # | |
7 # This software may be used and distributed according to the terms | |
8 # of the GNU General Public License, incorporated herein by reference. | |
9 | |
10 import time, sys, signal | |
11 from mercurial import hg, mdiff, fancyopts, commands, ui | |
12 | |
396
8f8bb77d560e
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
357
diff
changeset
|
13 def difftree(args, ui, repo): |
267 | 14 def __difftree(repo, files = None, node1 = None, node2 = None): |
334 | 15 def date(c): |
16 return time.asctime(time.gmtime(float(c[2].split(' ')[0]))) | |
267 | 17 |
334 | 18 if node2: |
19 change = repo.changelog.read(node2) | |
20 mmap2 = repo.manifest.read(change[0]) | |
719
dda258572847
Fix hgit usage of repo.changes and fancyopts to reflect current hg api
mason@suse.com
parents:
429
diff
changeset
|
21 (c, a, d, u) = repo.changes(node1, node2) |
334 | 22 def read(f): return repo.file(f).read(mmap2[f]) |
23 date2 = date(change) | |
24 else: | |
25 date2 = time.asctime() | |
26 (c, a, d, u) = repo.diffdir(repo.root, node1) | |
27 if not node1: | |
28 node1 = repo.dirstate.parents()[0] | |
29 def read(f): return file(os.path.join(repo.root, f)).read() | |
267 | 30 |
334 | 31 change = repo.changelog.read(node1) |
32 mmap = repo.manifest.read(change[0]) | |
33 date1 = date(change) | |
34 empty = "0" * 40; | |
267 | 35 |
334 | 36 if files: |
37 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d)) | |
267 | 38 |
334 | 39 for f in c: |
40 # TODO get file permissions | |
406
d8abb687d501
[PATCH] Using monotone-viz/git-viz with mercurial
mpm@selenic.com
parents:
396
diff
changeset
|
41 print ":100664 100664 %s %s M\t%s\t%s" % (hg.hex(mmap[f]), |
334 | 42 hg.hex(mmap2[f]), f, f) |
43 for f in a: | |
406
d8abb687d501
[PATCH] Using monotone-viz/git-viz with mercurial
mpm@selenic.com
parents:
396
diff
changeset
|
44 print ":000000 100664 %s %s N\t%s\t%s" % (empty, hg.hex(mmap2[f]), f, f) |
334 | 45 for f in d: |
406
d8abb687d501
[PATCH] Using monotone-viz/git-viz with mercurial
mpm@selenic.com
parents:
396
diff
changeset
|
46 print ":100664 000000 %s %s D\t%s\t%s" % (hg.hex(mmap[f]), empty, f, f) |
267 | 47 ## |
48 | |
49 revs = [] | |
50 if args: | |
334 | 51 doptions = {} |
52 opts = [('p', 'patch', None, 'patch'), | |
53 ('r', 'recursive', None, 'recursive')] | |
719
dda258572847
Fix hgit usage of repo.changes and fancyopts to reflect current hg api
mason@suse.com
parents:
429
diff
changeset
|
54 args = fancyopts.fancyopts(args, opts, doptions) |
267 | 55 |
56 if len(args) < 2: | |
334 | 57 help() |
267 | 58 sys.exit(1) |
59 revs.append(repo.lookup(args[0])) | |
60 revs.append(repo.lookup(args[1])) | |
61 args = args[2:] | |
62 if doptions['patch']: | |
719
dda258572847
Fix hgit usage of repo.changes and fancyopts to reflect current hg api
mason@suse.com
parents:
429
diff
changeset
|
63 commands.dodiff(sys.stdout, ui, repo, args, *revs) |
267 | 64 else: |
334 | 65 __difftree(repo, args, *revs) |
267 | 66 |
67 def catcommit(repo, n, prefix): | |
68 nlprefix = '\n' + prefix; | |
69 changes = repo.changelog.read(n) | |
70 (p1, p2) = repo.changelog.parents(n) | |
71 (h, h1, h2) = map(hg.hex, (n, p1, p2)) | |
72 (i1, i2) = map(repo.changelog.rev, (p1, p2)) | |
73 print "tree %s" % (h) | |
74 if i1 != -1: print "%sparent %s" % (prefix, h1) | |
75 if i2 != -1: print "%sparent %s" % (prefix, h2) | |
76 date_ar = changes[2].split(' ') | |
77 date = int(float(date_ar[0])) | |
78 print "%sauthor <%s> %s %s" % (prefix, changes[1], date, date_ar[1]) | |
79 print "%scommitter <%s> %s %s" % (prefix, changes[1], date, date_ar[1]) | |
80 print prefix | |
81 if prefix != "": | |
334 | 82 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip()) |
267 | 83 else: |
334 | 84 print changes[4] |
267 | 85 |
86 def catfile(args, ui, repo): | |
87 doptions = {} | |
88 opts = [('s', 'stdin', None, 'stdin')] | |
719
dda258572847
Fix hgit usage of repo.changes and fancyopts to reflect current hg api
mason@suse.com
parents:
429
diff
changeset
|
89 args = fancyopts.fancyopts(args, opts, doptions) |
267 | 90 |
91 # in stdin mode, every line except the commit is prefixed with two | |
92 # spaces. This way the our caller can find the commit without magic | |
93 # strings | |
94 # | |
95 prefix = "" | |
96 if doptions['stdin']: | |
334 | 97 try: |
98 (type, r) = raw_input().split(' '); | |
99 prefix = " " | |
100 except EOFError: | |
101 return | |
267 | 102 |
103 else: | |
334 | 104 if len(args) < 2: |
105 help() | |
106 sys.exit(1) | |
267 | 107 type = args[0] |
334 | 108 r = args[1] |
267 | 109 |
110 while r: | |
334 | 111 if type != "commit": |
112 sys.stderr.write("aborting hg cat-file only understands commits\n") | |
113 sys.exit(1); | |
720
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
114 n = repo.lookup(r) |
334 | 115 catcommit(repo, n, prefix) |
116 if doptions['stdin']: | |
117 try: | |
118 (type, r) = raw_input().split(' '); | |
119 except EOFError: | |
120 break | |
121 else: | |
122 break | |
267 | 123 |
124 # git rev-tree is a confusing thing. You can supply a number of | |
125 # commit sha1s on the command line, and it walks the commit history | |
126 # telling you which commits are reachable from the supplied ones via | |
127 # a bitmask based on arg position. | |
128 # you can specify a commit to stop at by starting the sha1 with ^ | |
356 | 129 def revtree(args, repo, full="tree", maxnr=0): |
267 | 130 # calculate and return the reachability bitmask for sha |
131 def is_reachable(ar, reachable, sha): | |
334 | 132 if len(ar) == 0: |
133 return 1 | |
134 mask = 0 | |
135 for i in range(len(ar)): | |
136 if sha in reachable[i]: | |
137 mask |= 1 << i | |
267 | 138 |
334 | 139 return mask |
267 | 140 |
141 reachable = [] | |
142 stop_sha1 = [] | |
143 want_sha1 = [] | |
356 | 144 count = 0 |
267 | 145 |
146 # figure out which commits they are asking for and which ones they | |
147 # want us to stop on | |
148 for i in range(len(args)): | |
720
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
149 if args[i].startswith('^'): |
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
150 s = repo.lookup(args[i][1:]) |
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
151 stop_sha1.append(s) |
334 | 152 want_sha1.append(s) |
153 elif args[i] != 'HEAD': | |
720
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
154 want_sha1.append(repo.lookup(args[i])) |
356 | 155 |
267 | 156 # calculate the graph for the supplied commits |
157 for i in range(len(want_sha1)): | |
334 | 158 reachable.append({}); |
720
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
159 n = want_sha1[i]; |
334 | 160 visit = [n]; |
161 reachable[i][n] = 1 | |
162 while visit: | |
163 n = visit.pop(0) | |
164 if n in stop_sha1: | |
165 break | |
166 for p in repo.changelog.parents(n): | |
167 if p not in reachable[i]: | |
168 reachable[i][p] = 1 | |
169 visit.append(p) | |
170 if p in stop_sha1: | |
171 break | |
356 | 172 |
267 | 173 # walk the repository looking for commits that are in our |
174 # reachability graph | |
356 | 175 for i in range(repo.changelog.count()-1, -1, -1): |
334 | 176 n = repo.changelog.node(i) |
177 mask = is_reachable(want_sha1, reachable, n) | |
178 if mask: | |
356 | 179 if not full: |
180 print hg.hex(n) | |
181 elif full is "commit": | |
182 print hg.hex(n) | |
183 catcommit(repo, n, ' ') | |
184 else: | |
185 changes = repo.changelog.read(n) | |
186 (p1, p2) = repo.changelog.parents(n) | |
187 (h, h1, h2) = map(hg.hex, (n, p1, p2)) | |
188 (i1, i2) = map(repo.changelog.rev, (p1, p2)) | |
267 | 189 |
356 | 190 date = changes[2].split(' ')[0] |
191 print "%s %s:%s" % (date, h, mask), | |
192 mask = is_reachable(want_sha1, reachable, p1) | |
193 if i1 != -1 and mask > 0: | |
194 print "%s:%s " % (h1, mask), | |
195 mask = is_reachable(want_sha1, reachable, p2) | |
196 if i2 != -1 and mask > 0: | |
197 print "%s:%s " % (h2, mask), | |
198 print "" | |
199 if maxnr and count >= maxnr: | |
200 break | |
201 count += 1 | |
267 | 202 |
203 # git rev-list tries to order things by date, and has the ability to stop | |
204 # at a given commit without walking the whole repo. TODO add the stop | |
205 # parameter | |
206 def revlist(args, repo): | |
207 doptions = {} | |
356 | 208 opts = [('c', 'commit', None, 'commit'), |
209 ('n', 'max-nr', 0, 'max-nr')] | |
719
dda258572847
Fix hgit usage of repo.changes and fancyopts to reflect current hg api
mason@suse.com
parents:
429
diff
changeset
|
210 args = fancyopts.fancyopts(args, opts, doptions) |
356 | 211 if doptions['commit']: |
212 full = "commit" | |
213 else: | |
214 full = None | |
215 for i in range(1, len(args)): | |
216 args[i] = '^' + args[i] | |
217 revtree(args, repo, full, doptions['max-nr']) | |
267 | 218 |
219 def catchterm(*args): | |
220 raise SignalInterrupt | |
221 | |
222 def help(): | |
223 sys.stderr.write("commands:\n") | |
224 sys.stderr.write(" hgit cat-file [type] sha1\n") | |
225 sys.stderr.write(" hgit diff-tree [-p] [-r] sha1 sha1\n") | |
226 sys.stderr.write(" hgit rev-tree [sha1 ... [^stop sha1]]\n") | |
720
095dd8c757e0
Change hgit revision lookup to use repo.lookup
mason@suse.com
parents:
719
diff
changeset
|
227 sys.stderr.write(" hgit rev-list [-c] [sha1 [stop sha1]\n") |
267 | 228 |
229 cmd = sys.argv[1] | |
230 args = sys.argv[2:] | |
231 u = ui.ui() | |
232 signal.signal(signal.SIGTERM, catchterm) | |
233 repo = hg.repository(ui = u) | |
234 | |
235 if cmd == "diff-tree": | |
396
8f8bb77d560e
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
357
diff
changeset
|
236 difftree(args, u, repo) |
267 | 237 |
238 elif cmd == "cat-file": | |
396
8f8bb77d560e
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
357
diff
changeset
|
239 catfile(args, u, repo) |
267 | 240 |
241 elif cmd == "rev-tree": | |
242 revtree(args, repo) | |
243 | |
244 elif cmd == "rev-list": | |
245 revlist(args, repo) | |
246 | |
247 elif cmd == "help": | |
248 help() | |
249 | |
250 else: | |
251 if cmd: sys.stderr.write("unknown command\n\n") | |
252 help() | |
253 sys.exit(1) | |
254 | |
255 sys.exit(0) |