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