git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/3] clone: make --local handle URLs

From
Jeff King <peff@peff.net>
Date
May 26, 2012, 03:45 UTC
Message-ID
<20120526034532.GB14332@sigill.intra.peff.net>
In-Reply-To
<20120526034226.GA14287@sigill.intra.peff.net>

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@peff.net>
---
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
Previous: Jeff KingNext: Johannes Sixt
Message 3 of 19 in “clone --local fixes”
  1. 0/3 clone --local fixesJeff King, May 26, 2012
  2. 1/3 t5701: modernize styleJeff King, May 26, 2012
  3. 2/3 clone: make --local handle URLsJeff King, May 26, 2012
  4. Johannes SixtMay 28, 2012
  5. Jeff KingMay 28, 2012
  6. 3/3 clone: allow --no-local to turn off local optimizationsJeff King, May 26, 2012
  7. 4/3 clone: send diagnostic messages to stderrJeff King, May 26, 2012
  8. Junio C HamanoMay 27, 2012
  9. Jeff KingMay 28, 2012
  10. Junio C HamanoMay 29, 2012
  11. Jeff KingMay 30, 2012
  12. Jeff KingMay 30, 2012
  13. 1/2 docs/clone: mention that --local may be ignoredJeff King, May 30, 2012
  14. 2/2 clone: allow --no-local to turn off local optimizationsJeff King, May 30, 2012
  15. Junio C HamanoMay 30, 2012
  16. Jeff KingMay 30, 2012
  17. Junio C HamanoMay 30, 2012
  18. Jeff KingMay 30, 2012
  19. Junio C HamanoMay 30, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.