hgext/git/gitlog.py
changeset 46113 59fa3890d40a
parent 45939 66f6ca2b7aee
child 46780 6266d19556ad
equal deleted inserted replaced
46112:d6afa9c149c3 46113:59fa3890d40a
     1 from __future__ import absolute_import
     1 from __future__ import absolute_import
     2 
     2 
     3 from mercurial.i18n import _
     3 from mercurial.i18n import _
     4 
     4 
       
     5 from mercurial.node import (
       
     6     bin,
       
     7     hex,
       
     8     nullhex,
       
     9     nullid,
       
    10     nullrev,
       
    11     wdirhex,
       
    12 )
     5 from mercurial import (
    13 from mercurial import (
     6     ancestor,
    14     ancestor,
     7     changelog as hgchangelog,
    15     changelog as hgchangelog,
     8     dagop,
    16     dagop,
     9     encoding,
    17     encoding,
    10     error,
    18     error,
    11     manifest,
    19     manifest,
    12     node as nodemod,
       
    13     pycompat,
    20     pycompat,
    14 )
    21 )
    15 from mercurial.interfaces import (
    22 from mercurial.interfaces import (
    16     repository,
    23     repository,
    17     util as interfaceutil,
    24     util as interfaceutil,
    37         return int(
    44         return int(
    38             self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
    45             self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
    39         )
    46         )
    40 
    47 
    41     def rev(self, n):
    48     def rev(self, n):
    42         if n == nodemod.nullid:
    49         if n == nullid:
    43             return -1
    50             return -1
    44         t = self._db.execute(
    51         t = self._db.execute(
    45             'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
    52             'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
    46         ).fetchone()
    53         ).fetchone()
    47         if t is None:
    54         if t is None:
    48             raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
    55             raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
    49         return t[0]
    56         return t[0]
    50 
    57 
    51     def node(self, r):
    58     def node(self, r):
    52         if r == nodemod.nullrev:
    59         if r == nullrev:
    53             return nodemod.nullid
    60             return nullid
    54         t = self._db.execute(
    61         t = self._db.execute(
    55             'SELECT node FROM changelog WHERE rev = ?', (r,)
    62             'SELECT node FROM changelog WHERE rev = ?', (r,)
    56         ).fetchone()
    63         ).fetchone()
    57         if t is None:
    64         if t is None:
    58             raise error.LookupError(r, b'00changelog.i', _(b'no node'))
    65             raise error.LookupError(r, b'00changelog.i', _(b'no node'))
    59         return nodemod.bin(t[0])
    66         return bin(t[0])
    60 
    67 
    61     def hasnode(self, n):
    68     def hasnode(self, n):
    62         t = self._db.execute(
    69         t = self._db.execute(
    63             'SELECT node FROM changelog WHERE node = ?', (n,)
    70             'SELECT node FROM changelog WHERE node = ?', (n,)
    64         ).fetchone()
    71         ).fetchone()
   121         return baselogindex(self)
   128         return baselogindex(self)
   122 
   129 
   123     @property
   130     @property
   124     def nodemap(self):
   131     def nodemap(self):
   125         r = {
   132         r = {
   126             nodemod.bin(v[0]): v[1]
   133             bin(v[0]): v[1]
   127             for v in self._db.execute('SELECT node, rev FROM changelog')
   134             for v in self._db.execute('SELECT node, rev FROM changelog')
   128         }
   135         }
   129         r[nodemod.nullid] = nodemod.nullrev
   136         r[nullid] = nullrev
   130         return r
   137         return r
   131 
   138 
   132     def tip(self):
   139     def tip(self):
   133         t = self._db.execute(
   140         t = self._db.execute(
   134             'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
   141             'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
   135         ).fetchone()
   142         ).fetchone()
   136         if t:
   143         if t:
   137             return nodemod.bin(t[0])
   144             return bin(t[0])
   138         return nodemod.nullid
   145         return nullid
   139 
   146 
   140     def revs(self, start=0, stop=None):
   147     def revs(self, start=0, stop=None):
   141         if stop is None:
   148         if stop is None:
   142             stop = self.tip()
   149             stop = self.tip()
   143         t = self._db.execute(
   150         t = self._db.execute(
   153             'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
   160             'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
   154         )
   161         )
   155         return next(t)
   162         return next(t)
   156 
   163 
   157     def _partialmatch(self, id):
   164     def _partialmatch(self, id):
   158         if nodemod.wdirhex.startswith(id):
   165         if wdirhex.startswith(id):
   159             raise error.WdirUnsupported
   166             raise error.WdirUnsupported
   160         candidates = [
   167         candidates = [
   161             nodemod.bin(x[0])
   168             bin(x[0])
   162             for x in self._db.execute(
   169             for x in self._db.execute(
   163                 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
   170                 'SELECT node FROM changelog WHERE node LIKE ?', (id + b'%',)
   164             )
   171             )
   165         ]
   172         ]
   166         if nodemod.nullhex.startswith(id):
   173         if nullhex.startswith(id):
   167             candidates.append(nodemod.nullid)
   174             candidates.append(nullid)
   168         if len(candidates) > 1:
   175         if len(candidates) > 1:
   169             raise error.AmbiguousPrefixLookupError(
   176             raise error.AmbiguousPrefixLookupError(
   170                 id, b'00changelog.i', _(b'ambiguous identifier')
   177                 id, b'00changelog.i', _(b'ambiguous identifier')
   171             )
   178             )
   172         if candidates:
   179         if candidates:
   175 
   182 
   176     def flags(self, rev):
   183     def flags(self, rev):
   177         return 0
   184         return 0
   178 
   185 
   179     def shortest(self, node, minlength=1):
   186     def shortest(self, node, minlength=1):
   180         nodehex = nodemod.hex(node)
   187         nodehex = hex(node)
   181         for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
   188         for attempt in pycompat.xrange(minlength, len(nodehex) + 1):
   182             candidate = nodehex[:attempt]
   189             candidate = nodehex[:attempt]
   183             matches = int(
   190             matches = int(
   184                 self._db.execute(
   191                 self._db.execute(
   185                     'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
   192                     'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
   207         if isinstance(nodeorrev, int):
   214         if isinstance(nodeorrev, int):
   208             n = self.node(nodeorrev)
   215             n = self.node(nodeorrev)
   209         else:
   216         else:
   210             n = nodeorrev
   217             n = nodeorrev
   211         # handle looking up nullid
   218         # handle looking up nullid
   212         if n == nodemod.nullid:
   219         if n == nullid:
   213             return hgchangelog._changelogrevision(extra={})
   220             return hgchangelog._changelogrevision(extra={})
   214         hn = gitutil.togitnode(n)
   221         hn = gitutil.togitnode(n)
   215         # We've got a real commit!
   222         # We've got a real commit!
   216         files = [
   223         files = [
   217             r[0]
   224             r[0]
   224         filesremoved = [
   231         filesremoved = [
   225             r[0]
   232             r[0]
   226             for r in self._db.execute(
   233             for r in self._db.execute(
   227                 'SELECT filename FROM changedfiles '
   234                 'SELECT filename FROM changedfiles '
   228                 'WHERE node = ? and filenode = ?',
   235                 'WHERE node = ? and filenode = ?',
   229                 (hn, nodemod.nullhex),
   236                 (hn, nullhex),
   230             )
   237             )
   231         ]
   238         ]
   232         c = self.gitrepo[hn]
   239         c = self.gitrepo[hn]
   233         return hgchangelog._changelogrevision(
   240         return hgchangelog._changelogrevision(
   234             manifest=n,  # pretend manifest the same as the commit node
   241             manifest=n,  # pretend manifest the same as the commit node
   265 
   272 
   266         'common' is a list of revision numbers. If common is not supplied, uses
   273         'common' is a list of revision numbers. If common is not supplied, uses
   267         nullrev.
   274         nullrev.
   268         """
   275         """
   269         if common is None:
   276         if common is None:
   270             common = [nodemod.nullrev]
   277             common = [nullrev]
   271 
   278 
   272         return ancestor.incrementalmissingancestors(self.parentrevs, common)
   279         return ancestor.incrementalmissingancestors(self.parentrevs, common)
   273 
   280 
   274     def findmissing(self, common=None, heads=None):
   281     def findmissing(self, common=None, heads=None):
   275         """Return the ancestors of heads that are not ancestors of common.
   282         """Return the ancestors of heads that are not ancestors of common.
   285 
   292 
   286         'heads' and 'common' are both lists of node IDs.  If heads is
   293         'heads' and 'common' are both lists of node IDs.  If heads is
   287         not supplied, uses all of the revlog's heads.  If common is not
   294         not supplied, uses all of the revlog's heads.  If common is not
   288         supplied, uses nullid."""
   295         supplied, uses nullid."""
   289         if common is None:
   296         if common is None:
   290             common = [nodemod.nullid]
   297             common = [nullid]
   291         if heads is None:
   298         if heads is None:
   292             heads = self.heads()
   299             heads = self.heads()
   293 
   300 
   294         common = [self.rev(n) for n in common]
   301         common = [self.rev(n) for n in common]
   295         heads = [self.rev(n) for n in heads]
   302         heads = [self.rev(n) for n in heads]
   300     def children(self, node):
   307     def children(self, node):
   301         """find the children of a given node"""
   308         """find the children of a given node"""
   302         c = []
   309         c = []
   303         p = self.rev(node)
   310         p = self.rev(node)
   304         for r in self.revs(start=p + 1):
   311         for r in self.revs(start=p + 1):
   305             prevs = [pr for pr in self.parentrevs(r) if pr != nodemod.nullrev]
   312             prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
   306             if prevs:
   313             if prevs:
   307                 for pr in prevs:
   314                 for pr in prevs:
   308                     if pr == p:
   315                     if pr == p:
   309                         c.append(self.node(r))
   316                         c.append(self.node(r))
   310             elif p == nodemod.nullrev:
   317             elif p == nullrev:
   311                 c.append(self.node(r))
   318                 c.append(self.node(r))
   312         return c
   319         return c
   313 
   320 
   314     def reachableroots(self, minroot, heads, roots, includepath=False):
   321     def reachableroots(self, minroot, heads, roots, includepath=False):
   315         return dagop._reachablerootspure(
   322         return dagop._reachablerootspure(
   321         a, b = self.rev(a), self.rev(b)
   328         a, b = self.rev(a), self.rev(b)
   322         return self.isancestorrev(a, b)
   329         return self.isancestorrev(a, b)
   323 
   330 
   324     # Cleanup opportunity: this is *identical* to the revlog.py version
   331     # Cleanup opportunity: this is *identical* to the revlog.py version
   325     def isancestorrev(self, a, b):
   332     def isancestorrev(self, a, b):
   326         if a == nodemod.nullrev:
   333         if a == nullrev:
   327             return True
   334             return True
   328         elif a == b:
   335         elif a == b:
   329             return True
   336             return True
   330         elif a > b:
   337         elif a > b:
   331             return False
   338             return False
   335         n = self.node(rev)
   342         n = self.node(rev)
   336         hn = gitutil.togitnode(n)
   343         hn = gitutil.togitnode(n)
   337         if hn != gitutil.nullgit:
   344         if hn != gitutil.nullgit:
   338             c = self.gitrepo[hn]
   345             c = self.gitrepo[hn]
   339         else:
   346         else:
   340             return nodemod.nullrev, nodemod.nullrev
   347             return nullrev, nullrev
   341         p1 = p2 = nodemod.nullrev
   348         p1 = p2 = nullrev
   342         if c.parents:
   349         if c.parents:
   343             p1 = self.rev(c.parents[0].id.raw)
   350             p1 = self.rev(c.parents[0].id.raw)
   344             if len(c.parents) > 2:
   351             if len(c.parents) > 2:
   345                 raise error.Abort(b'TODO octopus merge handling')
   352                 raise error.Abort(b'TODO octopus merge handling')
   346             if len(c.parents) == 2:
   353             if len(c.parents) == 2:
   384         filesadded=None,
   391         filesadded=None,
   385         filesremoved=None,
   392         filesremoved=None,
   386     ):
   393     ):
   387         parents = []
   394         parents = []
   388         hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
   395         hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
   389         if p1 != nodemod.nullid:
   396         if p1 != nullid:
   390             parents.append(hp1)
   397             parents.append(hp1)
   391         if p2 and p2 != nodemod.nullid:
   398         if p2 and p2 != nullid:
   392             parents.append(hp2)
   399             parents.append(hp2)
   393         assert date is not None
   400         assert date is not None
   394         timestamp, tz = date
   401         timestamp, tz = date
   395         sig = pygit2.Signature(
   402         sig = pygit2.Signature(
   396             encoding.unifromlocal(stringutil.person(user)),
   403             encoding.unifromlocal(stringutil.person(user)),
   417 class manifestlog(baselog):
   424 class manifestlog(baselog):
   418     def __getitem__(self, node):
   425     def __getitem__(self, node):
   419         return self.get(b'', node)
   426         return self.get(b'', node)
   420 
   427 
   421     def get(self, relpath, node):
   428     def get(self, relpath, node):
   422         if node == nodemod.nullid:
   429         if node == nullid:
   423             # TODO: this should almost certainly be a memgittreemanifestctx
   430             # TODO: this should almost certainly be a memgittreemanifestctx
   424             return manifest.memtreemanifestctx(self, relpath)
   431             return manifest.memtreemanifestctx(self, relpath)
   425         commit = self.gitrepo[gitutil.togitnode(node)]
   432         commit = self.gitrepo[gitutil.togitnode(node)]
   426         t = commit.tree
   433         t = commit.tree
   427         if relpath:
   434         if relpath:
   438         super(filelog, self).__init__(gr, db)
   445         super(filelog, self).__init__(gr, db)
   439         assert isinstance(path, bytes)
   446         assert isinstance(path, bytes)
   440         self.path = path
   447         self.path = path
   441 
   448 
   442     def read(self, node):
   449     def read(self, node):
   443         if node == nodemod.nullid:
   450         if node == nullid:
   444             return b''
   451             return b''
   445         return self.gitrepo[gitutil.togitnode(node)].data
   452         return self.gitrepo[gitutil.togitnode(node)].data
   446 
   453 
   447     def lookup(self, node):
   454     def lookup(self, node):
   448         if len(node) not in (20, 40):
   455         if len(node) not in (20, 40):
   449             node = int(node)
   456             node = int(node)
   450         if isinstance(node, int):
   457         if isinstance(node, int):
   451             assert False, b'todo revnums for nodes'
   458             assert False, b'todo revnums for nodes'
   452         if len(node) == 40:
   459         if len(node) == 40:
   453             node = nodemod.bin(node)
   460             node = bin(node)
   454         hnode = gitutil.togitnode(node)
   461         hnode = gitutil.togitnode(node)
   455         if hnode in self.gitrepo:
   462         if hnode in self.gitrepo:
   456             return node
   463             return node
   457         raise error.LookupError(self.path, node, _(b'no match found'))
   464         raise error.LookupError(self.path, node, _(b'no match found'))
   458 
   465 
   498 ''',
   505 ''',
   499             (rev, pycompat.fsdecode(self.path)),
   506             (rev, pycompat.fsdecode(self.path)),
   500         ).fetchone()
   507         ).fetchone()
   501         if maybe is None:
   508         if maybe is None:
   502             raise IndexError('gitlog %r out of range %d' % (self.path, rev))
   509             raise IndexError('gitlog %r out of range %d' % (self.path, rev))
   503         return nodemod.bin(maybe[0])
   510         return bin(maybe[0])
   504 
   511 
   505     def parents(self, node):
   512     def parents(self, node):
   506         gn = gitutil.togitnode(node)
   513         gn = gitutil.togitnode(node)
   507         gp = pycompat.fsdecode(self.path)
   514         gp = pycompat.fsdecode(self.path)
   508         ps = []
   515         ps = []
   523                 if pycompat.ispy3:
   530                 if pycompat.ispy3:
   524                     commit = commit.decode('ascii')
   531                     commit = commit.decode('ascii')
   525                 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
   532                 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
   526                 return self.parents(node)
   533                 return self.parents(node)
   527             else:
   534             else:
   528                 ps.append(nodemod.bin(p))
   535                 ps.append(bin(p))
   529         return ps
   536         return ps
   530 
   537 
   531     def renamed(self, node):
   538     def renamed(self, node):
   532         # TODO: renames/copies
   539         # TODO: renames/copies
   533         return False
   540         return False