diff options
74 files changed, 633 insertions, 351 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index ef0768b91a..3042d80978 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -348,6 +348,8 @@ include::config/diff.txt[] include::config/difftool.txt[] +include::config/extensions.txt[] + include::config/fastimport.txt[] include::config/feature.txt[] diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt new file mode 100644 index 0000000000..4e23d73cdc --- /dev/null +++ b/Documentation/config/extensions.txt @@ -0,0 +1,8 @@ +extensions.objectFormat:: + Specify the hash algorithm to use. The acceptable values are `sha1` and + `sha256`. If not specified, `sha1` is assumed. It is an error to specify + this key unless `core.repositoryFormatVersion` is 1. ++ +Note that this setting should only be set by linkgit:git-init[1] or +linkgit:git-clone[1]. Trying to change it after initialization will not +work and will produce hard-to-diagnose issues. diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index d34b0964be..53804cad4b 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -9,7 +9,8 @@ git-bundle - Move objects and refs by archive SYNOPSIS -------- [verse] -'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied] <file> <git-rev-list-args> +'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied] + [--version=<version>] <file> <git-rev-list-args> 'git bundle' verify [-q | --quiet] <file> 'git bundle' list-heads <file> [<refname>...] 'git bundle' unbundle <file> [<refname>...] @@ -102,6 +103,12 @@ unbundle <file>:: is activated. Unlike --all-progress this flag doesn't actually force any progress display by itself. +--version=<version>:: + Specify the bundle version. Version 2 is the older format and can only be + used with SHA-1 repositories; the newer version 3 contains capabilities that + permit extensions. The default is the oldest supported format, based on the + hash algorithm in use. + -q:: --quiet:: This flag makes the command not to report its progress diff --git a/Documentation/technical/bundle-format.txt b/Documentation/technical/bundle-format.txt index 0e828151a5..bac558d049 100644 --- a/Documentation/technical/bundle-format.txt +++ b/Documentation/technical/bundle-format.txt @@ -7,6 +7,8 @@ The Git bundle format is a format that represents both refs and Git objects. We will use ABNF notation to define the Git bundle format. See protocol-common.txt for the details. +A v2 bundle looks like this: + ---- bundle = signature *prerequisite *reference LF pack signature = "# v2 git bundle" LF @@ -18,9 +20,28 @@ reference = obj-id SP refname LF pack = ... ; packfile ---- +A v3 bundle looks like this: + +---- +bundle = signature *capability *prerequisite *reference LF pack +signature = "# v3 git bundle" LF + +capability = "@" key ["=" value] LF +prerequisite = "-" obj-id SP comment LF +comment = *CHAR +reference = obj-id SP refname LF +key = 1*(ALPHA / DIGIT / "-") +value = *(%01-09 / %0b-FF) + +pack = ... ; packfile +---- + == Semantics -A Git bundle consists of three parts. +A Git bundle consists of several parts. + +* "Capabilities", which are only in the v3 format, indicate functionality that + the bundle requires to be read properly. * "Prerequisites" lists the objects that are NOT included in the bundle and the reader of the bundle MUST already have, in order to use the data in the @@ -46,3 +67,10 @@ put any string here. The reader of the bundle MUST ignore the comment. Note that the prerequisites does not represent a shallow-clone boundary. The semantics of the prerequisites and the shallow-clone boundaries are different, and the Git bundle v2 format cannot represent a shallow clone repository. + +== Capabilities + +Because there is no opportunity for negotiation, unknown capabilities cause 'git +bundle' to abort. The only known capability is `object-format`, which specifies +the hash algorithm in use, and can take the same values as the +`extensions.objectFormat` configuration value. diff --git a/builtin/bundle.c b/builtin/bundle.c index 750630bdd9..ea6948110b 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -60,6 +60,7 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { int all_progress_implied = 0; int progress = isatty(STDERR_FILENO); struct strvec pack_opts; + int version = -1; struct option options[] = { OPT_SET_INT('q', "quiet", &progress, @@ -71,6 +72,8 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { OPT_BOOL(0, "all-progress-implied", &all_progress_implied, N_("similar to --all-progress when progress meter is shown")), + OPT_INTEGER(0, "version", &version, + N_("specify bundle format version")), OPT_END() }; const char* bundle_file; @@ -91,7 +94,7 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { if (!startup_info->have_repository) die(_("Need a repository to create a bundle.")); - return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts); + return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); } static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { diff --git a/builtin/init-db.c b/builtin/init-db.c index cee64823cb..f70076d38e 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -183,11 +183,6 @@ void initialize_repository_version(int hash_algo) char repo_version_string[10]; int repo_version = GIT_REPO_VERSION; -#ifndef ENABLE_SHA256 - if (hash_algo != GIT_HASH_SHA1) - die(_("The hash algorithm %s is not supported in this build."), hash_algos[hash_algo].name); -#endif - if (hash_algo != GIT_HASH_SHA1) repo_version = GIT_REPO_VERSION_READ; diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index c2a1a5c504..05c5213594 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -7,21 +7,26 @@ #define VERIFY_PACK_VERBOSE 01 #define VERIFY_PACK_STAT_ONLY 02 -static int verify_one_pack(const char *path, unsigned int flags) +static int verify_one_pack(const char *path, unsigned int flags, const char *hash_algo) { struct child_process index_pack = CHILD_PROCESS_INIT; - const char *argv[] = {"index-pack", NULL, NULL, NULL }; + struct strvec *argv = &index_pack.args; struct strbuf arg = STRBUF_INIT; int verbose = flags & VERIFY_PACK_VERBOSE; int stat_only = flags & VERIFY_PACK_STAT_ONLY; int err; + strvec_push(argv, "index-pack"); + if (stat_only) - argv[1] = "--verify-stat-only"; + strvec_push(argv, "--verify-stat-only"); else if (verbose) - argv[1] = "--verify-stat"; + strvec_push(argv, "--verify-stat"); else - argv[1] = "--verify"; + strvec_push(argv, "--verify"); + + if (hash_algo) + strvec_pushf(argv, "--object-format=%s", hash_algo); /* * In addition to "foo.pack" we accept "foo.idx" and "foo"; @@ -31,9 +36,8 @@ static int verify_one_pack(const char *path, unsigned int flags) if (strbuf_strip_suffix(&arg, ".idx") || !ends_with(arg.buf, ".pack")) strbuf_addstr(&arg, ".pack"); - argv[2] = arg.buf; + strvec_push(argv, arg.buf); - index_pack.argv = argv; index_pack.git_cmd = 1; err = run_command(&index_pack); @@ -60,12 +64,15 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix) { int err = 0; unsigned int flags = 0; + const char *object_format = NULL; int i; const struct option verify_pack_options[] = { OPT_BIT('v', "verbose", &flags, N_("verbose"), VERIFY_PACK_VERBOSE), OPT_BIT('s', "stat-only", &flags, N_("show statistics only"), VERIFY_PACK_STAT_ONLY), + OPT_STRING(0, "object-format", &object_format, N_("hash"), + N_("specify the hash algorithm to use")), OPT_END() }; @@ -75,7 +82,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix) if (argc < 1) usage_with_options(verify_pack_usage, verify_pack_options); for (i = 0; i < argc; i++) { - if (verify_one_pack(argv[i], flags)) + if (verify_one_pack(argv[i], flags, object_format)) err = 1; } @@ -12,7 +12,16 @@ #include "refs.h" #include "strvec.h" -static const char bundle_signature[] = "# v2 git bundle\n"; + +static const char v2_bundle_signature[] = "# v2 git bundle\n"; +static const char v3_bundle_signature[] = "# v3 git bundle\n"; +static struct { + int version; + const char *signature; +} bundle_sigs[] = { + { 2, v2_bundle_signature }, + { 3, v3_bundle_signature }, +}; static void add_to_ref_list(const struct object_id *oid, const char *name, struct ref_list *list) @@ -23,15 +32,30 @@ static void add_to_ref_list(const struct object_id *oid, const char *name, list->nr++; } -static const struct git_hash_algo *detect_hash_algo(struct strbuf *buf) +static int parse_capability(struct bundle_header *header, const char *capability) +{ + const char *arg; + if (skip_prefix(capability, "object-format=", &arg)) { + int algo = hash_algo_by_name(arg); + if (algo == GIT_HASH_UNKNOWN) + return error(_("unrecognized bundle hash algorithm: %s"), arg); + header->hash_algo = &hash_algos[algo]; + return 0; + } + return error(_("unknown capability '%s'"), capability); +} + +static int parse_bundle_signature(struct bundle_header *header, const char *line) { - size_t len = strcspn(buf->buf, " \n"); - int algo; + int i; - algo = hash_algo_by_length(len / 2); - if (algo == GIT_HASH_UNKNOWN) - return NULL; - return &hash_algos[algo]; + for (i = 0; i < ARRAY_SIZE(bundle_sigs); i++) { + if (!strcmp(line, bundle_sigs[i].signature)) { + header->version = bundle_sigs[i].version; + return 0; + } + } + return -1; } static int parse_bundle_header(int fd, struct bundle_header *header, @@ -42,14 +66,16 @@ static int parse_bundle_header(int fd, struct bundle_header *header, /* The bundle header begins with the signature */ if (strbuf_getwholeline_fd(&buf, fd, '\n') || - strcmp(buf.buf, bundle_signature)) { + parse_bundle_signature(header, buf.buf)) { if (report_path) - error(_("'%s' does not look like a v2 bundle file"), + error(_("'%s' does not look like a v2 or v3 bundle file"), report_path); status = -1; goto abort; } + header->hash_algo = the_hash_algo; + /* The bundle header ends with an empty line */ while (!strbuf_getwholeline_fd(&buf, fd, '\n') && buf.len && buf.buf[0] != '\n') { @@ -57,19 +83,19 @@ static int parse_bundle_header(int fd, struct bundle_header *header, int is_prereq = 0; const char *p; - if (*buf.buf == '-') { - is_prereq = 1; - strbuf_remove(&buf, 0, 1); - } strbuf_rtrim(&buf); - if (!header->hash_algo) { - header->hash_algo = detect_hash_algo(&buf); - if (!header->hash_algo) { - error(_("unknown hash algorithm length")); + if (header->version == 3 && *buf.buf == '@') { + if (parse_capability(header, buf.buf + 1)) { status = -1; break; } + continue; + } + + if (*buf.buf == '-') { + is_prereq = 1; + strbuf_remove(&buf, 0, 1); } /* @@ -449,13 +475,14 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) } int create_bundle(struct repository *r, const char *path, - int argc, const char **argv, struct strvec *pack_options) + int argc, const char **argv, struct strvec *pack_options, int version) { struct lock_file lock = LOCK_INIT; int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; struct rev_info revs; + int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) @@ -464,8 +491,22 @@ int create_bundle(struct repository *r, const char *path, bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); - /* write signature */ - write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); + if (version == -1) + version = min_version; + + if (version < 2 || version > 3) { + die(_("unsupported bundle version %d"), version); + } else if (version < min_version) { + die(_("cannot write bundle version %d with algorithm %s"), version, the_hash_algo->name); + } else if (version == 2) { + write_or_die(bundle_fd, v2_bundle_signature, strlen(v2_bundle_signature)); + } else { + const char *capability = "@object-format="; + write_or_die(bundle_fd, v3_bundle_signature, strlen(v3_bundle_signature)); + write_or_die(bundle_fd, capability, strlen(capability)); + write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name)); + write_or_die(bundle_fd, "\n", 1); + } /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; @@ -13,6 +13,7 @@ struct ref_list { }; struct bundle_header { + unsigned version; struct ref_list prerequisites; struct ref_list references; const struct git_hash_algo *hash_algo; @@ -21,7 +22,8 @@ struct bundle_header { int is_bundle(const char *path, int quiet); int read_bundle_header(const char *path, struct bundle_header *header); int create_bundle(struct repository *r, const char *path, - int argc, const char **argv, struct strvec *pack_options); + int argc, const char **argv, struct strvec *pack_options, + int version); int verify_bundle(struct repository *r, struct bundle_header *header, int verbose); #define BUNDLE_VERBOSE 1 int unbundle(struct repository *r, struct bundle_header *header, diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh index 17e25aade9..6c27b886b8 100755 --- a/ci/run-build-and-tests.sh +++ b/ci/run-build-and-tests.sh @@ -24,6 +24,12 @@ linux-gcc) export GIT_TEST_ADD_I_USE_BUILTIN=1 make test ;; +linux-clang) + export GIT_TEST_DEFAULT_HASH=sha1 + make test + export GIT_TEST_DEFAULT_HASH=sha256 + make test + ;; linux-gcc-4.8) # Don't run the tests; we only care about whether Git can be # built with GCC 4.8, as it errors out on some undesired (C99) diff --git a/config.mak.dev b/config.mak.dev index cd4a82a9eb..89b218d11a 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -16,8 +16,6 @@ DEVELOPER_CFLAGS += -Wstrict-prototypes DEVELOPER_CFLAGS += -Wunused DEVELOPER_CFLAGS += -Wvla -DEVELOPER_CFLAGS += -DENABLE_SHA256 - ifndef COMPILER_FEATURES COMPILER_FEATURES := $(shell ./detect-compiler $(CC)) endif diff --git a/http-fetch.c b/http-fetch.c index 1df376e745..c4ccc5fea9 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -84,8 +84,11 @@ int cmd_main(int argc, const char **argv) int get_verbosely = 0; int get_recover = 0; int packfile = 0; + int nongit; struct object_id packfile_hash; + setup_git_directory_gently(&nongit); + while (arg < argc && argv[arg][0] == '-') { const char *p; @@ -115,7 +118,8 @@ int cmd_main(int argc, const char **argv) if (argc != arg + 2 - (commits_on_stdin || packfile)) usage(http_fetch_usage); - setup_git_directory(); + if (nongit) + die(_("not a git repository")); git_config(git_default_config, NULL); diff --git a/repository.c b/repository.c index 6f7f6f002b..a4174ddb06 100644 --- a/repository.c +++ b/repository.c @@ -89,10 +89,6 @@ void repo_set_gitdir(struct repository *repo, void repo_set_hash_algo(struct repository *repo, int hash_algo) { repo->hash_algo = &hash_algos[hash_algo]; -#ifndef ENABLE_SHA256 - if (hash_algo != GIT_HASH_SHA1) - die(_("The hash algorithm %s is not supported in this build."), repo->hash_algo->name); -#endif } /* @@ -490,8 +490,17 @@ static enum extension_result handle_extension(const char *var, { if (!strcmp(ext, "noop-v1")) { return EXTENSION_OK; - } + } else if (!strcmp(ext, "objectformat")) { + int format; + if (!value) + return config_error_nonbool(var); + format = hash_algo_by_name(value); + if (format == GIT_HASH_UNKNOWN) + return error("invalid value for 'extensions.objectformat'"); + data->hash_algo = format; + return EXTENSION_OK; + } return EXTENSION_UNKNOWN; } diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c index f0aa80b98e..5e77d56f59 100644 --- a/t/helper/test-bloom.c +++ b/t/helper/test-bloom.c @@ -50,6 +50,8 @@ static const char *bloom_usage = "\n" int cmd__bloom(int argc, const char **argv) { + setup_git_directory(); + if (argc < 2) usage(bloom_usage); diff --git a/t/lib-pack.sh b/t/lib-pack.sh index f3463170b3..bb8938ccbe 100644 --- a/t/lib-pack.sh +++ b/ |