hgext/git/index.py
author Augie Fackler <augie@google.com>
Tue, 11 Feb 2020 00:44:59 -0500
changeset 44477 ad718271a9eb
child 44484 ec54b3d2af0b
permissions -rw-r--r--
git: skeleton of a new extension to _directly_ operate on git repos This is based in part of work I did years ago in hgit, but it's mostly new code since I'm using pygit2 instead of dulwich and the hg storage interfaces have improved. Some cleanup of old hgit code by Pulkit, which I greatly appreciate. test-git-interop.t does not cover a whole lot of cases, but it passes. It includes status, diff, making a new commit, and `hg annotate` working on the git repository. This is _not_ (yet) production quality code: this is an experiment. Known technical debt lurking in this implementation: * Writing bookmarks just totally ignores transactions. * The way progress is threaded down into the gitstore is awful. * Ideally we'd find a way to incrementally reindex DAGs. I'm not sure how to do that efficiently, so we might need a "known only fast-forwards" mode on the DAG indexer for use on `hg commit` and friends. * We don't even _try_ to do anything reasonable for `hg pull` or `hg push`. * Mercurial need an interface for the changelog type. Tests currently require git 2.24 as far as I'm aware: `git status` has some changed output that I didn't try and handle in a compatible way. This patch has produced some interesting cleanups, most recently on the manifest type. I expect continuing down this road will produce other meritorious cleanups throughout our code. Differential Revision: https://phab.mercurial-scm.org/D6734
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
44477
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     1
from __future__ import absolute_import
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     2
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     3
import collections
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     4
import os
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     5
import sqlite3
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     6
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     7
import pygit2
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     8
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
     9
