diff options
50 files changed, 570 insertions, 182 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd4df939b5..802a4bf7cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,39 @@ env: DEVELOPER: 1 jobs: + ci-config: + runs-on: ubuntu-latest + outputs: + enabled: ${{ steps.check-ref.outputs.enabled }} + steps: + - name: try to clone ci-config branch + continue-on-error: true + run: | + git -c protocol.version=2 clone \ + --no-tags \ + --single-branch \ + -b ci-config \ + --depth 1 \ + --no-checkout \ + --filter=blob:none \ + https://github.com/${{ github.repository }} \ + config-repo && + cd config-repo && + git checkout HEAD -- ci/config + - id: check-ref + name: check whether CI is enabled for ref + run: | + enabled=yes + if test -x config-repo/ci/config/allow-ref && + ! config-repo/ci/config/allow-ref '${{ github.ref }}' + then + enabled=no + fi + echo "::set-output name=enabled::$enabled" + windows-build: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' runs-on: windows-latest steps: - uses: actions/checkout@v1 @@ -70,6 +102,8 @@ jobs: name: failed-tests-windows path: ${{env.FAILED_TEST_ARTIFACTS}} vs-build: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' env: MSYSTEM: MINGW64 NO_PERL: 1 @@ -154,6 +188,8 @@ jobs: ${{matrix.nr}} 10 t[0-9]*.sh) "@ regular: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' strategy: matrix: vector: @@ -189,6 +225,8 @@ jobs: name: failed-tests-${{matrix.vector.jobname}} path: ${{env.FAILED_TEST_ARTIFACTS}} dockerized: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' strategy: matrix: vector: @@ -213,6 +251,8 @@ jobs: name: failed-tests-${{matrix.vector.jobname}} path: ${{env.FAILED_TEST_ARTIFACTS}} static-analysis: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' env: jobname: StaticAnalysis runs-on: ubuntu-latest @@ -221,6 +261,8 @@ jobs: - run: ci/install-dependencies.sh - run: ci/run-static-analysis.sh documentation: + needs: ci-config + if: needs.ci-config.outputs.enabled == 'yes' env: jobname: Documentation runs-on: ubuntu-latest diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index a89e8dcfbc..227f46ae40 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -232,6 +232,18 @@ For C programs: while( condition ) func (bar+1); + - Do not explicitly compare an integral value with constant 0 or '\0', + or a pointer value with constant NULL. For instance, to validate that + counted array <ptr, cnt> is initialized but has no elements, write: + + if (!ptr || cnt) + BUG("empty array expected"); + + and not: + + if (ptr == NULL || cnt != 0); + BUG("empty array expected"); + - We avoid using braces unnecessarily. I.e. if (bla) { diff --git a/Documentation/RelNotes/2.27.0.txt b/Documentation/RelNotes/2.27.0.txt index 2a8c6e8f28..b398961c8a 100644 --- a/Documentation/RelNotes/2.27.0.txt +++ b/Documentation/RelNotes/2.27.0.txt @@ -161,6 +161,12 @@ Performance, Internal Implementation, Development Support etc. * The "bugreport" tool has been added. + * The object walk with object filter "--filter=tree:0" can now take + advantage of the pack bitmap when available. + + * Instead of always building all branches at GitHub via Actions, + users can specify which branches to build. + Fixes since v2.26 ----------------- @@ -420,6 +426,24 @@ Fixes since v2.26 been corrected. (merge 0555e4af58 cb/t0000-use-the-configured-shell later to maint). + * Minor in-code comments and documentation updates around credential + API. + (merge 1aed817f99 cb/credential-doc-fixes later to maint). + + * Teach "am", "commit", "merge" and "rebase", when they are run with + the "--quiet" option, to pass "--quiet" down to "gc --auto". + (merge 7c3e9e8cfb jc/auto-gc-quiet later to maint). + + * The code to skip unmerged paths in the index when sparse checkout + is in use would have made out-of-bound access of the in-core index + when the last path was unmerged, which has been corrected. + + * Serving a "git fetch" client over "git://" and "ssh://" protocols + using the on-wire protocol version 2 was buggy on the server end + when the client needs to make a follow-up request to + e.g. auto-follow tags. + (merge 08450ef791 cc/upload-pack-v2-fetch-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 564956f358 jc/maintain-doc later to maint). (merge 7422b2a0a1 sg/commit-slab-clarify-peek later to maint). diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt index 643d1b2884..7fe9aef34e 100644 --- a/Documentation/git-bugreport.txt +++ b/Documentation/git-bugreport.txt @@ -28,6 +28,7 @@ The following information is captured automatically: - 'git version --build-options' - uname sysname, release, version, and machine strings - Compiler-specific info string + - A list of enabled hooks This tool is invoked via the typical Git setup process, which means that in some cases, it might not be able to launch - for example, if a relevant config file diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt index 6f0c7ca80f..8d990e92fd 100644 --- a/Documentation/git-credential.txt +++ b/Documentation/git-credential.txt @@ -103,17 +103,20 @@ INPUT/OUTPUT FORMAT `git credential` reads and/or writes (depending on the action used) credential information in its standard input/output. This information can correspond either to keys for which `git credential` will obtain -the login/password information (e.g. host, protocol, path), or to the -actual credential data to be obtained (login/password). +the login information (e.g. host, protocol, path), or to the actual +credential data to be obtained (username/password). The credential is split into a set of named attributes, with one -attribute per line. Each attribute is -specified by a key-value pair, separated by an `=` (equals) sign, -followed by a newline. The key may contain any bytes except `=`, -newline, or NUL. The value may contain any bytes except newline or NUL. +attribute per line. Each attribute is specified by a key-value pair, +separated by an `=` (equals) sign, followed by a newline. + +The key may contain any bytes except `=`, newline, or NUL. The value may +contain any bytes except newline or NUL. + In both cases, all bytes are treated as-is (i.e., there is no quoting, and one cannot transmit a value with newline or NUL in it). The list of attributes is terminated by a blank line or end-of-file. + Git understands the following attributes: `protocol`:: @@ -123,7 +126,8 @@ Git understands the following attributes: `host`:: - The remote hostname for a network credential. + The remote hostname for a network credential. This includes + the port number if one was specified (e.g., "example.com:8088"). `path`:: @@ -134,7 +138,7 @@ Git understands the following attributes: `username`:: The credential's username, if we already have one (e.g., from a - URL, from the user, or from a previously run helper). + URL, the configuration, the user, or from a previously run helper). `password`:: @@ -146,8 +150,12 @@ Git understands the following attributes: value is parsed as a URL and treated as if its constituent parts were read (e.g., `url=https://example.com` would behave as if `protocol=https` and `host=example.com` had been provided). This - can help callers avoid parsing URLs themselves. Note that any - components which are missing from the URL (e.g., there is no - username in the example above) will be set to empty; if you want - to provide a URL and override some attributes, provide the URL - attribute first, followed by any overrides. + can help callers avoid parsing URLs themselves. + + Note that specifying a protocol is mandatory and if the URL + doesn't specify a hostname (e.g., "cert:///path/to/file") the + credential will contain a hostname attribute whose value is an + empty string. + + Components which are missing from the URL (e.g., there is no + username in the example above) will be left unset. diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt index 0d0f7149bd..9e481aec85 100644 --- a/Documentation/gitcredentials.txt +++ b/Documentation/gitcredentials.txt @@ -268,16 +268,26 @@ For a `get` operation, the helper should produce a list of attributes on stdout in the same format (see linkgit:git-credential[1] for common attributes). A helper is free to produce a subset, or even no values at all if it has nothing useful to provide. Any provided attributes will -overwrite those already known about by Git. If a helper outputs a -`quit` attribute with a value of `true` or `1`, no further helpers will -be consulted, nor will the user be prompted (if no credential has been -provided, the operation will then fail). +overwrite those already known about by Git's credential subsystem. + +While it is possible to override all attributes, well behaving helpers +should refrain from doing so for any attribute other than username and +password. + +If a helper outputs a `quit` attribute with a value of `true` or `1`, +no further helpers will be consulted, nor will the user be prompted +(if no credential has been provided, the operation will then fail). + +Similarly, no more helpers will be consulted once both username and +password had been provided. For a `store` or `erase` operation, the helper's output is ignored. -If it fails to perform the requested operation, it may complain to -stderr to inform the user. If it does not support the requested -operation (e.g., a read-only store), it should silently ignore the -request. + +If a helper fails to perform the requested operation or needs to notify +the user of a potential issue, it may write to stderr. + +If it does not support the requested operation (e.g., a read-only store), +it should silently ignore the request. If a helper receives any other operation, it should silently ignore the request. This leaves room for future operations to be added (older diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt index 1cf83df118..370d62dae4 100644 --- a/Documentation/gitfaq.txt +++ b/Documentation/gitfaq.txt @@ -223,6 +223,24 @@ a file checked into the repository which is a template or set of defaults which can then be copied alongside and modified as appropriate. This second, modified file is usually ignored to prevent accidentally committing it. +[[files-in-.gitignore-are-tracked]] +I asked Git to ignore various files, yet they are still tracked:: + A `gitignore` file ensures that certain file(s) which are not + tracked by Git remain untracked. However, sometimes particular + file(s) may have been tracked before adding them into the + `.gitignore`, hence they still remain tracked. To untrack and + ignore files/patterns, use `git rm --cached <file/pattern>` + and add a pattern to `.gitignore` that matches the <file>. + See linkgit:gitignore[5] for details. + +[[fetching-and-pulling]] +How do I know if I want to do a fetch or a pull?:: + A fetch stores a copy of the latest changes from the remote + repository, without modifying the working tree or current branch. + You can then at your leisure inspect, merge, rebase on top of, or + ignore the upstream changes. A pull consists of a fetch followed + immediately by either a merge or rebase. See linkgit:git-pull[1]. + Hooks ----- @@ -9,7 +9,7 @@ define_commit_slab(bloom_filter_slab, struct bloom_filter); -struct bloom_filter_slab bloom_filters; +static struct bloom_filter_slab bloom_filters; struct pathmap_hash_entry { struct hashmap_entry entry; @@ -273,4 +273,4 @@ int bloom_filter_contains(const struct bloom_filter *filter, } return 1; -}
\ No newline at end of file +} @@ -87,4 +87,4 @@ int bloom_filter_contains(const struct bloom_filter *filter, const struct bloom_key *key, const struct bloom_filter_settings *settings); -#endif
\ No newline at end of file +#endif diff --git a/bugreport.c b/bugreport.c index acacca8fef..aa8a489c35 100644 --- a/bugreport.c +++ b/bugreport.c @@ -3,6 +3,8 @@ #include "strbuf.h" #include "help.h" #include "compat/compiler.h" +#include "run-command.h" + static void get_system_info(struct strbuf *sys_info) { @@ -31,6 +33,53 @@ static void get_system_info(struct strbuf *sys_info) get_libc_info(sys_info); } +static void get_populated_hooks(struct strbuf *hook_info, int nongit) +{ + /* + * NEEDSWORK: Doesn't look like there is a list of all possible hooks; + * so below is a transcription of `git help hooks`. Later, this should + * be replaced with some programmatically generated list (generated from + * doc or else taken from some library which tells us about all the + * hooks) + */ + static const char *hook[] = { + "applypatch-msg", + "pre-applypatch", + "post-applypatch", + "pre-commit", + "pre-merge-commit", + "prepare-commit-msg", + "commit-msg", + "post-commit", + "pre-rebase", + "post-checkout", + "post-merge", + "pre-push", + "pre-receive", + "update", + "post-receive", + "post-update", + "push-to-checkout", + "pre-auto-gc", + "post-rewrite", + "sendemail-validate", + "fsmonitor-watchman", + "p4-pre-submit", + "post-index-change", + }; + int i; + + if (nongit) { + strbuf_addstr(hook_info, + _("not run from a git repository - no hooks to show\n")); + return; + } + + for (i = 0; i < ARRAY_SIZE(hook); i++) + if (find_hook(hook[i])) + strbuf_addf(hook_info, "%s\n", hook[i]); +} + static const char * const bugreport_usage[] = { N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"), NULL @@ -114,6 +163,9 @@ int cmd_main(int argc, const char **argv) get_header(&buffer, _("System Info")); get_system_info(&buffer); + get_header(&buffer, _("Enabled Hooks")); + get_populated_hooks(&buffer, nongit_ok); + /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */ report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666); diff --git a/builtin/am.c b/builtin/am.c index e3dfd93c25..69e50de018 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1691,7 +1691,6 @@ static int do_interactive(struct am_state *state) */ static void am_run(struct am_state *state, int resume) { - const char *argv_gc_auto[] = {"gc", "--auto", NULL}; struct strbuf sb = STRBUF_INIT; unlink(am_path(state, "dirtyindex")); @@ -1796,7 +1795,7 @@ next: if (!state->rebasing) { am_destroy(state); close_object_store(the_repository->objects); - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + run_auto_gc(state->quiet); } } diff --git a/builtin/commit.c b/builtin/commit.c index a73de0a4c5..d1b7396052 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1494,7 +1494,6 @@ static int git_commit_config(const char *k, const char *v, void *cb) int cmd_commit(int argc, const char **argv, const char *prefix) { - const char *argv_gc_auto[] = {"gc", "--auto", NULL}; static struct wt_status s; static struct option builtin_commit_options[] = { OPT__QUIET(&quiet, N_("suppress summary after successful commit")), @@ -1703,7 +1702,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) git_test_write_commit_graph_or_die(); repo_rerere(the_repository, 0); - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + run_auto_gc(quiet); run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { commit_post_rewrite(the_repository, current_head, &oid); diff --git a/builtin/fetch.c b/builtin/fetch.c index 3ae52c015d..b5788c16bf 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -27,6 +27,7 @@ #include "branch.h" #include "promisor-remote.h" #include "commit-graph.h" +#include "shallow.h" #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000) @@ -1752,7 +1753,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct remote *remote = NULL; int result = 0; int prune_tags_ok = 1; - struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; packet_trace_identity("fetch"); @@ -1879,13 +1879,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) close_object_store(the_repository->objects); - if (enable_auto_gc) { - argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); - if (verbosity < 0) - argv_array_push(&argv_gc_auto, "--quiet"); - run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD); - argv_array_clear(&argv_gc_auto); - } + if (enable_auto_gc) + run_auto_gc(verbosity < 0); return result; } diff --git a/builtin/merge.c b/builtin/merge.c index 923e32acf1..ca6a5dc4bf 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -450,7 +450,6 @@ static void finish(struct commit *head_commit, if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); else { - const char *argv_gc_auto[] = { "gc", "--auto", NULL }; update_ref(reflog_message.buf, "HEAD", new_head, head, 0, UPDATE_REFS_DIE_ON_ERR); /* @@ -458,7 +457,7 @@ static void finish(struct commit *head_commit, * user should see them. */ close_object_store(the_repository->objects); - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + run_auto_gc(verbosity < 0); } } if (new_head && show_diffstat) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 03b85f5166..c5b433a23f 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -34,6 +34,7 @@ #include "dir.h" #include "midx.h" #include "trace2.h" +#include "shallow.h" #define IN_PACK(obj) oe_in_pack(&to_pack, obj) #define SIZE(obj) oe_size(&to_pack, obj) diff --git a/builtin/prune.c b/builtin/prune.c index fd9acc7222..02c6ab7cba 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -8,6 +8,7 @@ #include "progress.h" #include "prune-packed.h" #include "object-store.h" +#include "shallow.h" static const char * const prune_usage[] = { N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"), diff --git a/builtin/rebase.c b/builtin/rebase.c index ca6aa0dc7a..37ba76ac3d 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -722,7 +722,6 @@ static int rebase_write_basic_state(struct rebase_options *opts) static int finish_rebase(struct rebase_options *opts) { struct strbuf dir = STRBUF_INIT; - const char *argv_gc_auto[] = { "gc", "--auto", NULL }; int ret = 0; delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); @@ -732,7 +731,7 @@ static int finish_rebase(struct rebase_options *opts) * We ignore errors in 'gc --auto', since the * user should see them. */ - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + run_auto_gc(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE))); if (opts->type == REBASE_MERGE) { struct replay_opts replay = REPLAY_OPTS_INIT; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d37ab776b3..ea3d0f01af 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -28,6 +28,7 @@ #include "protocol.h" #include "commit-reach.h" #include "worktree.h" +#include "shallow.h" static const char * const receive_pack_usage[] = { N_("git receive-pack <git-dir>"), @@ -876,7 +877,7 @@ static void refuse_unconfigured_deny_delete_current(void) static int command_singleton_iterator(void *cb_data, struct object_id *oid); static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { - struct lock_file shallow_lock = LOCK_INIT; + struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT; struct oid_array extra = OID_ARRAY_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT; uint32_t mask = 1 << (cmd->index % 32); diff --git a/builtin/repack.c b/builtin/repack.c index 1b686ee9ce..df287739d9 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -13,6 +13,7 @@ #include "prune-packed.h" #include "object-store.h" #include "promisor-remote.h" +#include "shallow.h" static int delta_base_offset = 1; static int pack_kept_objects = -1; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 06056434ed..669dd2fd6f 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -16,6 +16,7 @@ #include "split-index.h" #include "submodule.h" #include "commit-reach.h" +#include "shallow.h" #define DO_REVS 1 #define DO_NOREV 2 diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 1a4b391c88..46c03d2a12 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2246,6 +2246,37 @@ static int module_config(int argc, const char **argv, const char *prefix) usage_with_options(git_submodule_helper_usage, module_config_options); } +static int module_set_url(int argc, const char **argv, const char *prefix) +{ + int quiet = 0; + const char *newurl; + const char *path; + char *config_name; + + struct option options[] = { + OPT__QUIET(&quiet, N_("Suppress output for setting url of a submodule")), + OPT_END() + }; + const char *const usage[] = { + N_("git submodule--helper set-url [--quiet] <path> <newurl>"), + NULL + }; + + argc = parse_options(argc, argv, prefix, options, usage, 0); + + if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1])) + usage_with_options(usage, options); + + config_name = xstrfmt("submodule.%s.url", path); + + config_set_in_gitmodules_file_gently(config_name, newurl); + sync_submodule(path, prefix, quiet ? OPT_QUIET : 0); + + free(config_name); + + return 0; +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -2276,6 +2307,7 @@ static struct cmd_struct commands[] = { {"is-active", is_active, 0}, {"check-name", check_name, 0}, {"config", module_config, 0}, + {"set-url", module_set_url, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --git a/ci/config/allow-refs.sample b/ci/config/allow-refs.sample new file mode 100755 index 0000000000..f157f1945a --- /dev/null +++ b/ci/config/allow-refs.sample @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Sample script for enabling/disabling GitHub Actions CI runs on +# particular refs. By default, CI is run for all branches pushed to +# GitHub. You can override this by dropping the ".sample" from the script, +# editing it, committing, and pushing the result to the "ci-config" branch of +# your repository: +# +# git checkout -b ci-config +# cp allow-refs.sample allow-refs +# $EDITOR allow-refs +# git commit -am "implement my ci preferences" +# git push +# +# This script will then be run when any refs are pushed to that repository. It +# gets the fully qualified refname as the first argument, and should exit with +# success only for refs for which you want to run CI. + +case "$1" in +# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch" +refs/heads/for-ci*) true ;; +# always build your integration branch +refs/heads/my-integration-branch) true ;; +# don't build any other branches or tags +*) false ;; +esac diff --git a/commit-graph.c b/commit-graph.c index 5ea0c8e15c..e3420ddcbf 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -18,6 +18,7 @@ #include "progress.h" #include "bloom.h" #include "commit-slab.h" +#include "shallow.h" void git_test_write_commit_graph_or_die(void) { @@ -20,6 +20,7 @@ #include "refs.h" #include "commit-reach.h" #include "run-command.h" +#include "shallow.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -110,7 +111,7 @@ static const unsigned char *commit_graft_sha1_access(size_t index, void *table) return commit_graft_table[index]->oid.hash; } -static int commit_graft_pos(struct repository *r, const unsigned char *sha1) +int commit_graft_pos(struct repository *r, const unsigned char *sha1) { return sha1_pos(sha1, r->parsed_objects->grafts, r->parsed_objects->grafts_nr, @@ -245,19 +246,6 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data) return ret; } -int unregister_shallow(const struct object_id *oid) -{ - int pos = commit_graft_pos(the_repository, oid->hash); - if (pos < 0) - return -1; - if (pos + 1 < the_repository->parsed_objects->grafts_nr) - MOVE_ARRAY(the_repository->parsed_objects->grafts + pos, - the_repository->parsed_objects->grafts + pos + 1, - the_repository->parsed_objects->grafts_nr - pos - 1); - the_repository->parsed_objects->grafts_nr--; - return 0; -} - struct commit_buffer { void *buffer; unsigned long size; @@ -236,6 +236,8 @@ struct commit_graft { typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *); struct commit_graft *read_graft_line(struct strbuf *line); +/* commit_graft_pos returns an index into r->parsed_objects->grafts. */ +int commit_graft_pos(struct repository *r, const unsigned char *sha1); int register_commit_graft(struct repository *r, struct commit_graft *, int); void prepare_commit_graft(struct repository *r); struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid); @@ -247,55 +249,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit); struct oid_array; struct ref; -int register_shallow(struct repository *r, const struct object_id *oid); -int unregister_shallow(const struct object_id *oid); -int commit_shallow_file(struct repository *r, struct lock_file *lk); -void rollback_shallow_file(struct repository *r, struct lock_file *lk); int for_each_commit_graft(each_commit_graft_fn, void *); -int is_repository_shallow(struct repository *r); -struct commit_list *get_shallow_commits(struct object_array *heads, - int depth, int shallow_flag, int not_shallow_flag); -struct commit_list *get_shallow_commits_by_rev_list( - int ac, const char **av, int shallow_flag, int not_shallow_flag); -void set_alternate_shallow_file(struct repository *r, const char *path, int override); -int write_shallow_commits(struct strbuf *out, int use_pack_protocol, - const struct oid_array *extra); -void setup_alternate_shallow(struct lock_file *shallow_lock, - const char **alternate_shallow_file, - const struct oid_array *extra); -const char *setup_temporary_shallow(const struct oid_array *extra); -void advertise_shallow_grafts(int); - -/* - * Initialize with prepare_shallow_info() or zero-initialize (equivalent to - * prepare_shallow_info with a NULL oid_array). - */ -struct shallow_info { - struct oid_array *shallow; - int *ours, nr_ours; - int *theirs, nr_theirs; - struct oid_array *ref; - - /* for receive-pack */ - uint32_t **used_shallow; - int *need_reachability_test; - int *reachable; - int *shallow_ref; - struct commit **commits; - int nr_commits; -}; - -void prepare_shallow_info(struct shallow_info *, struct oid_array *); -void clear_shallow_info(struct shallow_info *); -void remove_nonexistent_theirs_shallow(struct shallow_info *); -void assign_shallow_commits_to_refs(struct shallow_info *info, - uint32_t **used, - int *ref_status); -int delayed_reachability_test(struct shallow_info *si, int c); -#define PRUNE_SHOW_ONLY 1 -#define PRUNE_QUICK 2 -void prune_shallow(unsigned options); -extern struct trace_key trace_shallow; int interactive_add(int argc, const char **argv, const char *prefix, int patch); int run_add_interactive(const char *revision, const char *patch_mode, diff --git a/credential.h b/credential.h index d99ec42b2a..c0e17e3554 100644 --- a/credential.h +++ b/credential.h @@ -177,8 +177,8 @@ void credential_write(const struct credential *, FILE *); * Parse a url into a credential struct, replacing any existing contents. * * If the url can't be parsed (e.g., a missing "proto://" component), the - * resulting credential will be empty but we'll still return success from the - * "gently" form. + * resulting credential will be empty and the function will return an + * error (even in the "gently" form). * * If we encounter a component which cannot be represented as a credential * value (e.g., because it contains a newline), the "gently" form will return @@ -189,7 +189,7 @@ void credential_write(const struct credential *, FILE *); void credential_from_url(struct credential *, const char *url); int credential_from_url_gently(struct credential *, const char *url, int quiet); -int credential_match(const struct credential *have, - const struct credential *want); +int credential_match(const struct credential *want, + const struct credential *have); #endif /* CREDENTIAL_H */ diff --git a/environment.c b/environment.c index 10c9061c43..aaca0e91ac 100644 --- a/environment.c +++ b/environment.c @@ -17,6 +17,7 @@ #include "argv-array.h" #include "object-store.h" #include "chdir-notify.h" +#include "shallow.h" int trust_executable_bit = 1; int trust_ctime = 1; diff --git a/fetch-pack.c b/fetch-pack.c index f73a2ce6cb..7eaa19d7c1 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -22,6 +22,7 @@ #include "connected.h" #include "fetch-negotiator.h" #include "fsck.h" +#include "shallow.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@ -34,7 +35,7 @@ static int fetch_fsck_objects = -1; static int transfer_fsck_objects = -1; static int agent_supported; static int server_supports_filtering; -static struct lock_file shallow_lock; +static struct shallow_lock shallow_lock; static const char *alternate_shallow_file; static struct strbuf fsck_msg_types = STRBUF_INIT; diff --git a/git-bisect.sh b/git-bisect.sh index efee12b8b1..71b367a944 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -209,6 +209,7 @@ bisect_replay () { test "$#" -eq 1 || die "$(gettext "No logfile given")" test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")" git bisect--helper --bisect-reset || exit + oIFS="$IFS" IFS="$IFS$(printf '\015')" while read git bisect command rev do test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue @@ -232,6 +233,7 @@ bisect_replay () { die "$(gettext "?? what are you talking about?")" ;; esac done <"$file" + IFS="$oIFS" bisect_auto_next } diff --git a/git-submodule.sh b/git-submodule.sh index 08e0439df0..39ebdf25b5 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -805,27 +805,7 @@ cmd_set_url() { shift done - if test $# -ne 2 - then - usage - fi - - # we can't use `git submodule--helper name` here because internally, it - # hashes the path so a trailing slash could lead to an unintentional no match - name="$(git submodule--helper list "$1" | cut -f2)" - if test -z "$name" - then - exit 1 - fi - - url="$2" - if test -z "$url" - then - exit 1 - fi - - git submodule--helper config submodule."$name".url "$url" - git submodule--helper sync ${GIT_QUIET:+--quiet} "$name" + git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-url ${GIT_QUIET:+--quiet} -- "$@" } # @@ -4,6 +4,7 @@ #include "help.h" #include "run-command.h" #include "alias.h" +#include "shallow.h" #define RUN_SETUP (1<<0) #define RUN_SETUP_GENTLY (1<<1) diff --git a/list-objects-filter.c b/list-objects-filter.c index 1e8d4e763d..0a3ef3cab3 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -663,6 +663,9 @@ struct filter *list_objects_filter__init( assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT); + if (!filter_options) + return NULL; + if (filter_options->choice >= LOFC__COUNT) BUG("invalid list-objects filter choice: %d", filter_options->choice); diff --git a/pack-bitmap.c b/pack-bitmap.c index 49a8d10d0c..4077e731e8 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -506,7 +506,8 @@ static int should_include(struct commit *commit, void *_data) static struct bitmap *find_objects(struct bitmap_index *bitmap_git, struct rev_info *revs, struct object_list *roots, - struct bitmap *seen) + struct bitmap *seen, + struct list_objects_filter_options *filter) { struct bitmap *base = NULL; int needs_walk = 0; @@ -599,8 +600,9 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git, show_data.bitmap_git = bitmap_git; show_data.base = base; - traverse_commit_list(revs, show_commit, show_object, - &show_data); + traverse_commit_list_filtered(filter, revs, + show_commit, show_object, + &show_data, NULL); } return base; @@ -715,8 +717,9 @@ static int in_bitmapped_pack(struct bitmap_index *bitmap_git, return 0; } -static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git, - struct object_list *tip_objects) +static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git, + struct object_list *tip_objects, + enum object_type type) { struct bitmap *result = bitmap_new(); struct object_list *p; @@ -724,7 +727,7 @@ static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git, for (p = tip_objects; p; p = p->next) { int pos; - if (p->item->type != OBJ_BLOB) + if (p->item->type != type) continue; pos = bitmap_position(bitmap_git, &p->item->oid); @@ -737,9 +740,10 @@ static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git, return result; } -static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git, - struct object_list *tip_objects, - struct bitmap *to_filter) +static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git, + struct object_list *tip_objects, + struct bitmap *to_filter, + enum object_type type) { struct eindex *eindex = &bitmap_git->ext_index; struct bitmap *tips; @@ -747,18 +751,21 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git, eword_t mask; uint32_t i; + if (type != OBJ_BLOB && type != OBJ_TREE) + BUG("filter_bitmap_exclude_type: unsupported type '%d'", type); + /* * The non-bitmap version of this filter never removes - * blobs which the other side specifically asked for, + * objects which the other side specifically asked for, * so we must match that behavior. */ - tips = find_tip_blobs(bitmap_git, tip_objects); + tips = find_tip_objects(bitmap_git, tip_objects, type); /* * We can use the blob type-bitmap to work in whole words * for the objects that are actually in the bitmapped packfile. */ - for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB); + for (i = 0, init_type_iterator(&it, bitmap_git, type); i < to_filter->word_alloc && ewah_iterator_next(&mask, &it); i++) { if (i < tips->word_alloc) @@ -773,7 +780,7 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git, */ for (i = 0; i < eindex->count; i++) { uint32_t pos = i + bitmap_git->pack->num_objects; - if (eindex->objects[i]->type == OBJ_BLOB && + if (eindex->objects[i]->type == type && bitmap_get(to_filter, pos) && !bitmap_get(tips, pos)) bitmap_unset(to_filter, pos); @@ -782,6 +789,14 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git, bitmap_free(tips); } +static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git, + struct object_list *tip_objects, + struct bitmap *to_filter) +{ + filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, + OBJ_BLOB); +} + static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git, uint32_t pos) { @@ -820,7 +835,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git, eword_t mask; uint32_t i; - tips = find_tip_blobs(bitmap_git, tip_objects); + tips = find_tip_objects(bitmap_git, tip_objects, OBJ_BLOB); for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB); i < to_filter->word_alloc && ewah_iterator_next(&mask, &it); @@ -854,6 +869,20 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git, bitmap_free(tips); } +static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git, + struct object_list *tip_objects, + struct bitmap *to_filter, + unsigned long limit) +{ + if (limit) + BUG("filter_bitmap_tree_depth given non-zero limit"); + + filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, + OBJ_TREE); + filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, + OBJ_BLOB); +} + static int filter_bitmap(struct bitmap_index *bitmap_git, struct object_list *tip_objects, struct bitmap *to_filter, @@ -877,6 +906,15 @@ static int filter_bitmap(struct bitmap_index *bitmap_git, return 0; } + if (filter->choice == LOFC_TREE_DEPTH && + filter->tree_exclude_depth == 0) { + if (bitmap_git) + filter_bitmap_tree_depth(bitmap_git, tip_objects, + to_filter, + filter->tree_exclude_depth); + return 0; + } + /* filter choice not handled */ return -1; } @@ -963,7 +1001,8 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs, if (haves) { revs->ignore_missing_links = 1; - haves_bitmap = find_objects(bitmap_git, revs, haves, NULL); + haves_bitmap = find_objects(bitmap_git, revs, haves, NULL, + filter); reset_revision_walk(); revs->ignore_missing_links = 0; @@ -971,7 +1010,8 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs, BUG("failed to perform bitmap walk"); } - wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap); + wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap, + filter); if (!wants_bitmap) BUG("failed to perform bitmap walk"); diff --git a/run-command.c b/run-command.c index 0f41af3b55..9b3a57d1e3 100644 --- a/run-command.c +++ b/run-command.c @@ -1864,3 +1864,16 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, return result; } + +int run_auto_gc(int quiet) +{ + struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; + int status; + + argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); + if (quiet) + argv_array_push(&argv_gc_auto, "--quiet"); + status = run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD); + argv_array_clear(&argv_gc_auto); + return status; +} diff --git a/run-command.h b/run-command.h index 0f3cc73ab6..191dfcdafe 100644 --- a/run-command.h +++ b/run-command.h @@ -218,6 +218,11 @@ LAST_ARG_MUST_BE_NULL int run_hook_le(const char *const *env, const char *name, ...); int run_hook_ve(const char *const *env, const char *name, va_list args); +/* + * Trigger an auto-gc + */ +int run_auto_gc(int quiet); + #define RUN_COMMAND_NO_STDIN 1 #define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4 diff --git a/send-pack.c b/send-pack.c index d1b7edc995..0abee22283 100644 --- a/send-pack.c +++ b/send-pack.c @@ -15,6 +15,7 @@ #include "oid-array.h" #include "gpg-interface.h" #include "cache.h" +#include "shallow.h" int option_parse_push_signed(const struct option *opt, const char *arg, int unset) diff --git a/sequencer.c b/sequencer.c index 9d1b3e7d4f..fd7701c88a 100644 --- a/sequencer.c +++ b/sequencer.c @@ -5385,10 +5385,13 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) todo_list->items[i].command = starts_with(subject, "fixup!") ? TODO_FIXUP : TODO_SQUASH; - if (next[i2] < 0) + if (tail[i2] < 0) { + next[i] = next[i2]; next[i2] = i; - else + } else { + next[i] = next[tail[i2]]; next[tail[i2]] = i; + } tail[i2] = i; } else if (!hashmap_get_from_hash(&subject2item, strhash(subject), subject)) { @@ -14,6 +14,7 @@ #include "commit-slab.h" #include "list-objects.h" #include "commit-reach.h" +#include "shallow.h" void set_alternate_shallow_file(struct repository *r, const char *path, int override) { @@ -38,6 +39,19 @@ int register_shallow(struct repository *r, const struct object_id *oid) return register_commit_graft(r, graft, 0); } +int unregister_shallow(const struct object_id *oid) +{ + int pos = commit_graft_pos(the_repository, oid->hash); + if (pos < 0) + return -1; + if (pos + 1 < the_repository->parsed_objects->grafts_nr) + MOVE_ARRAY(the_repository->parsed_objects->grafts + pos, + the_repository->parsed_objects->grafts + pos + 1, + the_repository->parsed_objects->grafts_nr - pos - 1); + the_repository->parsed_objects->grafts_nr--; + return 0; +} + int is_repository_shallow(struct repository *r) { FILE *fp; @@ -78,16 +92,16 @@ static void reset_repository_shallow(struct repository *r) stat_validity_clear(r->parsed_objects->shallow_stat); } -int commit_shallow_file(struct repository *r, struct lock_file *lk) +int commit_shallow_file(struct repository *r, struct shallow_lock *lk) { - int res = commit_lock_file(lk); + int res = commit_lock_file(&lk->lock); reset_repository_shallow(r); return res; } -void rollback_shallow_file(struct repository *r, struct lock_file *lk) +void rollback_shallow_file(struct repository *r, struct shallow_lock *lk) { - rollback_lock_file(lk); + rollback_lock_file(&lk->lock); reset_repository_shallow(r); } @@ -352,22 +366,22 @@ const char *setup_temporary_shallow(const struct oid_array *extra) return ""; } -void setup_alternate_shallow(struct lock_file *shallow_lock, +void setup_alternate_shallow(struct shallow_lock *shallow_lock, const char **alternate_shallow_file, const struct oid_array *extra) { struct strbuf sb = STRBUF_INIT; int fd; - fd = hold_lock_file_for_update(shallow_lock, + fd = hold_lock_file_for_update(&shallow_lock->lock, git_path_shallow(the_repository), LOCK_DIE_ON_ERROR); check_shallow_file_for_update(the_repository); if (write_shallow_commits(&sb, 0, extra)) { if (write_in_full(fd, sb.buf, sb.len) < 0) die_errno("failed to write to %s", - get_lock_file_path(shallow_lock)); - *alternate_shallow_file = get_lock_file_path(shallow_lock); + get_lock_file_path(&shallow_lock->lock)); + *alternate_shallow_file = get_lock_file_path(&shallow_lock->lock); } else /* * is_repository_shallow() sees empty string as "no @@ -400,7 +414,7 @@ void advertise_shallow_grafts(int fd) */ void prune_shallow(unsigned options) { - struct lock_file shallow_lock = LOCK_INIT; + struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT; struct strbuf sb = STRBUF_INIT; unsigned flags = SEEN_ONLY; int fd; @@ -414,14 +428,14 @@ void prune_shallow(unsigned options) strbuf_release(&sb); return; } - fd = hold_lock_file_for_update(&shallow_lock, + fd = hold_lock_file_for_update(&shallow_lock.lock, git_path_shallow(the_repository), LOCK_DIE_ON_ERROR); check_shallow_file_for_update(the_repository); if (write_shallow_commits_1(&sb, 0, NULL, flags)) { if (write_in_full(fd, sb.buf, sb.len) < 0) die_errno("failed to write to %s", - get_lock_file_path(&shallow_lock)); + get_lock_file_path(&shallow_lock.lock)); commit_shallow_file(the_repository, &shallow_lock); } else { unlink(git_path_shallow(the_repository)); diff --git a/shallow.h b/shallow.h new file mode 100644 index 0000000000..5b4a96dcd6 --- /dev/null +++ b/shallow.h @@ -0,0 +1,81 @@ +#ifndef SHALLOW_H +#define SHALLOW_H + +#include "lockfile.h" +#include "object.h" +#include "repository.h" +#include "strbuf.h" + +void set_alternate_shallow_file(struct repository *r, const char *path, int override); +int register_shallow(struct repository *r, const struct object_id *oid); +int unregister_shallow(const struct object_id *oid); +int is_repository_shallow(struct repository *r); + +/* + * Lock for updating the $GIT_DIR/shallow file. + * + * Use `commit_shallow_file()` to commit an update, or + * `rollback_shallow_file()` to roll it back. In either case, any + * in-memory cached information about which commits are shallow will be + * appropriately invalidated so that future operations reflect the new + * state. + */ +struct shallow_lock { + struct lock_file lock; +}; +#define SHALLOW_LOCK_INIT { LOCK_INIT } + +/* commit $GIT_DIR/shallow and reset stat-validity checks */ +int commit_shallow_file(struct repository *r, struct shallow_lock *lk); +/* rollback $GIT_DIR/shallow and reset stat-validity checks */ +void rollback_shallow_file(struct repository *r, struct shallow_lock *lk); + +struct commit_list *get_shallow_commits(struct object_array *heads, + int depth, int shallow_flag, int not_shallow_flag); +struct commit_list *get_shallow_commits_by_rev_list( + int ac, const char **av, int shallow_flag, int not_shallow_flag); +int write_shallow_commits(struct strbuf *out, int use_pack_protocol, + const struct oid_array *extra); + +void setup_alternate_shallow(struct shallow_lock *shallow_lock, + const char **alternate_shallow_file, + const struct oid_array *extra); + +const char *setup_temporary_shallow(const struct oid_array *extra); + +void advertise_shallow_grafts(int); + +#define PRUNE_SHOW_ONLY 1 +#define PRUNE_QUICK 2 +void prune_shallow(unsigned options); + +/* + * Initialize with prepare_shallow_info() or zero-initialize (equivalent to + * prepare_shallow_info with a NULL oid_array). + */ +struct shallow_info { + struct oid_array *shallow; + int *ours, nr_ours; + int *theirs, nr_theirs; + struct oid_array *ref; + + /* for receive-pack */ + uint32_t **used_shallow; + int *need_reachability_test; + int *reachable; + int *shallow_ref; + struct commit **commits; + int nr_commits; +}; + +void prepare_shallow_info(struct shallow_info *, struct oid_array *); +void clear_shallow_info(struct shallow_info *); +void remove_nonexistent_theirs_shallow(struct shallow_info *); +void assign_shallow_commits_to_refs(struct shallow_info *info, + uint32_t **used, + int *ref_status); +int delayed_reachability_test(struct shallow_info *si, int c); + +extern struct trace_key trace_shallow; + +#endif /* SHALLOW_H */ diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c index 77eb27adac..456f5ea7f9 100644 --- a/t/helper/test-bloom.c +++ b/t/helper/test-bloom.c @@ -3,7 +3,7 @@ #include "test-tool.h" #include "commit.h" -struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS; +static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS; static void add_string_to_filter(const char *data, struct bloom_filter *filter) { struct bloom_key key; diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh index 80c53edca7..b3e725f031 100755 --- a/t/perf/p5310-pack-bitmaps.sh +++ b/t/perf/p5310-pack-bitmaps.sh @@ -53,6 +53,11 @@ test_perf 'rev-list count with blob:limit=1k' ' --filter=blob:limit=1k >/dev/null ' +test_perf 'rev-list count with tree:0' ' + git rev-list --use-bitmap-index --count --objects --all \ + --filter=tree:0 >/dev/null +' + test_perf 'simulated partial clone' ' git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null ' @@ -86,4 +91,9 @@ test_perf 'pack to file (partial bitmap)' ' git pack-objects --use-bitmap-index --all pack2b </dev/null >/dev/null ' +test_perf 'rev-list with tree filter (partial bitmap)' ' + git rev-list --use-bitmap-index --count --objects --all \ + --filter=tree:0 >/dev/null +' + test_done diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh index 2e73658a5c..526304ff95 100755 --- a/t/t0091-bugreport.sh +++ b/t/t0091-bugreport.sh @@ -57,5 +57,20 @@ test_expect_success 'can create leading directories outside of a git dir' ' nongit git bugreport -o foo/bar/baz ' +test_expect_success 'indicates populated hooks' ' + test_when_finished rm git-bugreport-hooks.txt && + test_when_finished rm -fr .git/hooks && + rm -fr .git/hooks && + mkdir .git/hooks && + for hook in applypatch-msg prepare-commit-msg.sample + do + write_script ".git/hooks/$hook" <<-EOF || return 1 + echo "hook $hook exists" + EOF + done && + git bugreport -s hooks && + grep applypatch-msg git-bugreport-hooks.txt && + ! grep prepare-commit-msg git-bugreport-hooks.txt +' test_done diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh index 8f9eef116d..809ec7b0b8 100755 --- a/t/t0095-bloom.sh +++ b/t/t0095-bloom.sh @@ -114,4 +114,4 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' ' test_cmp expect actual ' -test_done
\ No newline at end of file +test_done diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index 093de9005b..7bab6000dc 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -424,4 +424,20 @@ test_expect_success 'abort last squash' ' ! grep first actual ' +test_expect_success 'fixup a fixup' ' + echo 0to-fixup >file0 && + test_tick && + git commit -m "to-fixup" file0 && + test_tick && + git commit --squash HEAD -m X --allow-empty && + test_tick && + git commit --squash HEAD^ -m Y --allow-empty && + test_tick && + git commit -m "squash! $(git rev-parse HEAD^)" -m Z --allow-empty && + test_tick && + git commit -m "squash! $(git rev-parse HEAD^^)" -m W --allow-empty && + git rebase -ki --autosquash HEAD~5 && + test XZWY = $(git show | tr -cd W-Z) +' + test_done diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh index c7011f33e2..21b68dd6c8 100755 --- a/t/t4216-log-bloom.sh +++ b/t/t4216-log-bloom.sh @@ -152,4 +152,4 @@ test_expect_success 'Use Bloom filters if they exist in the latest but not all c test_bloom_filters_used_when_some_filters_are_missing "-- A/B" ' -test_done
\ No newline at end of file +test_done diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 88002b24af..8a27452a51 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -384,12 +384,11 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' ' grep "want $(cat hash)" trace ' -# The following two tests must be in this order, or else -# the first will not fail. It is important that the srv.bare -# repository did not have tags during clone, but has tags +# The following two tests must be in this order. It is important that +# the srv.bare repository did not have tags during clone, but has tags # in the fetch. -test_expect_failure 'verify fetch succeeds when asking for new tags' ' +test_expect_success 'verify fetch succeeds when asking for new tags' ' git clone --filter=blob:none "file://$(pwd)/srv.bare" tag-test && for i in I J K do diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 1313142564..ac31faefa1 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -792,6 +792,13 @@ test_expect_success 'bisect replay with old and new' ' git bisect reset ' +test_expect_success 'bisect replay with CRLF log' ' + append_cr <log_to_replay.txt >log_to_replay_crlf.txt && + git bisect replay log_to_replay_crlf.txt >bisect_result_crlf && + grep "$HASH2 is the first new commit" bisect_result_crlf && + git bisect reset +' + test_expect_success 'bisect cannot mix old/new and good/bad' ' git bisect start && git bisect bad $HASH4 && diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh index 145603f124..2b551e6fd0 100755 --- a/t/t6113-rev-list-bitmap-filters.sh +++ b/t/t6113-rev-list-bitmap-filters.sh @@ -53,4 +53,25 @@ test_expect_success 'blob:limit filter with specified blob' ' test_bitmap_traversal expect actual ' +test_expect_success 'tree:0 filter' ' + git rev-list --objects --filter=tree:0 HEAD >expect && + git rev-list --use-bitmap-index \ + --objects --filter=tree:0 HEAD >actual && + test_bitmap_traversal expect actual +' + +test_expect_success 'tree:0 filter with specified blob, tree' ' + git rev-list --objects --filter=tree:0 HEAD HEAD:two.t >expect && + git rev-list --use-bitmap-index \ + --objects --filter=tree:0 HEAD HEAD:two.t >actual && + test_bitmap_traversal expect actual +' + +test_expect_success 'tree:1 filter' ' + git rev-list --objects --filter=tree:1 HEAD >expect && + git rev-list --use-bitmap-index \ + --objects --filter=tree:1 HEAD >actual && + test_cmp expect actual +' + test_done diff --git a/unpack-trees.c b/unpack-trees.c index 1fe3764f2b..02048dfdac 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -562,11 +562,11 @@ static int warn_conflicted_path(struct index_state *istate, add_rejected_path(o, WARNING_SPARSE_UNMERGED_FILE, conflicting_path); - /* Find out how many higher stage entries at same path */ - while (++count < istate->cache_nr && - !strcmp(conflicting_path, - istate->cache[i+count]->name)) - /* do nothing */; + /* Find out how many higher stage entries are at same path */ + while ((++count) + i < istate->cache_nr && + !strcmp(conflicting_path, istate->cache[count + i]->name)) + ; /* do nothing */ + return count; } diff --git a/upload-pack.c b/upload-pack.c index 902d0ad5e1..0478bff3e7 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -26,6 +26,7 @@ #include "serve.h" #include "commit-graph.h" #include "commit-reach.h" +#include "shallow.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11) @@ -68,7 +69,6 @@ static const char *pack_objects_hook; static int filter_capability_requested; static int allow_filter; static int allow_ref_in_want; -static struct list_objects_filter_options filter_options; static int allow_sideband_all; @@ -103,7 +103,8 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) } static void create_pack_file(const struct object_array *have_obj, - const struct object_array *want_obj) + const struct object_array *want_obj, + struct list_objects_filter_options *filter_options) { struct child_process pack_objects = CHILD_PROCESS_INIT; char data[8193], progress[128]; @@ -140,9 +141,9 @@ static void create_pack_file(const struct object_array *have_obj, argv_array_push(&pack_objects.args, "--delta-base-offset"); if (use_include_tag) argv_array_push(&pack_objects.args, "--include-tag"); - if (filter_options.choice) { + if (filter_options->choice) { const char *spec = - expand_list_objects_filter_spec(&filter_options); + expand_list_objects_filter_spec(filter_options); if (pack_objects.use_shell) { struct strbuf buf = STRBUF_INIT; sq_quote_buf(&buf, spec); @@ -848,7 +849,9 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not, return 0; } -static void receive_needs(struct packet_reader *reader, struct object_array *want_obj) +static void receive_needs(struct packet_reader *reader, + struct object_array *want_obj, + struct list_objects_filter_options *filter_options) { struct object_array shallows = OBJECT_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; @@ -883,8 +886,8 @@ static void receive_needs(struct packet_reader *reader, struct object_array *wan if (skip_prefix(reader->line, "filter ", &arg)) { if (!filter_capability_requested) die("git upload-pack: filtering capability not negotiated"); - list_objects_filter_die_if_populated(&filter_options); - parse_list_objects_filter(&filter_options, arg); + list_objects_filter_die_if_populated(filter_options); + parse_list_objects_filter(filter_options, arg); continue; } @@ -1087,11 +1090,14 @@ void upload_pack(struct upload_pack_options *options) struct string_list symref = STRING_LIST_INIT_DUP; struct object_array want_obj = OBJECT_ARRAY_INIT; struct packet_reader reader; + struct list_objects_filter_options filter_options; stateless_rpc = options->stateless_rpc; timeout = options->timeout; daemon_mode = options->daemon_mode; + memset(&filter_options, 0, sizeof(filter_options)); + git_config(upload_pack_config, NULL); head_ref_namespaced(find_symref, &symref); @@ -1114,12 +1120,14 @@ void upload_pack(struct upload_pack_options *options) PACKET_READ_CHOMP_NEWLINE | PACKET_READ_DIE_ON_ERR_PACKET); - receive_needs(&reader, &want_obj); + receive_needs(&reader, &want_obj, &filter_options); if (want_obj.nr) { struct object_array have_obj = OBJECT_ARRAY_INIT; get_common_commits(&reader, &have_obj, &want_obj); - create_pack_file(&have_obj, &want_obj); + create_pack_file(&have_obj, &want_obj, &filter_options); } + + list_objects_filter_release(&filter_options); } struct upload_pack_data { @@ -1134,6 +1142,8 @@ struct upload_pack_data { int deepen_rev_list; int deepen_relative; + struct list_objects_filter_options filter_options; + struct packet_writer writer; unsigned stateless_rpc : 1; @@ -1169,6 +1179,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data) oid_array_clear(&data->haves); object_array_clear(&data->shallows); string_list_clear(&data->deepen_not, 0); + list_objects_filter_release(&data->filter_options); } static int parse_want(struct packet_writer *writer, const char *line, @@ -1306,8 +1317,8 @@ static void process_args(struct packet_reader *request, } if (allow_filter && skip_prefix(arg, "filter ", &p)) { - list_objects_filter_die_if_populated(&filter_options); - parse_list_objects_filter(&filter_options, p); + list_objects_filter_die_if_populated(&data->filter_options); + parse_list_objects_filter(&data->filter_options, p); continue; } @@ -1514,7 +1525,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys, send_shallow_info(&data, &want_obj); packet_writer_write(&data.writer, "packfile\n"); - create_pack_file(&have_obj, &want_obj); + create_pack_file(&have_obj, &want_obj, &data.filter_options); state = FETCH_DONE; break; case FETCH_DONE: |