diff options
62 files changed, 1145 insertions, 650 deletions
diff --git a/.gitignore b/.gitignore index 7c4dff2095..3dcdb6bb5a 100644 --- a/.gitignore +++ b/.gitignore @@ -115,7 +115,6 @@ /git-pack-redundant /git-pack-objects /git-pack-refs -/git-parse-remote /git-patch-id /git-prune /git-prune-packed 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 c0892759ed..cb4d65f9ff 100644 --- a/Documentation/RelNotes/2.30.0.txt +++ b/Documentation/RelNotes/2.30.0.txt @@ -136,6 +136,8 @@ Performance, Internal Implementation, Development Support etc. file, causing everybody else but one to fail---reattempt locking with some timeout to reduce the rate of the failure. + * "git-parse-remote" shell script library outlived its usefulness. + Fixes since v2.29 ----------------- @@ -263,11 +265,6 @@ Fixes since v2.29 larger than 4GB. (merge 81c4c5cf2e jk/4gb-idx later to maint). - * "git fetch" did not work correctly with nested submodules where the - innermost submodule that is not of interest got updated in the - upstream, which has been corrected. - (merge 1b7ac4e6d4 pk/subsub-fetch-fix later to maint). - * Since jgit does not yet work with SHA-256 repositories, mark the tests that uses it not to run unless we are testing with ShA-1 repositories. @@ -283,6 +280,20 @@ Fixes since v2.29 * Error message fix. (merge eaf5341538 km/stash-error-message-fix later to maint). + * "git pull --rebase --recurse-submodules" checked for local changes + in a wrong range and failed to run correctly when it should. + (merge 5176f20ffe pb/pull-rebase-recurse-submodules later to maint). + + * "git push" that is killed may leave a pack-objects process behind, + still computing to find a good compression, wasting cycles. This + has been corrected. + (merge 8b59935114 jk/stop-pack-objects-when-push-is-killed later to maint). + + * "git fetch" that is killed may leave a pack-objects process behind, + still computing to find a good compression, wasting cycles. This + has been corrected. + (merge 309a4028e7 jk/stop-pack-objects-when-fetch-is-killed 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). 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-parse-remote.txt b/Documentation/git-parse-remote.txt deleted file mode 100644 index a45ea1ece8..0000000000 --- a/Documentation/git-parse-remote.txt +++ /dev/null @@ -1,23 +0,0 @@ -git-parse-remote(1) -=================== - -NAME ----- -git-parse-remote - Routines to help parsing remote repository access parameters - - -SYNOPSIS --------- -[verse] -'. "$(git --exec-path)/git-parse-remote"' - -DESCRIPTION ------------ -This script is included in various scripts to supply -routines to parse files under $GIT_DIR/remotes/ and -$GIT_DIR/branches/ and configuration variables that are related -to fetching, pulling and pushing. - -GIT ---- -Part of the linkgit:git[1] suite 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/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. @@ -613,7 +613,6 @@ SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib -SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--preserve-merges SCRIPT_LIB += git-sh-i18n SCRIPT_LIB += git-sh-setup @@ -769,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 @@ -2583,7 +2583,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \ --keyword=__ --keyword=N__ --keyword="__n:1,2" LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H) LOCALIZED_SH = $(SCRIPT_SH) -LOCALIZED_SH += git-parse-remote.sh LOCALIZED_SH += git-rebase--preserve-merges.sh LOCALIZED_SH += git-sh-setup.sh LOCALIZED_PERL = $(SCRIPT_PERL) 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/pull.c b/builtin/pull.c index 1034372f8b..aa56ebcdd0 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -852,21 +852,42 @@ static int get_octopus_merge_base(struct object_id *merge_base, /** * Given the current HEAD oid, the merge head returned from git-fetch and the - * fork point calculated by get_rebase_fork_point(), runs git-rebase with the - * appropriate arguments and returns its exit status. + * fork point calculated by get_rebase_fork_point(), compute the <newbase> and + * <upstream> arguments to use for the upcoming git-rebase invocation. */ -static int run_rebase(const struct object_id *curr_head, +static int get_rebase_newbase_and_upstream(struct object_id *newbase, + struct object_id *upstream, + const struct object_id *curr_head, const struct object_id *merge_head, const struct object_id *fork_point) { - int ret; struct object_id oct_merge_base; - struct strvec args = STRVEC_INIT; if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point)) if (!is_null_oid(fork_point) && oideq(&oct_merge_base, fork_point)) fork_point = NULL; + if (fork_point && !is_null_oid(fork_point)) + oidcpy(upstream, fork_point); + else + oidcpy(upstream, merge_head); + + oidcpy(newbase, merge_head); + + return 0; +} + +/** + * Given the <newbase> and <upstream> calculated by + * get_rebase_newbase_and_upstream(), runs git-rebase with the + * appropriate arguments and returns its exit status. + */ +static int run_rebase(const struct object_id *newbase, + const struct object_id *upstream) +{ + int ret; + struct strvec args = STRVEC_INIT; + strvec_push(&args, "rebase"); /* Shared options */ @@ -894,12 +915,9 @@ static int run_rebase(const struct object_id *curr_head, warning(_("ignoring --verify-signatures for rebase")); strvec_push(&args, "--onto"); - strvec_push(&args, oid_to_hex(merge_head)); + strvec_push(&args, oid_to_hex(newbase)); - if (fork_point && !is_null_oid(fork_point)) - strvec_push(&args, oid_to_hex(fork_point)); - else - strvec_push(&args, oid_to_hex(merge_head)); + strvec_push(&args, oid_to_hex(upstream)); ret = run_command_v_opt(args.v, RUN_GIT_CMD); strvec_clear(&args); @@ -1011,9 +1029,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_rebase) { int ret = 0; int ran_ff = 0; + + struct object_id newbase; + struct object_id upstream; + get_rebase_newbase_and_upstream(&newbase, &upstream, &curr_head, + merge_heads.oid, &rebase_fork_point); + if ((recurse_submodules == RECURSE_SUBMODULES_ON || recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) && - submodule_touches_in_range(the_repository, &rebase_fork_point, &curr_head)) + submodule_touches_in_range(the_repository, &upstream, &curr_head)) die(_("cannot rebase with locally recorded submodule modifications")); if (!autostash) { struct commit_list *list = NULL; @@ -1034,7 +1058,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) free_commit_list(list); } if (!ran_ff) - ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point); + ret = run_rebase(&newbase, &upstream); if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON || recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)) 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/repack.c b/builtin/repack.c index 01e7767c79..279be11a16 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -202,6 +202,37 @@ static int write_oid(const struct object_id *oid, struct packed_git *pack, return 0; } +static struct { + const char *name; + unsigned optional:1; +} exts[] = { + {".pack"}, + {".idx"}, + {".bitmap", 1}, + {".promisor", 1}, +}; + +static unsigned populate_pack_exts(char *name) +{ + struct stat statbuf; + struct strbuf path = STRBUF_INIT; + unsigned ret = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(exts); i++) { + strbuf_reset(&path); + strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name); + + if (stat(path.buf, &statbuf)) + continue; + + ret |= (1 << i); + } + + strbuf_release(&path); + return ret; +} + static void repack_promisor_objects(const struct pack_objects_args *args, struct string_list *names) { @@ -230,11 +261,12 @@ static void repack_promisor_objects(const struct pack_objects_args *args, out = xfdopen(cmd.out, "r"); while (strbuf_getline_lf(&line, out) != EOF) { + struct string_list_item *item; char *promisor_name; int fd; if (line.len != the_hash_algo->hexsz) die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - string_list_append(names, line.buf); + item = string_list_append(names, line.buf); /* * pack-objects creates the .pack and .idx files, but not the @@ -253,6 +285,9 @@ static void repack_promisor_objects(const struct pack_objects_args *args, if (fd < 0) die_errno(_("unable to create '%s'"), promisor_name); close(fd); + + item->util = (void *)(uintptr_t)populate_pack_exts(item->string); + free(promisor_name); } fclose(out); @@ -265,22 +300,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args, int cmd_repack(int argc, const char **argv, const char *prefix) { - struct { - const char *name; - unsigned optional:1; - } exts[] = { - {".pack"}, - {".idx"}, - {".bitmap", 1}, - {".promisor", 1}, - }; struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct string_list names = STRING_LIST_INIT_DUP; struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_packs = STRING_LIST_INIT_DUP; struct strbuf line = STRBUF_INIT; - int i, ext, ret, failed; + int i, ext, ret; FILE *out; /* variables to be filled by option parsing */ @@ -429,113 +455,42 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (!names.nr && !po_args.quiet) printf_ln(_("Nothing new to pack.")); + for_each_string_list_item(item, &names) { + item->util = (void *)(uintptr_t)populate_pack_exts(item->string); + } + close_object_store(the_repository->objects); /* * Ok we have prepared all new packfiles. - * First see if there are packs of the same name and if so - * if we can move them out of the way (this can happen if we - * repacked immediately after packing fully. */ - failed = 0; for_each_string_list_item(item, &names) { for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname, *fname_old; - fname = mkpathdup("%s/pack-%s%s", packdir, - item->string, exts[ext].name); - if (!file_exists(fname)) { - free(fname); - continue; - } - - fname_old = mkpathdup("%s/old-%s%s", packdir, - item->string, exts[ext].name); - if (file_exists(fname_old)) - if (unlink(fname_old)) - failed = 1; - - if (!failed && rename(fname, fname_old)) { - free(fname); - free(fname_old); - failed = 1; - break; - } else { - string_list_append(&rollback, fname); - free(fname_old); - } - } - if (failed) - break; - } - if (failed) { - struct string_list rollback_failure = STRING_LIST_INIT_DUP; - for_each_string_list_item(item, &rollback) { - char *fname, *fname_old; - fname = mkpathdup("%s/%s", packdir, item->string); - fname_old = mkpathdup("%s/old-%s", packdir, item->string); - if (rename(fname_old, fname)) - string_list_append(&rollback_failure, fname); - free(fname); - free(fname_old); - } - - if (rollback_failure.nr) { - int i; - fprintf(stderr, - _("WARNING: Some packs in use have been renamed by\n" - "WARNING: prefixing old- to their name, in order to\n" - "WARNING: replace them with the new version of the\n" - "WARNING: file. But the operation failed, and the\n" - "WARNING: attempt to rename them back to their\n" - "WARNING: original names also failed.\n" - "WARNING: Please rename them in %s manually:\n"), packdir); - for (i = 0; i < rollback_failure.nr; i++) - fprintf(stderr, "WARNING: old-%s -> %s\n", - rollback_failure.items[i].string, - rollback_failure.items[i].string); - } - exit(1); - } - - /* Now the ones with the same name are out of the way... */ - for_each_string_list_item(item, &names) { - for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname, *fname_old; - struct stat statbuffer; - int exists = 0; fname = mkpathdup("%s/pack-%s%s", packdir, item->string, exts[ext].name); fname_old = mkpathdup("%s-%s%s", packtmp, item->string, exts[ext].name); - if (!stat(fname_old, &statbuffer)) { - statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - chmod(fname_old, statbuffer.st_mode); - exists = 1; - } - if (exists || !exts[ext].optional) { + + if (((uintptr_t)item->util) & (1 << ext)) { + struct stat statbuffer; + if (!stat(fname_old, &statbuffer)) { + statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + chmod(fname_old, statbuffer.st_mode); + } + if (rename(fname_old, fname)) die_errno(_("renaming '%s' failed"), fname_old); - } - free(fname); - free(fname_old); - } - } + } else if (!exts[ext].optional) + die(_("missing required file: %s"), fname_old); + else if (unlink(fname) < 0 && errno != ENOENT) + die_errno(_("could not unlink: %s"), fname); - /* Remove the "old-" files */ - for_each_string_list_item(item, &names) { - for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname; - fname = mkpathdup("%s/old-%s%s", - packdir, - item->string, - exts[ext].name); - if (remove_path(fname)) - warning(_("failed to remove '%s'"), fname); free(fname); + free(fname_old); } } - /* End of pack replacement. */ reprepare_packed_git(the_repository); 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/command-list.txt b/command-list.txt index 581499be82..9379b02e5e 100644 --- a/command-list.txt +++ b/command-list.txt @@ -136,7 +136,6 @@ git-p4 foreignscminterface git-pack-objects plumbingmanipulators git-pack-redundant plumbinginterrogators git-pack-refs ancillarymanipulators -git-parse-remote synchelpers git-patch-id purehelpers git-prune ancillarymanipulators complete git-prune-packed plumbingmanipulators @@ -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/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), $_); } @_; } diff --git a/git-parse-remote.sh b/git-parse-remote.sh deleted file mode 100644 index d3c39980f3..0000000000 --- a/git-parse-remote.sh +++ /dev/null @@ -1,101 +0,0 @@ -# This is a shell library to calculate the remote repository and -# upstream branch that should be pulled by "git pull" from the current -# branch. - -# git-ls-remote could be called from outside a git managed repository; -# this would fail in that case and would issue an error message. -GIT_DIR=$(git rev-parse -q --git-dir) || :; - -get_default_remote () { - curr_branch=$(git symbolic-ref -q HEAD) - curr_branch="${curr_branch#refs/heads/}" - origin=$(git config --get "branch.$curr_branch.remote") - echo ${origin:-origin} -} - -get_remote_merge_branch () { - case "$#" in - 0|1) - origin="$1" - default=$(get_default_remote) - test -z "$origin" && origin=$default - curr_branch=$(git symbolic-ref -q HEAD) && - [ "$origin" = "$default" ] && - echo $(git for-each-ref --format='%(upstream)' $curr_branch) - ;; - *) - repo=$1 - shift - ref=$1 - # FIXME: It should return the tracking branch - # Currently only works with the default mapping - case "$ref" in - +*) - ref=$(expr "z$ref" : 'z+\(.*\)') - ;; - esac - expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:" - remote=$(expr "z$ref" : 'z\([^:]*\):') - case "$remote" in - '' | HEAD ) remote=HEAD ;; - heads/*) remote=${remote#heads/} ;; - refs/heads/*) remote=${remote#refs/heads/} ;; - refs/* | tags/* | remotes/* ) remote= - esac - [ -n "$remote" ] && case "$repo" in - .) - echo "refs/heads/$remote" - ;; - *) - echo "refs/remotes/$repo/$remote" - ;; - esac - esac -} - -error_on_missing_default_upstream () { - cmd="$1" - op_type="$2" - op_prep="$3" # FIXME: op_prep is no longer used - example="$4" - branch_name=$(git symbolic-ref -q HEAD) - display_branch_name="${branch_name#refs/heads/}" - # If there's only one remote, use that in the suggestion - remote="$(gettext "<remote>")" - branch="$(gettext "<branch>")" - if test $(git remote | wc -l) = 1 - then - remote=$(git remote) - fi - - if test -z "$branch_name" - then - gettextln "You are not currently on a branch." - else - gettextln "There is no tracking information for the current branch." - fi - case "$op_type" in - rebase) - gettextln "Please specify which branch you want to rebase against." - ;; - merge) - gettextln "Please specify which branch you want to merge with." - ;; - *) - echo >&2 "BUG: unknown operation type: $op_type" - exit 1 - ;; - esac - eval_gettextln "See git-\${cmd}(1) for details." - echo - echo " $example" - echo - if test -n "$branch_name" - then - gettextln "If you wish to set tracking information for this branch you can do so with:" - echo - echo " git branch --set-upstream-to=$remote/$branch $display_branch_name" - echo - fi - exit 1 -} diff --git a/git-submodule.sh b/git-submodule.sh index 7ce52872b7..eb90f18229 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -20,7 +20,6 @@ USAGE="[--quiet] [--cached] OPTIONS_SPEC= SUBDIRECTORY_OK=Yes . git-sh-setup -. git-parse-remote require_work_tree wt_prefix=$(git rev-parse --show-prefix) cd_to_toplevel @@ -413,16 +412,18 @@ is_tip_reachable () ( test -z "$rev" ) +# usage: fetch_in_submodule <module_path> [<depth>] [<sha1>] +# Because arguments are positional, use an empty string to omit <depth> +# but include <sha1>. fetch_in_submodule () ( sanitize_submodule_env && cd "$1" && - case "$2" in - '') - git fetch ;; - *) - shift - git fetch $(get_default_remote) "$@" ;; - esac + if test $# -eq 3 + then + echo "$3" | git fetch --stdin ${2:+"$2"} + else + git fetch ${2:+"$2"} + fi ) # @@ -576,7 +577,7 @@ cmd_update() fetch_in_submodule "$sm_path" $depth || die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" fi - remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote) + remote_name=$(sanitize_submodule_env; cd "$sm_path" && git submodule--helper print-default-remote) sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify "${remote_name}/${branch}") || die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")" @@ -602,7 +603,7 @@ cmd_update() # Now we tried the usual fetch, but $sha1 may # not be reachable from any of the refs is_tip_reachable "$sm_path" "$sha1" || - fetch_in_submodule "$sm_path" $depth "$sha1" || + fetch_in_submodule "$sm_path" "$depth" "$sha1" || die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain \$sha1. Direct fetching of that commit failed.")" fi @@ -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); 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" 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 eb4a44270b..9045f8a082 100644 --- a/send-pack.c +++ b/send-pack.c @@ -85,6 +85,7 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc po.in = -1; po.out = args->stateless_rpc ? -1 : fd; po.git_cmd = 1; + po.clean_on_exit = 1; if (start_command(&po)) die_errno("git pack-objects failed"); @@ -424,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; @@ -435,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; @@ -450,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")) @@ -506,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/submodule.c b/submodule.c index eef5204e64..b3bb59f066 100644 --- a/submodule.c +++ b/submodule.c @@ -499,6 +499,12 @@ void prepare_submodule_repo_env(struct strvec *out) DEFAULT_GIT_DIR_ENVIRONMENT); } +static void prepare_submodule_repo_env_in_gitdir(struct strvec *out) +{ + prepare_submodule_repo_env_no_git_dir(out); + strvec_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT); +} + /* * Initialize a repository struct for a submodule based on the provided 'path'. * @@ -1449,8 +1455,8 @@ static int get_next_submodule(struct child_process *cp, if (task->repo) { struct strbuf submodule_prefix = STRBUF_INIT; child_process_init(cp); - cp->dir = task->repo->worktree; - prepare_submodule_repo_env(&cp->env_array); + cp->dir = task->repo->gitdir; + prepare_submodule_repo_env_in_gitdir(&cp->env_array); cp->git_cmd = 1; if (!spf->quiet) strbuf_addf(err, _("Fetching submodule %s%s\n"), @@ -1499,9 +1505,9 @@ static int get_next_submodule(struct child_process *cp, spf->prefix, task->sub->path); child_process_init(cp); - prepare_submodule_repo_env(&cp->env_array); + prepare_submodule_repo_env_in_gitdir(&cp->env_array); cp->git_cmd = 1; - cp->dir = task->repo->worktree; + cp->dir = task->repo->gitdir; strvec_init(&cp->args); strvec_pushv(&cp->args, spf->args.v); 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/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index a7f6f9fdd6..dd8e423d25 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -719,67 +719,4 @@ test_expect_success 'fetch new submodule commit intermittently referenced by sup ) ' -add_commit_push () { - dir="$1" - msg="$2" - shift 2 - git -C "$dir" add "$@" && - git -C "$dir" commit -a -m "$msg" && - git -C "$dir" push -} - -compare_refs_in_dir () { - fail= && - if test "x$1" = 'x!' - then - fail='!' && - shift - fi && - git -C "$1" rev-parse --verify "$2" >expect && - git -C "$3" rev-parse --verify "$4" >actual && - eval $fail test_cmp expect actual -} - - -test_expect_success 'setup nested submodule fetch test' ' - # does not depend on any previous test setups - - for repo in outer middle inner - do - ( - git init --bare $repo && - git clone $repo ${repo}_content && - echo "$repo" >"${repo}_content/file" && - add_commit_push ${repo}_content "initial" file - ) || return 1 - done && - - git clone outer A && - git -C A submodule add "$pwd/middle" && - git -C A/middle/ submodule add "$pwd/inner" && - add_commit_push A/middle/ "adding inner sub" .gitmodules inner && - add_commit_push A/ "adding middle sub" .gitmodules middle && - - git clone outer B && - git -C B/ submodule update --init middle && - - compare_refs_in_dir A HEAD B HEAD && - compare_refs_in_dir A/middle HEAD B/middle HEAD && - test -f B/file && - test -f B/middle/file && - ! test -f B/middle/inner/file && - - echo "change on inner repo of A" >"A/middle/inner/file" && - add_commit_push A/middle/inner "change on inner" file && - add_commit_push A/middle "change on inner" inner && - add_commit_push A "change on inner" middle -' - -test_expect_success 'fetching a superproject containing an uninitialized sub/sub project' ' - # depends on previous test for setup - - git -C B/ fetch && - compare_refs_in_dir A origin/master B origin/master -' - test_done diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh index 1d75e3b12b..37fd06b0be 100755 --- a/t/t5572-pull-submodule.sh +++ b/t/t5572-pull-submodule.sh @@ -101,7 +101,12 @@ test_expect_success " --[no-]recurse-submodule and submodule.recurse" ' test_path_is_file super/sub/merge_strategy_4.t ' -test_expect_success 'recursive rebasing pull' ' +test_expect_success 'pull --rebase --recurse-submodules (remote superproject submodule changes, local submodule changes)' ' + # This tests the following scenario : + # - local submodule has new commits + # - local superproject does not have new commits + # - upstream superproject has new commits that change the submodule pointer + # change upstream test_commit -C child rebase_strategy && git -C parent submodule update --remote && @@ -116,7 +121,10 @@ test_expect_success 'recursive rebasing pull' ' test_path_is_file super/sub/local_stuff.t ' -test_expect_success 'pull rebase recursing fails with conflicts' ' +test_expect_success 'pull --rebase --recurse-submodules fails if both sides record submodule changes' ' + # This tests the following scenario : + # - local superproject has new commits that change the submodule pointer + # - upstream superproject has new commits that change the submodule pointer # local changes in submodule recorded in superproject: test_commit -C super/sub local_stuff_2 && @@ -136,6 +144,50 @@ test_expect_success 'pull rebase recursing fails with conflicts' ' test_i18ngrep "locally recorded submodule modifications" err ' +test_expect_success 'pull --rebase --recurse-submodules (no submodule changes, no fork-point)' ' + # This tests the following scenario : + # - local submodule does not have new commits + # - local superproject has new commits that *do not* change the submodule pointer + # - upstream superproject has new commits that *do not* change the submodule pointer + # - local superproject branch has no fork-point with its remote-tracking counter-part + + # create upstream superproject + test_create_repo submodule && + test_commit -C submodule first_in_sub && + + test_create_repo superprojet && + test_commit -C superprojet first_in_super && + git -C superprojet submodule add ../submodule && + git -C superprojet commit -m "add submodule" && + test_commit -C superprojet third_in_super && + + # clone superproject + git clone --recurse-submodules superprojet superclone && + + # add commits upstream + test_commit -C superprojet fourth_in_super && + + # create topic branch in clone, not based on any remote-tracking branch + git -C superclone checkout -b feat HEAD~1 && + test_commit -C superclone first_on_feat && + git -C superclone pull --rebase --recurse-submodules origin master +' + +# NOTE: +# +# This test is particular because there is only a single commit in the upstream superproject +# 'parent' (which adds the submodule 'a-submodule'). The clone of the superproject +# ('child') hard-resets its branch to a new root commit with the same tree as the one +# from the upstream superproject, so that its branch has no merge-base with its +# remote-tracking counterpart, and then calls 'git pull --recurse-submodules --rebase'. +# The result is that the local branch is reset to the remote-tracking branch (as it was +# originally before the hard-reset). + +# The only commit in the range generated by 'submodule.c::submodule_touches_in_range' and +# passed to 'submodule.c::collect_changed_submodules' is the new (regenerated) initial commit, +# which adds the submodule. +# However, 'submodule_touches_in_range' does not error (even though this commit adds the submodule) +# because 'combine-diff.c::diff_tree_combined' returns early, as the initial commit has no parents. test_expect_success 'branch has no merge base with remote-tracking counterpart' ' rm -rf parent child && 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..e987b44f87 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,6 +406,18 @@ 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 && @@ -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/test-lib-functions.sh b/t/test-lib-functions.sh index a791f2f1d6..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. @@ -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 5dc8e1f844..8abb7de5d7 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) @@ -321,6 +323,7 @@ static void create_pack_file(struct upload_pack_data *pack_data, pack_objects.in = -1; pack_objects.out = -1; pack_objects.err = -1; + pack_objects.clean_on_exit = 1; if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); @@ -1056,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) @@ -1108,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, @@ -1178,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) { @@ -1193,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) ? @@ -1205,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); } @@ -1299,6 +1319,8 @@ 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 && @@ -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); } |