diff options
73 files changed, 1244 insertions, 471 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c3453a10b..aef6643648 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -205,7 +205,6 @@ jobs: shell: bash run: | cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \ - -DIconv_LIBRARY=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows/lib/libiconv.lib -DIconv_INCLUDE_DIR=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows/include \ -DMSGFMT_EXE=`pwd`/git-sdk-64-minimal/mingw64/bin/msgfmt.exe -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON - name: MSBuild run: msbuild git.sln -property:Configuration=Release -property:Platform=x64 -maxCpuCount:4 -property:PlatformToolset=v142 diff --git a/.gitignore b/.gitignore index f22b7a4cf1..3dcdb6bb5a 100644 --- a/.gitignore +++ b/.gitignore @@ -134,7 +134,6 @@ /git-remote-ftps /git-remote-fd /git-remote-ext -/git-remote-testpy /git-repack /git-replace /git-request-pull @@ -147,11 +146,9 @@ /git-rm /git-send-email /git-send-pack -/git-serve /git-sh-i18n /git-sh-i18n--envsubst /git-sh-setup -/git-sh-i18n /git-shell /git-shortlog /git-show diff --git a/Documentation/Makefile b/Documentation/Makefile index 80d1908a44..69dbe4bb0b 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -380,7 +380,10 @@ SubmittingPatches.txt: SubmittingPatches $(QUIET_GEN) cp $< $@ XSLT = docbook.xsl -XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css +XSLTOPTS = +XSLTOPTS += --xinclude +XSLTOPTS += --stringparam html.stylesheet docbook-xsl.css +XSLTOPTS += --param generate.consistent.ids 1 user-manual.html: user-manual.xml $(XSLT) $(QUIET_XSLTPROC)$(RM) $@+ $@ && \ diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt index c3f2d1a831..2d10eea7a9 100644 --- a/Documentation/MyFirstObjectWalk.txt +++ b/Documentation/MyFirstObjectWalk.txt @@ -182,30 +182,6 @@ its `init_log_defaults()` sets its own state (`decoration_style`) and asks `grep` and `diff` to initialize themselves by calling each of their initialization functions. -For our first example within `git walken`, we don't intend to use any other -components within Git, and we don't have any configuration to do. However, we -may want to add some later, so for now, we can add an empty placeholder. Create -a new function in `builtin/walken.c`: - ----- -static void init_walken_defaults(void) -{ - /* - * We don't actually need the same components `git log` does; leave this - * empty for now. - */ -} ----- - -Make sure to add a line invoking it inside of `cmd_walken()`. - ----- -int cmd_walken(int argc, const char **argv, const char *prefix) -{ - init_walken_defaults(); -} ----- - ==== Configuring From `.gitconfig` Next, we should have a look at any relevant configuration settings (i.e., @@ -388,17 +364,9 @@ Next, let's try to filter the commits we see based on their author. This is equivalent to running `git log --author=<pattern>`. We can add a filter by modifying `rev_info.grep_filter`, which is a `struct grep_opt`. -First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add -`grep_config()` to `git_walken_config()`: +First some setup. Add `grep_config()` to `git_walken_config()`: ---- -static void init_walken_defaults(void) -{ - init_grep_defaults(the_repository); -} - -... - static int git_walken_config(const char *var, const char *value, void *cb) { grep_config(var, value, cb); diff --git a/Documentation/RelNotes/2.30.0.txt b/Documentation/RelNotes/2.30.0.txt index cb4d65f9ff..df8df8a5d1 100644 --- a/Documentation/RelNotes/2.30.0.txt +++ b/Documentation/RelNotes/2.30.0.txt @@ -78,6 +78,31 @@ UI, Workflows & Features * The command line completion script (in contrib/) learned to expand commands that are alias of alias. + * "git update-ref --stdin" learns to take multiple transactions in a + single session. + + * Various subcommands of "git config" that takes value_regex + learn the "--literal-value" option to take the value_regex option + as a literal string. + + * The transport layer was taught to optionally exchange the session + ID assigned by the trace2 subsystem during fetch/push transactions. + + * "git imap-send" used to ignore configuration variables like + core.askpass; this has been corrected. + + * "git $cmd $args", when $cmd is not a recognised subcommand, by + default tries to see if $cmd is a typo of an existing subcommand + and optionally executes the corrected command if there is only one + possibility, depending on the setting of help.autocorrect; the + users can now disable the whole thing, including the cycles spent + to find a likely typo, by setting the configuration variable to + 'never'. + + * "@" sometimes worked (e.g. "git push origin @:there") as a part of + a refspec element, but "git push origin @" did not work, which has + been corrected. + Performance, Internal Implementation, Development Support etc. @@ -138,6 +163,14 @@ Performance, Internal Implementation, Development Support etc. * "git-parse-remote" shell script library outlived its usefulness. + * Like die() and error(), a call to warning() will also trigger a + trace2 event. + + * Use of non-reentrant localtime() has been removed. + + * Non-reentrant time-related library functions and ctime/asctime with + awkward calling interfaces are banned from the codebase. + Fixes since v2.29 ----------------- @@ -294,6 +327,42 @@ Fixes since v2.29 has been corrected. (merge 309a4028e7 jk/stop-pack-objects-when-fetch-is-killed later to maint). + * "git add -i" failed to honor custom colors configured to show + patches, which has been corrected. + (merge 96386faa03 js/add-i-color-fix later to maint). + + * Processes that access packdata while the .idx file gets removed + (e.g. while repacking) did not fail or fall back gracefully as they + could. + (merge 506ec2fbda tb/idx-midx-race-fix later to maint). + + * "git apply" adjusted the permission bits of working-tree files and + directories according core.sharedRepository setting by mistake and + for a long time, which has been corrected. + (merge eb3c027e17 mt/do-not-use-scld-in-working-tree later to maint). + + * "fetch-pack" could pass NULL pointer to unlink(2) when it sees an + invalid filename; the error checking has been tightened to make + this impossible. + (merge 6031af387e rs/fetch-pack-invalid-lockfile later to maint). + + * "git maintenance run/start/stop" needed to be run in a repository + to hold the lockfile they use, but didn't make sure they are + actually in a repository, which has been corrected. + + * The glossary described a branch as an "active" line of development, + which is misleading---a stale and non-moving branch is still a + branch. + (merge eef1ceabd8 so/glossary-branch-is-not-necessarily-active later to maint). + + * Newer versions of xsltproc can assign IDs in HTML documents it + generates in a consistent manner. Use the feature to help format + HTML version of the user manual reproducibly. + (merge 3569e11d69 ae/doc-reproducible-html later to maint). + + * Tighten error checking in the codepath that responds to "git fetch". + (merge d43a21bdbb jk/check-config-parsing-error-in-upload-pack later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 3e0a5dc9af cc/doc-filter-branch-typofix later to maint). (merge 32c83afc2c cw/ci-ghwf-check-ws-errors later to maint). @@ -318,3 +387,7 @@ Fixes since v2.29 (merge 793c1464d3 ab/gc-keep-base-option later to maint). (merge b86339b12b mt/worktree-error-message-fix later to maint). (merge e01ae2a4a7 js/pull-rebase-use-advise later to maint). + (merge e63d774242 sn/config-doc-typofix later to maint). + (merge 08e9df2395 jk/multi-line-indent-style-fix later to maint). + (merge e66590348a da/vs-build-iconv-fix later to maint). + (merge 7fe07275be js/cmake-extra-built-ins-fix later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index f292c2689e..6ba50b1104 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -64,7 +64,7 @@ The variable names are case-insensitive, allow only alphanumeric characters and `-`, and must start with an alphabetic character. A line that defines a value can be continued to the next line by -ending it with a `\`; the backquote and the end-of-line are +ending it with a `\`; the backslash and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in diff --git a/Documentation/config/help.txt b/Documentation/config/help.txt index 224bbf5a28..783a90a0f9 100644 --- a/Documentation/config/help.txt +++ b/Documentation/config/help.txt @@ -8,13 +8,14 @@ help.format:: the default. 'web' and 'html' are the same. help.autoCorrect:: - Automatically correct and execute mistyped commands after - waiting for the given number of deciseconds (0.1 sec). If more - than one command can be deduced from the entered text, nothing - will be executed. If the value of this option is negative, - the corrected command will be executed immediately. If the - value is 0 - the command will be just shown but not executed. - This is the default. + If git detects typos and can identify exactly one valid command similar + to the error, git will automatically run the intended command after + waiting a duration of time defined by this configuration value in + deciseconds (0.1 sec). If this value is 0, the suggested corrections + will be shown, but not executed. If it is a negative integer, or + "immediate", the suggested command + is run immediately. If "never", suggestions are not shown at all. The + default value is zero. help.htmlPath:: Specify the path where the HTML documentation resides. File system paths diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt index f5b6245270..505126a780 100644 --- a/Documentation/config/transfer.txt +++ b/Documentation/config/transfer.txt @@ -69,3 +69,7 @@ transfer.unpackLimit:: When `fetch.unpackLimit` or `receive.unpackLimit` are not set, the value of this variable is used instead. The default value is 100. + +transfer.advertiseSID:: + Boolean. When true, client and server processes will advertise their + unique session IDs to their remote counterpart. Defaults to false. diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7573160f21..0e9351d3cb 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,15 +9,15 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]] +'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]] 'git config' [<file-option>] [--type=<type>] --add name value -'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex] -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex] -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex] -'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex] +'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value-pattern] +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern] +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern] +'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern] 'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL -'git config' [<file-option>] --unset name [value_regex] -'git config' [<file-option>] --unset-all name [value_regex] +'git config' [<file-option>] [--fixed-value] --unset name [value-pattern] +'git config' [<file-option>] [--fixed-value] --unset-all name [value-pattern] 'git config' [<file-option>] --rename-section old_name new_name 'git config' [<file-option>] --remove-section name 'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list @@ -33,10 +33,13 @@ escaped. Multiple lines can be added to an option by using the `--add` option. If you want to update or unset an option which can occur on multiple -lines, a POSIX regexp `value_regex` needs to be given. Only the -existing values that match the regexp are updated or unset. If -you want to handle the lines that do *not* match the regex, just -prepend a single exclamation mark in front (see also <<EXAMPLES>>). +lines, a `value-pattern` (which is an extended regular expression, +unless the `--fixed-value` option is given) needs to be given. Only the +existing values that match the pattern are updated or unset. If +you want to handle the lines that do *not* match the pattern, just +prepend a single exclamation mark in front (see also <<EXAMPLES>>), +but note that this only works when the `--fixed-value` option is not +in use. The `--type=<type>` option instructs 'git config' to ensure that incoming and outgoing values are canonicalize-able under the given <type>. If no @@ -73,11 +76,11 @@ OPTIONS --replace-all:: Default behavior is to replace at most one line. This replaces - all lines matching the key (and optionally the value_regex). + all lines matching the key (and optionally the `value-pattern`). --add:: Adds a new line to the option without altering any existing - values. This is the same as providing '^$' as the value_regex + values. This is the same as providing '^$' as the `value-pattern` in `--replace-all`. --get:: @@ -165,6 +168,12 @@ See also <<FILES>>. --list:: List all variables set in config file, along with their values. +--fixed-value:: + When used with the `value-pattern` argument, treat `value-pattern` as + an exact string instead of a regular expression. This will restrict + the name/value pairs that are matched to only those where the value + is exactly equal to the `value-pattern`. + --type <type>:: 'git config' will ensure that any input or output is valid under the given type constraint(s), and will canonicalize outgoing values in `<type>`'s diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index d401234b03..48b6683071 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -125,7 +125,8 @@ option:: start:: Start a transaction. In contrast to a non-transactional session, a transaction will automatically abort if the session ends without an - explicit commit. + explicit commit. This command may create a new empty transaction when + the current one has been committed or aborted already. prepare:: Prepare to commit the transaction. This will create lock files for all diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 090c888335..67c7a50b96 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -18,7 +18,7 @@ Untyped <<def_object,object>>, e.g. the contents of a file. [[def_branch]]branch:: - A "branch" is an active line of development. The most recent + A "branch" is a line of development. The most recent <<def_commit,commit>> on a branch is referred to as the tip of that branch. The tip of the branch is referenced by a branch <<def_head,head>>, which moves forward as additional development diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt index 6b6085585d..c65ffafc48 100644 --- a/Documentation/technical/api-trace2.txt +++ b/Documentation/technical/api-trace2.txt @@ -466,7 +466,7 @@ completed.) `"error"`:: This event is emitted when one of the `error()`, `die()`, - or `usage()` functions are called. + `warning()`, or `usage()` functions are called. + ------------ { diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index ba869a7d36..9dfade930d 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -27,8 +27,8 @@ and 'push-cert' capabilities are sent and recognized by the receive-pack (push to server) process. The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized -by both upload-pack and receive-pack protocols. The 'agent' capability -may optionally be sent in both protocols. +by both upload-pack and receive-pack protocols. The 'agent' and 'session-id' +capabilities may optionally be sent in both protocols. All other capabilities are only recognized by the upload-pack (fetch from server) process. @@ -365,3 +365,16 @@ If the upload-pack server advertises the 'filter' capability, fetch-pack may send "filter" commands to request a partial clone or partial fetch and request that the server omit various objects from the packfile. + +session-id=<session id> +----------------------- + +The server may advertise a session ID that can be used to identify this process +across multiple requests. The client may advertise its own session ID back to +the server as well. + +Session IDs should be unique to a given process. They must fit within a +packet-line, and must not contain non-printable or whitespace characters. The +current implementation uses trace2 session IDs (see +link:api-trace2.html[api-trace2] for details), but this may change and users of +the session ID should not rely on this fact. diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index e597b74da3..85daeb5d9e 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -492,3 +492,16 @@ form `object-format=X`) to notify the client that the server is able to deal with objects using hash algorithm X. If not specified, the server is assumed to only handle SHA-1. If the client would like to use a hash algorithm other than SHA-1, it should specify its object-format string. + +session-id=<session id> +~~~~~~~~~~~~~~~~~~~~~~~ + +The server may advertise a session ID that can be used to identify this process +across multiple requests. The client may advertise its own session ID back to +the server as well. + +Session IDs should be unique to a given process. They must fit within a +packet-line, and must not contain non-printable or whitespace characters. The +current implementation uses trace2 session IDs (see +link:api-trace2.html[api-trace2] for details), but this may change and users of +the session ID should not rely on this fact. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 82bcd13f4c..c3bbcc9cfa 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.29.GIT +DEF_VER=v2.30.0-rc0 LF=' ' @@ -768,6 +768,7 @@ BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X BUILT_INS += git-fsck-objects$X BUILT_INS += git-init$X +BUILT_INS += git-maintenance$X BUILT_INS += git-merge-subtree$X BUILT_INS += git-restore$X BUILT_INS += git-show$X diff --git a/add-interactive.c b/add-interactive.c index a14c0feaa2..9b8cdb4a31 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -12,10 +12,10 @@ #include "prompt.h" static void init_color(struct repository *r, struct add_i_state *s, - const char *slot_name, char *dst, + const char *section_and_slot, char *dst, const char *default_color) { - char *key = xstrfmt("color.interactive.%s", slot_name); + char *key = xstrfmt("color.%s", section_and_slot); const char *value; if (!s->use_color) @@ -40,20 +40,27 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) git_config_colorbool("color.interactive", value); s->use_color = want_color(s->use_color); - init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD); - init_color(r, s, "help", s->help_color, GIT_COLOR_BOLD_RED); - init_color(r, s, "prompt", s->prompt_color, GIT_COLOR_BOLD_BLUE); - init_color(r, s, "error", s->error_color, GIT_COLOR_BOLD_RED); - init_color(r, s, "reset", s->reset_color, GIT_COLOR_RESET); - init_color(r, s, "fraginfo", s->fraginfo_color, + init_color(r, s, "interactive.header", s->header_color, GIT_COLOR_BOLD); + init_color(r, s, "interactive.help", s->help_color, GIT_COLOR_BOLD_RED); + init_color(r, s, "interactive.prompt", s->prompt_color, + GIT_COLOR_BOLD_BLUE); + init_color(r, s, "interactive.error", s->error_color, + GIT_COLOR_BOLD_RED); + + init_color(r, s, "diff.frag", s->fraginfo_color, diff_get_color(s->use_color, DIFF_FRAGINFO)); - init_color(r, s, "context", s->context_color, - diff_get_color(s->use_color, DIFF_CONTEXT)); - init_color(r, s, "old", s->file_old_color, + init_color(r, s, "diff.context", s->context_color, "fall back"); + if (!strcmp(s->context_color, "fall back")) + init_color(r, s, "diff.plain", s->context_color, + diff_get_color(s->use_color, DIFF_CONTEXT)); + init_color(r, s, "diff.old", s->file_old_color, diff_get_color(s->use_color, DIFF_FILE_OLD)); - init_color(r, s, "new", s->file_new_color, + init_color(r, s, "diff.new", s->file_new_color, diff_get_color(s->use_color, DIFF_FILE_NEW)); + strlcpy(s->reset_color, + s->use_color ? GIT_COLOR_RESET : "", COLOR_MAXLEN); + FREE_AND_NULL(s->interactive_diff_filter); git_config_get_string("interactive.difffilter", &s->interactive_diff_filter); @@ -194,7 +201,8 @@ static ssize_t find_unique(const char *string, struct prefix_item_list *list) else if (index + 1 < list->sorted.nr && starts_with(list->sorted.items[index + 1].string, string)) return -1; - else if (index < list->sorted.nr) + else if (index < list->sorted.nr && + starts_with(list->sorted.items[index].string, string)) item = list->sorted.items[index].util; else return -1; @@ -364,7 +372,7 @@ static ssize_t list_and_choose(struct add_i_state *s, if (from < 0 || from >= items->items.nr || (singleton && from + 1 != to)) { - color_fprintf_ln(stdout, s->error_color, + color_fprintf_ln(stderr, s->error_color, _("Huh (%s)?"), p); break; } else if (singleton) { @@ -1131,7 +1139,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) print_file_item_data.color = data.color; print_file_item_data.reset = data.reset; - strbuf_addstr(&header, " "); + strbuf_addstr(&header, " "); strbuf_addf(&header, print_file_item_data.modified_fmt, _("staged"), _("unstaged"), _("path")); opts.list_opts.header = header.buf; diff --git a/add-patch.c b/add-patch.c index be4cf6e9e5..2fad92ca37 100644 --- a/add-patch.c +++ b/add-patch.c @@ -661,13 +661,18 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk, else new_offset += delta; - strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@", - old_offset, header->old_count, - new_offset, header->new_count); + strbuf_addf(out, "@@ -%lu", old_offset); + if (header->old_count != 1) + strbuf_addf(out, ",%lu", header->old_count); + strbuf_addf(out, " +%lu", new_offset); + if (header->new_count != 1) + strbuf_addf(out, ",%lu", header->new_count); + strbuf_addstr(out, " @@"); + if (len) strbuf_add(out, p, len); else if (colored) - strbuf_addf(out, "%s\n", GIT_COLOR_RESET); + strbuf_addf(out, "%s\n", s->s.reset_color); else strbuf_addch(out, '\n'); } @@ -1060,7 +1065,7 @@ static void recolor_hunk(struct add_p_state *s, struct hunk *hunk) s->s.file_new_color : s->s.context_color); strbuf_add(&s->colored, plain + current, eol - current); - strbuf_addstr(&s->colored, GIT_COLOR_RESET); + strbuf_addstr(&s->colored, s->s.reset_color); if (next > eol) strbuf_add(&s->colored, plain + eol, next - eol); current = next; @@ -1456,15 +1461,15 @@ static int patch_update_file(struct add_p_state *s, else prompt_mode_type = PROMPT_HUNK; - color_fprintf(stdout, s->s.prompt_color, - "(%"PRIuMAX"/%"PRIuMAX") ", + printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->s.prompt_color, (uintmax_t)hunk_index + 1, (uintmax_t)(file_diff->hunk_nr ? file_diff->hunk_nr : 1)); - color_fprintf(stdout, s->s.prompt_color, - _(s->mode->prompt_mode[prompt_mode_type]), - s->buf.buf); + printf(_(s->mode->prompt_mode[prompt_mode_type]), + s->buf.buf); + if (*s->s.reset_color) + fputs(s->s.reset_color, stdout); fflush(stdout); if (read_single_character(s) == EOF) break; @@ -4409,7 +4409,7 @@ static int create_one_file(struct apply_state *state, return 0; if (errno == ENOENT) { - if (safe_create_leading_directories(path)) + if (safe_create_leading_directories_no_share(path)) return 0; res = try_create_file(state, path, mode, buf, size); if (res < 0) @@ -29,4 +29,17 @@ #define vsprintf(buf,fmt,arg) BANNED(vsprintf) #endif +#undef gmtime +#define gmtime(t) BANNED(gmtime) +#undef localtime +#define localtime(t) BANNED(localtime) +#undef ctime +#define ctime(t) BANNED(ctime) +#undef ctime_r +#define ctime_r(t, buf) BANNED(ctime_r) +#undef asctime +#define asctime(t) BANNED(asctime) +#undef asctime_r +#define asctime_r(t, buf) BANNED(asctime_r) + #endif /* BANNED_H */ diff --git a/builtin/branch.c b/builtin/branch.c index efb30b8820..173b736dff 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("Branch '%s' has no upstream information"), branch->name); strbuf_addf(&buf, "branch.%s.remote", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); + git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.merge", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); + git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_release(&buf); } else if (argc > 0 && argc <= 2) { if (filter.kind != FILTER_REFS_BRANCHES) diff --git a/builtin/bugreport.c b/builtin/bugreport.c index 3ad4b9b62e..ad3cc9c02f 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -125,6 +125,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) struct strbuf report_path = STRBUF_INIT; int report = -1; time_t now = time(NULL); + struct tm tm; char *option_output = NULL; char *option_suffix = "%Y-%m-%d-%H%M"; const char *user_relative_path = NULL; @@ -147,7 +148,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) strbuf_complete(&report_path, '/'); strbuf_addstr(&report_path, "git-bugreport-"); - strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0); + strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0); strbuf_addstr(&report_path, ".txt"); switch (safe_create_leading_directories(report_path.buf)) { diff --git a/builtin/config.c b/builtin/config.c index 963d65fd3f..f71fa39b38 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -14,6 +14,7 @@ static const char *const builtin_config_usage[] = { static char *key; static regex_t *key_regexp; +static const char *value_pattern; static regex_t *regexp; static int show_keys; static int omit_values; @@ -34,6 +35,7 @@ static int respect_includes_opt = -1; static struct config_options config_options; static int show_origin; static int show_scope; +static int fixed_value; #define ACTION_GET (1<<0) #define ACTION_GET_ALL (1<<1) @@ -133,17 +135,18 @@ static struct option builtin_config_options[] = { OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), - OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), - OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), - OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP), + OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET), + OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL), + OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP), OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), - OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL), + OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL), OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), - OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET), - OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL), + OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET), + OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL), OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST), + OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")), OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT), OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR), OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL), @@ -296,6 +299,8 @@ static int collect_config(const char *key_, const char *value_, void *cb) return 0; if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) return 0; + if (fixed_value && strcmp(value_pattern, (value_?value_:""))) + return 0; if (regexp != NULL && (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) return 0; @@ -306,7 +311,7 @@ static int collect_config(const char *key_, const char *value_, void *cb) return format_config(&values->items[values->nr++], key_, value_); } -static int get_value(const char *key_, const char *regex_) +static int get_value(const char *key_, const char *regex_, unsigned flags) { int ret = CONFIG_GENERIC_ERROR; struct strbuf_list values = {NULL}; @@ -343,7 +348,9 @@ static int get_value(const char *key_, const char *regex_) } } - if (regex_) { + if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE)) + value_pattern = regex_; + else if (regex_) { if (regex_[0] == '!') { do_not_match = 1; regex_++; @@ -631,6 +638,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = !startup_info->have_repository; char *value; + int flags = 0; given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); @@ -766,6 +774,42 @@ int cmd_config(int argc, const char **argv, const char *prefix) usage_builtin_config(); } + /* check usage of --fixed-value */ + if (fixed_value) { + int allowed_usage = 0; + + switch (actions) { + /* git config --get <name> <value-pattern> */ + case ACTION_GET: + /* git config --get-all <name> <value-pattern> */ + case ACTION_GET_ALL: + /* git config --get-regexp <name-pattern> <value-pattern> */ + case ACTION_GET_REGEXP: + /* git config --unset <name> <value-pattern> */ + case ACTION_UNSET: + /* git config --unset-all <name> <value-pattern> */ + case ACTION_UNSET_ALL: + allowed_usage = argc > 1 && !!argv[1]; + break; + + /* git config <name> <value> <value-pattern> */ + case ACTION_SET_ALL: + /* git config --replace-all <name> <value> <value-pattern> */ + case ACTION_REPLACE_ALL: + allowed_usage = argc > 2 && !!argv[2]; + break; + + /* other options don't allow --fixed-value */ + } + + if (!allowed_usage) { + error(_("--fixed-value only applies with 'value-pattern'")); + usage_builtin_config(); + } + + flags |= CONFIG_FLAGS_FIXED_VALUE; + } + if (actions & PAGING_ACTIONS) setup_auto_pager("config", 1); @@ -827,7 +871,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) value = normalize_value(argv[0], argv[1]); UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], 0); + argv[0], value, argv[2], + flags); } else if (actions == ACTION_ADD) { check_write(); @@ -836,7 +881,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, - CONFIG_REGEX_NONE, 0); + CONFIG_REGEX_NONE, + flags); } else if (actions == ACTION_REPLACE_ALL) { check_write(); @@ -844,23 +890,24 @@ int cmd_config(int argc, const char **argv, const char *prefix) value = normalize_value(argv[0], argv[1]); UNLEAK(value); return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], 1); + argv[0], value, argv[2], + flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); + return get_value(argv[0], argv[1], flags); } else if (actions == ACTION_GET_ALL) { do_all = 1; check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); + return get_value(argv[0], argv[1], flags); } else if (actions == ACTION_GET_REGEXP) { show_keys = 1; use_key_regexp = 1; do_all = 1; check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); + return get_value(argv[0], argv[1], flags); } else if (actions == ACTION_GET_URLMATCH) { check_argc(argc, 2, 2); @@ -871,7 +918,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 1, 2); if (argc == 2) return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], 0); + argv[0], NULL, argv[1], + flags); else return git_config_set_in_file_gently(given_config_source.file, argv[0], NULL); @@ -880,7 +928,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_write(); check_argc(argc, 1, 2); return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], 1); + argv[0], NULL, argv[1], + flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { int ret; diff --git a/builtin/gc.c b/builtin/gc.c index 3e8d76fd5a..b57fda4924 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1446,10 +1446,6 @@ static int maintenance_register(void) struct child_process config_set = CHILD_PROCESS_INIT; struct child_process config_get = CHILD_PROCESS_INIT; - /* There is no current repository, so skip registering it */ - if (!the_repository || !the_repository->gitdir) - return 0; - /* Disable foreground maintenance */ git_config_set("maintenance.auto", "false"); @@ -1460,7 +1456,8 @@ static int maintenance_register(void) git_config_set("maintenance.strategy", "incremental"); config_get.git_cmd = 1; - strvec_pushl(&config_get.args, "config", "--global", "--get", "maintenance.repo", + strvec_pushl(&config_get.args, "config", "--global", "--get", + "--fixed-value", "maintenance.repo", the_repository->worktree ? the_repository->worktree : the_repository->gitdir, NULL); @@ -1486,12 +1483,9 @@ static int maintenance_unregister(void) { struct child_process config_unset = CHILD_PROCESS_INIT; - if (!the_repository || !the_repository->gitdir) - return error(_("no current repository to unregister")); - config_unset.git_cmd = 1; strvec_pushl(&config_unset.args, "config", "--global", "--unset", - "maintenance.repo", + "--fixed-value", "maintenance.repo", the_repository->worktree ? the_repository->worktree : the_repository->gitdir, NULL); diff --git a/builtin/grep.c b/builtin/grep.c index e58e57504c..ca259af441 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -950,7 +950,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_END() }; - init_grep_defaults(the_repository); git_config(grep_cmd_config, NULL); grep_init(&opt, the_repository, prefix); diff --git a/builtin/log.c b/builtin/log.c index 08204e3196..bd6ff4f9f9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -133,7 +133,6 @@ static int log_line_range_callback(const struct option *option, const char *arg, static void init_log_defaults(void) { - init_grep_defaults(the_repository); init_diff_ui_defaults(); decoration_style = auto_decoration_style(); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index f1f0f7bef6..d49d050e6e 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -54,6 +54,7 @@ static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int advertise_atomic_push = 1; static int advertise_push_options; +static int advertise_sid; static int unpack_limit = 100; static off_t max_input_size; static int report_status; @@ -248,6 +249,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "transfer.advertisesid") == 0) { + advertise_sid = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); } @@ -268,6 +274,8 @@ static void show_ref(const char *path, const struct object_id *oid) strbuf_addf(&cap, " push-cert=%s", push_cert_nonce); if (advertise_push_options) strbuf_addstr(&cap, " push-options"); + if (advertise_sid) + strbuf_addf(&cap, " session-id=%s", trace2_session_id()); strbuf_addf(&cap, " object-format=%s", the_hash_algo->name); strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized()); packet_write_fmt(1, "%s %s%c%s\n", @@ -2075,6 +2083,7 @@ static struct command *read_head_info(struct packet_reader *reader, if (linelen < reader->pktlen) { const char *feature_list = reader->line + linelen + 1; const char *hash = NULL; + const char *client_sid; int len = 0; if (parse_feature_request(feature_list, "report-status")) report_status = 1; @@ -2097,6 +2106,12 @@ static struct command *read_head_info(struct packet_reader *reader, } if (xstrncmpz(the_hash_algo->name, hash, len)) die("error: unsupported object format '%s'", hash); + client_sid = parse_feature_value(feature_list, "session-id", &len, NULL); + if (client_sid) { + char *sid = xstrndup(client_sid, len); + trace2_data_string("transfer", NULL, "client-sid", client_sid); + free(sid); + } } if (!strcmp(reader->line, "push-cert")) { diff --git a/builtin/remote.c b/builtin/remote.c index c1b211b272..d11a5589e4 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -712,7 +712,7 @@ static int mv(int argc, const char **argv) strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); + git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); for (i = 0; i < oldremote->fetch.raw_nr; i++) { char *ptr; @@ -1491,7 +1491,8 @@ static int update(int argc, const char **argv) static int remove_all_fetch_refspecs(const char *key) { - return git_config_set_multivar_gently(key, NULL, NULL, 1); + return git_config_set_multivar_gently(key, NULL, NULL, + CONFIG_FLAGS_MULTI_REPLACE); } static void add_branches(struct remote *remote, const char **branches, @@ -1686,7 +1687,8 @@ static int set_url(int argc, const char **argv) if (!delete_mode) git_config_set_multivar(name_buf.buf, newurl, oldurl, 0); else - git_config_set_multivar(name_buf.buf, NULL, oldurl, 1); + git_config_set_multivar(name_buf.buf, NULL, oldurl, + CONFIG_FLAGS_MULTI_REPLACE); out: strbuf_release(&name_buf); return 0; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 8a2df4459c..6029a80544 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -436,6 +436,8 @@ static void update_refs_stdin(void) switch (state) { case UPDATE_REFS_OPEN: case UPDATE_REFS_STARTED: + if (state == UPDATE_REFS_STARTED && cmd->state == UPDATE_REFS_STARTED) + die("cannot restart ongoing transaction"); /* Do not downgrade a transaction to a non-transaction. */ if (cmd->state >= state) state = cmd->state; @@ -446,7 +448,18 @@ static void update_refs_stdin(void) state = cmd->state; break; case UPDATE_REFS_CLOSED: - die("transaction is closed"); + if (cmd->state != UPDATE_REFS_STARTED) + die("transaction is closed"); + + /* + * Open a new transaction if we're currently closed and + * get a "start". + */ + state = cmd->state; + transaction = ref_transaction_begin(&err); + if (!transaction) + die("%s", err.buf); + break; } @@ -1255,7 +1255,11 @@ int adjust_shared_perm(const char *path); * safe_create_leading_directories() temporarily changes path while it * is working but restores it before returning. * safe_create_leading_directories_const() doesn't modify path, even - * temporarily. + * temporarily. Both these variants adjust the permissions of the + * created directories to honor core.sharedRepository, so they are best + * suited for files inside the git dir. For working tree files, use + * safe_create_leading_directories_no_share() instead, as it ignores + * the core.sharedRepository setting. */ enum scld_error { SCLD_OK = 0, @@ -1266,6 +1270,7 @@ enum scld_error { }; enum scld_error safe_create_leading_directories(char *path); enum scld_error safe_create_leading_directories_const(const char *path); +enum scld_error safe_create_leading_directories_no_share(char *path); /* * Callback function for raceproof_create_file(). This function is diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index df167d1e1a..3bd824154b 100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -45,7 +45,7 @@ while (@ARGV) { push(@args, "zlib.lib"); } } elsif ("$arg" eq "-liconv") { - push(@args, "libiconv.lib"); + push(@args, "iconv.lib"); } elsif ("$arg" eq "-lcrypto") { push(@args, "libcrypto.lib"); } elsif ("$arg" eq "-lssl") { @@ -2415,7 +2415,8 @@ struct config_store_data { size_t baselen; char *key; int do_not_match; - regex_t *value_regex; + const char *fixed_value; + regex_t *value_pattern; int multi_replace; struct { size_t begin, end; @@ -2429,10 +2430,10 @@ struct config_store_data { static void config_store_data_clear(struct config_store_data *store) { free(store->key); - if (store->value_regex != NULL && - store->value_regex != CONFIG_REGEX_NONE) { - regfree(store->value_regex); - free(store->value_regex); + if (store->value_pattern != NULL && + store->value_pattern != CONFIG_REGEX_NONE) { + regfree(store->value_pattern); + free(store->value_pattern); } free(store->parsed); free(store->seen); @@ -2444,13 +2445,15 @@ static int matches(const char *key, const char *value, { if (strcmp(key, store->key)) return 0; /* not ours */ - if (!store->value_regex) + if (store->fixed_value) + return !strcmp(store->fixed_value, value); + if (!store->value_pattern) return 1; /* always matches */ - if (store->value_regex == CONFIG_REGEX_NONE) + if (store->value_pattern == CONFIG_REGEX_NONE) return 0; /* never matches */ return store->do_not_match ^ - (value && !regexec(store->value_regex, value, 0, NULL, 0)); + (value && !regexec(store->value_pattern, value, 0, NULL, 0)); } static int store_aux_event(enum config_event_t type, @@ -2726,12 +2729,12 @@ void git_config_set(const char *key, const char *value) /* * If value==NULL, unset in (remove from) config, - * if value_regex!=NULL, disregard key/value pairs where value does not match. - * if value_regex==CONFIG_REGEX_NONE, do not match any existing values + * if value_pattern!=NULL, disregard key/value pairs where value does not match. + * if value_pattern==CONFIG_REGEX_NONE, do not match any existing values * (only add a new one) - * if multi_replace==0, nothing, or only one matching key/value is replaced, - * else all matching key/values (regardless how many) are removed, - * before the new pair is written. + * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching + * key/values are removed before a single new pair is written. If the + * flag is not present, then replace only the first match. * * Returns 0 on success. * @@ -2751,8 +2754,8 @@ void git_config_set(const char *key, const char *value) */ int git_config_set_multivar_in_file_gently(const char *config_filename, const char *key, const char *value, - const char *value_regex, - int multi_replace) + const char *value_pattern, + unsigned flags) { int fd = -1, in_fd = -1; int ret; @@ -2769,7 +2772,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, if (ret) goto out_free; - store.multi_replace = multi_replace; + store.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0; if (!config_filename) config_filename = filename_buf = git_pathdup("config"); @@ -2812,22 +2815,24 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, int i, new_line = 0; struct config_options opts; - if (value_regex == NULL) - store.value_regex = NULL; - else if (value_regex == CONFIG_REGEX_NONE) - store.value_regex = CONFIG_REGEX_NONE; + if (value_pattern == NULL) + store.value_pattern = NULL; + else if (value_pattern == CONFIG_REGEX_NONE) + store.value_pattern = CONFIG_REGEX_NONE; + else if (flags & CONFIG_FLAGS_FIXED_VALUE) + store.fixed_value = value_pattern; else { - if (value_regex[0] == '!') { + if (value_pattern[0] == '!') { store.do_not_match = 1; - value_regex++; + value_pattern++; } else store.do_not_match = 0; - store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(store.value_regex, value_regex, + store.value_pattern = (regex_t*)xmalloc(sizeof(regex_t)); + if (regcomp(store.value_pattern, value_pattern, REG_EXTENDED)) { - error(_("invalid pattern: %s"), value_regex); - FREE_AND_NULL(store.value_regex); + error(_("invalid pattern: %s"), value_pattern); + FREE_AND_NULL(store.value_pattern); ret = CONFIG_INVALID_PATTERN; goto out_free; } @@ -2858,7 +2863,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, /* if nothing to unset, or too many matches, error out */ if ((store.seen_nr == 0 && value == NULL) || - (store.seen_nr > 1 && multi_replace == 0)) { + (store.seen_nr > 1 && !store.multi_replace)) { ret = CONFIG_NOTHING_SET; goto out_free; } @@ -2997,10 +3002,10 @@ write_err_out: void git_config_set_multivar_in_file(const char *config_filename, const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_pattern, unsigned flags) { if (!git_config_set_multivar_in_file_gently(config_filename, key, value, - value_regex, multi_replace)) + value_pattern, flags)) return; if (value) die(_("could not set '%s' to '%s'"), key, value); @@ -3009,17 +3014,17 @@ void git_config_set_multivar_in_file(const char *config_filename, } int git_config_set_multivar_gently(const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_pattern, unsigned flags) { - return git_config_set_multivar_in_file_gently(NULL, key, value, value_regex, - multi_replace); + return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern, + flags); } void git_config_set_multivar(const char *key, const char *value, - const char *value_regex, int multi_replace) + const char *value_pattern, unsigned flags) { - git_config_set_multivar_in_file(NULL, key, value, value_regex, - multi_replace); + git_config_set_multivar_in_file(NULL, key, value, value_pattern, + flags); } static int section_name_match (const char *buf, const char *name) @@ -256,9 +256,29 @@ void git_config_set(const char *, const char *); int git_config_parse_key(const char *, char **, size_t *); int git_config_key_is_valid(const char *key); -int git_config_set_multivar_gently(const char *, const char *, const char *, int); -void git_config_set_multivar(const char *, const char *, const char *, int); -int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int); + +/* + * The following macros specify flag bits that alter the behavior + * of the git_config_set_multivar*() methods. + */ + +/* + * When CONFIG_FLAGS_MULTI_REPLACE is specified, all matching key/values + * are removed before a single new pair is written. If the flag is not + * present, then set operations replace only the first match. + */ +#define CONFIG_FLAGS_MULTI_REPLACE (1 << 0) + +/* + * When CONFIG_FLAGS_FIXED_VALUE is specified, match key/value pairs + * by string comparison (not regex match) to the provided value_pattern + * parameter. + */ +#define CONFIG_FLAGS_FIXED_VALUE (1 << 1) + +int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned); +void git_config_set_multivar(const char *, const char *, const char *, unsigned); +int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned); /** * takes four parameters: @@ -276,13 +296,15 @@ int git_config_set_multivar_in_file_gently(const char *, const char *, const cha * - the value regex, as a string. It will disregard key/value pairs where value * does not match. * - * - a multi_replace value, as an int. If value is equal to zero, nothing or only - * one matching key/value is replaced, else all matching key/values (regardless - * how many) are removed, before the new pair is written. + * - a flags value with bits corresponding to the CONFIG_FLAG_* macros. * * It returns 0 on success. */ -void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int); +void git_config_set_multivar_in_file(const char *config_filename, + const char *key, + const char *value, + const char *value_pattern, + unsigned flags); /** * rename or remove sections in the config file diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index df539a44fa..c151dd7257 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -114,6 +114,16 @@ macro(parse_makefile_for_scripts list_var regex lang) endif() endmacro() +macro(parse_makefile_for_executables list_var regex) + file(STRINGS ${CMAKE_SOURCE_DIR}/Makefile ${list_var} REGEX "^${regex} \\+= git-(.*)") + string(REPLACE "${regex} +=" "" ${list_var} ${${list_var}}) + string(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces + string(REPLACE "git-" "" ${list_var} ${${list_var}}) #strip `git-` prefix + string(REPLACE "\$X" ";" ${list_var} ${${list_var}}) #strip $X, ; is for converting the string into a list + list(TRANSFORM ${list_var} STRIP) #remove trailing/leading whitespaces for each element in list + list(REMOVE_ITEM ${list_var} "") #remove empty list elements +endmacro() + include(CheckTypeSize) include(CheckCSourceRuns) include(CheckCSourceCompiles) @@ -673,10 +683,7 @@ if(CURL_FOUND) endif() endif() -set(git_builtin_extra - cherry cherry-pick format-patch fsck-objects - init merge-subtree restore show - stage status switch whatchanged) +parse_makefile_for_executables(git_builtin_extra "BUILT_INS") #Creating hardlinks foreach(s ${git_SOURCES} ${git_builtin_extra}) diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 2ff9620459..ed6c45988a 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -351,7 +351,7 @@ sub handleLinkLine } elsif ("$part" eq "-lexpat") { push(@libs, "libexpat.lib"); } elsif ("$part" eq "-liconv") { - push(@libs, "libiconv.lib"); + push(@libs, "iconv.lib"); } elsif ($part =~ /^[-\/]/) { push(@lflags, $part); } elsif ($part =~ /\.(a|lib)$/) { diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1ed03623cd..463a3124da 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1829,7 +1829,7 @@ _git_fsck () _git_gitk () { - _gitk + __gitk_main } # Lists matching symbol names from a tag (as in ctags) file. diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index e0fda27f4c..6c56296997 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -116,6 +116,7 @@ __gitcomp_file () { emulate -L zsh + compset -P '*[=:]' compadd -f -p "${2-}" -- ${(f)1} && _ret=0 } diff --git a/fetch-pack.c b/fetch-pack.c index b10c432315..876f90c759 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -35,6 +35,7 @@ static int fetch_fsck_objects = -1; static int transfer_fsck_objects = -1; static int agent_supported; static int server_supports_filtering; +static int advertise_sid; static struct shallow_lock shallow_lock; static const char *alternate_shallow_file; static struct strbuf fsck_msg_types = STRBUF_INIT; @@ -326,6 +327,8 @@ static int find_common(struct fetch_negotiator *negotiator, if (deepen_not_ok) strbuf_addstr(&c, " deepen-not"); if (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); + if (advertise_sid) + strbuf_addf(&c, " session-id=%s", trace2_session_id()); if (args->filter_options.choice) strbuf_addstr(&c, " filter"); packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf); @@ -915,8 +918,9 @@ static int get_pack(struct fetch_pack_args *args, if (start_command(&cmd)) die(_("fetch-pack: unable to fork off %s"), cmd_name); if (do_keep && pack_lockfiles) { - string_list_append_nodup(pack_lockfiles, - index_pack_lockfile(cmd.out)); + char *pack_lockfile = index_pack_lockfile(cmd.out); + if (pack_lockfile) + string_list_append_nodup(pack_lockfiles, pack_lockfile); close(cmd.out); } @@ -979,6 +983,9 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, agent_len, agent_feature); } + if (!server_supports("session-id")) + advertise_sid = 0; + if (server_supports("shallow")) print_verbose(args, _("Server supports %s"), "shallow"); else if (args->depth > 0 || is_repository_shallow(r)) @@ -1191,6 +1198,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, packet_buf_write(&req_buf, "command=fetch"); if (server_supports_v2("agent", 0)) packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized()); + if (advertise_sid && server_supports_v2("session-id", 0)) + packet_buf_write(&req_buf, "session-id=%s", trace2_session_id()); if (args->server_options && args->server_options->nr && server_supports_v2("server-option", 1)) { int i; @@ -1711,6 +1720,7 @@ static void fetch_pack_config(void) git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta); git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects); git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects); + git_config_get_bool("transfer.advertisesid", &advertise_sid); if (!uri_protocols.nr) { char *str; diff --git a/git-add--interactive.perl b/git-add--interactive.perl index e713fe3d02..bc3a1e8eff 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -30,9 +30,9 @@ my ($fraginfo_color) = $diff_use_color ? ( $repo->get_color('color.diff.frag', 'cyan'), ) : (); -my ($diff_plain_color) = +my ($diff_context_color) = $diff_use_color ? ( - $repo->get_color('color.diff.plain', ''), + $repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''), ) : (); my ($diff_old_color) = $diff_use_color ? ( @@ -483,10 +483,8 @@ sub list_and_choose { my $last_lf = 0; if ($opts->{HEADER}) { - if (!$opts->{LIST_FLAT}) { - print " "; - } - print colored $header_color, "$opts->{HEADER}\n"; + my $indent = $opts->{LIST_FLAT} ? "" : " "; + print colored $header_color, "$indent$opts->{HEADER}\n"; } for ($i = 0; $i < @stuff; $i++) { my $chosen = $chosen[$i] ? '*' : ' '; @@ -1048,7 +1046,7 @@ sub color_diff { colored((/^@/ ? $fraginfo_color : /^\+/ ? $diff_new_color : /^-/ ? $diff_old_color : - $diff_plain_color), + $diff_context_color), $_); } @_; } @@ -535,7 +535,7 @@ static struct cmd_struct commands[] = { { "ls-tree", cmd_ls_tree, RUN_SETUP }, { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT }, { "mailsplit", cmd_mailsplit, NO_PARSEOPT }, - { "maintenance", cmd_maintenance, RUN_SETUP_GENTLY | NO_PARSEOPT }, + { "maintenance", cmd_maintenance, RUN_SETUP | NO_PARSEOPT }, { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE }, { "merge-base", cmd_merge_base, RUN_SETUP }, { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY }, @@ -14,7 +14,31 @@ static int grep_source_load(struct grep_source *gs); static int grep_source_is_binary(struct grep_source *gs, struct index_state *istate); -static struct grep_opt grep_defaults; +static void std_output(struct grep_opt *opt, const void *buf, size_t size) +{ + fwrite(buf, size, 1, stdout); +} + +static struct grep_opt grep_defaults = { + .relative = 1, + .pathname = 1, + .max_depth = -1, + .pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED, + .colors = { + [GREP_COLOR_CONTEXT] = "", + [GREP_COLOR_FILENAME] = "", + [GREP_COLOR_FUNCTION] = "", + [GREP_COLOR_LINENO] = "", + [GREP_COLOR_COLUMNNO] = "", + [GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED, + [GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED, + [GREP_COLOR_SELECTED] = "", + [GREP_COLOR_SEP] = GIT_COLOR_CYAN, + }, + .only_matching = 0, + .color = -1, + .output = std_output, +}; #ifdef USE_LIBPCRE2 static pcre2_general_context *pcre2_global_context; @@ -42,50 +66,6 @@ static const char *color_grep_slots[] = { [GREP_COLOR_SEP] = "separator", }; -static void std_output(struct grep_opt *opt, const void *buf, size_t size) -{ - fwrite(buf, size, 1, stdout); -} - -static void color_set(char *dst, const char *color_bytes) -{ - xsnprintf(dst, COLOR_MAXLEN, "%s", color_bytes); -} - -/* - * Initialize the grep_defaults template with hardcoded defaults. - * We could let the compiler do this, but without C99 initializers - * the code gets unwieldy and unreadable, so... - */ -void init_grep_defaults(struct repository *repo) -{ - struct grep_opt *opt = &grep_defaults; - static int run_once; - - if (run_once) - return; - run_once++; - - memset(opt, 0, sizeof(*opt)); - opt->repo = repo; - opt->relative = 1; - opt->pathname = 1; - opt->max_depth = -1; - opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; - color_set(opt->colors[GREP_COLOR_CONTEXT], ""); - color_set(opt->colors[GREP_COLOR_FILENAME], ""); - color_set(opt->colors[GREP_COLOR_FUNCTION], ""); - color_set(opt->colors[GREP_COLOR_LINENO], ""); - color_set(opt->colors[GREP_COLOR_COLUMNNO], ""); - color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED); - color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED); - color_set(opt->colors[GREP_COLOR_SELECTED], ""); - color_set(opt->colors[GREP_COLOR_SEP], GIT_COLOR_CYAN); - opt->only_matching = 0; - opt->color = -1; - opt->output = std_output; -} - static int parse_pattern_type_arg(const char *opt, const char *arg) { if (!strcmp(arg, "default")) @@ -115,6 +95,14 @@ int grep_config(const char *var, const char *value, void *cb) if (userdiff_config(var, value) < 0) return -1; + /* + * The instance of grep_opt that we set up here is copied by + * grep_init() to be used by each individual invocation. + * When populating a new field of this structure here, be + * sure to think about ownership -- e.g., you might need to + * override the shallow copy in grep_init() with a deep copy. + */ + if (!strcmp(var, "grep.extendedregexp")) { opt->extended_regexp_option = git_config_bool(var, value); return 0; @@ -172,9 +160,6 @@ int grep_config(const char *var, const char *value, void *cb) */ void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix) { - struct grep_opt *def = &grep_defaults; - int i; - #if defined(USE_LIBPCRE2) if (!pcre2_global_context) pcre2_global_context = pcre2_general_context_create( @@ -186,26 +171,13 @@ void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix pcre_free = free; #endif - memset(opt, 0, sizeof(*opt)); + *opt = grep_defaults; + opt->repo = repo; opt->prefix = prefix; opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt->pattern_tail = &opt->pattern_list; opt->header_tail = &opt->header_list; - - opt->only_matching = def->only_matching; - opt->color = def->color; - opt->extended_regexp_option = def->extended_regexp_option; - opt->pattern_type_option = def->pattern_type_option; - opt->linenum = def->linenum; - opt->columnnum = def->columnnum; - opt->max_depth = def->max_depth; - opt->pathname = def->pathname; - opt->relative = def->relative; - opt->output = def->output; - - for (i = 0; i < NR_GREP_COLORS; i++) - color_set(opt->colors[i], def->colors[i]); } void grep_destroy(void) @@ -170,7 +170,6 @@ struct grep_opt { void *output_priv; }; -void init_grep_defaults(struct repository *); int grep_config(const char *var, const char *value, void *); void grep_init(struct grep_opt *, struct repository *repo, const char *prefix); void grep_destroy(void); @@ -472,12 +472,26 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) static int autocorrect; static struct cmdnames aliases; +#define AUTOCORRECT_NEVER (-2) +#define AUTOCORRECT_IMMEDIATELY (-1) + static int git_unknown_cmd_config(const char *var, const char *value, void *cb) { const char *p; - if (!strcmp(var, "help.autocorrect")) - autocorrect = git_config_int(var,value); + if (!strcmp(var, "help.autocorrect")) { + if (!value) + return config_error_nonbool(var); + if (!strcmp(value, "never")) { + autocorrect = AUTOCORRECT_NEVER; + } else if (!strcmp(value, "immediate")) { + autocorrect = AUTOCORRECT_IMMEDIATELY; + } else { + int v = git_config_int(var, value); + autocorrect = (v < 0) + ? AUTOCORRECT_IMMEDIATELY : v; + } + } /* Also use aliases for command lookup */ if (skip_prefix(var, "alias.", &p)) add_cmdname(&aliases, p, strlen(p)); @@ -525,6 +539,11 @@ const char *help_unknown_cmd(const char *cmd) read_early_config(git_unknown_cmd_config, NULL); + if (autocorrect == AUTOCORRECT_NEVER) { + fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd); + exit(1); + } + load_command_list("git-", &main_cmds, &other_cmds); add_cmd_list(&main_cmds, &aliases); @@ -594,7 +613,7 @@ const char *help_unknown_cmd(const char *cmd) _("WARNING: You called a Git command named '%s', " "which does not exist."), cmd); - if (autocorrect < 0) + if (autocorrect == AUTOCORRECT_IMMEDIATELY) fprintf_ln(stderr, _("Continuing under the assumption that " "you meant '%s'."), diff --git a/imap-send.c b/imap-send.c index 5764dd812c..d0b94f911e 100644 --- a/imap-send.c +++ b/imap-send.c @@ -84,17 +84,17 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap) } struct imap_server_conf { - char *name; - char *tunnel; - char *host; + const char *name; + const char *tunnel; + const char *host; int port; - char *folder; - char *user; - char *pass; + const char *folder; + const char *user; + const char *pass; int use_ssl; int ssl_verify; int use_html; - char *auth_method; + const char *auth_method; }; static struct imap_server_conf server = { @@ -955,7 +955,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent srvc->pass = xstrdup(cred->password); } -static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder) +static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder) { struct credential cred = CREDENTIAL_INIT; struct imap_store *ctx; @@ -1338,15 +1338,26 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) return 1; } -static void git_imap_config(void) +static int git_imap_config(const char *var, const char *val, void *cb) { - const char *val = NULL; - git_config_get_bool("imap.sslverify", &server.ssl_verify); - git_config_get_bool("imap.preformattedhtml", &server.use_html); - git_config_get_string("imap.folder", &server.folder); - - if (!git_config_get_value("imap.host", &val)) { + if (!strcmp("imap.sslverify", var)) + server.ssl_verify = git_config_bool(var, val); + else if (!strcmp("imap.preformattedhtml", var)) + server.use_html = git_config_bool(var, val); + else if (!strcmp("imap.folder", var)) + return git_config_string(&server.folder, var, val); + else if (!strcmp("imap.user", var)) + return git_config_string(&server.user, var, val); + else if (!strcmp("imap.pass", var)) + return git_config_string(&server.pass, var, val); + else if (!strcmp("imap.tunnel", var)) + return git_config_string(&server.tunnel, var, val); + else if (!strcmp("imap.authmethod", var)) + return git_config_string(&server.auth_method, var, val); + else if (!strcmp("imap.port", var)) + server.port = git_config_int(var, val); + else if (!strcmp("imap.host", var)) { if (!val) { git_die_config("imap.host", "Missing value for 'imap.host'"); } else { @@ -1360,13 +1371,10 @@ static void git_imap_config(void) val += 2; server.host = xstrdup(val); } - } + } else + return git_default_config(var, val, cb); - git_config_get_string("imap.user", &server.user); - git_config_get_string("imap.pass", &server.pass); - git_config_get_int("imap.port", &server.port); - git_config_get_string("imap.tunnel", &server.tunnel); - git_config_get_string("imap.authmethod", &server.auth_method); + return 0; } static int append_msgs_to_imap(struct imap_server_conf *server, @@ -1539,7 +1547,7 @@ int cmd_main(int argc, const char **argv) int nongit_ok; setup_git_directory_gently(&nongit_ok); - git_imap_config(); + git_config(git_imap_config, NULL); argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0); @@ -298,7 +298,7 @@ static int nth_midxed_pack_entry(struct repository *r, pack_int_id = nth_midxed_pack_int_id(m, pos); if (prepare_midx_pack(r, m, pack_int_id)) - die(_("error preparing packfile from multi-pack-index")); + return 0; p = m->packs[pack_int_id]; /* diff --git a/packfile.c b/packfile.c index 9702b1218b..86f5c8dbf6 100644 --- a/packfile.c +++ b/packfile.c @@ -514,19 +514,8 @@ static int open_packed_git_1(struct packed_git *p) ssize_t read_result; const unsigned hashsz = the_hash_algo->rawsz; - if (!p->index_data) { - struct multi_pack_index *m; - const char *pack_name = pack_basename(p); - - for (m = the_repository->objects->multi_pack_index; - m; m = m->next) { - if (midx_contains_pack(m, pack_name)) - break; - } - - if (!m && open_pack_index(p)) - return error("packfile %s index unavailable", p->pack_name); - } + if (open_pack_index(p)) + return error("packfile %s index unavailable", p->pack_name); if (!pack_max_fds) { unsigned int max_fds = get_max_fd_limit(); @@ -567,10 +556,6 @@ static int open_packed_git_1(struct packed_git *p) " supported (try upgrading GIT to a newer version)", p->pack_name, ntohl(hdr.hdr_version)); - /* Skip index checking if in multi-pack-index */ - if (!p->index_data) - return 0; - /* Verify the pack matches its index. */ if (p->num_objects != ntohl(hdr.hdr_entries)) return error("packfile %s claims to have %"PRIu32" objects" @@ -71,7 +71,10 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet } item->pattern = is_glob; - item->src = xstrndup(lhs, llen); + if (llen == 1 && *lhs == '@') + item->src = xstrdup("HEAD"); + else + item->src = xstrndup(lhs, llen); flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); if (item->negative) { @@ -272,15 +275,16 @@ void refspec_ref_prefixes(const struct refspec *rs, else if (item->src && !item->exact_sha1) prefix = item->src; - if (prefix) { - if (item->pattern) { - const char *glob = strchr(prefix, '*'); - strvec_pushf(ref_prefixes, "%.*s", - (int)(glob - prefix), - prefix); - } else { - expand_ref_prefix(ref_prefixes, prefix); - } + if (!prefix) + continue; + + if (item->pattern) { + const char *glob = strchr(prefix, '*'); + strvec_pushf(ref_prefixes, "%.*s", + (int)(glob - prefix), + prefix); + } else { + expand_ref_prefix(ref_prefixes, prefix); } } } @@ -355,7 +355,7 @@ static int handle_config(const char *key, const char *value, void *cb) remote = make_remote(name, namelen); remote->origin = REMOTE_CONFIG; if (current_config_scope() == CONFIG_SCOPE_LOCAL || - current_config_scope() == CONFIG_SCOPE_WORKTREE) + current_config_scope() == CONFIG_SCOPE_WORKTREE) remote->configured_in_repo = 1; if (!strcmp(subkey, "mirror")) remote->mirror = git_config_bool(key, value); diff --git a/revision.c b/revision.c index c6e169e3eb..9dff845bed 100644 --- a/revision.c +++ b/revision.c @@ -1827,7 +1827,6 @@ void repo_init_revisions(struct repository *r, revs->commit_format = CMIT_FMT_DEFAULT; revs->expand_tabs_in_log_default = 8; - init_grep_defaults(revs->repo); grep_init(&revs->grep_filter, revs->repo, prefix); revs->grep_filter.status_only = 1; diff --git a/send-pack.c b/send-pack.c index d2701bf35c..9045f8a082 100644 --- a/send-pack.c +++ b/send-pack.c @@ -425,6 +425,7 @@ int send_pack(struct send_pack_args *args, int use_sideband = 0; int quiet_supported = 0; int agent_supported = 0; + int advertise_sid = 0; int use_atomic = 0; int atomic_supported = 0; int use_push_options = 0; @@ -436,6 +437,8 @@ int send_pack(struct send_pack_args *args, const char *push_cert_nonce = NULL; struct packet_reader reader; + git_config_get_bool("transfer.advertisesid", &advertise_sid); + /* Does the other end support the reporting? */ if (server_supports("report-status-v2")) status_report = 2; @@ -451,6 +454,8 @@ int send_pack(struct send_pack_args *args, quiet_supported = 1; if (server_supports("agent")) agent_supported = 1; + if (!server_supports("session-id")) + advertise_sid = 0; if (server_supports("no-thin")) args->use_thin_pack = 0; if (server_supports("atomic")) @@ -507,6 +512,8 @@ int send_pack(struct send_pack_args *args, strbuf_addf(&cap_buf, " object-format=%s", the_hash_algo->name); if (agent_supported) strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized()); + if (advertise_sid) + strbuf_addf(&cap_buf, " session-id=%s", trace2_session_id()); /* * NEEDSWORK: why does delete-refs have to be so specific to @@ -8,6 +8,8 @@ #include "serve.h" #include "upload-pack.h" +static int advertise_sid; + static int always_advertise(struct repository *r, struct strbuf *value) { @@ -30,6 +32,15 @@ static int object_format_advertise(struct repository *r, return 1; } +static int session_id_advertise(struct repository *r, struct strbuf *value) +{ + if (!advertise_sid) + return 0; + if (value) + strbuf_addstr(value, trace2_session_id()); + return 1; +} + struct protocol_capability { /* * The name of the capability. The server uses this name when @@ -66,6 +77,7 @@ static struct protocol_capability capabilities[] = { { "fetch", upload_pack_advertise, upload_pack_v2 }, { "server-option", always_advertise, NULL }, { "object-format", object_format_advertise, NULL }, + { "session-id", session_id_advertise, NULL }, }; static void advertise_capabilities(void) @@ -189,6 +201,7 @@ static int process_request(void) struct packet_reader reader; struct strvec keys = STRVEC_INIT; struct protocol_capability *command = NULL; + const char *client_sid; packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE | @@ -252,6 +265,9 @@ static int process_request(void) check_algorithm(the_repository, &keys); + if (has_capability(&keys, "session-id", &client_sid)) + trace2_data_string("transfer", NULL, "client-sid", client_sid); + command->command(the_repository, &keys, &reader); strvec_clear(&keys); @@ -261,6 +277,8 @@ static int process_request(void) /* Main serve loop for protocol version 2 */ void serve(struct serve_options *options) { + git_config_get_bool("transfer.advertisesid", &advertise_sid); + if (options->advertise_capabilities || !options->stateless_rpc) { /* serve by default supports v2 */ packet_write_fmt(1, "version 2\n"); diff --git a/sha1-file.c b/sha1-file.c index dd65bd5c68..c3c49d2fa5 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -291,7 +291,7 @@ int mkdir_in_gitdir(const char *path) return adjust_shared_perm(path); } -enum scld_error safe_create_leading_directories(char *path) +static enum scld_error safe_create_leading_directories_1(char *path, int share) { char *next_component = path + offset_1st_component(path); enum scld_error ret = SCLD_OK; @@ -337,7 +337,7 @@ enum scld_error safe_create_leading_directories(char *path) ret = SCLD_VANISHED; else ret = SCLD_FAILED; - } else if (adjust_shared_perm(path)) { + } else if (share && adjust_shared_perm(path)) { ret = SCLD_PERMS; } *slash = slash_character; @@ -345,6 +345,16 @@ enum scld_error safe_create_leading_directories(char *path) return ret; } +enum scld_error safe_create_leading_directories(char *path) +{ + return safe_create_leading_directories_1(path, 1); +} + +enum scld_error safe_create_leading_directories_no_share(char *path) +{ + return safe_create_leading_directories_1(path, 0); +} + enum scld_error safe_create_leading_directories_const(const char *path) { int save_errno; diff --git a/t/perf/p1400-update-ref.sh b/t/perf/p1400-update-ref.sh index ce5ac3ed85..dda8a74866 100755 --- a/t/perf/p1400-update-ref.sh +++ b/t/perf/p1400-update-ref.sh @@ -7,13 +7,14 @@ test_description="Tests performance of update-ref" test_perf_fresh_repo test_expect_success "setup" ' - git init --bare target-repo.git && test_commit PRE && test_commit POST && - printf "create refs/heads/%d PRE\n" $(test_seq 1000) >create && - printf "update refs/heads/%d POST PRE\n" $(test_seq 1000) >update && - printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete && - git update-ref --stdin <create + for i in $(test_seq 5000) + do + printf "start\ncreate refs/heads/%d PRE\ncommit\n" $i && + printf "start\nupdate refs/heads/%d POST PRE\ncommit\n" $i && + printf "start\ndelete refs/heads/%d POST\ncommit\n" $i + done >instructions ' test_perf "update-ref" ' @@ -26,14 +27,7 @@ test_perf "update-ref" ' ' test_perf "update-ref --stdin" ' - git update-ref --stdin <update && - git update-ref --stdin <delete && - git update-ref --stdin <create -' - -test_perf "nonatomic push" ' - git push ./target-repo.git $(test_seq 1000) && - git push --delete ./target-repo.git $(test_seq 1000) + git update-ref --stdin <instructions >/dev/null ' test_done diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh index 163a13bea3..9b43342806 100755 --- a/t/perf/p7519-fsmonitor.sh +++ b/t/perf/p7519-fsmonitor.sh @@ -129,8 +129,7 @@ setup_for_fsmonitor() { git config core.fsmonitor "$INTEGRATION_SCRIPT" && git update-index --fsmonitor 2>error && - cat error && - [ ! -s error ] # ensure no silent error + test_must_be_empty error # ensure no silent error } test_perf_w_drop_caches () { diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 825d9a184f..97a04c6cc2 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1917,4 +1917,153 @@ test_expect_success '--replace-all does not invent newlines' ' test_cmp expect .git/config ' +test_expect_success 'set all config with value-pattern' ' + test_when_finished rm -f config initial && + git config --file=initial abc.key one && + + # no match => add new entry + cp initial config && + git config --file=config abc.key two a+ && + git config --file=config --list >actual && + cat >expect <<-\EOF && + abc.key=one + abc.key=two + EOF + test_cmp expect actual && + + # multiple matches => failure + test_must_fail git config --file=config abc.key three o+ 2>err && + test_i18ngrep "has multiple values" err && + + # multiple values, no match => add + git config --file=config abc.key three a+ && + git config --file=config --list >actual && + cat >expect <<-\EOF && + abc.key=one + abc.key=two + abc.key=three + EOF + test_cmp expect actual && + + # single match => replace + git config --file=config abc.key four h+ && + git config --file=config --list >actual && + cat >expect <<-\EOF && + abc.key=one + abc.key=two + abc.key=four + EOF + test_cmp expect actual +' + +test_expect_success '--replace-all and value-pattern' ' + test_when_finished rm -f config && + git config --file=config --add abc.key one && + git config --file=config --add abc.key two && + git config --file=config --add abc.key three && + git config --file=config --replace-all abc.key four "o+" && + git config --file=config --list >actual && + cat >expect <<-\EOF && + abc.key=four + abc.key=three + EOF + test_cmp expect actual +' + +test_expect_success 'refuse --fixed-value for incompatible actions' ' + test_when_finished rm -f config && + git config --file=config dev.null bogus && + + # These modes do not allow --fixed-value at all + test_must_fail git config --file=config --fixed-value --add dev.null bogus && + test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus && + test_must_fail git config --file=config --fixed-value --get-urlmatch dev.null bogus && + test_must_fail git config --file=config --fixed-value --rename-section dev null && + test_must_fail git config --file=config --fixed-value --remove-section dev && + test_must_fail git config --file=config --fixed-value --list && + test_must_fail git config --file=config --fixed-value --get-color dev.null && + test_must_fail git config --file=config --fixed-value --get-colorbool dev.null && + + # These modes complain when --fixed-value has no value-pattern + test_must_fail git config --file=config --fixed-value dev.null bogus && + test_must_fail git config --file=config --fixed-value --replace-all dev.null bogus && + test_must_fail git config --file=config --fixed-value --get dev.null && + test_must_fail git config --file=config --fixed-value --get-all dev.null && + test_must_fail git config --file=config --fixed-value --get-regexp "dev.*" && + test_must_fail git config --file=config --fixed-value --unset dev.null && + test_must_fail git config --file=config --fixed-value --unset-all dev.null +' + +test_expect_success '--fixed-value uses exact string matching' ' + test_when_finished rm -f config initial && + META="a+b*c?d[e]f.g" && + git config --file=initial fixed.test "$META" && + + cp initial config && + git config --file=config fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=$META + fixed.test=bogus + EOF + test_cmp expect actual && + + cp initial config && + git config --file=config --fixed-value fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-\EOF && + fixed.test=bogus + EOF + test_cmp expect actual && + + cp initial config && + test_must_fail git config --file=config --unset fixed.test "$META" && + git config --file=config --fixed-value --unset fixed.test "$META" && + test_must_fail git config --file=config fixed.test && + + cp initial config && + test_must_fail git config --file=config --unset-all fixed.test "$META" && + git config --file=config --fixed-value --unset-all fixed.test "$META" && + test_must_fail git config --file=config fixed.test && + + cp initial config && + git config --file=config --replace-all fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=$META + fixed.test=bogus + EOF + test_cmp expect actual && + + git config --file=config --fixed-value --replace-all fixed.test bogus "$META" && + git config --file=config --list >actual && + cat >expect <<-EOF && + fixed.test=bogus + fixed.test=bogus + EOF + test_cmp expect actual +' + +test_expect_success '--get and --get-all with --fixed-value' ' + test_when_finished rm -f config && + META="a+b*c?d[e]f.g" && + git config --file=config fixed.test bogus && + git config --file=config --add fixed.test "$META" && + + git config --file=config --get fixed.test bogus && + test_must_fail git config --file=config --get fixed.test "$META" && + git config --file=config --get --fixed-value fixed.test "$META" && + test_must_fail git config --file=config --get --fixed-value fixed.test non-existent && + + git config --file=config --get-all fixed.test bogus && + test_must_fail git config --file=config --get-all fixed.test "$META" && + git config --file=config --get-all --fixed-value fixed.test "$META" && + test_must_fail git config --file=config --get-all --fixed-value fixed.test non-existent && + + git config --file=config --get-regexp fixed+ bogus && + test_must_fail git config --file=config --get-regexp fixed+ "$META" && + git config --file=config --get-regexp --fixed-value fixed+ "$META" && + test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent +' + test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 4c01e08551..31b64be521 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -48,17 +48,17 @@ test_expect_success "fail to delete $m with stale ref" ' test $B = "$(git show-ref -s --verify $m)" ' test_expect_success "delete $m" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref -d $m $B && - test_path_is_missing .git/$m + test_must_fail git show-ref --verify -q $m ' test_expect_success "delete $m without oldvalue verification" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref $m $A && test $A = $(git show-ref -s --verify $m) && git update-ref -d $m && - test_path_is_missing .git/$m + test_must_fail git show-ref --verify -q $m ' test_expect_success "fail to create $n" ' @@ -80,26 +80,26 @@ test_expect_success "fail to delete $m (by HEAD) with stale ref" ' test $B = $(git show-ref -s --verify $m) ' test_expect_success "delete $m (by HEAD)" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref -d HEAD $B && - test_path_is_missing .git/$m + test_must_fail git show-ref --verify -q $m ' test_expect_success "deleting current branch adds message to HEAD's log" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref $m $A && git symbolic-ref HEAD $m && git update-ref -m delete-$m -d $m && - test_path_is_missing .git/$m && + test_must_fail git show-ref --verify -q $m && grep "delete-$m$" .git/logs/HEAD ' test_expect_success "deleting by HEAD adds message to HEAD's log" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref $m $A && git symbolic-ref HEAD $m && git update-ref -m delete-by-head -d HEAD && - test_path_is_missing .git/$m && + test_must_fail git show-ref --verify -q $m && grep "delete-by-head$" .git/logs/HEAD ' @@ -188,31 +188,37 @@ test_expect_success "move $m (by HEAD)" ' test $B = $(git show-ref -s --verify $m) ' test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && git update-ref -d HEAD $B && ! grep "$m" .git/packed-refs && - test_path_is_missing .git/$m + test_must_fail git show-ref --verify -q $m ' -cp -f .git/HEAD .git/HEAD.orig test_expect_success 'delete symref without dereference' ' - test_when_finished "cp -f .git/HEAD.orig .git/HEAD" && - git update-ref --no-deref -d HEAD && - test_path_is_missing .git/HEAD + test_when_finished "git update-ref -d $m" && + echo foo >foo.c && + git add foo.c && + git commit -m foo && + git symbolic-ref SYMREF $m && + git update-ref --no-deref -d SYMREF && + git show-ref --verify -q $m && + test_must_fail git show-ref --verify -q SYMREF && + test_must_fail git symbolic-ref SYMREF ' test_expect_success 'delete symref without dereference when the referred ref is packed' ' - test_when_finished "cp -f .git/HEAD.orig .git/HEAD" && + test_when_finished "git update-ref -d $m" && echo foo >foo.c && git add foo.c && git commit -m foo && + git symbolic-ref SYMREF $m && git pack-refs --all && - git update-ref --no-deref -d HEAD && - test_path_is_missing .git/HEAD + git update-ref --no-deref -d SYMREF && + git show-ref --verify -q $m && + test_must_fail git show-ref --verify -q SYMREF && + test_must_fail git symbolic-ref SYMREF ' -git update-ref -d $m - test_expect_success 'update-ref -d is not confused by self-reference' ' git symbolic-ref refs/heads/self refs/heads/self && test_when_finished "rm -f .git/refs/heads/self" && @@ -226,25 +232,25 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' ' test_when_finished "rm -f .git/refs/heads/self" && test_path_is_file .git/refs/heads/self && git update-ref --no-deref -d refs/heads/self && - test_path_is_missing .git/refs/heads/self + test_must_fail git show-ref --verify -q refs/heads/self ' test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' ' >.git/refs/heads/bad && test_when_finished "rm -f .git/refs/heads/bad" && git symbolic-ref refs/heads/ref-to-bad refs/heads/bad && - test_when_finished "rm -f .git/refs/heads/ref-to-bad" && + test_when_finished "git update-ref -d refs/heads/ref-to-bad" && test_path_is_file .git/refs/heads/ref-to-bad && git update-ref --no-deref -d refs/heads/ref-to-bad && - test_path_is_missing .git/refs/heads/ref-to-bad + test_must_fail git show-ref --verify -q refs/heads/ref-to-bad ' test_expect_success '(not) create HEAD with old sha1' ' test_must_fail git update-ref HEAD $A $B ' test_expect_success "(not) prior created .git/$m" ' - test_when_finished "rm -f .git/$m" && - test_path_is_missing .git/$m + test_when_finished "git update-ref -d $m" && + test_must_fail git show-ref --verify -q $m ' test_expect_success 'create HEAD' ' @@ -254,7 +260,7 @@ test_expect_success '(not) change HEAD with wrong SHA1' ' test_must_fail git update-ref HEAD $B $Z ' test_expect_success "(not) changed .git/$m" ' - test_when_finished "rm -f .git/$m" && + test_when_finished "git update-ref -d $m" && ! test $B = $(git show-ref -s --verify $m) ' @@ -284,8 +290,8 @@ test_expect_success 'empty directory removal' ' test_path_is_file .git/refs/heads/d1/d2/r1 && test_path_is_file .git/logs/refs/heads/d1/d2/r1 && git branch -d d1/d2/r1 && - test_path_is_missing .git/refs/heads/d1/d2 && - test_path_is_missing .git/logs/refs/heads/d1/d2 && + test_must_fail git show-ref --verify -q refs/heads/d1/d2 && + test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 && test_path_is_file .git/refs/heads/d1/r2 && test_path_is_file .git/logs/refs/heads/d1/r2 ' @@ -298,8 +304,8 @@ test_expect_success 'symref empty directory removal' ' test_path_is_file .git/refs/heads/e1/e2/r1 && test_path_is_file .git/logs/refs/heads/e1/e2/r1 && git update-ref -d HEAD && - test_path_is_missing .git/refs/heads/e1/e2 && - test_path_is_missing .git/logs/refs/heads/e1/e2 && + test_must_fail git show-ref --verify -q refs/heads/e1/e2 && + test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 && test_path_is_file .git/refs/heads/e1/r2 && test_path_is_file .git/logs/refs/heads/e1/r2 && test_path_is_file .git/logs/HEAD @@ -1388,7 +1394,8 @@ test_expect_success 'handle per-worktree refs in refs/bisect' ' git rev-parse refs/bisect/something >../worktree-head && git for-each-ref | grep refs/bisect/something ) && - test_path_is_missing .git/refs/bisect && + git show-ref >actual && + ! grep 'refs/bisect' actual && test_must_fail git rev-parse refs/bisect/something && git update-ref refs/bisect/something HEAD && git rev-parse refs/bisect/something >main-head && @@ -1500,7 +1507,7 @@ test_expect_success 'transaction can handle abort' ' git update-ref --stdin <stdin >actual && printf "%s: ok\n" start abort >expect && test_cmp expect actual && - test_path_is_missing .git/$b + test_must_fail git show-ref --verify -q $b ' test_expect_success 'transaction aborts by default' ' @@ -1511,7 +1518,7 @@ test_expect_success 'transaction aborts by default' ' git update-ref --stdin <stdin >actual && printf "%s: ok\n" start >expect && test_cmp expect actual && - test_path_is_missing .git/$b + test_must_fail git show-ref --verify -q $b ' test_expect_success 'transaction with prepare aborts by default' ' @@ -1523,7 +1530,68 @@ test_expect_success 'transaction with prepare aborts by default' ' git update-ref --stdin <stdin >actual && printf "%s: ok\n" start prepare >expect && test_cmp expect actual && - test_path_is_missing .git/$b + test_must_fail git show-ref --verify -q $b +' + +test_expect_success 'transaction can commit multiple times' ' + cat >stdin <<-EOF && + start + create refs/heads/branch-1 $A + commit + start + create refs/heads/branch-2 $B + commit + EOF + git update-ref --stdin <stdin >actual && + printf "%s: ok\n" start commit start commit >expect && + test_cmp expect actual && + echo "$A" >expect && + git rev-parse refs/heads/branch-1 >actual && + test_cmp expect actual && + echo "$B" >expect && + git rev-parse refs/heads/branch-2 >actual && + test_cmp expect actual +' + +test_expect_success 'transaction can create and delete' ' + cat >stdin <<-EOF && + start + create refs/heads/create-and-delete $A + commit + start + delete refs/heads/create-and-delete $A + commit + EOF + git update-ref --stdin <stdin >actual && + printf "%s: ok\n" start commit start commit >expect && + test_must_fail git show-ref --verify refs/heads/create-and-delete +' + +test_expect_success 'transaction can commit after abort' ' + cat >stdin <<-EOF && + start + create refs/heads/abort $A + abort + start + create refs/heads/abort $A + commit + EOF + git update-ref --stdin <stdin >actual && + printf "%s: ok\n" start abort start commit >expect && + echo "$A" >expect && + git rev-parse refs/heads/abort >actual && + test_cmp expect actual +' + +test_expect_success 'transaction cannot restart ongoing transaction' ' + cat >stdin <<-EOF && + start + create refs/heads/restart $A + start + commit + EOF + test_must_fail git update-ref --stdin <stdin >actual && + test_must_fail git show-ref --verify refs/heads/restart ' test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index ca04fac417..cc3f434a97 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -589,6 +589,90 @@ test_expect_success 'diffs can be colorized' ' grep "$(printf "\\033")" output ' +test_expect_success 'colors can be overridden' ' + git reset --hard && + test_when_finished "git rm -f color-test" && + test_write_lines context old more-context >color-test && + git add color-test && + test_write_lines context new more-context another-one >color-test && + + echo trigger an error message >input && + force_color git \ + -c color.interactive.error=blue \ + add -i 2>err.raw <input && + test_decode_color <err.raw >err && + grep "<BLUE>Huh (trigger)?<RESET>" err && + + test_write_lines help quit >input && + force_color git \ + -c color.interactive.header=red \ + -c color.interactive.help=green \ + -c color.interactive.prompt=yellow \ + add -i >actual.raw <input && + test_decode_color <actual.raw >actual && + cat >expect <<-\EOF && + <RED> staged unstaged path<RESET> + 1: +3/-0 +2/-1 color-test + + <RED>*** Commands ***<RESET> + 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked + 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp + <YELLOW>What now<RESET>> <GREEN>status - show paths with changes<RESET> + <GREEN>update - add working tree state to the staged set of changes<RESET> + <GREEN>revert - revert staged set of changes back to the HEAD version<RESET> + <GREEN>patch - pick hunks and update selectively<RESET> + <GREEN>diff - view diff between HEAD and index<RESET> + <GREEN>add untracked - add contents of untracked files to the staged set of changes<RESET> + <RED>*** Commands ***<RESET> + 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked + 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp + <YELLOW>What now<RESET>> Bye. + EOF + test_cmp expect actual && + + : exercise recolor_hunk by editing and then look at the hunk again && + test_write_lines s e K q >input && + force_color git \ + -c color.interactive.prompt=yellow \ + -c color.diff.meta=italic \ + -c color.diff.frag=magenta \ + -c color.diff.context=cyan \ + -c color.diff.old=bold \ + -c color.diff.new=blue \ + -c core.editor=touch \ + add -p >actual.raw <input && + test_decode_color <actual.raw >actual.decoded && + sed "s/index [0-9a-f]*\\.\\.[0-9a-f]* 100644/<INDEX-LINE>/" <actual.decoded >actual && + cat >expect <<-\EOF && + <ITALIC>diff --git a/color-test b/color-test<RESET> + <ITALIC><INDEX-LINE><RESET> + <ITALIC>--- a/color-test<RESET> + <ITALIC>+++ b/color-test<RESET> + <MAGENTA>@@ -1,3 +1,4 @@<RESET> + <CYAN> context<RESET> + <BOLD>-old<RESET> + <BLUE>+<RESET><BLUE>new<RESET> + <CYAN> more-context<RESET> + <BLUE>+<RESET><BLUE>another-one<RESET> + <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET> + <MAGENTA>@@ -1,3 +1,3 @@<RESET> + <CYAN> context<RESET> + <BOLD>-old<RESET> + <BLUE>+<RESET><BLUE>new<RESET> + <CYAN> more-context<RESET> + <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET> + <CYAN> more-context<RESET> + <BLUE>+<RESET><BLUE>another-one<RESET> + <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET> + <CYAN> context<RESET> + <BOLD>-old<RESET> + <BLUE>+new<RESET> + <CYAN> more-context<RESET> + <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET> + EOF + test_cmp expect actual +' + test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' git reset --hard && diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 5cdd76dfa7..41818d8315 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -73,4 +73,30 @@ test_expect_success FILEMODE 'bogus mode is rejected' ' test_i18ngrep "invalid mode" err ' +test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree files' ' + git reset --hard && + test_config core.sharedRepository 0666 && + ( + # Remove a default ACL if possible. + (setfacl -k newdir 2>/dev/null || true) && + umask 0077 && + + # Test both files (f1) and leading dirs (d) + mkdir d && + touch f1 d/f2 && + git add f1 d/f2 && + git diff --staged >patch-f1-and-f2.txt && + + rm -rf d f1 && + git apply patch-f1-and-f2.txt && + + echo "-rw-------" >f1_mode.expected && + echo "drwx------" >d_mode.expected && + test_modebits f1 >f1_mode.actual && + test_modebits d >d_mode.actual && + test_cmp f1_mode.expected f1_mode.actual && + test_cmp d_mode.expected d_mode.actual + ) +' + test_done diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index ace469c95c..297de502a9 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -138,7 +138,7 @@ test_expect_success 'write midx with one v2 pack' ' compare_results_with_midx "one v2 pack" -test_expect_success 'corrupt idx not opened' ' +test_expect_success 'corrupt idx reports errors' ' idx=$(test-tool read-midx $objdir | grep "\.idx\$") && mv $objdir/pack/$idx backup-$idx && test_when_finished "mv backup-\$idx \$objdir/pack/\$idx" && @@ -149,7 +149,7 @@ test_expect_success 'corrupt idx not opened' ' test_copy_bytes 1064 <backup-$idx >$objdir/pack/$idx && git -c core.multiPackIndex=true rev-list --objects --all 2>err && - test_must_be_empty err + grep "index unavailable" err ' test_expect_success 'add more objects' ' @@ -755,4 +755,30 @@ test_expect_success 'repack --batch-size=<large> repacks everything' ' ) ' +test_expect_success 'load reverse index when missing .idx, .pack' ' + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + git config core.multiPackIndex true && + + test_commit base && + git repack -ad && + git multi-pack-index write && + + git rev-parse HEAD >tip && + pack=$(ls .git/objects/pack/pack-*.pack) && + idx=$(ls .git/objects/pack/pack-*.idx) && + + mv $idx $idx.bak && + git cat-file --batch-check="%(objectsize:disk)" <tip && + + mv $idx.bak $idx && + + mv $pack $pack.bak && + git cat-file --batch-check="%(objectsize:disk)" <tip + ) +' + test_done diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index f541f30bc2..f808649de4 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -58,6 +58,8 @@ test_refspec fetch 'HEAD~4:refs/remotes/frotz/new' invalid test_refspec push 'HEAD' test_refspec fetch 'HEAD' +test_refspec push '@' +test_refspec fetch '@' test_refspec push 'refs/heads/ nitfol' invalid test_refspec fetch 'refs/heads/ nitfol' invalid diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index d11382f769..01004ff680 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -436,70 +436,76 @@ test_expect_success 'push ref expression with non-existent, incomplete dest' ' ' -test_expect_success 'push with HEAD' ' +for head in HEAD @ +do - mk_test testrepo heads/master && - git checkout master && - git push testrepo HEAD && - check_push_result testrepo $the_commit heads/master + test_expect_success "push with $head" ' -' + mk_test testrepo heads/master && + git checkout master && + git push testrepo $head && + check_push_result testrepo $the_commit heads/master -test_expect_success 'push with HEAD nonexisting at remote' ' + ' - mk_test testrepo heads/master && - git checkout -b local master && - git push testrepo HEAD && - check_push_result testrepo $the_commit heads/local -' + test_expect_success "push with $head nonexisting at remote" ' -test_expect_success 'push with +HEAD' ' + mk_test testrepo heads/master && + git checkout -b local master && + test_when_finished "git checkout master; git branch -D local" && + git push testrepo $head && + check_push_result testrepo $the_commit heads/local + ' - mk_test testrepo heads/master && - git checkout master && - git branch -D local && - git checkout -b local && - git push testrepo master local && - check_push_result testrepo $the_commit heads/master && - check_push_result testrepo $the_commit heads/local && + test_expect_success "push with +$head" ' - # Without force rewinding should fail - git reset --hard HEAD^ && - test_must_fail git push testrepo HEAD && - check_push_result testrepo $the_commit heads/local && + mk_test testrepo heads/master && + git checkout -b local master && + test_when_finished "git checkout master; git branch -D local" && + git push testrepo master local && + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_commit heads/local && - # With force rewinding should succeed - git push testrepo +HEAD && - check_push_result testrepo $the_first_commit heads/local + # Without force rewinding should fail + git reset --hard $head^ && + test_must_fail git push testrepo $head && + check_push_result testrepo $the_commit heads/local && -' + # With force rewinding should succeed + git push testrepo +$head && + check_push_result testrepo $the_first_commit heads/local -test_expect_success 'push HEAD with non-existent, incomplete dest' ' + ' - mk_test testrepo && - git checkout master && - git push testrepo HEAD:branch && - check_push_result testrepo $the_commit heads/branch + test_expect_success "push $head with non-existent, incomplete dest" ' -' + mk_test testrepo && + git checkout master && + git push testrepo $head:branch && + check_push_result testrepo $the_commit heads/branch -test_expect_success 'push with config remote.*.push = HEAD' ' + ' - mk_test testrepo heads/local && - git checkout master && - git branch -f local $the_commit && - ( - cd testrepo && - git checkout local && - git reset --hard $the_first_commit - ) && - test_config remote.there.url testrepo && - test_config remote.there.push HEAD && - test_config branch.master.remote there && - git push && - check_push_result testrepo $the_commit heads/master && - check_push_result testrepo $the_first_commit heads/local -' + test_expect_success "push with config remote.*.push = $head" ' + + mk_test testrepo heads/local && + git checkout master && + git branch -f local $the_commit && + test_when_finished "git branch -D local" && + ( + cd testrepo && + git checkout local && + git reset --hard $the_first_commit + ) && + test_config remote.there.url testrepo && + test_config remote.there.push $head && + test_config branch.master.remote there && + git push && + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/local + ' + +done test_expect_success 'push with remote.pushdefault' ' mk_test up_repo heads/master && diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index dd8e423d25..a877dd145e 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -18,7 +18,7 @@ add_upstream_commit() { head2=$(git rev-parse --short HEAD) && echo "Fetching submodule submodule" > ../expect.err && echo "From $pwd/submodule" >> ../expect.err && - echo " $head1..$head2 main -> origin/main" >> ../expect.err + echo " $head1..$head2 sub -> origin/sub" >> ../expect.err ) && ( cd deepsubmodule && @@ -30,7 +30,7 @@ add_upstream_commit() { head2=$(git rev-parse --short HEAD) && echo "Fetching submodule submodule/subdir/deepsubmodule" >> ../expect.err echo "From $pwd/deepsubmodule" >> ../expect.err && - echo " $head1..$head2 main -> origin/main" >> ../expect.err + echo " $head1..$head2 deep -> origin/deep" >> ../expect.err ) } @@ -41,7 +41,8 @@ test_expect_success setup ' git init && echo deepsubcontent > deepsubfile && git add deepsubfile && - git commit -m new deepsubfile + git commit -m new deepsubfile && + git branch -M deep ) && mkdir submodule && ( @@ -50,10 +51,12 @@ test_expect_success setup ' echo subcontent > subfile && git add subfile && git submodule add "$pwd/deepsubmodule" subdir/deepsubmodule && - git commit -a -m new + git commit -a -m new && + git branch -M sub ) && git submodule add "$pwd/submodule" submodule && git commit -am initial && + git branch -M super && git clone . downstream && ( cd downstream && @@ -61,7 +64,7 @@ test_expect_success setup ' ) ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "fetch --recurse-submodules recurses into submodules" ' +test_expect_success "fetch --recurse-submodules recurses into submodules" ' add_upstream_commit && ( cd downstream && @@ -71,7 +74,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "fetch --recurse-submodules recurses test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "submodule.recurse option triggers recursive fetch" ' +test_expect_success "submodule.recurse option triggers recursive fetch" ' add_upstream_commit && ( cd downstream && @@ -81,7 +84,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "submodule.recurse option triggers r test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "fetch --recurse-submodules -j2 has the same output behaviour" ' +test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" ' add_upstream_commit && ( cd downstream && @@ -111,7 +114,7 @@ test_expect_success "fetch --no-recurse-submodules only fetches superproject" ' test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" ' +test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" ' ( cd downstream && git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true && @@ -141,7 +144,7 @@ test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" ' +test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" ' ( cd downstream && git fetch --recurse-submodules >../actual.out 2>../actual.err && @@ -170,7 +173,7 @@ test_expect_success "--quiet propagates to parallel submodules" ' test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "--dry-run propagates to submodules" ' +test_expect_success "--dry-run propagates to submodules" ' add_upstream_commit && ( cd downstream && @@ -180,7 +183,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "--dry-run propagates to submodules" test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Without --dry-run propagates to submodules" ' +test_expect_success "Without --dry-run propagates to submodules" ' ( cd downstream && git fetch --recurse-submodules >../actual.out 2>../actual.err @@ -189,7 +192,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Without --dry-run propagates to sub test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "recurseSubmodules=true propagates into submodules" ' +test_expect_success "recurseSubmodules=true propagates into submodules" ' add_upstream_commit && ( cd downstream && @@ -200,7 +203,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "recurseSubmodules=true propagates i test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "--recurse-submodules overrides config in submodule" ' +test_expect_success "--recurse-submodules overrides config in submodule" ' add_upstream_commit && ( cd downstream && @@ -225,7 +228,7 @@ test_expect_success "--no-recurse-submodules overrides config setting" ' test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when no new commits are fetched in the superproject" ' +test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" ' ( cd downstream && ( @@ -239,13 +242,13 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when no ne test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion stops when no new submodule commits are fetched" ' +test_expect_success "Recursion stops when no new submodule commits are fetched" ' head1=$(git rev-parse --short HEAD) && git add submodule && git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.sub && - echo " $head1..$head2 main -> origin/main" >>expect.err.sub && + echo " $head1..$head2 super -> origin/super" >>expect.err.sub && head -3 expect.err >> expect.err.sub && ( cd downstream && @@ -255,7 +258,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion stops when no new submodu test_must_be_empty actual.out ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when new superproject commits don't change any submodules" ' +test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" ' add_upstream_commit && head1=$(git rev-parse --short HEAD) && echo a > file && @@ -263,7 +266,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when new s git commit -m "new file" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.file && - echo " $head1..$head2 main -> origin/main" >> expect.err.file && + echo " $head1..$head2 super -> origin/super" >> expect.err.file && ( cd downstream && git fetch >../actual.out 2>../actual.err @@ -272,7 +275,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when new s test_i18ncmp expect.err.file actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up config in submodule" ' +test_expect_success "Recursion picks up config in submodule" ' ( cd downstream && git fetch --recurse-submodules && @@ -287,7 +290,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up config in submod git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.sub && - echo " $head1..$head2 main -> origin/main" >> expect.err.sub && + echo " $head1..$head2 super -> origin/super" >> expect.err.sub && cat expect.err >> expect.err.sub && ( cd downstream && @@ -301,7 +304,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up config in submod test_must_be_empty actual.out ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up all submodules when necessary" ' +test_expect_success "Recursion picks up all submodules when necessary" ' add_upstream_commit && ( cd submodule && @@ -316,14 +319,14 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up all submodules w head2=$(git rev-parse --short HEAD) && echo "Fetching submodule submodule" > ../expect.err.sub && echo "From $pwd/submodule" >> ../expect.err.sub && - echo " $head1..$head2 main -> origin/main" >> ../expect.err.sub + echo " $head1..$head2 sub -> origin/sub" >> ../expect.err.sub ) && head1=$(git rev-parse --short HEAD) && git add submodule && git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.2 && - echo " $head1..$head2 main -> origin/main" >> expect.err.2 && + echo " $head1..$head2 super -> origin/super" >> expect.err.2 && cat expect.err.sub >> expect.err.2 && tail -3 expect.err >> expect.err.2 && ( @@ -334,7 +337,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up all submodules w test_must_be_empty actual.out ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" ' +test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" ' add_upstream_commit && ( cd submodule && @@ -349,7 +352,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' do head2=$(git rev-parse --short HEAD) && echo Fetching submodule submodule > ../expect.err.sub && echo "From $pwd/submodule" >> ../expect.err.sub && - echo " $head1..$head2 main -> origin/main" >> ../expect.err.sub + echo " $head1..$head2 sub -> origin/sub" >> ../expect.err.sub ) && ( cd downstream && @@ -361,14 +364,14 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' do test_must_be_empty actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" ' +test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" ' head1=$(git rev-parse --short HEAD) && git add submodule && git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && tail -3 expect.err > expect.err.deepsub && echo "From $pwd/." > expect.err && - echo " $head1..$head2 main -> origin/main" >>expect.err && + echo " $head1..$head2 super -> origin/super" >>expect.err && cat expect.err.sub >> expect.err && cat expect.err.deepsub >> expect.err && ( @@ -389,7 +392,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' re test_i18ncmp expect.err actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" ' +test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" ' add_upstream_commit && head1=$(git rev-parse --short HEAD) && echo a >> file && @@ -397,7 +400,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' st git commit -m "new file" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.file && - echo " $head1..$head2 main -> origin/main" >> expect.err.file && + echo " $head1..$head2 super -> origin/super" >> expect.err.file && ( cd downstream && git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err @@ -406,7 +409,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' st test_i18ncmp expect.err.file actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' overrides global config" ' +test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" ' ( cd downstream && git fetch --recurse-submodules @@ -418,7 +421,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.2 && - echo " $head1..$head2 main -> origin/main" >>expect.err.2 && + echo " $head1..$head2 super -> origin/super" >>expect.err.2 && head -3 expect.err >> expect.err.2 && ( cd downstream && @@ -434,7 +437,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' test_i18ncmp expect.err.2 actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" ' +test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" ' ( cd downstream && git fetch --recurse-submodules @@ -446,7 +449,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'submodule.<sub>.fetchRecurseSubmod git commit -m "new submodule" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err.2 && - echo " $head1..$head2 main -> origin/main" >>expect.err.2 && + echo " $head1..$head2 super -> origin/super" >>expect.err.2 && head -3 expect.err >> expect.err.2 && ( cd downstream && @@ -462,7 +465,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'submodule.<sub>.fetchRecurseSubmod test_i18ncmp expect.err.2 actual.err ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "don't fetch submodule when newly recorded commits are already present" ' +test_expect_success "don't fetch submodule when newly recorded commits are already present" ' ( cd submodule && git checkout -q HEAD^^ @@ -472,7 +475,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "don't fetch submodule when newly re git commit -m "submodule rewound" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." > expect.err && - echo " $head1..$head2 main -> origin/main" >> expect.err && + echo " $head1..$head2 super -> origin/super" >> expect.err && ( cd downstream && git fetch >../actual.out 2>../actual.err @@ -481,11 +484,11 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "don't fetch submodule when newly re test_i18ncmp expect.err actual.err && ( cd submodule && - git checkout -q master + git checkout -q sub ) ' -test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" ' +test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" ' ( cd downstream && git fetch --recurse-submodules @@ -497,7 +500,7 @@ test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' git commit -m "new submodule without .gitmodules" && head2=$(git rev-parse --short HEAD) && echo "From $pwd/." >expect.err.2 && - echo " $head1..$head2 main -> origin/main" >>expect.err.2 && + echo " $head1..$head2 super -> origin/super" >>expect.err.2 && head -3 expect.err >>expect.err.2 && ( cd downstream && @@ -663,9 +666,9 @@ test_expect_success 'fetch new submodule commits on-demand without .gitmodules e git config -f .gitmodules --remove-section submodule.sub1 && git add .gitmodules && git commit -m "delete gitmodules file" && - git checkout -B master && + git checkout -B super && git -C downstream fetch && - git -C downstream checkout origin/master && + git -C downstream checkout origin/super && C=$(git -C submodule commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) && git -C submodule update-ref refs/changes/7 $C && diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 5da945cf15..d98c550267 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -251,6 +251,14 @@ test_expect_success 'implicitly construct combine: filter with repeated flags' ' test_cmp unique_types.expected unique_types.actual ' +test_expect_success 'upload-pack complains of bogus filter config' ' + printf 0000 | + test_must_fail git \ + -c uploadpackfilter.tree.maxdepth \ + upload-pack . >/dev/null 2>err && + test_i18ngrep "unable to parse.*tree.maxdepth" err +' + test_expect_success 'upload-pack fails banned object filters' ' test_config -C srv.bare uploadpackfilter.blob:none.allow false && test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \ diff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh new file mode 100755 index 0000000000..f1d189d5bc --- /dev/null +++ b/t/t5705-session-id-in-capabilities.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +test_description='session ID in capabilities' + +. ./test-lib.sh + +REPO="$(pwd)/repo" +LOCAL_PRISTINE="$(pwd)/local_pristine" + +test_expect_success 'setup repos for session ID capability tests' ' + git init "$REPO" && + test_commit -C "$REPO" a && + git clone "file://$REPO" "$LOCAL_PRISTINE" && + test_commit -C "$REPO" b +' + +for PROTO in 0 1 2 +do + test_expect_success "session IDs not advertised by default (fetch v${PROTO})" ' + test_when_finished "rm -rf local tr2-client-events tr2-server-events" && + cp -r "$LOCAL_PRISTINE" local && + GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \ + git -c protocol.version=$PROTO -C local fetch \ + --upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \ + origin && + test -z "$(grep \"key\":\"server-sid\" tr2-client-events)" && + test -z "$(grep \"key\":\"client-sid\" tr2-server-events)" + ' + + test_expect_success "session IDs not advertised by default (push v${PROTO})" ' + test_when_finished "rm -rf local tr2-client-events tr2-server-events" && + test_when_finished "git -C local push --delete origin new-branch" && + cp -r "$LOCAL_PRISTINE" local && + git -C local pull --no-rebase origin && + GIT_TRACE2_EVENT_NESTING=5 \ + GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \ + git -c protocol.version=$PROTO -C local push \ + --receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \ + origin HEAD:new-branch && + test -z "$(grep \"key\":\"server-sid\" tr2-client-events)" && + test -z "$(grep \"key\":\"client-sid\" tr2-server-events)" + ' +done + +test_expect_success 'enable SID advertisement' ' + git -C "$REPO" config transfer.advertiseSID true && + git -C "$LOCAL_PRISTINE" config transfer.advertiseSID true +' + +for PROTO in 0 1 2 +do + test_expect_success "session IDs advertised (fetch v${PROTO})" ' + test_when_finished "rm -rf local tr2-client-events tr2-server-events" && + cp -r "$LOCAL_PRISTINE" local && + GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \ + git -c protocol.version=$PROTO -C local fetch \ + --upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \ + origin && + grep \"key\":\"server-sid\" tr2-client-events && + grep \"key\":\"client-sid\" tr2-server-events + ' + + test_expect_success "session IDs advertised (push v${PROTO})" ' + test_when_finished "rm -rf local tr2-client-events tr2-server-events" && + test_when_finished "git -C local push --delete origin new-branch" && + cp -r "$LOCAL_PRISTINE" local && + git -C local pull --no-rebase origin && + GIT_TRACE2_EVENT_NESTING=5 \ + GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \ + git -c protocol.version=$PROTO -C local push \ + --receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \ + origin HEAD:new-branch && + grep \"key\":\"server-sid\" tr2-client-events && + grep \"key\":\"client-sid\" tr2-server-events + ' +done + +test_done diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index d9e68bb2bf..d1e0c8f830 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -239,13 +239,15 @@ test_expect_success 'incremental-repack task' ' ' test_expect_success EXPENSIVE 'incremental-repack 2g limit' ' + test_config core.compression 0 && + for i in $(test_seq 1 5) do test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big || return 1 done && git add big && - git commit -m "Add big file (1)" && + git commit -qm "Add big file (1)" && # ensure any possible loose objects are in a pack-file git maintenance run --task=loose-objects && @@ -257,7 +259,7 @@ test_expect_success EXPENSIVE 'incremental-repack 2g limit' ' return 1 done && git add big && - git commit -m "Add big file (2)" && + git commit -qm "Add big file (2)" && # ensure any possible loose objects are in a pack-file git maintenance run --task=loose-objects && @@ -404,11 +406,23 @@ test_expect_success 'register and unregister' ' test_cmp before actual ' +test_expect_success !MINGW 'register and unregister with regex metacharacters' ' + META="a+b*c" && + git init "$META" && + git -C "$META" maintenance register && + git config --get-all --show-origin maintenance.repo && + git config --get-all --global --fixed-value \ + maintenance.repo "$(pwd)/$META" && + git -C "$META" maintenance unregister && + test_must_fail git config --get-all --global --fixed-value \ + maintenance.repo "$(pwd)/$META" +' + test_expect_success 'start from empty cron table' ' GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start && # start registers the repo - git config --get --global maintenance.repo "$(pwd)" && + git config --get --global --fixed-value maintenance.repo "$(pwd)" && grep "for-each-repo --config=maintenance.repo maintenance run --schedule=daily" cron.txt && grep "for-each-repo --config=maintenance.repo maintenance run --schedule=hourly" cron.txt && @@ -419,7 +433,7 @@ test_expect_success 'stop from existing schedule' ' GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop && # stop does not unregister the repo - git config --get --global maintenance.repo "$(pwd)" && + git config --get --global --fixed-value maintenance.repo "$(pwd)" && # Operation is idempotent GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop && @@ -441,4 +455,12 @@ test_expect_success 'register preserves existing strategy' ' test_config maintenance.strategy incremental ' +test_expect_success 'fails when running outside of a repository' ' + nongit test_must_fail git maintenance run && + nongit test_must_fail git maintenance stop && + nongit test_must_fail git maintenance start && + nongit test_must_fail git maintenance register && + nongit test_must_fail git maintenance unregister +' + test_done diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh index b1c7919c4a..03cd5c5423 100755 --- a/t/t9003-help-autocorrect.sh +++ b/t/t9003-help-autocorrect.sh @@ -37,16 +37,30 @@ test_expect_success 'autocorrect showing candidates' ' grep "^ distimdistim" actual ' -test_expect_success 'autocorrect running commands' ' - git config help.autocorrect -1 && +for immediate in -1 immediate +do + test_expect_success 'autocorrect running commands' ' + git config help.autocorrect $immediate && - git lfg >actual && - echo "a single log entry" >expect && - test_cmp expect actual && + git lfg >actual && + echo "a single log entry" >expect && + test_cmp expect actual && - git distimdist >actual && - echo "distimdistim was called" >expect && - test_cmp expect actual + git distimdist >actual && + echo "distimdistim was called" >expect && + test_cmp expect actual + ' +done + +test_expect_success 'autocorrect can be declined altogether' ' + git config help.autocorrect never && + + test_must_fail git lfg 2>actual && + if test_have_prereq C_LOCALE_OUTPUT + then + grep "is not a git command" actual && + test_line_count = 1 actual + fi ' test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 5c01c75d40..81a728c743 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -906,7 +906,7 @@ test_expect_success '__git_refs - after --opt= - full refs' ' test_cmp expected "$actual" ' -test_expect_success '__git refs - exluding refs' ' +test_expect_success '__git refs - excluding refs' ' cat >expected <<-EOF && ^HEAD ^master @@ -922,7 +922,7 @@ test_expect_success '__git refs - exluding refs' ' test_cmp expected "$actual" ' -test_expect_success '__git refs - exluding full refs' ' +test_expect_success '__git refs - excluding full refs' ' cat >expected <<-EOF && ^refs/heads/master ^refs/heads/matching-branch diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 7ba3011b90..eca4d311ef 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -367,9 +367,9 @@ test_chmod () { git update-index --add "--chmod=$@" } -# Get the modebits from a file. +# Get the modebits from a file or directory. test_modebits () { - ls -l "$1" | sed -e 's|^\(..........\).*|\1|' + ls -ld "$1" | sed -e 's|^\(..........\).*|\1|' } # Unset a configuration variable, but don't fail if it doesn't exist. @@ -955,7 +955,7 @@ test_expect_code () { # - cmp's output is not nearly as easy to read as diff -u # - not all diff versions understand "-u" -test_cmp() { +test_cmp () { eval "$GIT_TEST_CMP" '"$@"' } @@ -968,7 +968,7 @@ test_cmp() { # # test_cmp_config foo core.bar # -test_cmp_config() { +test_cmp_config () { local GD && if test "$1" = "-C" then @@ -984,7 +984,7 @@ test_cmp_config() { # test_cmp_bin - helper to compare binary files -test_cmp_bin() { +test_cmp_bin () { cmp "$@" } @@ -1418,7 +1418,7 @@ nongit () { # whitespace and put in a single packet. Note that data containing NULs must be # given on stdin, and that empty input becomes an empty packet, not a flush # packet (for that you can just print 0000 yourself). -packetize() { +packetize () { if test $# -gt 0 then packet="$*" @@ -792,3 +792,8 @@ void trace2_printf(const char *fmt, ...) va_end(ap); } #endif + +const char *trace2_session_id(void) +{ + return tr2_sid_get(); +} @@ -500,4 +500,6 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason); } while (0) #endif +const char *trace2_session_id(void); + #endif /* TRACE2_H */ diff --git a/transport.c b/transport.c index 47da955e4f..679a35e7c1 100644 --- a/transport.c +++ b/transport.c @@ -286,6 +286,8 @@ static struct ref *handshake(struct transport *transport, int for_push, struct git_transport_data *data = transport->data; struct ref *refs = NULL; struct packet_reader reader; + int sid_len; + const char *server_sid; connect_setup(transport, for_push); @@ -297,6 +299,8 @@ static struct ref *handshake(struct transport *transport, int for_push, data->version = discover_version(&reader); switch (data->version) { case protocol_v2: + if (server_feature_v2("session-id", &server_sid)) + trace2_data_string("transfer", NULL, "server-sid", server_sid); if (must_list_refs) get_remote_refs(data->fd[1], &reader, &refs, for_push, ref_prefixes, @@ -310,6 +314,12 @@ static struct ref *handshake(struct transport *transport, int for_push, for_push ? REF_NORMAL : 0, &data->extra_have, &data->shallow); + server_sid = server_feature_value("session-id", &sid_len); + if (server_sid) { + char *sid = xstrndup(server_sid, sid_len); + trace2_data_string("transfer", NULL, "server-sid", sid); + free(sid); + } break; case protocol_unknown_version: BUG("unknown protocol version"); diff --git a/upload-pack.c b/upload-pack.c index be8fffbc07..3b66bf92ba 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -110,6 +110,7 @@ struct upload_pack_data { unsigned done : 1; /* v2 only */ unsigned allow_ref_in_want : 1; /* v2 only */ unsigned allow_sideband_all : 1; /* v2 only */ + unsigned advertise_sid : 1; }; static void upload_pack_data_init(struct upload_pack_data *data) @@ -141,6 +142,7 @@ static void upload_pack_data_init(struct upload_pack_data *data) packet_writer_init(&data->writer, 1); data->keepalive = 5; + data->advertise_sid = 0; } static void upload_pack_data_clear(struct upload_pack_data *data) @@ -1057,6 +1059,7 @@ static void receive_needs(struct upload_pack_data *data, const char *features; struct object_id oid_buf; const char *arg; + int feature_len; reset_timeout(data->timeout); if (packet_reader_read(reader) != PACKET_READ_NORMAL) @@ -1109,6 +1112,13 @@ static void receive_needs(struct upload_pack_data *data, parse_feature_request(features, "filter")) data->filter_capability_requested = 1; + arg = parse_feature_value(features, "session-id", &feature_len, NULL); + if (arg) { + char *client_sid = xstrndup(arg, feature_len); + trace2_data_string("transfer", NULL, "client-sid", client_sid); + free(client_sid); + } + o = parse_object(the_repository, &oid_buf); if (!o) { packet_writer_error(&data->writer, @@ -1179,6 +1189,11 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref) strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util); } +static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) { + if (d->advertise_sid) + strbuf_addf(buf, " session-id=%s", trace2_session_id()); +} + static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { @@ -1194,9 +1209,11 @@ static int send_ref(const char *refname, const struct object_id *oid, if (capabilities) { struct strbuf symref_info = STRBUF_INIT; + struct strbuf session_id = STRBUF_INIT; format_symref_info(&symref_info, &data->symref); - packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s object-format=%s agent=%s\n", + format_session_id(&session_id, data); + packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n", oid_to_hex(oid), refname_nons, 0, capabilities, (data->allow_uor & ALLOW_TIP_SHA1) ? @@ -1206,9 +1223,11 @@ static int send_ref(const char *refname, const struct object_id *oid, data->stateless_rpc ? " no-done" : "", symref_info.buf, data->allow_filter ? " filter" : "", + session_id.buf, the_hash_algo->name, git_user_agent_sanitized()); strbuf_release(&symref_info); + strbuf_release(&session_id); } else { packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons); } @@ -1300,15 +1319,18 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data) data->allow_sideband_all = git_config_bool(var, value); } else if (!strcmp("core.precomposeunicode", var)) { precomposed_unicode = git_config_bool(var, value); + } else if (!strcmp("transfer.advertisesid", var)) { + data->advertise_sid = git_config_bool(var, value); } if (current_config_scope() != CONFIG_SCOPE_LOCAL && - current_config_scope() != CONFIG_SCOPE_WORKTREE) { + current_config_scope() != CONFIG_SCOPE_WORKTREE) { if (!strcmp("uploadpack.packobjectshook", var)) return git_config_string(&data->pack_objects_hook, var, value); } - parse_object_filter_config(var, value, data); + if (parse_object_filter_config(var, value, data) < 0) + return -1; return parse_hide_refs_config(var, value, "uploadpack"); } @@ -81,6 +81,12 @@ static void error_builtin(const char *err, va_list params) static void warn_builtin(const char *warn, va_list params) { + /* + * We call this trace2 function first and expect it to va_copy 'params' + * before using it (because an 'ap' can only be walked once). + */ + trace2_cmd_error_va(warn, params); + vreportf("warning: ", warn, params); } |