@matheusbsbs / matheus-bagliano / commits / ae4efe1957
Fork 0

Move WebDAV HTTP push under remote-curl

The remote helper interface now supports the push capability, which can be used to ask the implementation to push one or more specs to the remote repository. For remote-curl we implement this by calling the existing WebDAV based git-http-push executable. Internally the helper interface uses the push_refs transport hook so that the complexity of the refspec parsing and matching can be reused between remote implementations. When possible however the helper protocol uses source ref name rather than the source SHA-1, thereby allowing the helper to access this name if it is useful. >From Clemens Buchacher <drizzd@aon.at>: update http tests according to remote-curl capabilities o Pushing packed refs is now fixed. o The transport helper fails if refs are already up-to-date. Add a test for that. o The transport helper will notice if refs are already up-to-date. We therefore need to update server info in the unpacked-refs test. o The transport helper will purge deleted branches automatically. o Use a variable ($ORIG_HEAD) instead of full SHA-1 name. Signed-off-by: Tay Ray Chuan <rctay89@gmail.com> Signed-off-by: Clemens Buchacher <drizzd@aon.at> Signed-off-by: Shawn O. Pearce <spearce@spearce.org> CC: Daniel Barkalow <barkalow@iabervon.org> CC: Mike Hommey <mh@glandium.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Shawn O. Pearce committed Oct 30, 2009 at 17:47 UTC ae4efe195752c27cb25fca9451852c0f4eebdb28
6 files changed +287 -54
Documentation/git-remote-helpers.txt
+32 -1
@@ -34,6 +34,10 @@ Commands are given by the caller on the helper's standard input, one per line.
34 value of the ref. A space-separated list of attributes follows
35 the name; unrecognized attributes are ignored. After the
36 complete list, outputs a blank line.
37 ++
38 +If 'push' is supported this may be called as 'list for-push'
39 +to obtain the current refs prior to sending one or more 'push'
40 +commands to the helper.
41
42 'option' <name> <value>::
43 Set the transport helper option <name> to <value>. Outputs a
@@ -59,6 +63,22 @@ suitably updated.
63 +
64 Supported if the helper has the "fetch" capability.
65
66 +'push' +<src>:<dst>::
67 + Pushes the given <src> commit or branch locally to the
68 + remote branch described by <dst>. A batch sequence of
69 + one or more push commands is terminated with a blank line.
70 ++
71 +Zero or more protocol options may be entered after the last 'push'
72 +command, before the batch's terminating blank line.
73 ++
74 +When the push is complete, outputs one or more 'ok <dst>' or
75 +'error <dst> <why>?' lines to indicate success or failure of
76 +each pushed ref. The status report output is terminated by
77 +a blank line. The option field <why> may be quoted in a C
78 +style string if it contains an LF.
79 ++
80 +Supported if the helper has the "push" capability.
81 +
82 If a fatal error occurs, the program writes the error message to
83 stderr and exits. The caller should expect that a suitable error
84 message has been printed if the child closes the connection without
@@ -76,10 +96,16 @@ CAPABILITIES
96 'option'::
97 This helper supports the option command.
98
99 +'push'::
100 + This helper supports the 'push' command.
101 +
102 REF LIST ATTRIBUTES
103 -------------------
104
82 -None are defined yet, but the caller must accept any which are supplied.
105 +'for-push'::
106 + The caller wants to use the ref list to prepare push
107 + commands. A helper might chose to acquire the ref list by
108 + opening a different type of connection to the destination.
109
110 OPTIONS
111 -------
@@ -106,6 +132,11 @@ OPTIONS
132 ask for the tag specifically. Some helpers may be able to
133 use this option to avoid a second network connection.
134
135 +'option dry-run' \{'true'|'false'\}:
136 + If true, pretend the operation completed successfully,
137 + but don't actually change any repository data. For most
138 + helpers this only applies to the 'push', if supported.
139 +
140 Documentation
141 -------------
142 Documentation by Daniel Barkalow.
http-push.c
+27 -2
@@ -78,6 +78,7 @@ static int push_verbosely;
78 static int push_all = MATCH_REFS_NONE;
79 static int force_all;
80 static int dry_run;
81 +static int helper_status;
82
83 static struct object_list *objects;
84
@@ -1813,6 +1814,10 @@ int main(int argc, char **argv)
1814 dry_run = 1;
1815 continue;
1816 }
1817 + if (!strcmp(arg, "--helper-status")) {
1818 + helper_status = 1;
1819 + continue;
1820 + }
1821 if (!strcmp(arg, "--verbose")) {
1822 push_verbosely = 1;
1823 http_is_verbose = 1;
@@ -1911,9 +1916,12 @@ int main(int argc, char **argv)
1916
1917 /* Remove a remote branch if -d or -D was specified */
1918 if (delete_branch) {
1914 - if (delete_remote_branch(refspec[0], force_delete) == -1)
1919 + if (delete_remote_branch(refspec[0], force_delete) == -1) {
1920 fprintf(stderr, "Unable to delete remote branch %s\n",
1921 refspec[0]);
1922 + if (helper_status)
1923 + printf("error %s cannot remove\n", refspec[0]);
1924 + }
1925 goto cleanup;
1926 }
1927
@@ -1925,6 +1933,8 @@ int main(int argc, char **argv)
1933 }
1934 if (!remote_refs) {
1935 fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
1936 + if (helper_status)
1937 + printf("error null no match\n");
1938 rc = 0;
1939 goto cleanup;
1940 }
@@ -1942,8 +1952,12 @@ int main(int argc, char **argv)
1952 if (is_null_sha1(ref->peer_ref->new_sha1)) {
1953 if (delete_remote_branch(ref->name, 1) == -1) {
1954 error("Could not remove %s", ref->name);
1955 + if (helper_status)
1956 + printf("error %s cannot remove\n", ref->name);
1957 rc = -4;
1958 }
1959 + else if (helper_status)
1960 + printf("ok %s\n", ref->name);
1961 new_refs++;
1962 continue;
1963 }
@@ -1951,6 +1965,8 @@ int main(int argc, char **argv)
1965 if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
1966 if (push_verbosely || 1)
1967 fprintf(stderr, "'%s': up-to-date\n", ref->name);
1968 + if (helper_status)
1969 + printf("ok %s up to date\n", ref->name);
1970 continue;
1971 }
1972
@@ -1974,6 +1990,8 @@ int main(int argc, char **argv)
1990 "need to pull first?",
1991 ref->name,
1992 ref->peer_ref->name);
1993 + if (helper_status)
1994 + printf("error %s non-fast forward\n", ref->name);
1995 rc = -2;
1996 continue;
1997 }
@@ -1987,14 +2005,19 @@ int main(int argc, char **argv)
2005 if (strcmp(ref->name, ref->peer_ref->name))
2006 fprintf(stderr, " using '%s'", ref->peer_ref->name);
2007 fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
1990 - if (dry_run)
2008 + if (dry_run) {
2009 + if (helper_status)
2010 + printf("ok %s\n", ref->name);
2011 continue;
2012 + }
2013
2014 /* Lock remote branch ref */
2015 ref_lock = lock_remote(ref->name, LOCK_TIME);
2016 if (ref_lock == NULL) {
2017 fprintf(stderr, "Unable to lock remote branch %s\n",
2018 ref->name);
2019 + if (helper_status)
2020 + printf("error %s lock error\n", ref->name);
2021 rc = 1;
2022 continue;
2023 }
@@ -2045,6 +2068,8 @@ int main(int argc, char **argv)
2068
2069 if (!rc)
2070 fprintf(stderr, " done\n");
2071 + if (helper_status)
2072 + printf("%s %s\n", !rc ? "ok" : "error", ref->name);
2073 unlock_remote(ref_lock);
2074 check_locks();
2075 }
remote-curl.c
+85 -12
@@ -4,6 +4,7 @@
4 #include "walker.h"
5 #include "http.h"
6 #include "exec_cmd.h"
7 +#include "run-command.h"
8
9 static struct remote *remote;
10 static const char *url;
@@ -13,7 +14,8 @@ struct options {
14 int verbosity;
15 unsigned long depth;
16 unsigned progress : 1,
16 - followtags : 1;
17 + followtags : 1,
18 + dry_run : 1;
19 };
20 static struct options options;
21
@@ -59,6 +61,15 @@ static int set_option(const char *name, const char *value)
61 return -1;
62 return 1 /* TODO implement later */;
63 }
64 + else if (!strcmp(name, "dry-run")) {
65 + if (!strcmp(value, "true"))
66 + options.dry_run = 1;
67 + else if (!strcmp(value, "false"))
68 + options.dry_run = 0;
69 + else
70 + return -1;
71 + return 0;
72 + }
73 else {
74 return 1 /* unsupported */;
75 }
@@ -136,6 +147,20 @@ static struct ref *get_refs(void)
147 return refs;
148 }
149
150 +static void output_refs(struct ref *refs)
151 +{
152 + struct ref *posn;
153 + for (posn = refs; posn; posn = posn->next) {
154 + if (posn->symref)
155 + printf("@%s %s\n", posn->symref, posn->name);
156 + else
157 + printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
158 + }
159 + printf("\n");
160 + fflush(stdout);
161 + free_refs(refs);
162 +}
163 +
164 static int fetch_dumb(int nr_heads, struct ref **to_fetch)
165 {
166 char **targets = xmalloc(nr_heads * sizeof(char*));
@@ -211,6 +236,58 @@ static void parse_fetch(struct strbuf *buf)
236 strbuf_reset(buf);
237 }
238
239 +static int push_dav(int nr_spec, char **specs)
240 +{
241 + const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
242 + int argc = 0, i;
243 +
244 + argv[argc++] = "http-push";
245 + argv[argc++] = "--helper-status";
246 + if (options.dry_run)
247 + argv[argc++] = "--dry-run";
248 + if (options.verbosity > 1)
249 + argv[argc++] = "--verbose";
250 + argv[argc++] = url;
251 + for (i = 0; i < nr_spec; i++)
252 + argv[argc++] = specs[i];
253 + argv[argc++] = NULL;
254 +
255 + if (run_command_v_opt(argv, RUN_GIT_CMD))
256 + die("git-%s failed", argv[0]);
257 + free(argv);
258 + return 0;
259 +}
260 +
261 +static void parse_push(struct strbuf *buf)
262 +{
263 + char **specs = NULL;
264 + int alloc_spec = 0, nr_spec = 0, i;
265 +
266 + do {
267 + if (!prefixcmp(buf->buf, "push ")) {
268 + ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
269 + specs[nr_spec++] = xstrdup(buf->buf + 5);
270 + }
271 + else
272 + die("http transport does not support %s", buf->buf);
273 +
274 + strbuf_reset(buf);
275 + if (strbuf_getline(buf, stdin, '\n') == EOF)
276 + return;
277 + if (!*buf->buf)
278 + break;
279 + } while (1);
280 +
281 + if (push_dav(nr_spec, specs))
282 + exit(128); /* error already reported */
283 + for (i = 0; i < nr_spec; i++)
284 + free(specs[i]);
285 + free(specs);
286 +
287 + printf("\n");
288 + fflush(stdout);
289 +}
290 +
291 int main(int argc, const char **argv)
292 {
293 struct strbuf buf = STRBUF_INIT;
@@ -239,17 +316,12 @@ int main(int argc, const char **argv)
316 if (!prefixcmp(buf.buf, "fetch ")) {
317 parse_fetch(&buf);
318
242 - } else if (!strcmp(buf.buf, "list")) {
243 - struct ref *refs = get_refs();
244 - struct ref *posn;
245 - for (posn = refs; posn; posn = posn->next) {
246 - if (posn->symref)
247 - printf("@%s %s\n", posn->symref, posn->name);
248 - else
249 - printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
250 - }
251 - printf("\n");
252 - fflush(stdout);
319 + } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
320 + output_refs(get_refs());
321 +
322 + } else if (!prefixcmp(buf.buf, "push ")) {
323 + parse_push(&buf);
324 +
325 } else if (!prefixcmp(buf.buf, "option ")) {
326 char *name = buf.buf + strlen("option ");
327 char *value = strchr(name, ' ');
@@ -272,6 +344,7 @@ int main(int argc, const char **argv)
344 } else if (!strcmp(buf.buf, "capabilities")) {
345 printf("fetch\n");
346 printf("option\n");
347 + printf("push\n");
348 printf("\n");
349 fflush(stdout);
350 } else {
t/t5540-http-push.sh
+9 -5
@@ -36,6 +36,7 @@ test_expect_success 'setup remote repository' '
36 cd test_repo.git &&
37 git --bare update-server-info &&
38 mv hooks/post-update.sample hooks/post-update &&
39 + ORIG_HEAD=$(git rev-parse --verify HEAD) &&
40 cd - &&
41 mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
42 '
@@ -45,7 +46,7 @@ test_expect_success 'clone remote repository' '
46 git clone $HTTPD_URL/test_repo.git test_repo_clone
47 '
48
48 -test_expect_failure 'push to remote repository with packed refs' '
49 +test_expect_success 'push to remote repository with packed refs' '
50 cd "$ROOT_PATH"/test_repo_clone &&
51 : >path2 &&
52 git add path2 &&
@@ -57,11 +58,15 @@ test_expect_failure 'push to remote repository with packed refs' '
58 test $HEAD = $(git rev-parse --verify HEAD))
59 '
60
60 -test_expect_success ' push to remote repository with unpacked refs' '
61 +test_expect_failure 'push already up-to-date' '
62 + git push
63 +'
64 +
65 +test_expect_success 'push to remote repository with unpacked refs' '
66 (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
67 rm packed-refs &&
63 - git update-ref refs/heads/master \
64 - 0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
68 + git update-ref refs/heads/master $ORIG_HEAD &&
69 + git --bare update-server-info) &&
70 git push &&
71 (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
72 test $HEAD = $(git rev-parse --verify HEAD))
@@ -113,7 +118,6 @@ test_expect_success 'create and delete remote branch' '
118 git push origin dev &&
119 git fetch &&
120 git push origin :dev &&
116 - git branch -d -r origin/dev &&
121 git fetch &&
122 test_must_fail git show-ref --verify refs/remotes/origin/dev
123 '
transport-helper.c
+134 -3
@@ -1,6 +1,6 @@
1 #include "cache.h"
2 #include "transport.h"
3 -
3 +#include "quote.h"
4 #include "run-command.h"
5 #include "commit.h"
6 #include "diff.h"
@@ -13,7 +13,8 @@ struct helper_data
13 struct child_process *helper;
14 FILE *out;
15 unsigned fetch : 1,
16 - option : 1;
16 + option : 1,
17 + push : 1;
18 };
19
20 static struct child_process *get_helper(struct transport *transport)
@@ -52,6 +53,8 @@ static struct child_process *get_helper(struct transport *transport)
53 data->fetch = 1;
54 if (!strcmp(buf.buf, "option"))
55 data->option = 1;
56 + if (!strcmp(buf.buf, "push"))
57 + data->push = 1;
58 }
59 return data->helper;
60 }
@@ -214,6 +217,130 @@ static int fetch(struct transport *transport,
217 return -1;
218 }
219
220 +static int push_refs(struct transport *transport,
221 + struct ref *remote_refs, int flags)
222 +{
223 + int force_all = flags & TRANSPORT_PUSH_FORCE;
224 + int mirror = flags & TRANSPORT_PUSH_MIRROR;
225 + struct helper_data *data = transport->data;
226 + struct strbuf buf = STRBUF_INIT;
227 + struct child_process *helper;
228 + struct ref *ref;
229 +
230 + if (!remote_refs)
231 + return 0;
232 +
233 + helper = get_helper(transport);
234 + if (!data->push)
235 + return 1;
236 +
237 + for (ref = remote_refs; ref; ref = ref->next) {
238 + if (ref->peer_ref)
239 + hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
240 + else if (!mirror)
241 + continue;
242 +
243 + ref->deletion = is_null_sha1(ref->new_sha1);
244 + if (!ref->deletion &&
245 + !hashcmp(ref->old_sha1, ref->new_sha1)) {
246 + ref->status = REF_STATUS_UPTODATE;
247 + continue;
248 + }
249 +
250 + if (force_all)
251 + ref->force = 1;
252 +
253 + strbuf_addstr(&buf, "push ");
254 + if (!ref->deletion) {
255 + if (ref->force)
256 + strbuf_addch(&buf, '+');
257 + if (ref->peer_ref)
258 + strbuf_addstr(&buf, ref->peer_ref->name);
259 + else
260 + strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
261 + }
262 + strbuf_addch(&buf, ':');
263 + strbuf_addstr(&buf, ref->name);
264 + strbuf_addch(&buf, '\n');
265 + }
266 +
267 + transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
268 + standard_options(transport);
269 +
270 + if (flags & TRANSPORT_PUSH_DRY_RUN) {
271 + if (set_helper_option(transport, "dry-run", "true") != 0)
272 + die("helper %s does not support dry-run", data->name);
273 + }
274 +
275 + strbuf_addch(&buf, '\n');
276 + if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
277 + exit(128);
278 +
279 + ref = remote_refs;
280 + while (1) {
281 + char *refname, *msg;
282 + int status;
283 +
284 + strbuf_reset(&buf);
285 + if (strbuf_getline(&buf, data->out, '\n') == EOF)
286 + exit(128); /* child died, message supplied already */
287 + if (!buf.len)
288 + break;
289 +
290 + if (!prefixcmp(buf.buf, "ok ")) {
291 + status = REF_STATUS_OK;
292 + refname = buf.buf + 3;
293 + } else if (!prefixcmp(buf.buf, "error ")) {
294 + status = REF_STATUS_REMOTE_REJECT;
295 + refname = buf.buf + 6;
296 + } else
297 + die("expected ok/error, helper said '%s'\n", buf.buf);
298 +
299 + msg = strchr(refname, ' ');
300 + if (msg) {
301 + struct strbuf msg_buf = STRBUF_INIT;
302 + const char *end;
303 +
304 + *msg++ = '\0';
305 + if (!unquote_c_style(&msg_buf, msg, &end))
306 + msg = strbuf_detach(&msg_buf, NULL);
307 + else
308 + msg = xstrdup(msg);
309 + strbuf_release(&msg_buf);
310 +
311 + if (!strcmp(msg, "no match")) {
312 + status = REF_STATUS_NONE;
313 + free(msg);
314 + msg = NULL;
315 + }
316 + else if (!strcmp(msg, "up to date")) {
317 + status = REF_STATUS_UPTODATE;
318 + free(msg);
319 + msg = NULL;
320 + }
321 + else if (!strcmp(msg, "non-fast forward")) {
322 + status = REF_STATUS_REJECT_NONFASTFORWARD;
323 + free(msg);
324 + msg = NULL;
325 + }
326 + }
327 +
328 + if (ref)
329 + ref = find_ref_by_name(ref, refname);
330 + if (!ref)
331 + ref = find_ref_by_name(remote_refs, refname);
332 + if (!ref) {
333 + warning("helper reported unexpected status of %s", refname);
334 + continue;
335 + }
336 +
337 + ref->status = status;
338 + ref->remote_status = msg;
339 + }
340 + strbuf_release(&buf);
341 + return 0;
342 +}
343 +
344 static struct ref *get_refs_list(struct transport *transport, int for_push)
345 {
346 struct helper_data *data = transport->data;
@@ -225,7 +352,10 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
352
353 helper = get_helper(transport);
354
228 - write_str_in_full(helper->in, "list\n");
355 + if (data->push && for_push)
356 + write_str_in_full(helper->in, "list for-push\n");
357 + else
358 + write_str_in_full(helper->in, "list\n");
359
360 while (1) {
361 char *eov, *eon;
@@ -266,6 +396,7 @@ int transport_helper_init(struct transport *transport, const char *name)
396 transport->set_option = set_helper_option;
397 transport->get_refs_list = get_refs_list;
398 transport->fetch = fetch;
399 + transport->push_refs = push_refs;
400 transport->disconnect = disconnect_helper;
401 return 0;
402 }
transport.c
-31
@@ -349,35 +349,6 @@ static int rsync_transport_push(struct transport *transport,
349 return result;
350 }
351
352 -#ifndef NO_CURL
353 -static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
354 -{
355 - const char **argv;
356 - int argc;
357 -
358 - if (flags & TRANSPORT_PUSH_MIRROR)
359 - return error("http transport does not support mirror mode");
360 -
361 - argv = xmalloc((refspec_nr + 12) * sizeof(char *));
362 - argv[0] = "http-push";
363 - argc = 1;
364 - if (flags & TRANSPORT_PUSH_ALL)
365 - argv[argc++] = "--all";
366 - if (flags & TRANSPORT_PUSH_FORCE)
367 - argv[argc++] = "--force";
368 - if (flags & TRANSPORT_PUSH_DRY_RUN)
369 - argv[argc++] = "--dry-run";
370 - if (flags & TRANSPORT_PUSH_VERBOSE)
371 - argv[argc++] = "--verbose";
372 - argv[argc++] = transport->url;
373 - while (refspec_nr--)
374 - argv[argc++] = *refspec++;
375 - argv[argc] = NULL;
376 - return !!run_command_v_opt(argv, RUN_GIT_CMD);
377 -}
378 -
379 -#endif
380 -
352 struct bundle_transport_data {
353 int fd;
354 struct bundle_header header;
@@ -826,8 +797,6 @@ struct transport *transport_get(struct remote *remote, const char *url)
797 transport_helper_init(ret, "curl");
798 #ifdef NO_CURL
799 error("git was compiled without libcurl support.");
829 -#else
830 - ret->push = curl_transport_push;
800 #endif
801
802 } else if (is_local(url) && is_file(url)) {