Merge branch 'jk/quote-env-path-list-component'

A recent update to receive-pack to make it easier to drop garbage
objects made it clear that GIT_ALTERNATE_OBJECT_DIRECTORIES cannot
have a pathname with a colon in it (no surprise!), and this in turn
made it impossible to push into a repository at such a path.  This
has been fixed by introducing a quoting mechanism used when
appending such a path to the colon-separated list.

* jk/quote-env-path-list-component:
  t5615-alternate-env: double-quotes in file names do not work on Windows
  t5547-push-quarantine: run the path separator test on Windows, too
  tmp-objdir: quote paths we add to alternates
  alternates: accept double-quoted paths
This commit is contained in:
Junio C Hamano 2016-12-21 14:55:02 -08:00
commit fe05033407
5 changed files with 102 additions and 12 deletions

View File

@ -871,6 +871,12 @@ Git so take care if using a foreign front-end.
specifies a ":" separated (on Windows ";" separated) list specifies a ":" separated (on Windows ";" separated) list
of Git object directories which can be used to search for Git of Git object directories which can be used to search for Git
objects. New objects will not be written to these directories. objects. New objects will not be written to these directories.
+
Entries that begin with `"` (double-quote) will be interpreted
as C-style quoted paths, removing leading and trailing
double-quotes and respecting backslash escapes. E.g., the value
`"path-with-\"-and-:-in-it":vanilla-path` has two paths:
`path-with-"-and-:-in-it` and `vanilla-path`.
`GIT_DIR`:: `GIT_DIR`::
If the `GIT_DIR` environment variable is set then it If the `GIT_DIR` environment variable is set then it

View File

@ -26,6 +26,7 @@
#include "mru.h" #include "mru.h"
#include "list.h" #include "list.h"
#include "mergesort.h" #include "mergesort.h"
#include "quote.h"
#ifndef O_NOATIME #ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@ -329,13 +330,40 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
return 0; return 0;
} }
static const char *parse_alt_odb_entry(const char *string,
int sep,
struct strbuf *out)
{
const char *end;
strbuf_reset(out);
if (*string == '#') {
/* comment; consume up to next separator */
end = strchrnul(string, sep);
} else if (*string == '"' && !unquote_c_style(out, string, &end)) {
/*
* quoted path; unquote_c_style has copied the
* data for us and set "end". Broken quoting (e.g.,
* an entry that doesn't end with a quote) falls
* back to the unquoted case below.
*/
} else {
/* normal, unquoted path */
end = strchrnul(string, sep);
strbuf_add(out, string, end - string);
}
if (*end)
end++;
return end;
}
static void link_alt_odb_entries(const char *alt, int len, int sep, static void link_alt_odb_entries(const char *alt, int len, int sep,
const char *relative_base, int depth) const char *relative_base, int depth)
{ {
struct string_list entries = STRING_LIST_INIT_NODUP;
char *alt_copy;
int i;
struct strbuf objdirbuf = STRBUF_INIT; struct strbuf objdirbuf = STRBUF_INIT;
struct strbuf entry = STRBUF_INIT;
if (depth > 5) { if (depth > 5) {
error("%s: ignoring alternate object stores, nesting too deep.", error("%s: ignoring alternate object stores, nesting too deep.",
@ -348,16 +376,13 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
die("unable to normalize object directory: %s", die("unable to normalize object directory: %s",
objdirbuf.buf); objdirbuf.buf);
alt_copy = xmemdupz(alt, len); while (*alt) {
string_list_split_in_place(&entries, alt_copy, sep, -1); alt = parse_alt_odb_entry(alt, sep, &entry);
for (i = 0; i < entries.nr; i++) { if (!entry.len)
const char *entry = entries.items[i].string;
if (entry[0] == '\0' || entry[0] == '#')
continue; continue;
link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf); link_alt_odb_entry(entry.buf, relative_base, depth, objdirbuf.buf);
} }
string_list_clear(&entries, 0); strbuf_release(&entry);
free(alt_copy);
strbuf_release(&objdirbuf); strbuf_release(&objdirbuf);
} }

View File

@ -33,4 +33,29 @@ test_expect_success 'rejected objects are removed' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'push to repo path with path separator (colon)' '
# The interesting failure case here is when the
# receiving end cannot access its original object directory,
# so make it likely for us to generate a delta by having
# a non-trivial file with multiple versions.
test-genrandom foo 4096 >file.bin &&
git add file.bin &&
git commit -m bin &&
if test_have_prereq MINGW
then
pathsep=";"
else
pathsep=":"
fi &&
git clone --bare . "xxx${pathsep}yyy.git" &&
echo change >>file.bin &&
git commit -am change &&
# Note that we have to use the full path here, or it gets confused
# with the ssh host:path syntax.
git push "$(pwd)/xxx${pathsep}yyy.git" HEAD
'
test_done test_done

View File

@ -68,4 +68,22 @@ test_expect_success 'access alternate via relative path (subdir)' '
EOF EOF
' '
# set variables outside test to avoid quote insanity; the \057 is '/',
# which doesn't need quoting, but just confirms that de-quoting
# is working.
quoted='"one.git\057objects"'
unquoted='two.git/objects'
test_expect_success 'mix of quoted and unquoted alternates' '
check_obj "$quoted:$unquoted" <<-EOF
$one blob
$two blob
'
test_expect_success !MINGW 'broken quoting falls back to interpreting raw' '
mv one.git \"one.git &&
check_obj \"one.git/objects <<-EOF
$one blob
EOF
'
test_done test_done

View File

@ -5,6 +5,7 @@
#include "string-list.h" #include "string-list.h"
#include "strbuf.h" #include "strbuf.h"
#include "argv-array.h" #include "argv-array.h"
#include "quote.h"
struct tmp_objdir { struct tmp_objdir {
struct strbuf path; struct strbuf path;
@ -79,12 +80,27 @@ static void remove_tmp_objdir_on_signal(int signo)
*/ */
static void env_append(struct argv_array *env, const char *key, const char *val) static void env_append(struct argv_array *env, const char *key, const char *val)
{ {
const char *old = getenv(key); struct strbuf quoted = STRBUF_INIT;
const char *old;
/*
* Avoid quoting if it's not necessary, for maximum compatibility
* with older parsers which don't understand the quoting.
*/
if (*val == '"' || strchr(val, PATH_SEP)) {
strbuf_addch(&quoted, '"');
quote_c_style(val, &quoted, NULL, 1);
strbuf_addch(&quoted, '"');
val = quoted.buf;
}
old = getenv(key);
if (!old) if (!old)
argv_array_pushf(env, "%s=%s", key, val); argv_array_pushf(env, "%s=%s", key, val);
else else
argv_array_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val); argv_array_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
strbuf_release(&quoted);
} }
static void env_replace(struct argv_array *env, const char *key, const char *val) static void env_replace(struct argv_array *env, const char *key, const char *val)