]>
Commit | Line | Data |
---|---|---|
7aeaa2fc SR |
1 | #!/usr/bin/env python |
2 | ||
9609dc9d MM |
3 | # This command is a simple remote-helper, that is used both as a |
4 | # testcase for the remote-helper functionality, and as an example to | |
5 | # show remote-helper authors one possible implementation. | |
6 | # | |
7 | # This is a Git <-> Git importer/exporter, that simply uses git | |
8 | # fast-import and git fast-export to consume and produce fast-import | |
9 | # streams. | |
10 | # | |
11 | # To understand better the way things work, one can activate debug | |
12 | # traces by setting (to any value) the environment variables | |
13 | # GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages | |
14 | # from the transport-helper side, or from this example remote-helper. | |
15 | ||
23b093ee BC |
16 | # hashlib is only available in python >= 2.5 |
17 | try: | |
18 | import hashlib | |
19 | _digest = hashlib.sha1 | |
20 | except ImportError: | |
21 | import sha | |
22 | _digest = sha.new | |
7aeaa2fc | 23 | import sys |
f733f6a0 | 24 | import os |
7fb8e163 | 25 | import time |
f733f6a0 | 26 | sys.path.insert(0, os.getenv("GITPYTHONLIB",".")) |
7aeaa2fc SR |
27 | |
28 | from git_remote_helpers.util import die, debug, warn | |
29 | from git_remote_helpers.git.repo import GitRepo | |
30 | from git_remote_helpers.git.exporter import GitExporter | |
31 | from git_remote_helpers.git.importer import GitImporter | |
32 | from git_remote_helpers.git.non_local import NonLocalGit | |
33 | ||
34 | def get_repo(alias, url): | |
35 | """Returns a git repository object initialized for usage. | |
36 | """ | |
37 | ||
38 | repo = GitRepo(url) | |
39 | repo.get_revs() | |
40 | repo.get_head() | |
41 | ||
23b093ee | 42 | hasher = _digest() |
7aeaa2fc SR |
43 | hasher.update(repo.path) |
44 | repo.hash = hasher.hexdigest() | |
45 | ||
46 | repo.get_base_path = lambda base: os.path.join( | |
47 | base, 'info', 'fast-import', repo.hash) | |
48 | ||
49 | prefix = 'refs/testgit/%s/' % alias | |
50 | debug("prefix: '%s'", prefix) | |
51 | ||
e1735872 | 52 | repo.gitdir = os.environ["GIT_DIR"] |
7aeaa2fc SR |
53 | repo.alias = alias |
54 | repo.prefix = prefix | |
55 | ||
56 | repo.exporter = GitExporter(repo) | |
57 | repo.importer = GitImporter(repo) | |
58 | repo.non_local = NonLocalGit(repo) | |
59 | ||
60 | return repo | |
61 | ||
62 | ||
63 | def local_repo(repo, path): | |
64 | """Returns a git repository object initalized for usage. | |
65 | """ | |
66 | ||
67 | local = GitRepo(path) | |
68 | ||
69 | local.non_local = None | |
70 | local.gitdir = repo.gitdir | |
71 | local.alias = repo.alias | |
72 | local.prefix = repo.prefix | |
73 | local.hash = repo.hash | |
74 | local.get_base_path = repo.get_base_path | |
75 | local.exporter = GitExporter(local) | |
76 | local.importer = GitImporter(local) | |
77 | ||
78 | return local | |
79 | ||
80 | ||
81 | def do_capabilities(repo, args): | |
82 | """Prints the supported capabilities. | |
83 | """ | |
84 | ||
85 | print "import" | |
86 | print "export" | |
7aeaa2fc SR |
87 | print "refspec refs/heads/*:%s*" % repo.prefix |
88 | ||
a515ebe9 SR |
89 | dirname = repo.get_base_path(repo.gitdir) |
90 | ||
91 | if not os.path.exists(dirname): | |
92 | os.makedirs(dirname) | |
93 | ||
94 | path = os.path.join(dirname, 'testgit.marks') | |
95 | ||
96 | print "*export-marks %s" % path | |
97 | if os.path.exists(path): | |
98 | print "*import-marks %s" % path | |
99 | ||
7aeaa2fc SR |
100 | print # end capabilities |
101 | ||
102 | ||
103 | def do_list(repo, args): | |
104 | """Lists all known references. | |
105 | ||
106 | Bug: This will always set the remote head to master for non-local | |
107 | repositories, since we have no way of determining what the remote | |
108 | head is at clone time. | |
109 | """ | |
110 | ||
111 | for ref in repo.revs: | |
112 | debug("? refs/heads/%s", ref) | |
113 | print "? refs/heads/%s" % ref | |
114 | ||
115 | if repo.head: | |
116 | debug("@refs/heads/%s HEAD" % repo.head) | |
117 | print "@refs/heads/%s HEAD" % repo.head | |
118 | else: | |
119 | debug("@refs/heads/master HEAD") | |
120 | print "@refs/heads/master HEAD" | |
121 | ||
122 | print # end list | |
123 | ||
124 | ||
125 | def update_local_repo(repo): | |
126 | """Updates (or clones) a local repo. | |
127 | """ | |
128 | ||
129 | if repo.local: | |
130 | return repo | |
131 | ||
132 | path = repo.non_local.clone(repo.gitdir) | |
133 | repo.non_local.update(repo.gitdir) | |
134 | repo = local_repo(repo, path) | |
135 | return repo | |
136 | ||
137 | ||
138 | def do_import(repo, args): | |
139 | """Exports a fast-import stream from testgit for git to import. | |
140 | """ | |
141 | ||
142 | if len(args) != 1: | |
143 | die("Import needs exactly one ref") | |
144 | ||
145 | if not repo.gitdir: | |
146 | die("Need gitdir to import") | |
147 | ||
9504bc9d SR |
148 | ref = args[0] |
149 | refs = [ref] | |
150 | ||
151 | while True: | |
152 | line = sys.stdin.readline() | |
153 | if line == '\n': | |
154 | break | |
155 | if not line.startswith('import '): | |
156 | die("Expected import line.") | |
157 | ||
158 | # strip of leading 'import ' | |
159 | ref = line[7:].strip() | |
160 | refs.append(ref) | |
161 | ||
7aeaa2fc | 162 | repo = update_local_repo(repo) |
9504bc9d | 163 | repo.exporter.export_repo(repo.gitdir, refs) |
7aeaa2fc | 164 | |
1f25c504 SR |
165 | print "done" |
166 | ||
7aeaa2fc SR |
167 | |
168 | def do_export(repo, args): | |
169 | """Imports a fast-import stream from git to testgit. | |
170 | """ | |
171 | ||
172 | if not repo.gitdir: | |
173 | die("Need gitdir to export") | |
174 | ||
7aeaa2fc | 175 | update_local_repo(repo) |
6c8151a3 | 176 | changed = repo.importer.do_import(repo.gitdir) |
0fb56ce7 SR |
177 | |
178 | if not repo.local: | |
179 | repo.non_local.push(repo.gitdir) | |
7aeaa2fc | 180 | |
6c8151a3 SR |
181 | for ref in changed: |
182 | print "ok %s" % ref | |
183 | ||
184 | ||
7aeaa2fc | 185 | |
7aeaa2fc SR |
186 | COMMANDS = { |
187 | 'capabilities': do_capabilities, | |
188 | 'list': do_list, | |
189 | 'import': do_import, | |
190 | 'export': do_export, | |
7aeaa2fc SR |
191 | } |
192 | ||
193 | ||
194 | def sanitize(value): | |
195 | """Cleans up the url. | |
196 | """ | |
197 | ||
198 | if value.startswith('testgit::'): | |
199 | value = value[9:] | |
200 | ||
201 | return value | |
202 | ||
203 | ||
204 | def read_one_line(repo): | |
205 | """Reads and processes one command. | |
206 | """ | |
207 | ||
7fb8e163 PW |
208 | sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY") |
209 | if sleepy: | |
210 | debug("Sleeping %d sec before readline" % int(sleepy)) | |
211 | time.sleep(int(sleepy)) | |
212 | ||
7aeaa2fc SR |
213 | line = sys.stdin.readline() |
214 | ||
215 | cmdline = line | |
216 | ||
217 | if not cmdline: | |
218 | warn("Unexpected EOF") | |
219 | return False | |
220 | ||
221 | cmdline = cmdline.strip().split() | |
222 | if not cmdline: | |
223 | # Blank line means we're about to quit | |
224 | return False | |
225 | ||
226 | cmd = cmdline.pop(0) | |
227 | debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline)) | |
228 | ||
229 | if cmd not in COMMANDS: | |
230 | die("Unknown command, %s", cmd) | |
231 | ||
232 | func = COMMANDS[cmd] | |
233 | func(repo, cmdline) | |
234 | sys.stdout.flush() | |
235 | ||
236 | return True | |
237 | ||
238 | ||
239 | def main(args): | |
240 | """Starts a new remote helper for the specified repository. | |
241 | """ | |
242 | ||
243 | if len(args) != 3: | |
244 | die("Expecting exactly three arguments.") | |
245 | sys.exit(1) | |
246 | ||
247 | if os.getenv("GIT_DEBUG_TESTGIT"): | |
248 | import git_remote_helpers.util | |
249 | git_remote_helpers.util.DEBUG = True | |
250 | ||
251 | alias = sanitize(args[1]) | |
252 | url = sanitize(args[2]) | |
253 | ||
254 | if not alias.isalnum(): | |
255 | warn("non-alnum alias '%s'", alias) | |
256 | alias = "tmp" | |
257 | ||
258 | args[1] = alias | |
259 | args[2] = url | |
260 | ||
261 | repo = get_repo(alias, url) | |
262 | ||
263 | debug("Got arguments %s", args[1:]) | |
264 | ||
265 | more = True | |
266 | ||
7fb8e163 | 267 | sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) |
7aeaa2fc SR |
268 | while (more): |
269 | more = read_one_line(repo) | |
270 | ||
271 | if __name__ == '__main__': | |
272 | sys.exit(main(sys.argv)) |