Once upon a time, the --local flag was used to inform git that it should perform some optimizations when cloning a local repository. These days, we perform those optimizations automatically, and --local is largely a no-op. The one exception is that we will complain when "--local" is given if hardlinking fails, whereas the default behavior is to simply fallback to a copy. Using --local with anything except a local path is also a no-op, because we never bother to look at the flag. So this: git clone --local file:///path/to/repo silently ignores the local flag, when a more sensible behavior would be to treat it the same as: git clone --local /path/to/repo Likewise, this: git clone --local http://example.com/repo.git is nonsense, but git silently accepts it. This patch causes --local to treat file:// URLs as local, and to flag an error when nonsensical combinations are requested. Signed-off-by: Jeff King <peff@xxxxxxxx> --- I had hoped this could just be: if (opt_local && !prefixcmp(repo_name, "file://")) repo_name += 7; but we have to handle URL decoding. And did you know that file:// URLs can have a hostname in them? How useless. I made sure this behaves the same as the parser in connect.c, though I couldn't find a good way to share the code without making both sides less readable. Documentation/git-clone.txt | 17 ++++++++++------- builtin/clone.c | 26 ++++++++++++++++++++++++++ t/t5550-http-fetch.sh | 4 ++++ t/t5701-clone-local.sh | 15 +++++++++++++++ 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 6e22522..589f3ce 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -46,13 +46,16 @@ OPTIONS mechanism and clones the repository by making a copy of HEAD and everything under objects and refs directories. The files under `.git/objects/` directory are hardlinked - to save space when possible. This is now the default when - the source repository is specified with `/path/to/repo` - syntax, so it essentially is a no-op option. To force - copying instead of hardlinking (which may be desirable - if you are trying to make a back-up of your repository), - but still avoid the usual "git aware" transport - mechanism, `--no-hardlinks` can be used. + to save space when possible. ++ +This is the default when the source repository is specified with +`/path/to/repo`, but not with a `file://` URL; specifying `--local` with +a file URL will bypass the regular transport mechanism as if a direct +path had been provided. ++ +To force copying instead of hardlinking (which may be desirable if you +are trying to make a back-up of your repository), but still avoid the +usual "git aware" transport mechanism, `--no-hardlinks` can be used. --no-hardlinks:: Optimize the cloning process from a repository on a diff --git a/builtin/clone.c b/builtin/clone.c index a4d8d25..05944db 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -23,6 +23,7 @@ #include "branch.h" #include "remote.h" #include "run-command.h" +#include "url.h" /* * Overall FIXMEs: @@ -607,6 +608,22 @@ static void write_config(struct string_list *config) } } +static const char *url_to_local_path(const char *url) +{ + const char *decoded = url_decode(url); + const char *p; + + decoded = skip_prefix(decoded, "file://"); + if (!decoded) + return NULL; + + if (has_dos_drive_prefix(decoded)) + return decoded; + + p = strchr(decoded, '/'); + return p ? p : decoded; +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; @@ -666,6 +683,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix) repo = xstrdup(absolute_path(repo_name)); else if (!strchr(repo_name, ':')) die(_("repository '%s' does not exist"), repo_name); + else if (option_local) { + const char *url_path = url_to_local_path(repo_name); + if (!url_path) + die(_("cannot use --local with a non-local URL")); + path = get_repo_path(url_path, &is_bundle); + if (!path) + die(_("repository '%s' does not exist"), url_path); + repo = xstrdup(absolute_path(url_path)); + } else repo = repo_name; is_local = path && !is_bundle; diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index b06f817..b8d77b6 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -40,6 +40,10 @@ test_expect_success 'clone http repository' ' test_cmp file clone/file ' +test_expect_success 'cloning http with --local fails' ' + test_must_fail git clone --local $HTTPD_URL/dumb/repo.git local-clone +' + test_expect_success 'create password-protected repository' ' mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" && cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index c6feca4..d2e0165 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -124,4 +124,19 @@ test_expect_success 'cloning non-git directory fails' ' test_must_fail git clone not-a-git-repo not-a-git-repo-clone ' +test_expect_success 'cloning file:// turns off local optimizations' ' + git clone --bare file://"$PWD"/a non-local && + ! repo_is_hardlinked non-local +' + +test_expect_success 'cloning file:// with --local uses hardlinks' ' + git clone --bare --local file://"$PWD"/a force-local && + repo_is_hardlinked force-local +' + +test_expect_success 'cloning file:// with --local parses URL properly' ' + git clone --bare --local file://host/"$PWD"/%61 force-local-odd && + repo_is_hardlinked force-local-odd +' + test_done -- 1.7.10.1.21.g62fda49.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html