From: Junio C Hamano Date: Wed, 21 Dec 2016 22:55:02 +0000 (-0800) Subject: Merge branch 'jk/quote-env-path-list-component' X-Git-Tag: v2.12.0-rc0~113 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/fe050334074c5132d01e1df2c1b9a82c9b8d394c?ds=inline;hp=-c 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 --- fe050334074c5132d01e1df2c1b9a82c9b8d394c diff --combined Documentation/git.txt index af191c51b1,5f7826bf39..98033302bb --- a/Documentation/git.txt +++ b/Documentation/git.txt @@@ -13,7 -13,6 +13,7 @@@ SYNOPSI [--exec-path[=]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=] [--work-tree=] [--namespace=] + [--super-prefix=] [] DESCRIPTION @@@ -44,15 -43,9 +44,15 @@@ unreleased) version of Git, that is ava branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.10.1/git.html[documentation for release 2.10.1] +* link:v2.11.0/git.html[documentation for release 2.11] * release notes for + link:RelNotes/2.11.0.txt[2.11]. + +* link:v2.10.2/git.html[documentation for release 2.10.2] + +* release notes for + link:RelNotes/2.10.2.txt[2.10.2], link:RelNotes/2.10.1.txt[2.10.1], link:RelNotes/2.10.0.txt[2.10]. @@@ -609,11 -602,6 +609,11 @@@ foo.bar= ...`) sets `foo.bar` to the em details. Equivalent to setting the `GIT_NAMESPACE` environment variable. +--super-prefix=:: + Currently for internal use only. Set a prefix which gives a path from + above a repository down to its root. One use is to give submodules + context about the superproject that invoked it. + --bare:: Treat the repository as a bare repository. If GIT_DIR environment is not set, it is set to the current working @@@ -871,6 -859,12 +871,12 @@@ Git so take care if using a foreign fro specifies a ":" separated (on Windows ";" separated) list of Git object directories which can be used to search for Git 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`:: If the `GIT_DIR` environment variable is set then it diff --combined sha1_file.c index 9c86d1924a,fc6d864fda..1173071859 --- a/sha1_file.c +++ b/sha1_file.c @@@ -26,6 -26,7 +26,7 @@@ #include "mru.h" #include "list.h" #include "mergesort.h" + #include "quote.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@@ -329,13 -330,40 +330,40 @@@ static int link_alt_odb_entry(const cha 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, 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 entry = STRBUF_INIT; if (depth > 5) { error("%s: ignoring alternate object stores, nesting too deep.", @@@ -348,16 -376,13 +376,13 @@@ die("unable to normalize object directory: %s", objdirbuf.buf); - alt_copy = xmemdupz(alt, len); - string_list_split_in_place(&entries, alt_copy, sep, -1); - for (i = 0; i < entries.nr; i++) { - const char *entry = entries.items[i].string; - if (entry[0] == '\0' || entry[0] == '#') + while (*alt) { + alt = parse_alt_odb_entry(alt, sep, &entry); + if (!entry.len) 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); - free(alt_copy); + strbuf_release(&entry); strbuf_release(&objdirbuf); } @@@ -370,7 -395,7 +395,7 @@@ void read_info_alternates(const char * int fd; path = xstrfmt("%s/info/alternates", relative_base); - fd = git_open_noatime(path); + fd = git_open(path); free(path); if (fd < 0) return; @@@ -663,7 -688,7 +688,7 @@@ static int check_packed_git_idx(const c struct pack_idx_header *hdr; size_t idx_size; uint32_t version, nr, i, *index; - int fd = git_open_noatime(path); + int fd = git_open(path); struct stat st; if (fd < 0) @@@ -1069,7 -1094,7 +1094,7 @@@ static int open_packed_git_1(struct pac while (pack_max_fds <= pack_open_fds && close_one_pack()) ; /* nothing */ - p->pack_fd = git_open_noatime(p->pack_name); + p->pack_fd = git_open(p->pack_name); if (p->pack_fd < 0 || fstat(p->pack_fd, &st)) return -1; pack_open_fds++; @@@ -1410,32 -1435,6 +1435,32 @@@ static void prepare_packed_git_one(cha strbuf_release(&path); } +static int approximate_object_count_valid; + +/* + * Give a fast, rough count of the number of objects in the repository. This + * ignores loose objects completely. If you have a lot of them, then either + * you should repack because your performance will be awful, or they are + * all unreachable objects about to be pruned, in which case they're not really + * interesting as a measure of repo size in the first place. + */ +unsigned long approximate_object_count(void) +{ + static unsigned long count; + if (!approximate_object_count_valid) { + struct packed_git *p; + + prepare_packed_git(); + count = 0; + for (p = packed_git; p; p = p->next) { + if (open_pack_index(p)) + continue; + count += p->num_objects; + } + } + return count; +} + static void *get_next_packed_git(const void *p) { return ((const struct packed_git *)p)->next; @@@ -1507,7 -1506,6 +1532,7 @@@ void prepare_packed_git(void void reprepare_packed_git(void) { + approximate_object_count_valid = 0; prepare_packed_git_run_once = 0; prepare_packed_git(); } @@@ -1586,9 -1584,9 +1611,9 @@@ int check_sha1_signature(const unsigne return hashcmp(sha1, real_sha1) ? -1 : 0; } -int git_open_noatime(const char *name) +int git_open(const char *name) { - static int sha1_file_open_flag = O_NOATIME; + static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC; for (;;) { int fd; @@@ -1598,17 -1596,12 +1623,17 @@@ if (fd >= 0) return fd; - /* Might the failure be due to O_NOATIME? */ - if (errno != ENOENT && sha1_file_open_flag) { - sha1_file_open_flag = 0; + /* Try again w/o O_CLOEXEC: the kernel might not support it */ + if ((sha1_file_open_flag & O_CLOEXEC) && errno == EINVAL) { + sha1_file_open_flag &= ~O_CLOEXEC; continue; } + /* Might the failure be due to O_NOATIME? */ + if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) { + sha1_file_open_flag &= ~O_NOATIME; + continue; + } return -1; } } @@@ -1637,7 -1630,7 +1662,7 @@@ static int open_sha1_file(const unsigne struct alternate_object_database *alt; int most_interesting_errno; - fd = git_open_noatime(sha1_file_name(sha1)); + fd = git_open(sha1_file_name(sha1)); if (fd >= 0) return fd; most_interesting_errno = errno; @@@ -1645,7 -1638,7 +1670,7 @@@ prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { const char *path = alt_sha1_path(alt, sha1); - fd = git_open_noatime(path); + fd = git_open(path); if (fd >= 0) return fd; if (most_interesting_errno == ENOENT) @@@ -1884,9 -1877,11 +1909,9 @@@ static int parse_sha1_header_extended(c int parse_sha1_header(const char *hdr, unsigned long *sizep) { - struct object_info oi; + struct object_info oi = OBJECT_INFO_INIT; oi.sizep = sizep; - oi.typename = NULL; - oi.typep = NULL; return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT); } @@@ -2124,8 -2119,8 +2149,8 @@@ unwind goto out; } -static int packed_object_info(struct packed_git *p, off_t obj_offset, - struct object_info *oi) +int packed_object_info(struct packed_git *p, off_t obj_offset, + struct object_info *oi) { struct pack_window *w_curs = NULL; unsigned long size; @@@ -2896,7 -2891,7 +2921,7 @@@ int sha1_object_info_extended(const uns int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) { enum object_type type; - struct object_info oi = {NULL}; + struct object_info oi = OBJECT_INFO_INIT; oi.typep = &type; oi.sizep = sizep; @@@ -3367,11 -3362,6 +3392,11 @@@ int has_object_file(const struct object return has_sha1_file(oid->hash); } +int has_object_file_with_flags(const struct object_id *oid, int flags) +{ + return has_sha1_file_with_flags(oid->hash, flags); +} + static void check_tree(const void *buf, size_t size) { struct tree_desc desc; diff --combined t/t5615-alternate-env.sh index eec4137ca5,79628db3ec..26ebb0375d --- a/t/t5615-alternate-env.sh +++ b/t/t5615-alternate-env.sh @@@ -31,14 -31,14 +31,14 @@@ test_expect_success 'objects inaccessib ' test_expect_success 'access alternate via absolute path' ' - check_obj "$(pwd)/one.git/objects" <<-EOF + check_obj "$PWD/one.git/objects" <<-EOF $one blob $two missing EOF ' test_expect_success 'access multiple alternates' ' - check_obj "$(pwd)/one.git/objects:$(pwd)/two.git/objects" <<-EOF + check_obj "$PWD/one.git/objects:$PWD/two.git/objects" <<-EOF $one blob $two blob EOF @@@ -68,4 -68,22 +68,22 @@@ test_expect_success 'access alternate v 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