{"thread":{"id":"28188","subject":"[PATCH v2 0/1] git-clone: fix relative path problem in the alternates","startedAt":"2011-08-22T09:05:56Z","lastAt":"2011-08-23T16:26:28Z","messageCount":10,"participants":["Hui Wang","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":1},"messages":[{"id":"174006","messageId":"1314003957-9455-1-git-send-email-jason77.wang@gmail.com","threadId":"28188","inReplyTo":null,"subject":"[PATCH v2 0/1] git-clone: fix relative path problem in the alternates","fromName":"Hui Wang","fromEmail":"jason77.wang@gmail.com","sentAt":"2011-08-22T09:05:56Z","receivedAt":"2011-08-22T09:05:56Z","isPatch":true,"sender":{"key":"jason77.wang@gmail.com","avatar":null},"body":"\nHi Junio,\n\nIn the V2 version, i use strbuf_xxxx() to help to parse alternates file\nand change relative paths to absolute paths according to your suggestion,\nnow the strbuf operations are safer.\n\nWhen updating new content to the lockfile, i choose to write whole content at\none time instead of update it line by line. The reason is:\n\n1. we will conditionally update the lockfile, if original alternates doesn't\ninclude relative path, we don't need to update the lockfile. This logic can't\neasily be handled line by line.\n2. current git system only provides add_to_alternates_file()in the sha1_file.c\nto update lockfile, this function can add a line to lockfile but can't empty\nand write lockfile, we need to add a similar function to empty and write to\nlockfile, and we must guarantee the first line update will call this function,\nthen remaining update line call add_to_alternates_file(), this logic is a\nlittle complicated comparing to update whole content at one time.\n3. current existing add_to_alternates_file() will unconditionally append\n\"/objects\" at each new added line, that need us to parse and  remove\n\"/objects\" from each line read out from source alternates, this is a little\nbit complicated.\n\nIf we choose update lockfile at one time, we can add a new function to\nimplement this requirement and overcome all above complexity.\n\n\nHui Wang (1):\n      clone: replace relative paths in the alternates\n\n builtin/clone.c |   47 ++++++++++++++++++++++++++++++++++++++++++++++-\n cache.h         |    1 +\n sha1_file.c     |   11 +++++++++++\n"},{"id":"174007","messageId":"1314003957-9455-2-git-send-email-jason77.wang@gmail.com","threadId":"28188","inReplyTo":"1314003957-9455-1-git-send-email-jason77.wang@gmail.com","subject":"[PATCH v2 1/1] clone: replace relative paths in the alternates","fromName":"Hui Wang","fromEmail":"jason77.wang@gmail.com","sentAt":"2011-08-22T09:05:57Z","receivedAt":"2011-08-22T09:05:57Z","isPatch":true,"sender":{"key":"jason77.wang@gmail.com","avatar":null},"body":"When we clone a local source repository to a new local destination\nrepository without \"--shared\" option, if source repository has\nrelative path in the alternates, the clone will fail.\n\nThe root cause is when cloning local source repository to local\ndestination repository, the alternates of the source repository\nwill be copied to the destination repository, the relative path in\nthe alternates is source repository to base repository rather than\ndestination repository to base repository, so the destination\nrepository get a wrong relative path in the alternates, this will\ncause errors when refer to base repository in the destination\nrepository.\n\nTo fix it, adding a function update_alt_rel_path() to replace all\nrelative paths to absolute paths in the alternates of the destination\nrepository before destination repository use those paths to look for\nthe base repository.\n\nSigned-off-by: Hui Wang <jason77.wang@gmail.com>\n---\n builtin/clone.c |   47 ++++++++++++++++++++++++++++++++++++++++++++++-\n cache.h         |    1 +\n sha1_file.c     |   11 +++++++++++\n 3 files changed, 58 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 7663bc2..7b5e603 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -286,6 +286,50 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)\n \tclosedir(dir);\n }\n \n+/*\n+ * This function is called by clone_local() of clone.c\n+ * If alternates in the src_repo (source repository) has relative path,\n+ * call this function will replace the relative path to absolute path\n+ * in the des_repo (destination repository) alternates.\n+ */\n+static void update_alt_rel_path(const char *src_repo)\n+{\n+\tint need_update = 0;\n+\tFILE *ifp;\n+\tstruct strbuf istr = STRBUF_INIT;\n+\tstruct strbuf ostr = STRBUF_INIT;\n+\n+\tifp = fopen(git_path(\"objects/info/alternates\"), \"r\");\n+\tif (!ifp)\n+\t\treturn;\n+\n+\twhile (strbuf_getline(&istr, ifp, '\\n') != EOF) {\n+\t\t/* comment line, empty line or absolute path line */\n+\t\tif (istr.buf[0] == '#' || istr.len == 0 || is_absolute_path(istr.buf)) {\n+\t\t\tstrbuf_add(&ostr, istr.buf, istr.len);\n+\t\t\tstrbuf_add(&ostr, \"\\n\", 1);\n+\t\t} else {\n+\t\t\tchar *abs_path, abs_buf[PATH_MAX];\n+\t\t\tint len;\n+\n+\t\t\tabs_path = mkpath(\"%s/objects/%s\", src_repo, istr.buf);\n+\t\t\tnormalize_path_copy(abs_buf, abs_path);\n+\t\t\tlen = strlen(abs_buf);\n+\t\t\tstrbuf_add(&ostr, abs_buf, len);\n+\t\t\tstrbuf_add(&ostr, \"\\n\", 1);\n+\t\t\tneed_update = 1;\n+\t\t}\n+\t}\n+\n+\tfclose(ifp);\n+\n+\tif (need_update)\n+\t\tupdate_alternates_file_content(ostr.buf);\n+\n+\tstrbuf_release(&istr);\n+\tstrbuf_release(&ostr);\n+}\n+\n static const struct ref *clone_local(const char *src_repo,\n \t\t\t\t     const char *dest_repo)\n {\n@@ -303,8 +347,9 @@ static const struct ref *clone_local(const char *src_repo,\n \t\tcopy_or_link_directory(&src, &dest);\n \t\tstrbuf_release(&src);\n \t\tstrbuf_release(&dest);\n-\t}\n \n+\t\tupdate_alt_rel_path(src_repo);\n+\t}\n \tremote = remote_get(src_repo);\n \ttransport = transport_get(remote, src_repo);\n \tret = transport_get_remote_refs(transport);\ndiff --git a/cache.h b/cache.h\nindex fcf4501..d81c0f0 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -913,6 +913,7 @@ 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+extern void update_alternates_file_content(const char *content);\n typedef int alt_odb_fn(struct alternate_object_database *, void *);\n extern void foreach_alt_odb(alt_odb_fn, void*);\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex d5616dc..dae7bf6 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -388,6 +388,17 @@ 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 update_alternates_file_content(const char *content)\n+{\n+\tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n+\tint fd = hold_lock_file_for_update(lock, git_path(\"objects/info/alternates\"), LOCK_DIE_ON_ERROR);\n+\twrite_or_die(fd, content, strlen(content));\n+\tif (commit_lock_file(lock))\n+\t\tdie(\"could not close alternates file\");\n+\tif (alt_odb_tail)\n+\t\tlink_alt_odb_entries(content, content + strlen(content), '\\n', NULL, 0);\n+}\n+\n void foreach_alt_odb(alt_odb_fn fn, void *cb)\n {\n \tstruct alternate_object_database *ent;\n-- \n1.7.6\n"},{"id":"174035","messageId":"7v7h65w9d7.fsf@alter.siamese.dyndns.org","threadId":"28188","inReplyTo":"1314003957-9455-1-git-send-email-jason77.wang@gmail.com","subject":"Re: [PATCH v2 0/1] git-clone: fix relative path problem in the alternates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-22T19:10:12Z","receivedAt":"2011-08-22T19:10:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Hui Wang <jason77.wang@gmail.com> writes:\n\n> 2. current git system only provides add_to_alternates_file()in the sha1_file.c\n> to update lockfile, this function can add a line to lockfile but can't empty\n> and write lockfile,\n\nI do not see the need for \"empty and write\" in the first place. You are\nspending cycles to go through all the lines in the input, inspecting and\nholding the replacement somewhere, it is a one-time cost for cloning, and\nthe alternates file wouldn't be humongous anyway, so I do not see the need\nfor \"we don't touch if there is no relative path in the file\" if it makes\nthe code more complex than a stupid \"one line at a time\" approach.\n\n> 3. current existing add_to_alternates_file() will unconditionally append\n> \"/objects\" at each new added line, that need us to parse and  remove\n> \"/objects\" from each line read out from source alternates, this is a little\n> bit complicated.\n\nClone with --reference is the only other caller of that function; I would\nsay it is perfectly fine to make it the caller's responsibility to append\n\"/objects\" if that makes the resulting code easier to maintain.\n"},{"id":"174043","messageId":"7vzkj1uqpa.fsf@alter.siamese.dyndns.org","threadId":"28188","inReplyTo":"7v7h65w9d7.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 0/1] git-clone: fix relative path problem in the alternates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-22T20:38:41Z","receivedAt":"2011-08-22T20:38:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> 3. current existing add_to_alternates_file() will unconditionally append\n>> \"/objects\" at each new added line, that need us to parse and  remove\n>> \"/objects\" from each line read out from source alternates, this is a little\n>> bit complicated.\n>\n> Clone with --reference is the only other caller of that function; I would\n> say it is perfectly fine to make it the caller's responsibility to append\n> \"/objects\" if that makes the resulting code easier to maintain.\n\nI would further suggest moving add_to_alternates_file() from sha1_file.c\nto builtin/clone.c and make it file-scope static function, and change the\nway it should be used.\n\n - The caller should first obtain a lock file to info/alternates. If it\n   truncates first or opens the file for appending is up to the caller.\n\n - When adding an alternate (either from --reference or your new\n   codepath), the caller should give the lockfile instance it obtained\n   earlier, and a path to the object store it wants to borrow objects from\n   (not the repository, i.e. the caller should add \"/objects\" at the end\n   if needed before calling the function), to add_to_alternates_file()\n   function;\n\n - The function should just write the path to the locked info/alternates\n   file, and link it to alt_odb list. Do not commit the alternates file,\n   to allow multiple calls to the function be made without opening and\n   closing the file number of times.\n\n - The caller should commit the updated alternates file once it is done.\n\nWhile at it, we may probably want to update the command option handling so\nthat you can give more than one --reference= parameters.\n"},{"id":"174048","messageId":"7vliulun2a.fsf@alter.siamese.dyndns.org","threadId":"28188","inReplyTo":"7vzkj1uqpa.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 0/1] git-clone: fix relative path problem in the alternates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-22T21:57:17Z","receivedAt":"2011-08-22T21:57:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> While at it, we may probably want to update the command option handling so\n> that you can give more than one --reference= parameters.\n\nGeez. There appear to be more bugs in the existing code.\n\nIf you\n\n - specify --reference;\n - the source of the clone is a local directory;\n - you do not specify --shared; and\n - the source of the clone has its own alternates\n\nwe end up exercising this call flow:\n\n\tinit_db();\n        setup_reference();\n        -> add_to_alternates_file();\n           This writes objects/info/alternates in the new repository\n\tclone_local();\n        -> copy_or_link_directory(src/objects, dst/objects);\n           -> copy_or_link_directory(src/objects/info, dst/objects/info);\n              -> unlink(dst/objects/info/alternates);\n                 link(src/objects/info/alternates, dst/objects/info/alternates);\n\nand lose the --reference given from the command line.\n\nThe problem you are trying to address is a valid one, but it needs to be\nfixed by teaching copy-or-link-directory that objects/info/alternates is\nspecial. We need a more proper fix than \"just let the recursive copy do a\npossibly wrong thing and then patch it up if it is wrong\", which is the\napproach taken by your patch.\n"},{"id":"174062","messageId":"1314061516-24964-1-git-send-email-gitster@pobox.com","threadId":"28188","inReplyTo":"7vliulun2a.fsf@alter.siamese.dyndns.org","subject":"[PATCH 0/2] clone-local fixup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-23T01:05:14Z","receivedAt":"2011-08-23T01:05:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"So I ended up tackling this myself. I didn't bother moving the\nadd_to_alternates_file(), though.\n\nJunio C Hamano (2):\n  clone: allow more than one --reference\n  clone: clone from a repository with relative alternates\n\n builtin/clone.c  |   82 +++++++++++++++++++++++++++++++++++++++++------------\n sha1_file.c      |    2 +-\n t/t5601-clone.sh |   23 +++++++++++++++\n 3 files changed, 87 insertions(+), 20 deletions(-)\n\n-- \n1.7.6.557.gcee42\n"},{"id":"174063","messageId":"1314061516-24964-2-git-send-email-gitster@pobox.com","threadId":"28188","inReplyTo":"1314061516-24964-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/2] clone: allow more than one --reference","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-23T01:05:15Z","receivedAt":"2011-08-23T01:05:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Also add a test to expose a long-standing bug that is triggered when\ncloning with --reference option from a local repository that has its own\nalternates. The alternate object stores specified on the command line\nare lost, and only alternates copied from the source repository remain.\nThe bug will be fixed in the next patch.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/clone.c  |   32 ++++++++++++++++++++++++--------\n t/t5601-clone.sh |   15 +++++++++++++++\n 2 files changed, 39 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex f579794..ee5d651 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -39,13 +39,23 @@ static const char * const builtin_clone_usage[] = {\n \n static int option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared, option_recursive;\n-static char *option_template, *option_reference, *option_depth;\n+static char *option_template, *option_depth;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n static const char *real_git_dir;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n static int option_progress;\n+static struct string_list option_reference;\n+\n+static int opt_parse_reference(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct string_list *option_reference = opt->value;\n+\tif (!arg)\n+\t\treturn -1;\n+\tstring_list_append(option_reference, arg);\n+\treturn 0;\n+}\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -71,8 +81,8 @@ static struct option builtin_clone_options[] = {\n \t\t    \"initialize submodules in the clone\"),\n \tOPT_STRING(0, \"template\", &option_template, \"template-directory\",\n \t\t   \"directory from which templates will be used\"),\n-\tOPT_STRING(0, \"reference\", &option_reference, \"repo\",\n-\t\t   \"reference repository\"),\n+\tOPT_CALLBACK(0 , \"reference\", &option_reference, \"repo\",\n+\t\t     \"reference repository\", &opt_parse_reference),\n \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n \tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n@@ -197,7 +207,7 @@ static void strip_trailing_slashes(char *dir)\n \t*end = '\\0';\n }\n \n-static void setup_reference(const char *repo)\n+static int add_one_reference(struct string_list_item *item, void *cb_data)\n {\n \tconst char *ref_git;\n \tchar *ref_git_copy;\n@@ -206,13 +216,13 @@ static void setup_reference(const char *repo)\n \tstruct transport *transport;\n \tconst struct ref *extra;\n \n-\tref_git = real_path(option_reference);\n+\tref_git = real_path(item->string);\n \n \tif (is_directory(mkpath(\"%s/.git/objects\", ref_git)))\n \t\tref_git = mkpath(\"%s/.git\", ref_git);\n \telse if (!is_directory(mkpath(\"%s/objects\", ref_git)))\n \t\tdie(_(\"reference repository '%s' is not a local directory.\"),\n-\t\t    option_reference);\n+\t\t    item->string);\n \n \tref_git_copy = xstrdup(ref_git);\n \n@@ -227,6 +237,12 @@ static void setup_reference(const char *repo)\n \ttransport_disconnect(transport);\n \n \tfree(ref_git_copy);\n+\treturn 0;\n+}\n+\n+static void setup_reference(void)\n+{\n+\tfor_each_string_list(&option_reference, add_one_reference, NULL);\n }\n \n static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)\n@@ -521,8 +537,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n \n-\tif (option_reference)\n-\t\tsetup_reference(git_dir);\n+\tif (option_reference.nr)\n+\t\tsetup_reference();\n \n \tfetch_pattern = value.buf;\n \trefspec = parse_fetch_refspec(1, &fetch_pattern);\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 151ea53..0163ad1 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -207,4 +207,19 @@ test_expect_success 'clone separate gitdir where target already exists' '\n \ttest_must_fail git clone --separate-git-dir realgitdir src dst\n '\n \n+test_expect_failure 'clone --reference from original' '\n+\tgit clone --shared --bare src src-1 &&\n+\tgit clone --bare src src-2 &&\n+\tgit clone --reference=src-2 --bare src-1 target-8 &&\n+\tgrep /src-2/ target-8/objects/info/alternates\n+'\n+\n+test_expect_success 'clone with more than one --reference' '\n+\tgit clone --bare src src-3 &&\n+\tgit clone --bare src src-4 &&\n+\tgit clone --reference=src-3 --reference=src-4 src target-9 &&\n+\tgrep /src-3/ target-9/.git/objects/info/alternates &&\n+\tgrep /src-4/ target-9/.git/objects/info/alternates\n+'\n+\n test_done\n-- \n1.7.6.557.gcee42\n"},{"id":"174064","messageId":"1314061516-24964-3-git-send-email-gitster@pobox.com","threadId":"28188","inReplyTo":"1314061516-24964-1-git-send-email-gitster@pobox.com","subject":"[PATCH 2/2] clone: clone from a repository with relative alternates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-23T01:05:16Z","receivedAt":"2011-08-23T01:05:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Cloning from a local repository blindly copies or hardlinks all the files\nunder objects/ hierarchy. This results in two issues:\n\n - If the repository cloned has an \"objects/info/alternates\" file, and the\n   command line of clone specifies --reference, the ones specified on the\n   command line get overwritten by the copy from the original repository.\n\n - An entry in a \"objects/info/alternates\" file can specify the object\n   stores it borrows objects from as a path relative to the \"objects/\"\n   directory. When cloning a repository with such an alternates file, if\n   the new repository is not sitting next to the original repository, such\n   relative paths needs to be adjusted so that they can be used in the new\n   repository.\n\nThis updates add_to_alternates_file() to take the path to the alternate\nobject store, including the \"/objects\" part at the end (earlier, it was\ntaking the path to $GIT_DIR and was adding \"/objects\" itself), as it is\ntechnically possible to specify in objects/info/alternates file the path\nof a directory whose name does not end with \"/objects\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/clone.c  |   52 ++++++++++++++++++++++++++++++++++++++++------------\n sha1_file.c      |    2 +-\n t/t5601-clone.sh |   10 +++++++++-\n 3 files changed, 50 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex ee5d651..2842707 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -210,8 +210,7 @@ static void strip_trailing_slashes(char *dir)\n static int add_one_reference(struct string_list_item *item, void *cb_data)\n {\n \tconst char *ref_git;\n-\tchar *ref_git_copy;\n-\n+\tstruct strbuf alternate = STRBUF_INIT;\n \tstruct remote *remote;\n \tstruct transport *transport;\n \tconst struct ref *extra;\n@@ -224,19 +223,17 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)\n \t\tdie(_(\"reference repository '%s' is not a local directory.\"),\n \t\t    item->string);\n \n-\tref_git_copy = xstrdup(ref_git);\n-\n-\tadd_to_alternates_file(ref_git_copy);\n+\tstrbuf_addf(&alternate, \"%s/objects\", ref_git);\n+\tadd_to_alternates_file(alternate.buf);\n+\tstrbuf_release(&alternate);\n \n-\tremote = remote_get(ref_git_copy);\n-\ttransport = transport_get(remote, ref_git_copy);\n+\tremote = remote_get(ref_git);\n+\ttransport = transport_get(remote, ref_git);\n \tfor (extra = transport_get_remote_refs(transport); extra;\n \t     extra = extra->next)\n \t\tadd_extra_ref(extra->name, extra->old_sha1, 0);\n \n \ttransport_disconnect(transport);\n-\n-\tfree(ref_git_copy);\n \treturn 0;\n }\n \n@@ -245,7 +242,32 @@ static void setup_reference(void)\n \tfor_each_string_list(&option_reference, add_one_reference, NULL);\n }\n \n-static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)\n+static void copy_alternates(struct strbuf *src, struct strbuf *dst,\n+\t\t\t    const char *src_repo)\n+{\n+\t/*\n+\t * Read from the source objects/info/alternates file\n+\t * and copy the entries to corresponding file in the\n+\t * destination repository with add_to_alternates_file().\n+\t * Both src and dst have \"$path/objects/info/alternates\".\n+\t */\n+\tFILE *in = fopen(src->buf, \"r\");\n+\tstruct strbuf line = STRBUF_INIT;\n+\n+\twhile (strbuf_getline(&line, in, '\\n') != EOF) {\n+\t\tchar *abs_path, abs_buf[PATH_MAX];\n+\t\tif (!line.len || line.buf[0] == '#')\n+\t\t\tcontinue;\n+\t\tabs_path = mkpath(\"%s/objects/%s\", src_repo, line.buf);\n+\t\tnormalize_path_copy(abs_buf, abs_path);\n+\t\tadd_to_alternates_file(abs_buf);\n+\t}\n+\tstrbuf_release(&line);\n+\tfclose(in);\n+}\n+\n+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,\n+\t\t\t\t   const char *src_repo, int src_baselen)\n {\n \tstruct dirent *de;\n \tstruct stat buf;\n@@ -281,7 +303,13 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)\n \t\t}\n \t\tif (S_ISDIR(buf.st_mode)) {\n \t\t\tif (de->d_name[0] != '.')\n-\t\t\t\tcopy_or_link_directory(src, dest);\n+\t\t\t\tcopy_or_link_directory(src, dest,\n+\t\t\t\t\t\t       src_repo, src_baselen);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!strcmp(src->buf + src_baselen, \"/info/alternates\")) {\n+\t\t\tcopy_alternates(src, dest, src_repo);\n \t\t\tcontinue;\n \t\t}\n \n@@ -314,7 +342,7 @@ static const struct ref *clone_local(const char *src_repo,\n \telse {\n \t\tstrbuf_addf(&src, \"%s/objects\", src_repo);\n \t\tstrbuf_addf(&dest, \"%s/objects\", dest_repo);\n-\t\tcopy_or_link_directory(&src, &dest);\n+\t\tcopy_or_link_directory(&src, &dest, src_repo, src.len);\n \t\tstrbuf_release(&src);\n \t\tstrbuf_release(&dest);\n \t}\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 064a330..f7c3408 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -380,7 +380,7 @@ void add_to_alternates_file(const char *reference)\n {\n \tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n \tint fd = hold_lock_file_for_append(lock, git_path(\"objects/info/alternates\"), LOCK_DIE_ON_ERROR);\n-\tchar *alt = mkpath(\"%s/objects\\n\", reference);\n+\tchar *alt = mkpath(\"%s\\n\", reference);\n \twrite_or_die(fd, alt, strlen(alt));\n \tif (commit_lock_file(lock))\n \t\tdie(\"could not close alternates file\");\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 0163ad1..d87214c 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -207,7 +207,7 @@ test_expect_success 'clone separate gitdir where target already exists' '\n \ttest_must_fail git clone --separate-git-dir realgitdir src dst\n '\n \n-test_expect_failure 'clone --reference from original' '\n+test_expect_success 'clone --reference from original' '\n \tgit clone --shared --bare src src-1 &&\n \tgit clone --bare src src-2 &&\n \tgit clone --reference=src-2 --bare src-1 target-8 &&\n@@ -222,4 +222,12 @@ test_expect_success 'clone with more than one --reference' '\n \tgrep /src-4/ target-9/.git/objects/info/alternates\n '\n \n+test_expect_success 'clone from original with relative alternate' '\n+\tmkdir nest &&\n+\tgit clone --bare src nest/src-5 &&\n+\techo ../../../src/.git/objects >nest/src-5/objects/info/alternates &&\n+\tgit clone --bare nest/src-5 target-10 &&\n+\tgrep /src/\\\\.git/objects target-10/objects/info/alternates\n+'\n+\n test_done\n-- \n1.7.6.557.gcee42\n"},{"id":"174071","messageId":"4E5321C9.4080301@gmail.com","threadId":"28188","inReplyTo":"1314061516-24964-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 0/2] clone-local fixup","fromName":"Hui Wang","fromEmail":"jason77.wang@gmail.com","sentAt":"2011-08-23T03:43:05Z","receivedAt":"2011-08-23T03:43:05Z","isPatch":true,"sender":{"key":"jason77.wang@gmail.com","avatar":null},"body":"This patch series solved the problem from the root cause. :-)\n\nBut there is a little problem in the second patch, i provide an \nincremental patch basing on your second patch, if it is fine to you, it \nis OK to squash this patch to your second patch.\n\n\ncommit 47e890818fdac5f6493cd0bbaf9da350785a2bca\nAuthor: Hui Wang <jason77.wang@gmail.com>\nDate:   Tue Aug 23 11:32:26 2011 +0800\n\n    clone: don't change absolute path in the copy_alternates\n   \n    The source alternates may include empty lines, comment lines, relative\n    paths and absolute paths. Absolute paths don't need to be changed\n    before added to the destination alternates.\n   \n    Signed-off-by: Hui Wang <jason77.wang@gmail.com>\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 2842707..284e325 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -258,6 +258,10 @@ static void copy_alternates(struct strbuf *src, \nstruct strbuf *dst,\n                char *abs_path, abs_buf[PATH_MAX];\n                if (!line.len || line.buf[0] == '#')\n                        continue;\n+               if (is_absolute_path(line.buf)) {\n+                       add_to_alternates_file(line.buf);\n+                       continue;\n+               }\n                abs_path = mkpath(\"%s/objects/%s\", src_repo, line.buf);\n                normalize_path_copy(abs_buf, abs_path);\n                add_to_alternates_file(abs_buf);\n\n\nRegards,\nHui.\n\nJunio C Hamano wrote:\n> So I ended up tackling this myself. I didn't bother moving the\n> add_to_alternates_file(), though.\n>\n> Junio C Hamano (2):\n>   clone: allow more than one --reference\n>   clone: clone from a repository with relative alternates\n>\n>  builtin/clone.c  |   82 +++++++++++++++++++++++++++++++++++++++++------------\n>  sha1_file.c      |    2 +-\n>  t/t5601-clone.sh |   23 +++++++++++++++\n>  3 files changed, 87 insertions(+), 20 deletions(-)\n>\n>   \n"},{"id":"174097","messageId":"7vzkj0rt57.fsf@alter.siamese.dyndns.org","threadId":"28188","inReplyTo":"4E5321C9.4080301@gmail.com","subject":"Re: [PATCH 0/2] clone-local fixup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-23T16:26:28Z","receivedAt":"2011-08-23T16:26:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Hui Wang <jason77.wang@gmail.com> writes:\n\n> This patch series solved the problem from the root cause. :-)\n>\n> But there is a little problem in the second patch, i provide an\n> incremental patch basing on your second patch, if it is fine to you,\n> it is OK to squash this patch to your second patch.\n\nThanks. We also need the following fix-up, as I changed the semantics of\nadd_to_alternates_file() to take the path to the \"objects\" directory.\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 284e325..63c34d0 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -336,14 +336,17 @@ static const struct ref *clone_local(const char *src_repo,\n \t\t\t\t     const char *dest_repo)\n {\n \tconst struct ref *ret;\n-\tstruct strbuf src = STRBUF_INIT;\n-\tstruct strbuf dest = STRBUF_INIT;\n \tstruct remote *remote;\n \tstruct transport *transport;\n \n-\tif (option_shared)\n-\t\tadd_to_alternates_file(src_repo);\n-\telse {\n+\tif (option_shared) {\n+\t\tstruct strbuf alt = STRBUF_INIT;\n+\t\tstrbuf_addf(&alt, \"%s/objects\", src_repo);\n+\t\tadd_to_alternates_file(alt.buf);\n+\t\tstrbuf_release(&alt);\n+\t} else {\n+\t\tstruct strbuf src = STRBUF_INIT;\n+\t\tstruct strbuf dest = STRBUF_INIT;\n \t\tstrbuf_addf(&src, \"%s/objects\", src_repo);\n \t\tstrbuf_addf(&dest, \"%s/objects\", dest_repo);\n \t\tcopy_or_link_directory(&src, &dest, src_repo, src.len);\n"}]}