contrib/hgit
changeset 267 497aa6d276d2
child 280 a69c3b2957d1
equal deleted inserted replaced
266:4af7677de4a9 267:497aa6d276d2
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Minimal support for git commands on an hg repository
       
     4 #
       
     5 # Copyright 2005 Chris Mason <mason@suse.com>
       
     6 #
       
     7 # This software may be used and distributed according to the terms
       
     8 # of the GNU General Public License, incorporated herein by reference.
       
     9 
       
    10 import time, sys, signal
       
    11 from mercurial import hg, mdiff, fancyopts, commands, ui
       
    12     
       
    13 def difftree(args, repo):
       
    14     def __difftree(repo, files = None, node1 = None, node2 = None):
       
    15 	def date(c):
       
    16 	    return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
       
    17 
       
    18 	if node2:
       
    19 	    change = repo.changelog.read(node2)
       
    20 	    mmap2 = repo.manifest.read(change[0])
       
    21 	    (c, a, d) = repo.diffrevs(node1, node2)
       
    22 	    def read(f): return repo.file(f).read(mmap2[f])
       
    23 	    date2 = date(change)
       
    24 	else:
       
    25 	    date2 = time.asctime()
       
    26 	    (c, a, d, u) = repo.diffdir(repo.root, node1)
       
    27 	    if not node1:
       
    28 		node1 = repo.dirstate.parents()[0]
       
    29 	    def read(f): return file(os.path.join(repo.root, f)).read()
       
    30 
       
    31 	change = repo.changelog.read(node1)
       
    32 	mmap = repo.manifest.read(change[0])
       
    33 	date1 = date(change)
       
    34 	empty = "0" * 40;
       
    35 
       
    36 	if files:
       
    37 	    c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
       
    38 
       
    39 	for f in c:
       
    40 	    # TODO get file permissions
       
    41 	    print ":100664 100664 %s %s %s %s" % (hg.hex(mmap[f]), 
       
    42 	                                              hg.hex(mmap2[f]), f, f)
       
    43 	for f in a:
       
    44 	    print ":000000 100664 %s %s %s %s" % (empty, hg.hex(mmap2[f]), f, f)
       
    45 	for f in d:
       
    46 	    print ":100664 000000 %s %s %s %s" % (hg.hex(mmap[f]), empty, f, f)
       
    47     ##
       
    48 
       
    49     revs = []
       
    50     if args:
       
    51 	doptions = {}
       
    52 	opts = [('p', 'patch', None, 'patch'),
       
    53 		('r', 'recursive', None, 'recursive')]
       
    54 	args = fancyopts.fancyopts(args, opts, doptions,
       
    55                                    'hg diff-tree [options] sha1 sha1')
       
    56 
       
    57     if len(args) < 2:
       
    58 	help()
       
    59         sys.exit(1)
       
    60     revs.append(repo.lookup(args[0]))
       
    61     revs.append(repo.lookup(args[1]))
       
    62     args = args[2:]
       
    63     if doptions['patch']:
       
    64 	commands.dodiff(repo, args, *revs)
       
    65     else:
       
    66 	__difftree(repo, args, *revs)
       
    67 
       
    68 def catcommit(repo, n, prefix):
       
    69     nlprefix = '\n' + prefix;
       
    70     changes = repo.changelog.read(n)
       
    71     (p1, p2) = repo.changelog.parents(n)
       
    72     (h, h1, h2) = map(hg.hex, (n, p1, p2))
       
    73     (i1, i2) = map(repo.changelog.rev, (p1, p2))
       
    74     print "tree %s" % (h)
       
    75     if i1 != -1: print "%sparent %s" % (prefix, h1)
       
    76     if i2 != -1: print "%sparent %s" % (prefix, h2)
       
    77     date_ar = changes[2].split(' ')
       
    78     date = int(float(date_ar[0]))
       
    79     print "%sauthor <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
       
    80     print "%scommitter <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
       
    81     print prefix
       
    82     if prefix != "":
       
    83 	print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
       
    84     else:
       
    85 	print changes[4]
       
    86 
       
    87 def catfile(args, ui, repo):
       
    88     doptions = {}
       
    89     opts = [('s', 'stdin', None, 'stdin')]
       
    90     args = fancyopts.fancyopts(args, opts, doptions,
       
    91 			       'hg cat-file type sha1')
       
    92 
       
    93     # in stdin mode, every line except the commit is prefixed with two
       
    94     # spaces.  This way the our caller can find the commit without magic
       
    95     # strings
       
    96     #
       
    97     prefix = ""
       
    98     if doptions['stdin']:
       
    99 	try:
       
   100 	    (type, r) = raw_input().split(' ');
       
   101 	    prefix = "  "
       
   102 	except EOFError:
       
   103 	    return
       
   104 
       
   105     else:
       
   106 	if len(args) < 2:
       
   107 	    help()
       
   108 	    sys.exit(1)
       
   109         type = args[0]
       
   110 	r = args[1]
       
   111 
       
   112     while r:
       
   113 	if type != "commit":
       
   114 	    sys.stderr.write("aborting hg cat-file only understands commits\n")
       
   115 	    sys.exit(1);
       
   116 	n = repo.changelog.lookup(r)
       
   117 	catcommit(repo, n, prefix)
       
   118 	if doptions['stdin']:
       
   119 	    try:
       
   120 		(type, r) = raw_input().split(' ');
       
   121 	    except EOFError:
       
   122 		break
       
   123 	else:
       
   124 	    break
       
   125 
       
   126 # git rev-tree is a confusing thing.  You can supply a number of
       
   127 # commit sha1s on the command line, and it walks the commit history
       
   128 # telling you which commits are reachable from the supplied ones via
       
   129 # a bitmask based on arg position.
       
   130 # you can specify a commit to stop at by starting the sha1 with ^
       
   131 def revtree(args, repo):
       
   132     # calculate and return the reachability bitmask for sha
       
   133     def is_reachable(ar, reachable, sha):
       
   134 	if len(ar) == 0:
       
   135 	    return 1
       
   136 	mask = 0
       
   137 	for i in range(len(ar)):
       
   138 	    if sha in reachable[i]:
       
   139 	        mask |= 1 << i
       
   140 
       
   141 	return mask
       
   142 
       
   143     reachable = []
       
   144     stop_sha1 = []
       
   145     want_sha1 = []
       
   146 
       
   147     # figure out which commits they are asking for and which ones they
       
   148     # want us to stop on
       
   149     for i in range(len(args)):
       
   150         if args[i].count('^'):
       
   151 	    s = args[i].split('^')[1]
       
   152 	    stop_sha1.append(repo.changelog.lookup(s))
       
   153 	    want_sha1.append(s)
       
   154 	elif args[i] != 'HEAD':
       
   155 	    want_sha1.append(args[i])
       
   156     # calculate the graph for the supplied commits
       
   157     for i in range(len(want_sha1)):
       
   158 	reachable.append({});
       
   159 	n = repo.changelog.lookup(want_sha1[i]);
       
   160 	visit = [n];
       
   161 	reachable[i][n] = 1
       
   162 	while visit:
       
   163 	    n = visit.pop(0)
       
   164 	    if n in stop_sha1:
       
   165 		break
       
   166 	    for p in repo.changelog.parents(n):
       
   167 		if p not in reachable[i]:
       
   168 		    reachable[i][p] = 1
       
   169 		    visit.append(p)
       
   170 		if p in stop_sha1:
       
   171 		    break
       
   172     # walk the repository looking for commits that are in our
       
   173     # reachability graph
       
   174     for i in range(repo.changelog.count()):
       
   175 	n = repo.changelog.node(i)
       
   176 	mask = is_reachable(want_sha1, reachable, n)
       
   177 	if mask:
       
   178 	    changes = repo.changelog.read(n)
       
   179 	    (p1, p2) = repo.changelog.parents(n)
       
   180 	    (h, h1, h2) = map(hg.hex, (n, p1, p2))
       
   181 	    (i1, i2) = map(repo.changelog.rev, (p1, p2))
       
   182 
       
   183 	    date = changes[2].split(' ')[0]
       
   184 	    print "%s %s:%s" % (date, h, mask),
       
   185 	    mask = is_reachable(want_sha1, reachable, p1)
       
   186 	    if i1 != -1 and mask > 0:
       
   187 		print "%s:%s " % (h1, mask),
       
   188 	    mask = is_reachable(want_sha1, reachable, p2)
       
   189 	    if i2 != -1 and mask > 0:
       
   190 		print "%s:%s " % (h2, mask),
       
   191 	    print ""
       
   192 
       
   193 # git rev-list tries to order things by date, and has the ability to stop
       
   194 # at a given commit without walking the whole repo.  TODO add the stop
       
   195 # parameter
       
   196 def revlist(args, repo):
       
   197     doptions = {}
       
   198     opts = [('c', 'commit', None, 'commit')]
       
   199     args = fancyopts.fancyopts(args, opts, doptions,
       
   200 			       'hg rev-list')
       
   201     for i in range(repo.changelog.count()):
       
   202 	n = repo.changelog.node(i)
       
   203 	print hg.hex(n)
       
   204 	if doptions['commit']:
       
   205 	    catcommit(repo, n, '  ')
       
   206 
       
   207 def catchterm(*args):
       
   208     raise SignalInterrupt
       
   209 
       
   210 def help():
       
   211     sys.stderr.write("commands:\n")
       
   212     sys.stderr.write("  hgit cat-file [type] sha1\n")
       
   213     sys.stderr.write("  hgit diff-tree [-p] [-r] sha1 sha1\n")
       
   214     sys.stderr.write("  hgit rev-tree [sha1 ... [^stop sha1]]\n")
       
   215     sys.stderr.write("  hgit rev-list [-c]\n")
       
   216 
       
   217 cmd = sys.argv[1]
       
   218 args = sys.argv[2:]
       
   219 u = ui.ui()
       
   220 signal.signal(signal.SIGTERM, catchterm)
       
   221 repo = hg.repository(ui = u)
       
   222 
       
   223 if cmd == "diff-tree":
       
   224     difftree(args, repo)
       
   225 
       
   226 elif cmd == "cat-file":
       
   227     catfile(args, ui, repo)
       
   228 
       
   229 elif cmd == "rev-tree":
       
   230     revtree(args, repo)
       
   231 
       
   232 elif cmd == "rev-list":
       
   233     revlist(args, repo)
       
   234 
       
   235 elif cmd == "help":
       
   236     help()
       
   237 
       
   238 else:
       
   239     if cmd: sys.stderr.write("unknown command\n\n")
       
   240     help()
       
   241     sys.exit(1)
       
   242 
       
   243 sys.exit(0)