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