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