from mercurial.i18n import _
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    10
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    11
from mercurial import (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    12
    encoding,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    13
    error,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    14
    node as nodemod,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    15
    pycompat,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    16
)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    17
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    18
from . import gitutil
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    19
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    20
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    21
_CURRENT_SCHEMA_VERSION = 1
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    22
_SCHEMA = (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    23
    """
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    24
CREATE TABLE refs (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    25
  -- node and name are unique together. There may be more than one name for
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    26
  -- a given node, and there may be no name at all for a given node (in the
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    27
  -- case of an anonymous hg head).
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    28
  node TEXT NOT NULL,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    29
  name TEXT
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    30
);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    31
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    32
-- The "possible heads" of the repository, which we use to figure out
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    33
-- if we need to re-walk the changelog.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    34
CREATE TABLE possible_heads (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    35
  node TEXT NOT NULL
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    36
);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    37
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    38
-- The topological heads of the changelog, which hg depends on.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    39
CREATE TABLE heads (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    40
  node TEXT NOT NULL
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    41
);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    42
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    43
-- A total ordering of the changelog
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    44
CREATE TABLE changelog (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    45
  rev INTEGER NOT NULL PRIMARY KEY,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    46
  node TEXT NOT NULL,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    47
  p1 TEXT,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    48
  p2 TEXT
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    49
);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    50
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    51
CREATE UNIQUE INDEX changelog_node_idx ON changelog(node);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    52
CREATE UNIQUE INDEX changelog_node_rev_idx ON changelog(rev, node);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    53
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    54
-- Changed files for each commit, which lets us dynamically build
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    55
-- filelogs.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    56
CREATE TABLE changedfiles (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    57
  node TEXT NOT NULL,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    58
  filename TEXT NOT NULL,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    59
  -- 40 zeroes for deletions
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    60
  filenode TEXT NOT NULL,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    61
-- to handle filelog parentage:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    62
  p1node TEXT,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    63
  p1filenode TEXT,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    64
  p2node TEXT,
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    65
  p2filenode TEXT
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    66
);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    67
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    68
CREATE INDEX changedfiles_nodes_idx
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    69
  ON changedfiles(node);
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    70
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    71
PRAGMA user_version=%d
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    72
"""
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    73
    % _CURRENT_SCHEMA_VERSION
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    74
)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    75
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    76
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    77
def _createdb(path):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    78
    # print('open db', path)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    79
    # import traceback
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    80
    # traceback.print_stack()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    81
    db = sqlite3.connect(encoding.strfromlocal(path))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    82
    db.text_factory = bytes
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    83
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    84
    res = db.execute('PRAGMA user_version').fetchone()[0]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    85
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    86
    # New database.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    87
    if res == 0:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    88
        for statement in _SCHEMA.split(';'):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    89
            db.execute(statement.strip())
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    90
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    91
        db.commit()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    92
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    93
    elif res == _CURRENT_SCHEMA_VERSION:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    94
        pass
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    95
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    96
    else:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    97
        raise error.Abort(_(b'sqlite database has unrecognized version'))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    98
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
    99
    db.execute('PRAGMA journal_mode=WAL')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   100
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   101
    return db
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   102
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   103
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   104
_OUR_ORDER = (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   105
    pygit2.GIT_SORT_TOPOLOGICAL | pygit2.GIT_SORT_TIME | pygit2.GIT_SORT_REVERSE
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   106
)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   107
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   108
_DIFF_FLAGS = 1 << 21  # GIT_DIFF_FORCE_BINARY, which isn't exposed by pygit2
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   109
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   110
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   111
def _find_nearest_ancestor_introducing_node(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   112
    db, gitrepo, file_path, walk_start, filenode
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   113
):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   114
    """Find the nearest ancestor that introduces a file node.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   115
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   116
    Args:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   117
      db: a handle to our sqlite database.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   118
      gitrepo: A pygit2.Repository instance.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   119
      file_path: the path of a file in the repo
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   120
      walk_start: a pygit2.Oid that is a commit where we should start walking
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   121
                  for our nearest ancestor.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   122
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   123
    Returns:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   124
      A hexlified SHA that is the commit ID of the next-nearest parent.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   125
    """
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   126
    assert isinstance(file_path, str), 'file_path must be str, got %r' % type(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   127
        file_path
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   128
    )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   129
    assert isinstance(filenode, str), 'filenode must be str, got %r' % type(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   130
        filenode
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   131
    )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   132
    parent_options = {
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   133
        row[0].decode('ascii')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   134
        for row in db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   135
            'SELECT node FROM changedfiles '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   136
            'WHERE filename = ? AND filenode = ?',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   137
            (file_path, filenode),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   138
        )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   139
    }
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   140
    inner_walker = gitrepo.walk(walk_start, _OUR_ORDER)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   141
    for w in inner_walker:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   142
        if w.id.hex in parent_options:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   143
            return w.id.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   144
    raise error.ProgrammingError(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   145
        'Unable to find introducing commit for %s node %s from %s',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   146
        (file_path, filenode, walk_start),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   147
    )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   148
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   149
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   150
def fill_in_filelog(gitrepo, db, startcommit, path, startfilenode):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   151
    """Given a starting commit and path, fill in a filelog's parent pointers.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   152
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   153
    Args:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   154
      gitrepo: a pygit2.Repository
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   155
      db: a handle to our sqlite database
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   156
      startcommit: a hexlified node id for the commit to start at
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   157
      path: the path of the file whose parent pointers we should fill in.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   158
      filenode: the hexlified node id of the file at startcommit
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   159
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   160
    TODO: make filenode optional
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   161
    """
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   162
    assert isinstance(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   163
        startcommit, str
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   164
    ), 'startcommit must be str, got %r' % type(startcommit)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   165
    assert isinstance(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   166
        startfilenode, str
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   167
    ), 'startfilenode must be str, got %r' % type(startfilenode)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   168
    visit = collections.deque([(startcommit, startfilenode)])
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   169
    while visit:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   170
        cnode, filenode = visit.popleft()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   171
        commit = gitrepo[cnode]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   172
        parents = []
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   173
        for parent in commit.parents:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   174
            t = parent.tree
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   175
            for comp in path.split('/'):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   176
                try:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   177
                    t = gitrepo[t[comp].id]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   178
                except KeyError:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   179
                    break
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   180
            else:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   181
                introducer = _find_nearest_ancestor_introducing_node(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   182
                    db, gitrepo, path, parent.id, t.id.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   183
                )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   184
                parents.append((introducer, t.id.hex))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   185
        p1node = p1fnode = p2node = p2fnode = gitutil.nullgit
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   186
        for par, parfnode in parents:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   187
            found = int(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   188
                db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   189
                    'SELECT COUNT(*) FROM changedfiles WHERE '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   190
                    'node = ? AND filename = ? AND filenode = ? AND '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   191
                    'p1node NOT NULL',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   192
                    (par, path, parfnode),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   193
                ).fetchone()[0]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   194
            )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   195
            if found == 0:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   196
                assert par is not None
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   197
                visit.append((par, parfnode))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   198
        if parents:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   199
            p1node, p1fnode = parents[0]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   200
        if len(parents) == 2:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   201
            p2node, p2fnode = parents[1]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   202
        if len(parents) > 2:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   203
            raise error.ProgrammingError(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   204
                b"git support can't handle octopus merges"
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   205
            )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   206
        db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   207
            'UPDATE changedfiles SET '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   208
            'p1node = ?, p1filenode = ?, p2node = ?, p2filenode = ? '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   209
            'WHERE node = ? AND filename = ? AND filenode = ?',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   210
            (p1node, p1fnode, p2node, p2fnode, commit.id.hex, path, filenode),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   211
        )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   212
    db.commit()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   213
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   214
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   215
def _index_repo(gitrepo, db, progress_factory=lambda *args, **kwargs: None):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   216
    # Identify all references so we can tell the walker to visit all of them.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   217
    all_refs = gitrepo.listall_references()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   218
    possible_heads = set()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   219
    prog = progress_factory(b'refs')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   220
    for pos, ref in enumerate(all_refs):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   221
        if prog is not None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   222
            prog.update(pos)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   223
        if not (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   224
            ref.startswith('refs/heads/')  # local branch
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   225
            or ref.startswith('refs/tags/')  # tag
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   226
            or ref.startswith('refs/remotes/')  # remote branch
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   227
            or ref.startswith('refs/hg/')  # from this extension
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   228
        ):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   229
            continue
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   230
        try:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   231
            start = gitrepo.lookup_reference(ref).peel(pygit2.GIT_OBJ_COMMIT)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   232
        except ValueError:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   233
            # No commit to be found, so we don't care for hg's purposes.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   234
            continue
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   235
        possible_heads.add(start.id)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   236
    # Optimization: if the list of heads hasn't changed, don't
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   237
    # reindex, the changelog. This doesn't matter on small
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   238
    # repositories, but on even moderately deep histories (eg cpython)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   239
    # this is a very important performance win.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   240
    #
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   241
    # TODO: we should figure out how to incrementally index history
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   242
    # (preferably by detecting rewinds!) so that we don't have to do a
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   243
    # full changelog walk every time a new commit is created.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   244
    cache_heads = {x[0] for x in db.execute('SELECT node FROM possible_heads')}
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   245
    walker = None
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   246
    cur_cache_heads = {h.hex for h in possible_heads}
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   247
    if cur_cache_heads == cache_heads:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   248
        return
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   249
    for start in possible_heads:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   250
        if walker is None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   251
            walker = gitrepo.walk(start, _OUR_ORDER)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   252
        else:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   253
            walker.push(start)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   254
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   255
    # Empty out the existing changelog. Even for large-ish histories
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   256
    # we can do the top-level "walk all the commits" dance very
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   257
    # quickly as long as we don't need to figure out the changed files
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   258
    # list.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   259
    db.execute('DELETE FROM changelog')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   260
    if prog is not None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   261
        prog.complete()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   262
    prog = progress_factory(b'commits')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   263
    # This walker is sure to visit all the revisions in history, but
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   264
    # only once.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   265
    for pos, commit in enumerate(walker):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   266
        if prog is not None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   267
            prog.update(pos)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   268
        p1 = p2 = nodemod.nullhex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   269
        if len(commit.parents) > 2:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   270
            raise error.ProgrammingError(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   271
                (
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   272
                    b"git support can't handle octopus merges, "
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   273
                    b"found a commit with %d parents :("
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   274
                )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   275
                % len(commit.parents)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   276
            )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   277
        if commit.parents:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   278
            p1 = commit.parents[0].id.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   279
        if len(commit.parents) == 2:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   280
            p2 = commit.parents[1].id.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   281
        db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   282
            'INSERT INTO changelog (rev, node, p1, p2) VALUES(?, ?, ?, ?)',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   283
            (pos, commit.id.hex, p1, p2),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   284
        )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   285
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   286
        num_changedfiles = db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   287
            "SELECT COUNT(*) from changedfiles WHERE node = ?",
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   288
            (commit.id.hex,),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   289
        ).fetchone()[0]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   290
        if not num_changedfiles:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   291
            files = {}
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   292
            # I *think* we only need to check p1 for changed files
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   293
            # (and therefore linkrevs), because any node that would
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   294
            # actually have this commit as a linkrev would be
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   295
            # completely new in this rev.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   296
            p1 = commit.parents[0].id.hex if commit.parents else None
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   297
            if p1 is not None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   298
                patchgen = gitrepo.diff(p1, commit.id.hex, flags=_DIFF_FLAGS)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   299
            else:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   300
                patchgen = commit.tree.diff_to_tree(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   301
                    swap=True, flags=_DIFF_FLAGS
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   302
                )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   303
            new_files = (p.delta.new_file for p in patchgen)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   304
            files = {
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   305
                nf.path: nf.id.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   306
                for nf in new_files
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   307
                if nf.id.raw != nodemod.nullid
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   308
            }
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   309
            for p, n in files.items():
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   310
                # We intentionally set NULLs for any file parentage
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   311
                # information so it'll get demand-computed later. We
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   312
                # used to do it right here, and it was _very_ slow.
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   313
                db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   314
                    'INSERT INTO changedfiles ('
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   315
                    'node, filename, filenode, p1node, p1filenode, p2node, '
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   316
                    'p2filenode) VALUES(?, ?, ?, ?, ?, ?, ?)',
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   317
                    (commit.id.hex, p, n, None, None, None, None),
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   318
                )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   319
    db.execute('DELETE FROM heads')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   320
    db.execute('DELETE FROM possible_heads')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   321
    for hid in possible_heads:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   322
        h = hid.hex
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   323
        db.execute('INSERT INTO possible_heads (node) VALUES(?)', (h,))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   324
        haschild = db.execute(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   325
            'SELECT COUNT(*) FROM changelog WHERE p1 = ? OR p2 = ?', (h, h)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   326
        ).fetchone()[0]
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   327
        if not haschild:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   328
            db.execute('INSERT INTO heads (node) VALUES(?)', (h,))
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   329
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   330
    db.commit()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   331
    if prog is not None:
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   332
        prog.complete()
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   333
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   334
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   335
def get_index(gitrepo, progress_factory=lambda *args, **kwargs: None):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   336
    cachepath = os.path.join(
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   337
        pycompat.fsencode(gitrepo.path), b'..', b'.hg', b'cache'
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   338
    )
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   339
    if not os.path.exists(cachepath):
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   340
        os.makedirs(cachepath)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   341
    dbpath = os.path.join(cachepath, b'git-commits.sqlite')
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   342
    db = _createdb(dbpath)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   343
    # TODO check against gitrepo heads before doing a full index
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   344
    # TODO thread a ui.progress call into this layer
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   345
    _index_repo(gitrepo, db, progress_factory)
ad718271a9eb git: skeleton of a new extension to _directly_ operate on git repos
Augie Fackler <augie@google.com>
parents:
diff changeset
   346
    return db