comparison contrib/hgit @ 1239:29f17e083e84

Turn hgit into an extension, and add commands supporting the latest gitk
author mason@suse.com
date Tue, 13 Sep 2005 19:33:10 -0500
parents d2422f10c136
children 9d10f89b75a5
comparison
equal deleted inserted replaced
1238:6a4f181497c9 1239:29f17e083e84
6 # 6 #
7 # This software may be used and distributed according to the terms 7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference. 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 import time, sys, signal 10 import time, sys, signal
11 from mercurial import hg, mdiff, fancyopts, commands, ui 11 from mercurial import hg, mdiff, fancyopts, commands, ui, util
12 12
13 def difftree(args, ui, repo): 13 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
14 def __difftree(repo, files = None, node1 = None, node2 = None): 14 changes=None, text=False):
15 def date(c):
16 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
17
18 if not changes:
19 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
20 else:
21 (c, a, d, u) = changes
22 if files:
23 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
24
25 if not c and not a and not d:
26 return
27
28 if node2:
29 change = repo.changelog.read(node2)
30 mmap2 = repo.manifest.read(change[0])
31 date2 = date(change)
32 def read(f):
33 return repo.file(f).read(mmap2[f])
34 else:
35 date2 = time.asctime()
36 if not node1:
37 node1 = repo.dirstate.parents()[0]
38 def read(f):
39 return repo.wfile(f).read()
40
41 change = repo.changelog.read(node1)
42 mmap = repo.manifest.read(change[0])
43 date1 = date(change)
44
45 for f in c:
46 to = None
47 if f in mmap:
48 to = repo.file(f).read(mmap[f])
49 tn = read(f)
50 fp.write("diff --git a/%s b/%s\n" % (f, f))
51 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
52 for f in a:
53 to = None
54 tn = read(f)
55 fp.write("diff --git /dev/null b/%s\n" % (f))
56 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
57 for f in d:
58 to = repo.file(f).read(mmap[f])
59 tn = None
60 fp.write("diff --git a/%s /dev/null\n" % (f))
61 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
62
63 def difftree(ui, repo, node1=None, node2=None, **opts):
64 """diff trees from two commits"""
65 def __difftree(repo, node1, node2):
15 def date(c): 66 def date(c):
16 return time.asctime(time.gmtime(float(c[2].split(' ')[0]))) 67 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
17 68
18 if node2: 69 if node2:
19 change = repo.changelog.read(node2) 70 change = repo.changelog.read(node2)
21 (c, a, d, u) = repo.changes(node1, node2) 72 (c, a, d, u) = repo.changes(node1, node2)
22 def read(f): return repo.file(f).read(mmap2[f]) 73 def read(f): return repo.file(f).read(mmap2[f])
23 date2 = date(change) 74 date2 = date(change)
24 else: 75 else:
25 date2 = time.asctime() 76 date2 = time.asctime()
26 (c, a, d, u) = repo.diffdir(repo.root, node1) 77 (c, a, d, u) = repo.changes(node1, None)
27 if not node1: 78 if not node1:
28 node1 = repo.dirstate.parents()[0] 79 node1 = repo.dirstate.parents()[0]
29 def read(f): return file(os.path.join(repo.root, f)).read() 80 def read(f): return file(os.path.join(repo.root, f)).read()
30 81
31 change = repo.changelog.read(node1) 82 change = repo.changelog.read(node1)
32 mmap = repo.manifest.read(change[0]) 83 mmap = repo.manifest.read(change[0])
33 date1 = date(change) 84 date1 = date(change)
34 empty = "0" * 40; 85 empty = "0" * 40;
35
36 if files:
37 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
38 86
39 for f in c: 87 for f in c:
40 # TODO get file permissions 88 # TODO get file permissions
41 print ":100664 100664 %s %s M\t%s\t%s" % (hg.hex(mmap[f]), 89 print ":100664 100664 %s %s M\t%s\t%s" % (hg.hex(mmap[f]),
42 hg.hex(mmap2[f]), f, f) 90 hg.hex(mmap2[f]), f, f)
44 print ":000000 100664 %s %s N\t%s\t%s" % (empty, hg.hex(mmap2[f]), f, f) 92 print ":000000 100664 %s %s N\t%s\t%s" % (empty, hg.hex(mmap2[f]), f, f)
45 for f in d: 93 for f in d:
46 print ":100664 000000 %s %s D\t%s\t%s" % (hg.hex(mmap[f]), empty, f, f) 94 print ":100664 000000 %s %s D\t%s\t%s" % (hg.hex(mmap[f]), empty, f, f)
47 ## 95 ##
48 96
49 revs = [] 97 while True:
50 if args: 98 if opts['stdin']:
51 doptions = {} 99 try:
52 opts = [('p', 'patch', None, 'patch'), 100 line = raw_input().split(' ')
53 ('r', 'recursive', None, 'recursive')] 101 node1 = line[0]
54 args = fancyopts.fancyopts(args, opts, doptions) 102 if len(line) > 1:
55 103 node2 = line[1]
56 if len(args) < 2: 104 else:
57 help() 105 node2 = None
58 sys.exit(1) 106 except EOFError:
59 revs.append(repo.lookup(args[0])) 107 break
60 revs.append(repo.lookup(args[1])) 108 node1 = repo.lookup(node1)
61 args = args[2:] 109 if node2:
62 if doptions['patch']: 110 node2 = repo.lookup(node2)
63 commands.dodiff(sys.stdout, ui, repo, args, *revs) 111 else:
64 else: 112 node2 = node1
65 __difftree(repo, args, *revs) 113 node1 = repo.changelog.parents(node1)[0]
66 114 if opts['patch']:
67 def catcommit(repo, n, prefix): 115 if opts['pretty']:
116 catcommit(repo, node2, "")
117 dodiff(sys.stdout, ui, repo, node1, node2)
118 else:
119 __difftree(repo, node1, node2)
120 if not opts['stdin']:
121 break
122
123 def catcommit(repo, n, prefix, changes=None):
68 nlprefix = '\n' + prefix; 124 nlprefix = '\n' + prefix;
69 changes = repo.changelog.read(n)
70 (p1, p2) = repo.changelog.parents(n) 125 (p1, p2) = repo.changelog.parents(n)
71 (h, h1, h2) = map(hg.hex, (n, p1, p2)) 126 (h, h1, h2) = map(hg.hex, (n, p1, p2))
72 (i1, i2) = map(repo.changelog.rev, (p1, p2)) 127 (i1, i2) = map(repo.changelog.rev, (p1, p2))
73 print "tree %s" % (h) 128 if not changes:
74 if i1 != -1: print "%sparent %s" % (prefix, h1) 129 changes = repo.changelog.read(n)
75 if i2 != -1: print "%sparent %s" % (prefix, h2) 130 print "tree %s" % (hg.hex(changes[0]))
131 if i1 != -1: print "parent %s" % (h1)
132 if i2 != -1: print "parent %s" % (h2)
76 date_ar = changes[2].split(' ') 133 date_ar = changes[2].split(' ')
77 date = int(float(date_ar[0])) 134 date = int(float(date_ar[0]))
78 print "%sauthor <%s> %s %s" % (prefix, changes[1], date, date_ar[1]) 135 lines = changes[4].splitlines()
79 print "%scommitter <%s> %s %s" % (prefix, changes[1], date, date_ar[1]) 136 if lines[-1].startswith('committer:'):
80 print prefix 137 committer = lines[-1].split(': ')[1].rstrip()
138 else:
139 committer = "%s %s %s" % (changes[1], date, date_ar[1])
140
141 print "author %s %s %s" % (changes[1], date, date_ar[1])
142 print "committer %s" % (committer)
143 print ""
81 if prefix != "": 144 if prefix != "":
82 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip()) 145 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
83 else: 146 else:
84 print changes[4] 147 print changes[4]
85 148 if prefix:
86 def catfile(args, ui, repo): 149 sys.stdout.write('\0')
87 doptions = {} 150
88 opts = [('s', 'stdin', None, 'stdin')] 151 def base(ui, repo, node1, node2):
89 args = fancyopts.fancyopts(args, opts, doptions) 152 """Output common ancestor information"""
90 153 node1 = repo.lookup(node1)
154 node2 = repo.lookup(node2)
155 n = repo.changelog.ancestor(node1, node2)
156 print hg.hex(n)
157
158 def catfile(ui, repo, type=None, r=None, **opts):
159 """cat a specific revision"""
91 # in stdin mode, every line except the commit is prefixed with two 160 # 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 161 # spaces. This way the our caller can find the commit without magic
93 # strings 162 # strings
94 # 163 #
95 prefix = "" 164 prefix = ""
96 if doptions['stdin']: 165 if opts['stdin']:
97 try: 166 try:
98 (type, r) = raw_input().split(' '); 167 (type, r) = raw_input().split(' ');
99 prefix = " " 168 prefix = " "
100 except EOFError: 169 except EOFError:
101 return 170 return
102 171
103 else: 172 else:
104 if len(args) < 2: 173 if not type or not r:
105 help() 174 ui.warn("cat-file: type or revision not supplied\n")
106 sys.exit(1) 175 commands.help_(ui, 'cat-file')
107 type = args[0]
108 r = args[1]
109 176
110 while r: 177 while r:
111 if type != "commit": 178 if type != "commit":
112 sys.stderr.write("aborting hg cat-file only understands commits\n") 179 sys.stderr.write("aborting hg cat-file only understands commits\n")
113 sys.exit(1); 180 sys.exit(1);
114 n = repo.lookup(r) 181 n = repo.lookup(r)
115 catcommit(repo, n, prefix) 182 catcommit(repo, n, prefix)
116 if doptions['stdin']: 183 if opts['stdin']:
117 try: 184 try:
118 (type, r) = raw_input().split(' '); 185 (type, r) = raw_input().split(' ');
119 except EOFError: 186 except EOFError:
120 break 187 break
121 else: 188 else:
124 # git rev-tree is a confusing thing. You can supply a number of 191 # 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 192 # commit sha1s on the command line, and it walks the commit history
126 # telling you which commits are reachable from the supplied ones via 193 # telling you which commits are reachable from the supplied ones via
127 # a bitmask based on arg position. 194 # a bitmask based on arg position.
128 # you can specify a commit to stop at by starting the sha1 with ^ 195 # you can specify a commit to stop at by starting the sha1 with ^
129 def revtree(args, repo, full="tree", maxnr=0): 196 def revtree(args, repo, full="tree", maxnr=0, parents=False):
197 def chlogwalk():
198 ch = repo.changelog
199 count = ch.count()
200 i = count
201 l = [0] * 100
202 chunk = 100
203 while True:
204 if chunk > i:
205 chunk = i
206 i = 0
207 else:
208 i -= chunk
209
210 for x in xrange(0, chunk):
211 if i + x >= count:
212 l[chunk - x:] = [0] * (chunk - x)
213 break
214 if full != None:
215 l[x] = ch.read(ch.node(i + x))
216 else:
217 l[x] = 1
218 for x in xrange(chunk-1, -1, -1):
219 if l[x] != 0:
220 yield (i + x, full != None and l[x] or None)
221 if i == 0:
222 break
223
130 # calculate and return the reachability bitmask for sha 224 # calculate and return the reachability bitmask for sha
131 def is_reachable(ar, reachable, sha): 225 def is_reachable(ar, reachable, sha):
132 if len(ar) == 0: 226 if len(ar) == 0:
133 return 1 227 return 1
134 mask = 0 228 mask = 0
170 if p in stop_sha1: 264 if p in stop_sha1:
171 break 265 break
172 266
173 # walk the repository looking for commits that are in our 267 # walk the repository looking for commits that are in our
174 # reachability graph 268 # reachability graph
175 for i in range(repo.changelog.count()-1, -1, -1): 269 #for i in range(repo.changelog.count()-1, -1, -1):
270 for i, changes in chlogwalk():
176 n = repo.changelog.node(i) 271 n = repo.changelog.node(i)
177 mask = is_reachable(want_sha1, reachable, n) 272 mask = is_reachable(want_sha1, reachable, n)
178 if mask: 273 if mask:
274 parentstr = ""
275 if parents:
276 pp = repo.changelog.parents(n)
277 if pp[0] != hg.nullid:
278 parentstr += " " + hg.hex(pp[0])
279 if pp[1] != hg.nullid:
280 parentstr += " " + hg.hex(pp[1])
179 if not full: 281 if not full:
180 print hg.hex(n) 282 print hg.hex(n) + parentstr
181 elif full is "commit": 283 elif full is "commit":
182 print hg.hex(n) 284 print hg.hex(n) + parentstr
183 catcommit(repo, n, ' ') 285 catcommit(repo, n, ' ', changes)
184 else: 286 else:
185 changes = repo.changelog.read(n)
186 (p1, p2) = repo.changelog.parents(n) 287 (p1, p2) = repo.changelog.parents(n)
187 (h, h1, h2) = map(hg.hex, (n, p1, p2)) 288 (h, h1, h2) = map(hg.hex, (n, p1, p2))
188 (i1, i2) = map(repo.changelog.rev, (p1, p2)) 289 (i1, i2) = map(repo.changelog.rev, (p1, p2))
189 290
190 date = changes[2].split(' ')[0] 291 date = changes[2].split(' ')[0]
201 count += 1 302 count += 1
202 303
203 # git rev-list tries to order things by date, and has the ability to stop 304 # 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 305 # at a given commit without walking the whole repo. TODO add the stop
205 # parameter 306 # parameter
206 def revlist(args, repo): 307 def revlist(ui, repo, *revs, **opts):
207 doptions = {} 308 """print revisions"""
208 opts = [('c', 'commit', None, 'commit'), 309 if opts['header']:
209 ('n', 'max-nr', 0, 'max-nr')]
210 args = fancyopts.fancyopts(args, opts, doptions)
211 if doptions['commit']:
212 full = "commit" 310 full = "commit"
213 else: 311 else:
214 full = None 312 full = None
215 for i in range(1, len(args)): 313 copy = [x for x in revs]
216 args[i] = '^' + args[i] 314 revtree(copy, repo, full, opts['max_count'], opts['parents'])
217 revtree(args, repo, full, doptions['max-nr']) 315
218 316 cmdtable = {
219 def catchterm(*args): 317 "git-diff-tree": (difftree, [('p', 'patch', None, 'generate patch'),
220 raise SignalInterrupt 318 ('r', 'recursive', None, 'recursive'),
221 319 ('P', 'pretty', None, 'pretty'),
222 def help(): 320 ('s', 'stdin', None, 'stdin'),
223 sys.stderr.write("commands:\n") 321 ('C', 'copy', None, 'detect copies'),
224 sys.stderr.write(" hgit cat-file [type] sha1\n") 322 ('S', 'search', "", 'search')],
225 sys.stderr.write(" hgit diff-tree [-p] [-r] sha1 sha1\n") 323 "hg git-diff-tree [options] node1 node2"),
226 sys.stderr.write(" hgit rev-tree [sha1 ... [^stop sha1]]\n") 324 "git-cat-file": (catfile, [('s', 'stdin', None, 'stdin')],
227 sys.stderr.write(" hgit rev-list [-c] [sha1 [stop sha1]\n") 325 "hg cat-file [options] type file"),
228 326 "git-merge-base": (base, [], "hg git-merge-base node node"),
229 cmd = sys.argv[1] 327 "git-rev-list": (revlist, [('H', 'header', None, 'header'),
230 args = sys.argv[2:] 328 ('t', 'topo-order', None, 'topo-order'),
231 u = ui.ui() 329 ('p', 'parents', None, 'parents'),
232 signal.signal(signal.SIGTERM, catchterm) 330 ('n', 'max-count', 0, 'max-count')],
233 repo = hg.repository(ui = u) 331 "hg git-rev-list [options] revs"),
234 332 }
235 if cmd == "diff-tree": 333
236 difftree(args, u, repo) 334 def reposetup(ui, repo):
237 335 pass
238 elif cmd == "cat-file":
239 catfile(args, u, repo)
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)