contrib/hgit
changeset 1239 29f17e083e84
parent 740 d2422f10c136
child 1243 9d10f89b75a5
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)