Mercurial > hg
annotate contrib/hgit @ 820:89985a1b3427
Clean up walk and changes code to use normalised names properly.
New function: commands.pathto returns the relative path from one path
to another. For example, given foo/bar and baz/quux, it will return
../../baz/quux. This new function is used by the walk and status code
to print relative paths correctly.
New command: debugwalk exercises the walk code without doing anything
more.
hg.dirstate.walk now yields normalised names. For example, if you're
in the baz directory and you ask it to walk ../foo/bar/.., it will yield
names starting with foo/.
As a result of this change, all of the other walk and changes methods
in this module also return normalised names.
The util.matcher function now normalises globs and path names, so that
it will match normalised names properly.
Finally, util.matcher uses the non-glob prefix of a glob to tell walk
which directories to scan. Perviously, a glob like foo/* would scan
everything, but only return matches for foo/*. Now, foo/* only scans
under foo (using the globprefix function), which is much faster.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sun, 31 Jul 2005 17:42:46 -0800 |
parents | d2422f10c136 |
children | 29f17e083e84 |
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) |