ref namespaces: Support remote repositories via upload-pack and receive-pack

Change upload-pack and receive-pack to use the namespace-prefixed refs when working with the repository, and use the unprefixed refs when talking to the client, maintaining the masquerade. This allows clone, pull, fetch, and push to work with a suitably configured GIT_NAMESPACE. receive-pack advertises refs outside the current namespace as .have refs (as it currently does for refs in alternates), so that the client can use them to minimize data transfer but will otherwise ignore them. With appropriate configuration, this also allows http-backend to expose namespaces as multiple repositories with different paths. This only requires setting GIT_NAMESPACE, which http-backend passes through to upload-pack and receive-pack. Signed-off-by: Josh Triplett <josh@joshtriplett.org> Signed-off-by: Jamey Sharp <jamey@minilop.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Josh Triplett committed Jul 8, 2011 at 16:13 UTC 6b01ecfe22f0b6ccb9665cd8d85d78a381fecffc
2 files changed +47 -13
builtin/receive-pack.c
+39 -6
@@ -119,9 +119,25 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
119 return 0;
120 }
121
122 +static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
123 +{
124 + path = strip_namespace(path);
125 + /*
126 + * Advertise refs outside our current namespace as ".have"
127 + * refs, so that the client can use them to minimize data
128 + * transfer but will otherwise ignore them. This happens to
129 + * cover ".have" that are thrown in by add_one_alternate_ref()
130 + * to mark histories that are complete in our alternates as
131 + * well.
132 + */
133 + if (!path)
134 + path = ".have";
135 + return show_ref(path, sha1, flag, cb_data);
136 +}
137 +
138 static void write_head_info(void)
139 {
124 - for_each_ref(show_ref, NULL);
140 + for_each_ref(show_ref_cb, NULL);
141 if (!sent_capabilities)
142 show_ref("capabilities^{}", null_sha1, 0, NULL);
143
@@ -332,6 +348,8 @@ static void refuse_unconfigured_deny_delete_current(void)
348 static const char *update(struct command *cmd)
349 {
350 const char *name = cmd->ref_name;
351 + struct strbuf namespaced_name_buf = STRBUF_INIT;
352 + const char *namespaced_name;
353 unsigned char *old_sha1 = cmd->old_sha1;
354 unsigned char *new_sha1 = cmd->new_sha1;
355 struct ref_lock *lock;
@@ -342,7 +360,10 @@ static const char *update(struct command *cmd)
360 return "funny refname";
361 }
362
345 - if (is_ref_checked_out(name)) {
363 + strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
364 + namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
365 +
366 + if (is_ref_checked_out(namespaced_name)) {
367 switch (deny_current_branch) {
368 case DENY_IGNORE:
369 break;
@@ -370,7 +391,7 @@ static const char *update(struct command *cmd)
391 return "deletion prohibited";
392 }
393
373 - if (!strcmp(name, head_name)) {
394 + if (!strcmp(namespaced_name, head_name)) {
395 switch (deny_delete_current) {
396 case DENY_IGNORE:
397 break;
@@ -426,14 +447,14 @@ static const char *update(struct command *cmd)
447 rp_warning("Allowing deletion of corrupt ref.");
448 old_sha1 = NULL;
449 }
429 - if (delete_ref(name, old_sha1, 0)) {
450 + if (delete_ref(namespaced_name, old_sha1, 0)) {
451 rp_error("failed to delete %s", name);
452 return "failed to delete";
453 }
454 return NULL; /* good */
455 }
456 else {
436 - lock = lock_any_ref_for_update(name, old_sha1, 0);
457 + lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
458 if (!lock) {
459 rp_error("failed to lock %s", name);
460 return "failed to lock";
@@ -490,17 +511,29 @@ static void run_update_post_hook(struct command *commands)
511
512 static void check_aliased_update(struct command *cmd, struct string_list *list)
513 {
514 + struct strbuf buf = STRBUF_INIT;
515 + const char *dst_name;
516 struct string_list_item *item;
517 struct command *dst_cmd;
518 unsigned char sha1[20];
519 char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
520 int flag;
521
499 - const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
522 + strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
523 + dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
524 + strbuf_release(&buf);
525
526 if (!(flag & REF_ISSYMREF))
527 return;
528
529 + dst_name = strip_namespace(dst_name);
530 + if (!dst_name) {
531 + rp_error("refusing update to broken symref '%s'", cmd->ref_name);
532 + cmd->skip_update = 1;
533 + cmd->error_string = "broken symref";
534 + return;
535 + }
536 +
537 if ((item = string_list_lookup(list, dst_name)) == NULL)
538 return;
539
upload-pack.c
+8 -7
@@ -641,16 +641,17 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
641 " side-band-64k ofs-delta shallow no-progress"
642 " include-tag multi_ack_detailed";
643 struct object *o = parse_object(sha1);
644 + const char *refname_nons = strip_namespace(refname);
645
646 if (!o)
647 die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
648
649 if (capabilities)
649 - packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname,
650 + packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
651 0, capabilities,
652 stateless_rpc ? " no-done" : "");
653 else
653 - packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
654 + packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
655 capabilities = NULL;
656 if (!(o->flags & OUR_REF)) {
657 o->flags |= OUR_REF;
@@ -659,7 +660,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
660 if (o->type == OBJ_TAG) {
661 o = deref_tag(o, refname, 0);
662 if (o)
662 - packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
663 + packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
664 }
665 return 0;
666 }
@@ -680,12 +681,12 @@ static void upload_pack(void)
681 {
682 if (advertise_refs || !stateless_rpc) {
683 reset_timeout();
683 - head_ref(send_ref, NULL);
684 - for_each_ref(send_ref, NULL);
684 + head_ref_namespaced(send_ref, NULL);
685 + for_each_namespaced_ref(send_ref, NULL);
686 packet_flush(1);
687 } else {
687 - head_ref(mark_our_ref, NULL);
688 - for_each_ref(mark_our_ref, NULL);
688 + head_ref_namespaced(mark_our_ref, NULL);
689 + for_each_namespaced_ref(mark_our_ref, NULL);
690 }
691 if (advertise_refs)
692 return;