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