git-remote-testgit.pyon commit Merge branch 'maint' (b43688a)
   1#!/usr/bin/env python
   2
   3import hashlib
   4import sys
   5import os
   6sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
   7
   8from git_remote_helpers.util import die, debug, warn
   9from git_remote_helpers.git.repo import GitRepo
  10from git_remote_helpers.git.exporter import GitExporter
  11from git_remote_helpers.git.importer import GitImporter
  12from git_remote_helpers.git.non_local import NonLocalGit
  13
  14def get_repo(alias, url):
  15    """Returns a git repository object initialized for usage.
  16    """
  17
  18    repo = GitRepo(url)
  19    repo.get_revs()
  20    repo.get_head()
  21
  22    hasher = hashlib.sha1()
  23    hasher.update(repo.path)
  24    repo.hash = hasher.hexdigest()
  25
  26    repo.get_base_path = lambda base: os.path.join(
  27        base, 'info', 'fast-import', repo.hash)
  28
  29    prefix = 'refs/testgit/%s/' % alias
  30    debug("prefix: '%s'", prefix)
  31
  32    repo.gitdir = ""
  33    repo.alias = alias
  34    repo.prefix = prefix
  35
  36    repo.exporter = GitExporter(repo)
  37    repo.importer = GitImporter(repo)
  38    repo.non_local = NonLocalGit(repo)
  39
  40    return repo
  41
  42
  43def local_repo(repo, path):
  44    """Returns a git repository object initalized for usage.
  45    """
  46
  47    local = GitRepo(path)
  48
  49    local.non_local = None
  50    local.gitdir = repo.gitdir
  51    local.alias = repo.alias
  52    local.prefix = repo.prefix
  53    local.hash = repo.hash
  54    local.get_base_path = repo.get_base_path
  55    local.exporter = GitExporter(local)
  56    local.importer = GitImporter(local)
  57
  58    return local
  59
  60
  61def do_capabilities(repo, args):
  62    """Prints the supported capabilities.
  63    """
  64
  65    print "import"
  66    print "export"
  67    print "gitdir"
  68    print "refspec refs/heads/*:%s*" % repo.prefix
  69
  70    print # end capabilities
  71
  72
  73def do_list(repo, args):
  74    """Lists all known references.
  75
  76    Bug: This will always set the remote head to master for non-local
  77    repositories, since we have no way of determining what the remote
  78    head is at clone time.
  79    """
  80
  81    for ref in repo.revs:
  82        debug("? refs/heads/%s", ref)
  83        print "? refs/heads/%s" % ref
  84
  85    if repo.head:
  86        debug("@refs/heads/%s HEAD" % repo.head)
  87        print "@refs/heads/%s HEAD" % repo.head
  88    else:
  89        debug("@refs/heads/master HEAD")
  90        print "@refs/heads/master HEAD"
  91
  92    print # end list
  93
  94
  95def update_local_repo(repo):
  96    """Updates (or clones) a local repo.
  97    """
  98
  99    if repo.local:
 100        return repo
 101
 102    path = repo.non_local.clone(repo.gitdir)
 103    repo.non_local.update(repo.gitdir)
 104    repo = local_repo(repo, path)
 105    return repo
 106
 107
 108def do_import(repo, args):
 109    """Exports a fast-import stream from testgit for git to import.
 110    """
 111
 112    if len(args) != 1:
 113        die("Import needs exactly one ref")
 114
 115    if not repo.gitdir:
 116        die("Need gitdir to import")
 117
 118    repo = update_local_repo(repo)
 119    repo.exporter.export_repo(repo.gitdir)
 120
 121
 122def do_export(repo, args):
 123    """Imports a fast-import stream from git to testgit.
 124    """
 125
 126    if not repo.gitdir:
 127        die("Need gitdir to export")
 128
 129    dirname = repo.get_base_path(repo.gitdir)
 130
 131    if not os.path.exists(dirname):
 132        os.makedirs(dirname)
 133
 134    path = os.path.join(dirname, 'testgit.marks')
 135    print path
 136    print path if os.path.exists(path) else ""
 137    sys.stdout.flush()
 138
 139    update_local_repo(repo)
 140    repo.importer.do_import(repo.gitdir)
 141    repo.non_local.push(repo.gitdir)
 142
 143
 144def do_gitdir(repo, args):
 145    """Stores the location of the gitdir.
 146    """
 147
 148    if not args:
 149        die("gitdir needs an argument")
 150
 151    repo.gitdir = ' '.join(args)
 152
 153
 154COMMANDS = {
 155    'capabilities': do_capabilities,
 156    'list': do_list,
 157    'import': do_import,
 158    'export': do_export,
 159    'gitdir': do_gitdir,
 160}
 161
 162
 163def sanitize(value):
 164    """Cleans up the url.
 165    """
 166
 167    if value.startswith('testgit::'):
 168        value = value[9:]
 169
 170    return value
 171
 172
 173def read_one_line(repo):
 174    """Reads and processes one command.
 175    """
 176
 177    line = sys.stdin.readline()
 178
 179    cmdline = line
 180
 181    if not cmdline:
 182        warn("Unexpected EOF")
 183        return False
 184
 185    cmdline = cmdline.strip().split()
 186    if not cmdline:
 187        # Blank line means we're about to quit
 188        return False
 189
 190    cmd = cmdline.pop(0)
 191    debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
 192
 193    if cmd not in COMMANDS:
 194        die("Unknown command, %s", cmd)
 195
 196    func = COMMANDS[cmd]
 197    func(repo, cmdline)
 198    sys.stdout.flush()
 199
 200    return True
 201
 202
 203def main(args):
 204    """Starts a new remote helper for the specified repository.
 205    """
 206
 207    if len(args) != 3:
 208        die("Expecting exactly three arguments.")
 209        sys.exit(1)
 210
 211    if os.getenv("GIT_DEBUG_TESTGIT"):
 212        import git_remote_helpers.util
 213        git_remote_helpers.util.DEBUG = True
 214
 215    alias = sanitize(args[1])
 216    url = sanitize(args[2])
 217
 218    if not alias.isalnum():
 219        warn("non-alnum alias '%s'", alias)
 220        alias = "tmp"
 221
 222    args[1] = alias
 223    args[2] = url
 224
 225    repo = get_repo(alias, url)
 226
 227    debug("Got arguments %s", args[1:])
 228
 229    more = True
 230
 231    while (more):
 232        more = read_one_line(repo)
 233
 234if __name__ == '__main__':
 235    sys.exit(main(sys.argv))