{"thread":{"id":"15444","subject":"[PATCH 0/4] Pushing into a repository with alternates","startedAt":"2008-09-09T08:27:06Z","lastAt":"2008-09-09T14:51:06Z","messageCount":12,"participants":["Junio C Hamano","Paolo Bonzini","Johannes Sixt","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"90182","messageId":"1220948830-3275-1-git-send-email-gitster@pobox.com","threadId":"15444","inReplyTo":null,"subject":"[PATCH 0/4] Pushing into a repository with alternates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:27:06Z","receivedAt":"2008-09-09T08:27:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Here is a re-rolled series of the topic we discussed earlier.\n\nJunio C Hamano (4):\n  is_directory(): a generic helper function\n  receive-pack: make it a builtin\n  push: prepare sender to receive extended ref information from the receiver\n  push: receiver end advertises refs from alternate repositories\n\n Makefile                                 |    2 +-\n abspath.c                                |   13 ++++++++-\n builtin-clone.c                          |    9 +-----\n builtin-fetch-pack.c                     |    2 +-\n receive-pack.c => builtin-receive-pack.c |   44 +++++++++++++++++++++++++++--\n builtin-send-pack.c                      |   21 +++++++++++---\n builtin.h                                |    1 +\n cache.h                                  |    9 +++++-\n connect.c                                |   17 +++++++++++-\n daemon.c                                 |   10 ++-----\n git.c                                    |    1 +\n rerere.c                                 |    3 +-\n sha1_file.c                              |   13 +++++++-\n transport.c                              |    4 +-\n 14 files changed, 115 insertions(+), 34 deletions(-)\n rename receive-pack.c => builtin-receive-pack.c (91%)\n"},{"id":"90186","messageId":"1220948830-3275-2-git-send-email-gitster@pobox.com","threadId":"15444","inReplyTo":"1220948830-3275-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/4] is_directory(): a generic helper function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:27:07Z","receivedAt":"2008-09-09T08:27:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A simple \"grep -e stat --and -e S_ISDIR\" revealed there are many\nopen-coded implementations of this function.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n abspath.c       |   13 ++++++++++++-\n builtin-clone.c |    9 +--------\n cache.h         |    1 +\n daemon.c        |   10 +++-------\n rerere.c        |    3 +--\n sha1_file.c     |    3 +--\n 6 files changed, 19 insertions(+), 20 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 0d56124..8c06782 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -1,5 +1,16 @@\n #include \"cache.h\"\n \n+/*\n+ * Do not use this for inspecting *tracked* content.  When path is a\n+ * symlink to a directory, we do not want to say it is a directory.\n+ * Worth yet, leading components in path could contain symbolic links.\n+ */\n+int is_directory(const char *path)\n+{\n+\tstruct stat st;\n+\treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n #define MAXDEPTH 5\n \n@@ -17,7 +28,7 @@ const char *make_absolute_path(const char *path)\n \t\tdie (\"Too long path: %.*s\", 60, path);\n \n \twhile (depth--) {\n-\t\tif (stat(buf, &st) || !S_ISDIR(st.st_mode)) {\n+\t\tif (!is_directory(buf)) {\n \t\t\tchar *last_slash = strrchr(buf, '/');\n \t\t\tif (last_slash) {\n \t\t\t\t*last_slash = '\\0';\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex c843529..a4b8790 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -77,7 +77,7 @@ static char *get_repo_path(const char *repo, int *is_bundle)\n \tfor (i = 0; i < ARRAY_SIZE(suffix); i++) {\n \t\tconst char *path;\n \t\tpath = mkpath(\"%s%s\", repo, suffix[i]);\n-\t\tif (!stat(path, &st) && S_ISDIR(st.st_mode)) {\n+\t\tif (is_directory(path)) {\n \t\t\t*is_bundle = 0;\n \t\t\treturn xstrdup(make_nonrelative_path(path));\n \t\t}\n@@ -140,13 +140,6 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)\n \treturn xstrndup(start, end - start);\n }\n \n-static int is_directory(const char *path)\n-{\n-\tstruct stat buf;\n-\n-\treturn !stat(path, &buf) && S_ISDIR(buf.st_mode);\n-}\n-\n static void strip_trailing_slashes(char *dir)\n {\n \tchar *end = dir + strlen(dir);\ndiff --git a/cache.h b/cache.h\nindex de8c2b6..ce1f630 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -533,6 +533,7 @@ static inline int is_absolute_path(const char *path)\n {\n \treturn path[0] == '/' || has_dos_drive_prefix(path);\n }\n+int is_directory(const char *);\n const char *make_absolute_path(const char *path);\n const char *make_nonrelative_path(const char *path);\n const char *make_relative_path(const char *abs, const char *base);\ndiff --git a/daemon.c b/daemon.c\nindex c315932..ab7a273 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -1115,13 +1115,9 @@ int main(int argc, char **argv)\n \tif (strict_paths && (!ok_paths || !*ok_paths))\n \t\tdie(\"option --strict-paths requires a whitelist\");\n \n-\tif (base_path) {\n-\t\tstruct stat st;\n-\n-\t\tif (stat(base_path, &st) || !S_ISDIR(st.st_mode))\n-\t\t\tdie(\"base-path '%s' does not exist or \"\n-\t\t\t    \"is not a directory\", base_path);\n-\t}\n+\tif (base_path && !is_directory(base_path))\n+\t\tdie(\"base-path '%s' does not exist or is not a directory\",\n+\t\t    base_path);\n \n \tif (inetd_mode) {\n \t\tstruct sockaddr_storage ss;\ndiff --git a/rerere.c b/rerere.c\nindex 323e493..c38886b 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -319,7 +319,6 @@ static int git_rerere_config(const char *var, const char *value, void *cb)\n \n static int is_rerere_enabled(void)\n {\n-\tstruct stat st;\n \tconst char *rr_cache;\n \tint rr_cache_exists;\n \n@@ -327,7 +326,7 @@ static int is_rerere_enabled(void)\n \t\treturn 0;\n \n \trr_cache = git_path(\"rr-cache\");\n-\trr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);\n+\trr_cache_exists = is_directory(rr_cache);\n \tif (rerere_enabled < 0)\n \t\treturn rr_cache_exists;\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9ee1ed1..ae550e8 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -250,7 +250,6 @@ static void read_info_alternates(const char * alternates, int depth);\n  */\n static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)\n {\n-\tstruct stat st;\n \tconst char *objdir = get_object_directory();\n \tstruct alternate_object_database *ent;\n \tstruct alternate_object_database *alt;\n@@ -281,7 +280,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative\n \tent->base[pfxlen] = ent->base[entlen-1] = 0;\n \n \t/* Detect cases where alternate disappeared */\n-\tif (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {\n+\tif (!is_directory(ent->base)) {\n \t\terror(\"object directory %s does not exist; \"\n \t\t      \"check .git/objects/info/alternates.\",\n \t\t      ent->base);\n-- \n1.6.0.1.415.g7bb82\n"},{"id":"90183","messageId":"1220948830-3275-3-git-send-email-gitster@pobox.com","threadId":"15444","inReplyTo":"1220948830-3275-2-git-send-email-gitster@pobox.com","subject":"[PATCH 2/4] receive-pack: make it a builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:27:08Z","receivedAt":"2008-09-09T08:27:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"It is a good thing to do in general, but more importantly, transport\nroutines can only be used by built-ins, which is what I'll be adding next.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile                                 |    2 +-\n receive-pack.c => builtin-receive-pack.c |    6 +++---\n builtin.h                                |    1 +\n git.c                                    |    1 +\n 4 files changed, 6 insertions(+), 4 deletions(-)\n rename receive-pack.c => builtin-receive-pack.c (99%)\n\ndiff --git a/Makefile b/Makefile\nindex f4c31c8..f9c54ff 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -294,7 +294,6 @@ PROGRAMS += git-mktag$X\n PROGRAMS += git-mktree$X\n PROGRAMS += git-pack-redundant$X\n PROGRAMS += git-patch-id$X\n-PROGRAMS += git-receive-pack$X\n PROGRAMS += git-send-pack$X\n PROGRAMS += git-shell$X\n PROGRAMS += git-show-index$X\n@@ -546,6 +545,7 @@ BUILTIN_OBJS += builtin-prune-packed.o\n BUILTIN_OBJS += builtin-prune.o\n BUILTIN_OBJS += builtin-push.o\n BUILTIN_OBJS += builtin-read-tree.o\n+BUILTIN_OBJS += builtin-receive-pack.o\n BUILTIN_OBJS += builtin-reflog.o\n BUILTIN_OBJS += builtin-remote.o\n BUILTIN_OBJS += builtin-rerere.o\ndiff --git a/receive-pack.c b/builtin-receive-pack.c\nsimilarity index 99%\nrename from receive-pack.c\nrename to builtin-receive-pack.c\nindex b81678a..6d6027e 100644\n--- a/receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -462,14 +462,14 @@ static int delete_only(struct command *cmd)\n \treturn 1;\n }\n \n-int main(int argc, char **argv)\n+int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tchar *dir = NULL;\n \n \targv++;\n \tfor (i = 1; i < argc; i++) {\n-\t\tchar *arg = *argv++;\n+\t\tconst char *arg = *argv++;\n \n \t\tif (*arg == '-') {\n \t\t\t/* Do flag handling here */\n@@ -477,7 +477,7 @@ int main(int argc, char **argv)\n \t\t}\n \t\tif (dir)\n \t\t\tusage(receive_pack_usage);\n-\t\tdir = arg;\n+\t\tdir = xstrdup(arg);\n \t}\n \tif (!dir)\n \t\tusage(receive_pack_usage);\ndiff --git a/builtin.h b/builtin.h\nindex e67cb20..5d7cdca 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -78,6 +78,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);\n extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);\n extern int cmd_push(int argc, const char **argv, const char *prefix);\n extern int cmd_read_tree(int argc, const char **argv, const char *prefix);\n+extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_reflog(int argc, const char **argv, const char *prefix);\n extern int cmd_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_config(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex adf7352..2f5b4d7 100644\n--- a/git.c\n+++ b/git.c\n@@ -328,6 +328,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n \t\t{ \"push\", cmd_push, RUN_SETUP },\n \t\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n+\t\t{ \"receive-pack\", cmd_receive_pack },\n \t\t{ \"reflog\", cmd_reflog, RUN_SETUP },\n \t\t{ \"remote\", cmd_remote, RUN_SETUP },\n \t\t{ \"repo-config\", cmd_config },\n-- \n1.6.0.1.415.g7bb82\n"},{"id":"90185","messageId":"1220948830-3275-4-git-send-email-gitster@pobox.com","threadId":"15444","inReplyTo":"1220948830-3275-3-git-send-email-gitster@pobox.com","subject":"[PATCH 3/4] push: prepare sender to receive extended ref information from the receiver","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:27:09Z","receivedAt":"2008-09-09T08:27:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"git push\" enhancement allows the receiving end to report not only its own\nrefs but refs in repositories it borrows from via the alternate object\nstore mechanism.  By telling the sender that objects reachable from these\nextra refs are already complete in the receiving end, the number of\nobjects that need to be transfered can be cut down.\n\nThese entries are sent over the wire with string \".have\", instead of the\nactual names of the refs.  This string was chosen so that they are ignored\nby older programs at the sending end.  If we sent some random but valid\nlooking refnames for these entries, \"matching refs\" rule (triggered when\nrunning \"git push\" without explicit refspecs, where the sender learns what\nrefs the receiver has, and updates only the ones with the names of the\nrefs the sender also has) and \"delete missing\" rule (triggered when \"git\npush --mirror\" is used, where the sender tells the receiver to delete the\nrefs it itself does not have) would try to update/delete them, which is\nnot what we want.\n\nThis prepares the send-pack (and \"push\" that runs native protocol) to\naccept extended existing ref information and make use of it.  The \".have\"\nentries are excluded from ref matching rules, and are exempt from deletion\nrule while pushing with --mirror option, but are still used for pack\ngeneration purposes by providing more \"bottom\" range commits.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-fetch-pack.c |    2 +-\n builtin-send-pack.c  |   21 ++++++++++++++++-----\n cache.h              |    6 +++++-\n connect.c            |   17 ++++++++++++++++-\n transport.c          |    4 ++--\n 5 files changed, 40 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c\nindex 459c6f0..e0c2561 100644\n--- a/builtin-fetch-pack.c\n+++ b/builtin-fetch-pack.c\n@@ -735,7 +735,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \tconn = git_connect(fd, (char *)dest, args.uploadpack,\n \t\t\t   args.verbose ? CONNECT_VERBOSE : 0);\n \tif (conn) {\n-\t\tget_remote_heads(fd[0], &ref, 0, NULL, 0);\n+\t\tget_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);\n \n \t\tref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);\n \t\tclose(fd[0]);\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 7588d22..b3c22f6 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -18,7 +18,7 @@ static struct send_pack_args args = {\n /*\n  * Make a pack stream and spit it out into file descriptor fd\n  */\n-static int pack_objects(int fd, struct ref *refs)\n+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra)\n {\n \t/*\n \t * The child becomes pack-objects --revs; we feed\n@@ -34,6 +34,8 @@ static int pack_objects(int fd, struct ref *refs)\n \t\tNULL,\n \t};\n \tstruct child_process po;\n+\tint i;\n+\tchar buf[42];\n \n \tif (args.use_thin_pack)\n \t\targv[4] = \"--thin\";\n@@ -49,9 +51,15 @@ static int pack_objects(int fd, struct ref *refs)\n \t * We feed the pack-objects we just spawned with revision\n \t * parameters by writing to the pipe.\n \t */\n-\twhile (refs) {\n-\t\tchar buf[42];\n+\tfor (i = 0; i < extra->nr; i++) {\n+\t\tmemcpy(buf + 1, sha1_to_hex(&extra->array[i][0]), 40);\n+\t\tbuf[0] = '^';\n+\t\tbuf[41] = '\\n';\n+\t\tif (!write_or_whine(po.in, buf, 42, \"send-pack: send refs\"))\n+\t\t\tbreak;\n+\t}\n \n+\twhile (refs) {\n \t\tif (!is_null_sha1(refs->old_sha1) &&\n \t\t    has_sha1_file(refs->old_sha1)) {\n \t\t\tmemcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);\n@@ -381,14 +389,17 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \tint expect_status_report = 0;\n \tint flags = MATCH_REFS_NONE;\n \tint ret;\n+\tstruct extra_have_objects extra_have;\n \n+\tmemset(&extra_have, 0, sizeof(extra_have));\n \tif (args.send_all)\n \t\tflags |= MATCH_REFS_ALL;\n \tif (args.send_mirror)\n \t\tflags |= MATCH_REFS_MIRROR;\n \n \t/* No funny business with the matcher */\n-\tremote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);\n+\tremote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,\n+\t\t\t\t       &extra_have);\n \tget_local_heads();\n \n \t/* Does the other end support the reporting? */\n@@ -496,7 +507,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \n \tpacket_flush(out);\n \tif (new_refs && !args.dry_run) {\n-\t\tif (pack_objects(out, remote_refs) < 0)\n+\t\tif (pack_objects(out, remote_refs, &extra_have) < 0)\n \t\t\treturn -1;\n \t}\n \telse\ndiff --git a/cache.h b/cache.h\nindex ce1f630..98a7421 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -709,7 +709,11 @@ extern struct child_process *git_connect(int fd[2], const char *url, const char\n extern int finish_connect(struct child_process *conn);\n extern int path_match(const char *path, int nr, char **match);\n extern int get_ack(int fd, unsigned char *result_sha1);\n-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);\n+struct extra_have_objects {\n+\tint nr, alloc;\n+\tunsigned char (*array)[20];\n+};\n+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);\n extern int server_supports(const char *feature);\n \n extern struct packed_git *parse_pack_index(unsigned char *sha1);\ndiff --git a/connect.c b/connect.c\nindex dd96f8e..071e355 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -41,12 +41,21 @@ int check_ref_type(const struct ref *ref, int flags)\n \treturn check_ref(ref->name, strlen(ref->name), flags);\n }\n \n+static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1)\n+{\n+\tif (extra->alloc <= extra->nr)\n+\t\tALLOC_GROW(extra->array, extra->nr + 1, extra->alloc);\n+\thashcpy(&(extra->array[extra->nr][0]), sha1);\n+\textra->nr++;\n+}\n+\n /*\n  * Read all the refs from the other end\n  */\n struct ref **get_remote_heads(int in, struct ref **list,\n \t\t\t      int nr_match, char **match,\n-\t\t\t      unsigned int flags)\n+\t\t\t      unsigned int flags,\n+\t\t\t      struct extra_have_objects *extra_have)\n {\n \t*list = NULL;\n \tfor (;;) {\n@@ -72,6 +81,12 @@ struct ref **get_remote_heads(int in, struct ref **list,\n \t\t\tserver_capabilities = xstrdup(name + name_len + 1);\n \t\t}\n \n+\t\tif (extra_have &&\n+\t\t    name_len == 5 && !memcmp(\".have\", name, 5)) {\n+\t\t\tadd_extra_have(extra_have, old_sha1);\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (!check_ref(name, name_len, flags))\n \t\t\tcontinue;\n \t\tif (nr_match && !path_match(name, nr_match, match))\ndiff --git a/transport.c b/transport.c\nindex 71433d9..f7db5d9 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -619,7 +619,7 @@ static struct ref *get_refs_via_connect(struct transport *transport)\n \tstruct ref *refs;\n \n \tconnect_setup(transport);\n-\tget_remote_heads(data->fd[0], &refs, 0, NULL, 0);\n+\tget_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL);\n \n \treturn refs;\n }\n@@ -652,7 +652,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \n \tif (!data->conn) {\n \t\tconnect_setup(transport);\n-\t\tget_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);\n+\t\tget_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);\n \t}\n \n \trefs = fetch_pack(&args, data->fd, data->conn,\n-- \n1.6.0.1.415.g7bb82\n"},{"id":"90184","messageId":"1220948830-3275-5-git-send-email-gitster@pobox.com","threadId":"15444","inReplyTo":"1220948830-3275-4-git-send-email-gitster@pobox.com","subject":"[PATCH 4/4] push: receiver end advertises refs from alternate repositories","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:27:10Z","receivedAt":"2008-09-09T08:27:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier, when pushing into a repository that borrows from alternate object\nstores, we followed the longstanding design decision not to trust refs in\nthe alternate repository that houses the object store we are borrowing\nfrom.  If your public repository is borrowing from Linus's public\nrepository, you pushed into it long time ago, and now when you try to push\nyour updated history that is in sync with more recent history from Linus,\nyou will end up sending not just your own development, but also the\nchanges you acquired through Linus's tree, even though the objects needed\nfor the latter already exists at the receiving end.  This is because the\nreceiving end does not advertise that the objects only reachable from the\nborrowed repository (i.e. Linus's) are already available there.\n\nThis solves the issue by making the receiving end advertise refs from\nborrowed repositories.  They are not sent with their true names but with a\nphoney name \".have\" to make sure that the old senders will safely ignore\nthem (otherwise, the old senders will misbehave, trying to push matching\nrefs, and mirror push that deletes refs that only exist at the receiving\nend).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-receive-pack.c |   38 ++++++++++++++++++++++++++++++++++++++\n cache.h                |    2 ++\n sha1_file.c            |   10 ++++++++++\n 3 files changed, 50 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\nindex 6d6027e..45e3cd9 100644\n--- a/builtin-receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -6,6 +6,8 @@\n #include \"exec_cmd.h\"\n #include \"commit.h\"\n #include \"object.h\"\n+#include \"remote.h\"\n+#include \"transport.h\"\n \n static const char receive_pack_usage[] = \"git-receive-pack <git-dir>\";\n \n@@ -462,6 +464,40 @@ static int delete_only(struct command *cmd)\n \treturn 1;\n }\n \n+static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)\n+{\n+\tchar *other = xstrdup(make_absolute_path(e->base));\n+\tsize_t len = strlen(other);\n+\tstruct remote *remote;\n+\tstruct transport *transport;\n+\tconst struct ref *extra;\n+\n+\twhile (other[len-1] == '/')\n+\t\tother[--len] = '\\0';\n+\tif (len < 8 || memcmp(other + len - 8, \"/objects\", 8))\n+\t\treturn 0;\n+\t/* Is this a git repository with refs? */\n+\tmemcpy(other + len - 8, \"/refs\", 6);\n+\tif (!is_directory(other))\n+\t\treturn 0;\n+\tother[len - 8] = '\\0';\n+\tremote = remote_get(other);\n+\ttransport = transport_get(remote, other);\n+\tfor (extra = transport_get_remote_refs(transport);\n+\t     extra;\n+\t     extra = extra->next) {\n+\t\tadd_extra_ref(\".have\", extra->old_sha1, 0);\n+\t}\n+\ttransport_disconnect(transport);\n+\tfree(other);\n+\treturn 0;\n+}\n+\n+static void add_alternate_refs(void)\n+{\n+\tforeach_alt_odb(add_refs_from_alternate, NULL);\n+}\n+\n int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n@@ -497,7 +533,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \telse if (0 <= receive_unpack_limit)\n \t\tunpack_limit = receive_unpack_limit;\n \n+\tadd_alternate_refs();\n \twrite_head_info();\n+\tclear_extra_refs();\n \n \t/* EOF */\n \tpacket_flush(1);\ndiff --git a/cache.h b/cache.h\nindex 98a7421..99af83a 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -641,6 +641,8 @@ extern struct alternate_object_database {\n } *alt_odb_list;\n extern void prepare_alt_odb(void);\n extern void add_to_alternates_file(const char *reference);\n+typedef int alt_odb_fn(struct alternate_object_database *, void *);\n+extern void foreach_alt_odb(alt_odb_fn, void*);\n \n struct pack_window {\n \tstruct pack_window *next;\ndiff --git a/sha1_file.c b/sha1_file.c\nindex ae550e8..12be17b 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -393,6 +393,16 @@ void add_to_alternates_file(const char *reference)\n \t\tlink_alt_odb_entries(alt, alt + strlen(alt), '\\n', NULL, 0);\n }\n \n+void foreach_alt_odb(alt_odb_fn fn, void *cb)\n+{\n+\tstruct alternate_object_database *ent;\n+\n+\tprepare_alt_odb();\n+\tfor (ent = alt_odb_list; ent; ent = ent->next)\n+\t\tif (fn(ent, cb))\n+\t\t\treturn;\n+}\n+\n void prepare_alt_odb(void)\n {\n \tconst char *alt;\n-- \n1.6.0.1.415.g7bb82\n"},{"id":"90196","messageId":"48C64138.3020406@gnu.org","threadId":"15444","inReplyTo":"1220948830-3275-3-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 2/4] receive-pack: make it a builtin","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-09-09T09:26:16Z","receivedAt":"2008-09-09T09:26:16Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"Junio C Hamano wrote:\n> It is a good thing to do in general, but more importantly, transport\n> routines can only be used by built-ins, which is what I'll be adding next.\n\nSo how do I\n\n        receive-pack = /home/bonzinip/bin/git-receive-pack\n\nnow?  Using libexec or \".../bin/git receive-pack\" is fine, I just would\nlike to know it and see it in the release notes.  Or maybe\ngit-receive-pack should be installed in $(bindir) even if it is a builtin?\n\nPaolo\n"},{"id":"90202","messageId":"48C6508B.1080600@viscovery.net","threadId":"15444","inReplyTo":"1220948830-3275-2-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 1/4] is_directory(): a generic helper function","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-09-09T10:31:39Z","receivedAt":"2008-09-09T10:31:39Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Junio C Hamano schrieb:\n> +/*\n> + * Do not use this for inspecting *tracked* content.  When path is a\n> + * symlink to a directory, we do not want to say it is a directory.\n\nI though stat(2) checks the thing that a symlink points to. Then either\nthis comment is not correct or you want to use lstat(2), no?\n\n> + * Worth yet, leading components in path could contain symbolic links.\n> + */\n> +int is_directory(const char *path)\n> +{\n> +\tstruct stat st;\n> +\treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n> +}\n\n-- Hannes\n"},{"id":"90209","messageId":"20080909123207.GA25799@coredump.intra.peff.net","threadId":"15444","inReplyTo":"48C6508B.1080600@viscovery.net","subject":"Re: [PATCH 1/4] is_directory(): a generic helper function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-09-09T12:32:07Z","receivedAt":"2008-09-09T12:32:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 09, 2008 at 12:31:39PM +0200, Johannes Sixt wrote:\n\n> Junio C Hamano schrieb:\n> > +/*\n> > + * Do not use this for inspecting *tracked* content.  When path is a\n> > + * symlink to a directory, we do not want to say it is a directory.\n> \n> I though stat(2) checks the thing that a symlink points to. Then either\n> this comment is not correct or you want to use lstat(2), no?\n\nI assume he meant \"do not use this for tracked content, because in that\ncase...\". But I had to read it twice to make sense.\n\n> > + * Worth yet, leading components in path could contain symbolic links.\n\nAnd this didn't help. I assume it should be \"worse yet\" and Junio\ndeveloped some sort of typing lisp?\n\n-Peff\n"},{"id":"90211","messageId":"20080909130320.GA25935@coredump.intra.peff.net","threadId":"15444","inReplyTo":"1220948830-3275-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 3/4] push: prepare sender to receive extended ref information from the receiver","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-09-09T13:03:20Z","receivedAt":"2008-09-09T13:03:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 09, 2008 at 01:27:09AM -0700, Junio C Hamano wrote:\n\n> +struct extra_have_objects {\n> +\tint nr, alloc;\n> +\tunsigned char (*array)[20];\n> +};\n\nThis declaration made me double-take. I think it is correct, and you are\nsaving an extra pointer over a malloc. But the syntax ends up a little\nconfusing:\n\n        hashcpy(&(extra->array[extra->nr][0]), sha1);\n\nUsing a struct probably would have been a little easier to read. I don't\nknow if this is worth changing.\n\n> +\tif (extra->alloc <= extra->nr)\n> +\t\tALLOC_GROW(extra->array, extra->nr + 1, extra->alloc);\n\nIsn't the conditional superfluous (ALLOC_GROW already makes this check)?\n\n-Peff\n"},{"id":"90219","messageId":"7v63p59z3w.fsf@gitster.siamese.dyndns.org","threadId":"15444","inReplyTo":"48C64138.3020406@gnu.org","subject":"Re: [PATCH 2/4] receive-pack: make it a builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T14:24:03Z","receivedAt":"2008-09-09T14:24:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n> Junio C Hamano wrote:\n>> It is a good thing to do in general, but more importantly, transport\n>> routines can only be used by built-ins, which is what I'll be adding next.\n>\n> So how do I\n>\n>         receive-pack = /home/bonzinip/bin/git-receive-pack\n>\n> now?  Using libexec or \".../bin/git receive-pack\" is fine, I just would\n> like to know it and see it in the release notes.\n\nPlease check your Makefile (I'm too lazy to double check); don't we\ninstall these server side programs in $(bindir)?\n"},{"id":"90222","messageId":"48C68C9B.8070402@gnu.org","threadId":"15444","inReplyTo":"7v63p59z3w.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] receive-pack: make it a builtin","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-09-09T14:47:55Z","receivedAt":"2008-09-09T14:47:55Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":">> So how do I\n>>\n>>         receive-pack = /home/bonzinip/bin/git-receive-pack\n>>\n>> now?  Using libexec or \".../bin/git receive-pack\" is fine, I just would\n>> like to know it and see it in the release notes.\n> \n> Please check your Makefile (I'm too lazy to double check); don't we\n> install these server side programs in $(bindir)?\n\nYes, I interpreted the obsolete comment in Makefile\n\n# ... and all the rest that could be moved out of bindir to gitexecdir\n\nas s/could be/were/, but there is a special rule in install to place\nthese in $(bindir).\n\nPaolo\n"},{"id":"90224","messageId":"7vprnd8jad.fsf@gitster.siamese.dyndns.org","threadId":"15444","inReplyTo":"20080909123207.GA25799@coredump.intra.peff.net","subject":"Re: [PATCH 1/4] is_directory(): a generic helper function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T14:51:06Z","receivedAt":"2008-09-09T14:51:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Sep 09, 2008 at 12:31:39PM +0200, Johannes Sixt wrote:\n>\n>> Junio C Hamano schrieb:\n>> > +/*\n>> > + * Do not use this for inspecting *tracked* content.  When path is a\n>> > + * symlink to a directory, we do not want to say it is a directory.\n>> \n>> I though stat(2) checks the thing that a symlink points to. Then either\n>> this comment is not correct or you want to use lstat(2), no?\n>\n> I assume he meant \"do not use this for tracked content, because in that\n> case...\". But I had to read it twice to make sense.\n\nSorry, I can't type, but you assumed correctly.  When we are tracking a\npath \"a/b\" where \"b\" is a submodule, we want to notice the change made by\nthe user to rmdir \"a/b\" and replace it with a symbolic link that points\nelsewhere, so we shouldn't be using is_directory() in such a codepath.\nWorse yet, we also want to catch the case where you \"rm -fr a\" and make a\nsymlink that points at a directory that may (or may not) have a\nsubdirectory \"b\".  If we were tracking \"a/b\", then we have to say that the\ndirectory \"a/b\" does not exist anymore.  It shows that is_directory() is a\nwrong function to use while inspecting the tracked content (aka worktree).\n"}]}