FIXME: hotpatch for compatibility with latest hg
[git] / git_remote_helpers / helper.py
1 import os
2 import sys
3
4 # hashlib is only available in python >= 2.5
5 try:
6     import hashlib
7     _digest = hashlib.sha1
8 except ImportError:
9     import sha
10     _digest = sha.new
11
12 from git_remote_helpers.util import debug, die, warn
13
14
15 class RemoteHelper(object):
16     def __init__(self):
17         self.commands = {
18             'capabilities': self.do_capabilities,
19             'list': self.do_list,
20             'import': self.do_import,
21             'export': self.do_export,
22         }
23
24     def setup_repo(self, repo, alias):
25         """Returns a git repository object initialized for usage.
26         """
27
28         hasher = _digest()
29         hasher.update(repo.path)
30         repo.hash = hasher.hexdigest()
31
32         repo.get_base_path = lambda base: os.path.join(
33             base, 'info', 'fast-import', repo.hash)
34
35         repo.gitdir = os.environ["GIT_DIR"]
36         repo.alias = alias
37
38     def setup_local_repo(self, local, repo):
39         """Returns a git repository object initalized for usage.
40         """
41         local.non_local = None
42         local.gitdir = repo.gitdir
43         local.alias = repo.alias
44         local.prefix = repo.prefix
45         local.hash = repo.hash
46         local.get_base_path = repo.get_base_path
47
48     def do_capabilities(self, repo, args):
49         """Prints the supported capabilities.
50         """
51
52         print "import"
53         print "export"
54         print "refspec refs/heads/*:%s*" % repo.prefix
55
56         dirname = repo.get_base_path(repo.gitdir)
57
58         if not os.path.exists(dirname):
59             os.makedirs(dirname)
60
61         path = os.path.join(dirname, repo.marksfile)
62
63         print "*export-marks %s" % path
64         if os.path.exists(path):
65             print "*import-marks %s" % path
66
67         print # end capabilities
68
69     def update_local_repo(self, repo):
70         """Updates (or clones) a local repo.
71         """
72
73         if repo.local():
74             return repo
75
76         path = repo.non_local.clone(repo.gitdir)
77         repo.non_local.update(repo.gitdir)
78         repo = self.local_repo(repo, path)
79         return repo
80
81     def do_import(self, repo, args):
82         """Exports a fast-import stream from testgit for git to import.
83         """
84
85         if len(args) != 1:
86             die("Import needs exactly one ref")
87
88         if not repo.gitdir:
89             die("Need gitdir to import")
90
91         ref = args[0]
92         refs = [ref]
93
94         while True:
95             line = sys.stdin.readline()
96             if line == '\n':
97                 break
98             if not line.startswith('import '):
99                 die("Expected import line.")
100
101             # strip of leading 'import '
102             ref = line[7:].strip()
103             refs.append(ref)
104
105         repo = self.update_local_repo(repo)
106
107         repo.exporter.export_repo(repo.gitdir, refs)
108
109         print "done"
110
111     def do_export(self, repo, args):
112         """Imports a fast-import stream from git to testgit.
113         """
114
115         if not repo.gitdir:
116             die("Need gitdir to export")
117
118         localrepo = self.update_local_repo(repo)
119
120         refs_before = self.get_refs(repo, repo.gitdir)
121         localrepo.importer.do_import(localrepo.gitdir)
122         refs_after = self.get_refs(repo, repo.gitdir)
123
124         changed = {}
125
126         for name, value in refs_after.iteritems():
127             if refs_before.get(name) == value:
128                 continue
129
130             changed[name] = value
131
132         if not repo.local():
133             repo.non_local.push(repo.gitdir)
134
135         for ref in changed:
136             print "ok %s" % ref
137         print
138
139     def read_one_line(self, repo):
140         """Reads and processes one command.
141         """
142
143         line = sys.stdin.readline()
144
145         cmdline = line
146
147         if not cmdline:
148             warn("Unexpected EOF")
149             return False
150
151         cmdline = cmdline.strip().split()
152         if not cmdline:
153             # Blank line means we're about to quit
154             return False
155
156         cmd = cmdline.pop(0)
157         debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
158
159         if cmd not in self.commands:
160             die("Unknown command, %s", cmd)
161
162         func = self.commands[cmd]
163         func(repo, cmdline)
164         sys.stdout.flush()
165
166         return True
167
168     def main(self, args):
169         """Starts a new remote helper for the specified repository.
170         """
171
172         if len(args) != 3:
173             die("Expecting exactly three arguments.")
174             sys.exit(1)
175
176         if os.getenv("GIT_REMOTE_HELPER_DEBUG"):
177             import git_remote_helpers.util
178             git_remote_helpers.util.DEBUG = True
179
180         alias = self.sanitize(args[1])
181         url = self.sanitize(args[2])
182
183         if not alias.isalnum():
184             warn("non-alnum alias '%s'", alias)
185             alias = "tmp"
186
187         args[1] = alias
188         args[2] = url
189
190         repo = self.get_repo(alias, url)
191
192         debug("Got arguments %s", args[1:])
193
194         more = True
195
196         while (more):
197             more = self.read_one_line(repo)
198
199     if __name__ == '__main__':
200         sys.exit(main(sys.argv))