diff --git a/.gitignore b/.gitignore index 24635cf2d6f4a3..e406d3741cdee2 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ /git-for-each-ref /git-for-each-repo /git-format-patch +/git-format-rev /git-fsck /git-fsck-objects /git-fsmonitor--daemon diff --git a/Documentation/RelNotes/2.55.0.adoc b/Documentation/RelNotes/2.55.0.adoc index 4f254547771505..8cf939d53638a6 100644 --- a/Documentation/RelNotes/2.55.0.adoc +++ b/Documentation/RelNotes/2.55.0.adoc @@ -21,6 +21,10 @@ UI, Workflows & Features branch, but it gave only one chance to resolve conflicts. The command was taught to create a stash to save the local changes. + * A new builtin "git format-rev" is introduced for pretty formatting + one revision expression per line or commit object names found in + running text. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -31,6 +35,8 @@ Performance, Internal Implementation, Development Support etc. * Rust support is enabled by default (but still allows opting out) in some future version of Git. + * Preparation of the xdiff/ codebase to work with Rust. + Fixes since v2.54 ----------------- @@ -76,6 +82,30 @@ Fixes since v2.54 disabling C11 language features, causing build failures.. (merge 0a6d29090c ps/clang-w-glibc-2.43-and-_Generic later to maint). + * The 'http.emptyAuth=auto' configuration now correctly attempts + Negotiate authentication before falling back to manual credentials. + This allows seamless Kerberos ticket-based authentication without + requiring users to explicitly set 'http.emptyAuth=true'. + (merge 4919938d28 mc/http-emptyauth-negotiate-fix later to maint). + + * Ramifications of turning off commit-graph has been documented a bit + more clearly. + (merge 48c855bb8f kh/doc-commit-graph later to maint). + + * "git rebase --update-refs", when used with an rebase.instructionFormat + with "%d" (describe) in it, tried to update local branch HEAD by + mistake, which has been corrected. + (merge 106b6885c7 ag/rebase-update-refs-limit-to-branches later to maint). + + * Tweak the way how sideband messages from remote are printed while + we talk with a remote repository to avoid tickling terminal + emulator glitches. + (merge 31e8fcabd8 rs/sideband-clear-line-before-print later to maint). + + * The configuration variable submodule.fetchJobs was not read correctly, + which has been corrected. + (merge aa45a5902f sj/submodule-update-clone-config-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 80f4b802e9 ja/doc-difftool-synopsis-style later to maint). (merge b96490241e jc/doc-timestamps-in-stat later to maint). @@ -83,3 +113,5 @@ Fixes since v2.54 (merge ef85286e51 ss/t7004-unhide-git-failures later to maint). (merge 7584d10bc2 mf/format-patch-cover-letter-format-docfix later to maint). (merge 8547908eb3 pw/rename-to-get-current-worktree later to maint). + (merge 890229b3f3 sg/t6112-unwanted-tilde-expansion-fix later to maint). + (merge ab9753e7bc kh/doc-restore-double-underscores-fix later to maint). diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc index 849c89f36c5ad8..792a71b41350d4 100644 --- a/Documentation/config/http.adoc +++ b/Documentation/config/http.adoc @@ -59,7 +59,18 @@ http.emptyAuth:: Attempt authentication without seeking a username or password. This can be used to attempt GSS-Negotiate authentication without specifying a username in the URL, as libcurl normally requires a username for - authentication. + authentication. Possible values are: ++ +-- +* `auto` (default) - Send empty credentials only if the server's 401 response + advertises an authentication mechanism that requires them (such as + GSS-Negotiate); otherwise fall back to prompting via the credential helper. +* `true` - Always send empty credentials on the very first request, before + receiving any 401 response from the server. +* `false` - Never send empty credentials. Mechanisms that require + empty credentials or an explicit username, such as GSS-Negotiate, will not + work. +-- http.proactiveAuth:: Attempt authentication without first making an unauthenticated attempt and diff --git a/Documentation/git-commit-graph.adoc b/Documentation/git-commit-graph.adoc index 6d19026035f96a..f2a37e91634442 100644 --- a/Documentation/git-commit-graph.adoc +++ b/Documentation/git-commit-graph.adoc @@ -146,6 +146,12 @@ $ git show-ref -s | git commit-graph write --stdin-commits $ git rev-parse HEAD | git commit-graph write --stdin-commits --append ------------------------------------------------ +CAVEATS +------- + +The existence of replace objects or commit grafts turns off reading or +writing to the commit-graph. See linkgit:git-replace[1]. + CONFIGURATION ------------- diff --git a/Documentation/git-format-rev.adoc b/Documentation/git-format-rev.adoc new file mode 100644 index 00000000000000..c40d52e9f6d108 --- /dev/null +++ b/Documentation/git-format-rev.adoc @@ -0,0 +1,215 @@ +git-format-rev(1) +================= + +NAME +---- +git-format-rev - EXPERIMENTAL: Pretty format revisions on demand + + +SYNOPSIS +-------- +[synopsis] +(EXPERIMENTAL!) git format-rev --stdin-mode= --format= [--[no-]notes=] [-z] [--[no-]null-output] [--[no-]null-input] + +DESCRIPTION +----------- + +Pretty format revisions from standard input. + +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. + +OPTIONS +------- + +`--stdin-mode=`:: + How to interpret standard input data: ++ +-- +`revs`;; Each line or record (see the <> + section) is interpreted as a commit. Any kind of revision + expression can be used (see linkgit:gitrevisions[7]). Annotated + tags are peeled (see linkgit:gitglossary[7]). ++ +The argument `rev` is also accepted. + +`text`;; Formats all commit object names found in freeform text. These + must the full object names, i.e. abbreviated hexidecimal object + names will not be interpreted. ++ +Anything that is parsed as an object name but that is not found to be a +commit object name is left alone (echoed). +-- + +`--format=`:: + Pretty format string. + +`--notes=`:: +`--no-notes`:: + Custom notes ref. Notes are displayed when using the `%N` + atom. See linkgit:git-notes[1]. + +`-z`:: +`--null`:: + Use _NUL_ character to terminate both input and output instead + of newline. This option cannot be negated. ++ +This is useful if both the input and output could contain newlines or if +the input to this command also uses _NUL_ character termination; see the +<> section below. ++ +The mode `--stdin-mode=text` can have use for this option when it needs +to process input like for example `git last-modified -z`; see the +<> section below. + +`--null-output`:: +`--no-null-output`:: + Use _NUL_ character to terminate output instead of newline. The + default is `--no-null-output`. ++ +This is useful if the output could contain newlines, for example if the +`%n` (newline) atom is used. + +`--null-input`:: +`--no-null-input`:: + Use _NUL_ character to terminate input instead of newline. The + default is `--no-null-input`. ++ +This is useful if the input revision expressions could contain newlines. + +[[io]] +INPUT AND OUTPUT FORMAT +----------------------- + +The command uses newlines for both input and output termination by +default. See the `-z`, `--null-output`, and `--null-input` options for +using _NUL_ character as the terminator. + +The mode `--stdin-mode=revs` outputs one formatted commit followed by +the terminator. This could either be called a _line_ or a _record_ in +case "line" is too suggestive of newline termination. + +Note that this means that the terminator character (newline or _NUL_) +acts as a _terminator_, not a _separator_. In other words, the final +line or record is also terminated by the terminator character. + +The mode `--stdin-mode=text` replaces each object name with the +formatted commit, i.e. the format `%s` would transform some commit +object name to `` without any termination. Like this: + +---- +Did we not fix this in ""? +---- + +It is safe to interactively read and write from this command since each +record is immediately flushed. + +[[examples]] +EXAMPLES +-------- + +The command linkgit:git-last-modified[1] shows the commit that each file +was last modified in. + +---- +$ git last-modified -- README.md Makefile +7798034171030be0909c56377a4e0e10e6d2df93 Makefile +c50fbb2dd225e7e82abba4380423ae105089f4d7 README.md +---- + +We can pipe the result to this command in order to replace the object +name with the commit author. + +---- +$ git last-modified -- README.md Makefile | + git format-rev --stdin-mode=text --format=%an +Junio C Hamano Makefile +Todd Zullinger README.md +---- + +Another example is _formatting commits in commit messages_. Given this commit message: + +---- +Fix off-by-one error + +Fix off-by-one error introduced in +e83c5163316f89bfbde7d9ab23ca2e25604af290. + +We thought we fixed this in 5569bf9bbedd63a00780fc5c110e0cfab3aa97b9 but +that only covered 1/3 of the faulty cases. +---- + +We can format the commits and use par(1) to reflow the text, say in a +`commit-msg` hook: + +---- +$ git config set hook.reference-commits.event commit-msg +$ git config set hook.reference-commits.command reference-commits +$ cat $(which reference-commits) +#/bin/sh + +msg="$1" +rewritten=$(mktemp) +git format-rev --stdin-mode=text --format=reference <"$msg" | + par >"$rewritten" +mv "$rewritten" "$msg" +---- + +Which will produce something like this: + +---- +Fix off-by-one error + +Fix off-by-one error introduced in e83c5163316 (Implement better memory +allocator, 2005-04-07). + +We thought we fixed this in 5569bf9bbed (Fix memory allocator, +2005-06-22) but that only covered 1/3 of the faulty cases. +---- + +DISCUSSION +---------- + +This command lets you format any number of revisions in any order +through one command invocation. Consider the +linkgit:git-last-modified[1] case from the <> section +above: + +1. There might be hundreds of files +2. Commits can be repeated, i.e. two or more files were last modified in + the same commit + +Two widely-used commands which pretty formats commits are +linkgit:git-log[1] and linkgit:git-show[1]. It turns out that they are +not a good fit for the above use case. + +- The output of linkgit:git-last-modified[1] would have to be processed + in stages since you need to transform the first column separately and + then link the author to the filename. But this is surmountable. +- You can feed each commit to `git show` or `git log --no-walk -1`. But + that means that you need to create a process for each line. +- Let’s say that you want to use one process, not one per line. So you + want to feed all the commits to the command. Now you face the problem + that you have to feed all the commits to the commands before you get + any output (this is also the case for the `--stdin` modes). In other + words, you cannot loop through each line, get the author for the + commit, and output the author and the filename. You need to feed all + the commits, get back all the output, and match the output with the + filename. +- But the next problem is that commands will deduplicate the input and + only output one commit one single time only. Thus you cannot make the + output order match the input order, since a commit could have been + repeated in the original input. + +In short, it is straightforward to use these two commands if you use one +process per line. It is much more work if you just want to use one +process, but still doable. In contrast, this problem is solved with just +another shell pipeline with this command. + +SEE ALSO +-------- +linkgit:git-name-rev[1], +linkgit:git-log[1]. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-replace.adoc b/Documentation/git-replace.adoc index 0a65460adbded5..436a0e58caf0af 100644 --- a/Documentation/git-replace.adoc +++ b/Documentation/git-replace.adoc @@ -145,6 +145,13 @@ commit instead of the replaced commit. There may be other problems when using 'git rev-list' related to pending objects. +CAVEATS +------- + +The existence of replace objects or commit grafts turns off reading or +writing to the commit-graph, which can cause performance issues. See +linkgit:git-commit-graph[1]. + SEE ALSO -------- linkgit:git-hash-object[1] diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 961eef01373938..7d1942316514c8 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -41,7 +41,7 @@ given, otherwise from the index. + As a special case, you may use `"..."` as a shortcut for the merge base of __ and __ if there is exactly one merge base. You can -leave out at most one of ___ and __, in which case it defaults to `HEAD`. +leave out at most one of __ and __, in which case it defaults to `HEAD`. `-p`:: `--patch`:: diff --git a/Documentation/meson.build b/Documentation/meson.build index d6365b888bbed3..58e7c6a0b8a280 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -55,6 +55,7 @@ manpages = { 'git-for-each-ref.adoc' : 1, 'git-for-each-repo.adoc' : 1, 'git-format-patch.adoc' : 1, + 'git-format-rev.adoc' : 1, 'git-fsck-objects.adoc' : 1, 'git-fsck.adoc' : 1, 'git-fsmonitor--daemon.adoc' : 1, diff --git a/Makefile b/Makefile index f86173f93a590c..c739ae78d0efe1 100644 --- a/Makefile +++ b/Makefile @@ -895,6 +895,7 @@ BUILT_INS += $(patsubst builtin/%.o,git-%$X,$(BUILTIN_OBJS)) BUILT_INS += git-cherry$X BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X +BUILT_INS += git-format-rev$X BUILT_INS += git-fsck-objects$X BUILT_INS += git-init$X BUILT_INS += git-maintenance$X diff --git a/builtin.h b/builtin.h index 235c51f30e5380..63813c90125757 100644 --- a/builtin.h +++ b/builtin.h @@ -189,6 +189,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix, struct re int cmd_for_each_ref(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_for_each_repo(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_format_patch(int argc, const char **argv, const char *prefix, struct repository *repo); +int cmd_format_rev(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_fsck(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_gc(int argc, const char **argv, const char *prefix, struct repository *repo); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index d6594ada53f49d..60cbbfb4b7d190 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -17,6 +17,10 @@ #include "commit-graph.h" #include "wildmatch.h" #include "mem-pool.h" +#include "pretty.h" +#include "revision.h" +#include "notes.h" +#include "write-or-die.h" /* * One day. See the 'name a rev shortly after epoch' test in t6120 when @@ -270,6 +274,43 @@ struct name_ref_data { struct string_list exclude_filters; }; +struct pretty_format { + struct pretty_print_context ctx; + struct userformat_want want; +}; + +enum command_type { + NAME_REV = 1, + FORMAT_REV = 2, +}; + +enum stdin_mode { + TEXT = 1, + REVS = 2, +}; + +struct command { + enum command_type type; + union { + int name_only; + struct pretty_format *pretty_format; + } u; +}; + +static void init_name_rev_command(struct command *cmd, + int name_only) +{ + cmd->type = NAME_REV; + cmd->u.name_only = name_only; +} + +static void init_format_rev_command(struct command *cmd, + struct pretty_format *pretty_format) +{ + cmd->type = FORMAT_REV; + cmd->u.pretty_format = pretty_format; +} + static struct tip_table { struct tip_table_entry { struct object_id oid; @@ -464,9 +505,9 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf) if (!n) return NULL; - if (!n->generation) + if (!n->generation) { return n->tip_name; - else { + } else { strbuf_reset(buf); strbuf_addstr(buf, n->tip_name); strbuf_strip_suffix(buf, "^0"); @@ -475,6 +516,27 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf) } } +static const char *get_format_rev(const struct commit *c, + struct pretty_format *format_ctx, + struct strbuf *buf) +{ + strbuf_reset(buf); + + if (format_ctx->want.notes) { + struct strbuf notebuf = STRBUF_INIT; + + format_display_notes(&c->object.oid, ¬ebuf, + get_log_output_encoding(), + format_ctx->ctx.fmt == CMIT_FMT_USERFORMAT); + format_ctx->ctx.notes_message = strbuf_detach(¬ebuf, NULL); + } + + pretty_print_commit(&format_ctx->ctx, c, buf); + FREE_AND_NULL(format_ctx->ctx.notes_message); + + return buf->buf; +} + static void show_name(const struct object *obj, const char *caller_name, int always, int allow_undefined, int name_only) @@ -505,7 +567,7 @@ static char const * const name_rev_usage[] = { NULL }; -static void name_rev_line(char *p, struct name_ref_data *data) +static void name_rev_line(char *p, struct command *cmd) { struct strbuf buf = STRBUF_INIT; int counter = 0; @@ -514,33 +576,52 @@ static void name_rev_line(char *p, struct name_ref_data *data) for (p_start = p; *p; p++) { #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) - if (!ishex(*p)) + if (!ishex(*p)) { counter = 0; - else if (++counter == hexsz && - !ishex(*(p+1))) { + } else if (++counter == hexsz && + !ishex(*(p + 1))) { struct object_id oid; const char *name = NULL; - char c = *(p+1); + char c = *(p + 1); int p_len = p - p_start + 1; + struct object *o = NULL; + int oid_ret = 1; counter = 0; - *(p+1) = 0; - if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) { - struct object *o = - lookup_object(the_repository, &oid); + *(p + 1) = 0; + oid_ret = repo_get_oid(the_repository, p - (hexsz - 1), &oid); + *(p + 1) = c; + + switch (cmd->type) { + case NAME_REV: + if (!oid_ret) + o = lookup_object(the_repository, &oid); if (o) name = get_rev_name(o, &buf); + if (!name) + continue; + if (cmd->u.name_only) + printf("%.*s%s", p_len - hexsz, p_start, name); + else + printf("%.*s (%s)", p_len, p_start, name); + break; + case FORMAT_REV: + if (!oid_ret) + o = parse_object(the_repository, &oid); + if (o && o->type == OBJ_COMMIT) + name = get_format_rev((const struct commit *)o, + cmd->u.pretty_format, + &buf); + if (name) + printf("%.*s%s", p_len - hexsz, p_start, name); + else + printf("%.*s", p_len, p_start); + break; + default: + BUG("uncovered case: %d", cmd->type); } - *(p+1) = c; - if (!name) - continue; - - if (data->name_only) - printf("%.*s%s", p_len - hexsz, p_start, name); - else - printf("%.*s (%s)", p_len, p_start, name); p_start = p + 1; } } @@ -565,13 +646,14 @@ int cmd_name_rev(int argc, #endif int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP }; + struct command cmd; struct option opts[] = { OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")), OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")), OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"), - N_("only use refs matching ")), + N_("only use refs matching ")), OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"), - N_("ignore refs matching ")), + N_("ignore refs matching ")), OPT_GROUP(""), OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), #ifndef WITH_BREAKING_CHANGES @@ -583,10 +665,10 @@ int cmd_name_rev(int argc, #endif /* WITH_BREAKING_CHANGES */ OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")), OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), - OPT_BOOL(0, "always", &always, - N_("show abbreviated commit object as fallback")), + OPT_BOOL(0, "always", &always, + N_("show abbreviated commit object as fallback")), OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag, - N_("dereference tags in the input (internal use)")), + N_("dereference tags in the input (internal use)")), OPT_END(), }; @@ -594,6 +676,7 @@ int cmd_name_rev(int argc, init_commit_rev_name(&rev_names); repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); + init_name_rev_command(&cmd, data.name_only); #ifndef WITH_BREAKING_CHANGES if (transform_stdin) { @@ -661,7 +744,7 @@ int cmd_name_rev(int argc, while (strbuf_getline(&sb, stdin) != EOF) { strbuf_addch(&sb, '\n'); - name_rev_line(sb.buf, &data); + name_rev_line(sb.buf, &cmd); } strbuf_release(&sb); } else if (all) { @@ -688,3 +771,170 @@ int cmd_name_rev(int argc, object_array_clear(&revs); return 0; } + +struct format_nul_data { + bool nul_input; + bool nul_output; +}; + +static int format_nul_cb(const struct option *option, + const char *arg, + int unset) +{ + struct format_nul_data *data = option->value; + data->nul_input = 1; + data->nul_output = 1; + BUG_ON_OPT_NEG(unset); + BUG_ON_OPT_ARG(arg); + return 0; +} + +static enum stdin_mode parse_stdin_mode(const char *stdin_mode) +{ + if (!strcmp(stdin_mode, "text")) + return TEXT; + else if (!strcmp(stdin_mode, "revs") || + !strcmp(stdin_mode, "rev")) + return REVS; + else + die(_("'%s' needs to be either text, revs, or rev"), + "--stdin-mode"); +} + +static char const *const format_rev_usage[] = { + N_("(EXPERIMENTAL!) git format-rev --stdin-mode= " + "--format= [--[no-]notes=] " + "[-z] [--[no-]null-output] [--[no-]null-input]"), + NULL +}; + +int cmd_format_rev(int argc, + const char **argv, + const char *prefix, + struct repository *repo UNUSED) +{ + const char *format = NULL; + enum stdin_mode stdin_mode; + const char *stdin_mode_arg = NULL; + struct format_nul_data nul_data = { 0, 0 }; + char output_terminator; + strbuf_getline_fn getline_fn; + struct display_notes_opt format_notes_opt; + struct rev_info format_rev = REV_INFO_INIT; + struct pretty_format format_pp = { 0 }; + struct string_list notes = STRING_LIST_INIT_NODUP; + struct strbuf scratch_buf = STRBUF_INIT; + struct command cmd; + struct option opts[] = { + OPT_STRING(0, "format", &format, N_("format"), + N_("pretty format to use")), + OPT_STRING(0, "stdin-mode", &stdin_mode_arg, N_("stdin-mode"), + N_("how revs are processed")), + OPT_STRING_LIST(0, "notes", ¬es, N_("notes"), + N_("display notes for pretty format")), + OPT_CALLBACK_F('z', "null", &nul_data, N_("z"), + N_("Use NUL for input and output termination"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb), + OPT_BOOL(0, "null-input", &nul_data.nul_input, + N_("Use NUL for input termination")), + OPT_BOOL(0, "null-output", &nul_data.nul_output, + N_("Use NUL for output termination")), + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, opts, format_rev_usage, 0); + + if (argc > 0) { + error(_("too many arguments")); + usage_with_options(format_rev_usage, opts); + } + + if (!format) + die(_("'%s' is required"), "--format"); + if (!stdin_mode_arg) + die(_("'%s' is required"), "--stdin-mode"); + + getline_fn = nul_data.nul_input ? strbuf_getline_nul : strbuf_getline_lf; + output_terminator = nul_data.nul_output ? '\0' : '\n'; + + init_display_notes(&format_notes_opt); + stdin_mode = parse_stdin_mode(stdin_mode_arg); + + get_commit_format(format, &format_rev); + format_pp.ctx.rev = &format_rev; + format_pp.ctx.fmt = format_rev.commit_format; + format_pp.ctx.abbrev = format_rev.abbrev; + format_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit; + format_pp.ctx.date_mode = format_rev.date_mode; + format_pp.ctx.color = GIT_COLOR_AUTO; + + userformat_find_requirements(format, + &format_pp.want); + if (format_pp.want.notes) { + int ignore_show_notes = 0; + struct string_list_item *n; + + for_each_string_list_item(n, ¬es) + enable_ref_display_notes(&format_notes_opt, + &ignore_show_notes, + n->string); + load_display_notes(&format_notes_opt); + } + + init_format_rev_command(&cmd, &format_pp); + + switch (stdin_mode) { + case TEXT: + while (getline_fn(&scratch_buf, stdin) != EOF) { + name_rev_line(scratch_buf.buf, &cmd); + /* + * We do not pass on the terminator to name_rev_line, + * unlike name-rev. + */ + printf("%c", output_terminator); + maybe_flush_or_die(stdout, "stdout"); + } + break; + case REVS: + while (getline_fn(&scratch_buf, stdin) != EOF) { + struct object_id oid; + struct object *object; + struct object *peeled; + + if (repo_get_oid(the_repository, scratch_buf.buf, &oid)) { + fprintf(stderr, "Could not get object name for %s. Skipping.\n", + scratch_buf.buf); + continue; + } + + object = parse_object(the_repository, &oid); + if (!object) { + fprintf(stderr, "Could not get object for %s. Skipping.\n", + scratch_buf.buf); + continue; + } + + peeled = deref_tag(the_repository, object, scratch_buf.buf, 0); + if (!peeled || peeled->type != OBJ_COMMIT) { + fprintf(stderr, + "Could not get commit for %s. Skipping.\n", + scratch_buf.buf); + continue; + } + + get_format_rev((struct commit *)peeled, + &format_pp, &scratch_buf); + printf("%s%c", scratch_buf.buf, output_terminator); + maybe_flush_or_die(stdout, "stdout"); + strbuf_release(&scratch_buf); + } + break; + default: + BUG("uncovered case: %d", stdin_mode); + } + + strbuf_release(&scratch_buf); + string_list_clear(¬es, 0); + release_display_notes(&format_notes_opt); + return 0; +} diff --git a/command-list.txt b/command-list.txt index f9005cf45979f1..df729872dca37c 100644 --- a/command-list.txt +++ b/command-list.txt @@ -108,6 +108,7 @@ git-fmt-merge-msg purehelpers git-for-each-ref plumbinginterrogators git-for-each-repo plumbinginterrogators git-format-patch mainporcelain +git-format-rev plumbinginterrogators git-fsck ancillaryinterrogators complete git-gc mainporcelain git-get-tar-commit-id plumbinginterrogators diff --git a/git.c b/git.c index 5a40eab8a26a66..1f2a0864e7f019 100644 --- a/git.c +++ b/git.c @@ -578,6 +578,7 @@ static struct cmd_struct commands[] = { { "for-each-ref", cmd_for_each_ref, RUN_SETUP }, { "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY }, { "format-patch", cmd_format_patch, RUN_SETUP }, + { "format-rev", cmd_format_rev, RUN_SETUP }, { "fsck", cmd_fsck, RUN_SETUP }, { "fsck-objects", cmd_fsck, RUN_SETUP }, { "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP }, diff --git a/http.c b/http.c index 67c9c6fc60673d..412f7af2504b26 100644 --- a/http.c +++ b/http.c @@ -138,6 +138,7 @@ static unsigned long empty_auth_useless = CURLAUTH_BASIC | CURLAUTH_DIGEST_IE | CURLAUTH_DIGEST; +static int empty_auth_try_negotiate; static struct curl_slist *pragma_header; static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; @@ -665,6 +666,22 @@ static void init_curl_http_auth(CURL *result) } } +void http_reauth_prepare(int all_capabilities) +{ + /* + * If we deferred stripping Negotiate to give empty auth a + * chance (auto mode), skip credential_fill on this retry so + * that init_curl_http_auth() sends empty credentials and + * libcurl can attempt Negotiate with the system ticket cache. + */ + if (empty_auth_try_negotiate && + !http_auth.password && !http_auth.credential && + (http_auth_methods & CURLAUTH_GSSNEGOTIATE)) + return; + + credential_fill(the_repository, &http_auth, all_capabilities); +} + /* *var must be free-able */ static void var_override(char **var, char *value) { @@ -1890,7 +1907,18 @@ static int handle_curl_result(struct slot_results *results) http_proactive_auth = PROACTIVE_AUTH_NONE; return HTTP_NOAUTH; } else { - http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (curl_empty_auth == -1 && + !empty_auth_try_negotiate && + (results->auth_avail & CURLAUTH_GSSNEGOTIATE)) { + /* + * In auto mode, give Negotiate a chance via + * empty auth before stripping it. If it fails, + * we will strip it on the next 401. + */ + empty_auth_try_negotiate = 1; + } else { + http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + } if (results->auth_avail) { http_auth_methods &= results->auth_avail; http_auth_methods_restricted = 1; @@ -2398,7 +2426,7 @@ static int http_request_recoverable(const char *url, sleep(retry_delay); } } else if (ret == HTTP_REAUTH) { - credential_fill(the_repository, &http_auth, 1); + http_reauth_prepare(1); } ret = http_request(url, result, target, options); diff --git a/http.h b/http.h index f9ee888c3ed67e..729c51904d39ad 100644 --- a/http.h +++ b/http.h @@ -76,6 +76,12 @@ extern int http_is_verbose; extern ssize_t http_post_buffer; extern struct credential http_auth; +/** + * Prepare for an HTTP re-authentication retry. This fills credentials + * via credential_fill() so the next request can include them. + */ +void http_reauth_prepare(int all_capabilities); + extern char curl_errorstr[CURL_ERROR_SIZE]; enum http_follow_config { diff --git a/remote-curl.c b/remote-curl.c index aba60d571282d3..affdb880f7b3bf 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -946,7 +946,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece do { err = probe_rpc(rpc, &results); if (err == HTTP_REAUTH) - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; @@ -1068,7 +1068,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece rpc->any_written = 0; err = run_slot(slot, NULL); if (err == HTTP_REAUTH && !large_request) { - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); curl_slist_free_all(headers); goto retry; } diff --git a/sequencer.c b/sequencer.c index 746f85a44228b4..cc6137ebed5f49 100644 --- a/sequencer.c +++ b/sequencer.c @@ -6460,8 +6460,14 @@ static int add_decorations_to_list(const struct commit *commit, /* * If the branch is the current HEAD, then it will be * updated by the default rebase behavior. + * Exclude it from the list of refs to update, + * as well as any non-branch decorations. + * Non-branch decorations may be present if the pretty format + * includes "%d", which would have loaded all refs + * into the global decoration table. */ - if (head_ref && !strcmp(head_ref, decoration->name)) { + if ((head_ref && !strcmp(head_ref, decoration->name)) || + (decoration->type != DECORATION_REF_LOCAL)) { decoration = decoration->next; continue; } diff --git a/sideband.c b/sideband.c index 04282a568edd90..1523a53e1d781d 100644 --- a/sideband.c +++ b/sideband.c @@ -295,7 +295,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n) #define DISPLAY_PREFIX "remote: " -#define ANSI_SUFFIX "\033[K" +#define ANSI_PREFIX "\033[K" #define DUMB_SUFFIX " " int demultiplex_sideband(const char *me, int status, @@ -304,15 +304,18 @@ int demultiplex_sideband(const char *me, int status, struct strbuf *scratch, enum sideband_type *sideband_type) { - static const char *suffix; + static const char *prefix, *suffix; const char *b, *brk; int band; if (!suffix) { - if (isatty(2) && !is_terminal_dumb()) - suffix = ANSI_SUFFIX; - else + if (isatty(2) && !is_terminal_dumb()) { + prefix = ANSI_PREFIX DISPLAY_PREFIX; + suffix = ""; + } else { + prefix = DISPLAY_PREFIX; suffix = DUMB_SUFFIX; + } } if (status == PACKET_READ_EOF) { @@ -346,8 +349,7 @@ int demultiplex_sideband(const char *me, int status, case 3: if (die_on_error) die(_("remote error: %s"), buf + 1); - strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", - DISPLAY_PREFIX); + strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", prefix); maybe_colorize_sideband(scratch, buf + 1, len); *sideband_type = SIDEBAND_REMOTE_ERROR; @@ -378,7 +380,7 @@ int demultiplex_sideband(const char *me, int status, strbuf_addstr(scratch, suffix); if (!scratch->len) - strbuf_addstr(scratch, DISPLAY_PREFIX); + strbuf_addstr(scratch, prefix); /* * A use case that we should not add clear-to-eol suffix @@ -404,8 +406,8 @@ int demultiplex_sideband(const char *me, int status, } if (*b) { - strbuf_addstr(scratch, scratch->len ? - "" : DISPLAY_PREFIX); + if (!scratch->len) + strbuf_addstr(scratch, prefix); maybe_colorize_sideband(scratch, b, strlen(b)); } return 0; diff --git a/submodule-config.c b/submodule-config.c index 72a46b7a54bc3d..a81897b4e069b1 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -1038,5 +1038,5 @@ static int gitmodules_update_clone_config(const char *var, const char *value, void update_clone_config_from_gitmodules(int *max_jobs) { - config_from_gitmodules(gitmodules_update_clone_config, the_repository, &max_jobs); + config_from_gitmodules(gitmodules_update_clone_config, the_repository, max_jobs); } diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh index e1d35170de61a3..c557f2f55c2175 100755 --- a/t/t1517-outside-repo.sh +++ b/t/t1517-outside-repo.sh @@ -122,7 +122,8 @@ do archimport | citool | credential-netrc | credential-libsecret | \ credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \ daemon | \ - difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \ + difftool--helper | filter-branch | format-rev | fsck-objects | \ + get-tar-commit-id | \ gui | gui--askpass | \ http-backend | http-fetch | http-push | init-db | \ merge-octopus | merge-one-file | merge-resolve | mergetool | \ diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 3e44562afaab40..58b3bb0c271aae 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1960,6 +1960,24 @@ test_expect_success '--update-refs adds commands with --rebase-merges' ' ) ' +test_expect_success '--update-refs ignores non-branch decorations' ' + test_when_finished "git branch -D update-refs" && + test_when_finished "git checkout primary" && + git checkout -B update-refs no-conflict-branch && + ( + set_cat_todo_editor && + + # rebase.instructionFormat=%d loads normal log decorations before + # --update-refs adds its branch placeholders so we must ignore + # all non-local decorations. + test_must_fail git -c rebase.instructionFormat="%s%d" \ + rebase -i --update-refs HEAD^ >todo + ) && + grep ^update-ref todo >actual && + test_write_lines "update-ref refs/heads/no-conflict-branch" >expect && + test_cmp expect actual +' + test_expect_success '--update-refs updates refs correctly' ' git checkout -B update-refs no-conflict-branch && git branch -f base HEAD~4 && diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index 00635816156ba3..a7d475dd68dbd7 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -719,4 +719,78 @@ test_expect_success 'access using three-legged auth' ' EOF ' +test_lazy_prereq SPNEGO 'curl --version | grep -qi "SPNEGO\|GSS-API\|Kerberos\|negotiate"' + +test_expect_success SPNEGO 'http.emptyAuth=auto attempts Negotiate before credential_fill' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-auto" \ + git -c http.emptyAuth=auto \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # In auto mode with a Negotiate+Basic server, there should be + # three 401 responses: (1) initial no-auth request, (2) empty-auth + # retry where Negotiate fails (no Kerberos ticket), (3) libcurl + # internal Negotiate retry. The fourth attempt uses Basic + # credentials from credential_fill and succeeds. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-auto" >actual_401s && + test_line_count = 3 actual_401s && + + expect_credential_query get <<-EOF + capability[]=authtype + capability[]=state + protocol=http + host=$HTTPD_DEST + wwwauth[]=Negotiate + wwwauth[]=Basic realm="example.com" + EOF +' + +test_expect_success SPNEGO 'http.emptyAuth=false skips Negotiate' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-false" \ + git -c http.emptyAuth=false \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # With emptyAuth=false, Negotiate is stripped immediately and + # credential_fill is called right away. Only one 401 response. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-false" >actual_401s && + test_line_count = 1 actual_401s +' + test_done diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index 39211ef989cc58..e0a825bccfdfa7 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -623,9 +623,9 @@ test_expect_success 'verify collecting omits in combined: filter' ' omitted_2=$(echo a | git hash-object --stdin) && omitted_3=$(echo abcde | git hash-object --stdin) && - grep ~$omitted_1 actual && - grep ~$omitted_2 actual && - grep ~$omitted_3 actual && + grep "~$omitted_1" actual && + grep "~$omitted_2" actual && + grep "~$omitted_3" actual && test_line_count = 3 actual ' diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 2c70cc561ad5f6..8ee3d2c37d0253 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -298,6 +298,20 @@ test_expect_success 'name-rev --annotate-stdin' ' test_cmp expect actual ' +test_expect_success 'name-rev --annotate-stdin --name-only' ' + >expect.unsorted && + for rev in $(git rev-list --all) + do + name=$(git name-rev --name-only $rev) && + echo "$name" >>expect.unsorted || return 1 + done && + sort expect && + git name-rev --annotate-stdin --name-only \ + actual.unsorted && + sort actual && + test_cmp expect actual +' + test_expect_success 'name-rev --stdin deprecated' ' git rev-list --all >list && if ! test_have_prereq WITH_BREAKING_CHANGES @@ -787,4 +801,198 @@ test_expect_success 'do not be fooled by invalid describe format ' ' test_must_fail git cat-file -t "refs/tags/super-invalid/./../...../ ~^:/?*[////\\\\\\&}/busted.lock-42-g"$(cat out) ' +test_expect_success 'setup: format-rev' ' + mkdir repo-format && + git -C repo-format init && + test_commit -C repo-format first && + test_commit -C repo-format second && + test_commit -C repo-format third && + test_commit -C repo-format fourth && + test_commit -C repo-format fifth && + test_commit -C repo-format sixth && + test_commit -C repo-format seventh && + test_commit -C repo-format eighth +' + +test_expect_success 'format-rev --stdin-mode=revs' ' + cat >expect <<-\EOF && + eighth + seventh + fifth + EOF + git -C repo-format format-rev --stdin-mode=revs \ + --format=%s >actual <<-\EOF && + HEAD + HEAD~ + HEAD~3 + EOF + test_cmp expect actual +' + +test_expect_success 'format-rev --stdin-mode=text from rev-list same as log' ' + git -C repo-format log --format=reference >expect && + test_file_not_empty expect && + git -C repo-format rev-list HEAD >list && + git -C repo-format format-rev --stdin-mode=text \ + --format=reference actual && + test_cmp expect actual +' + +test_expect_success 'format-rev --stdin-mode=text with running text and tree oid' ' + cmit_oid=$(git -C repo-format rev-parse fifth) && + reference=$(git -C repo-format log -n1 --format=reference fifth) && + tree=$(git -C repo-format rev-parse HEAD^{tree}) && + cat >expect <<-EOF && + We thought we fixed this in ${reference}. + But look at this tree: ${tree}. + EOF + git -C repo-format format-rev --stdin-mode=text --format=reference \ + >actual <<-EOF && + We thought we fixed this in ${cmit_oid}. + But look at this tree: ${tree}. + EOF + test_cmp expect actual +' + +test_expect_success 'format-rev with %N (note)' ' + test_when_finished "git -C repo-format notes remove" && + git -C repo-format notes add -m"Make a note" && + printf "Make a note\n\n\n" >expect && + git -C repo-format format-rev --stdin-mode=revs \ + --format="tformat:%N" \ + >actual <<-\EOF && + HEAD + HEAD~ + EOF + test_cmp expect actual +' + +test_expect_success 'format-rev --notes (custom notes ref)' ' + # One custom notes ref + test_when_finished "git -C repo-format notes remove" && + test_when_finished "git -C repo-format notes --ref=word remove" && + git -C repo-format notes add -m"default" && + git -C repo-format notes --ref=word add -m"custom" && + printf "custom\n\n" >expect && + git -C repo-format format-rev --stdin-mode=revs \ + --format="tformat:%N" \ + --notes=word \ + >actual <<-\EOF && + HEAD + EOF + test_cmp expect actual && + # Glob all + printf "default\ncustom\n\n" >expect && + git -C repo-format format-rev --stdin-mode=revs \ + --format="tformat:%N" \ + --notes=* >actual <<-\EOF && + HEAD + EOF + test_cmp expect actual +' + +test_expect_success 'format-rev --stdin-mode=revs on annotated tag peels to commit' ' + test_when_finished "git -C repo-format tag -d version" && + git -C repo-format tag -a -m"new version" version && + cat >expect <<-\EOF && + eighth + EOF + git -C repo-format format-rev --stdin-mode=revs \ + --format=%s \ + >actual <<-\EOF && + version + EOF + test_cmp expect actual +' + +test_expect_success 'format-rev --stdin-mode=revs lookup failures' ' + test_when_finished "git -C repo-format tag -d tag-to-tree" && + invalid_syntax=not-valid && + non_existing_oid=${EMPTY_BLOB} && + tree=$(git -C repo-format rev-parse eighth^{tree}) && + git -C repo-format tag -a -mmessage tag-to-tree "$tree" && + tag_to_tree=$(git -C repo-format rev-parse tag-to-tree) && + cat >expect <<-EOF && + Could not get object name for ${invalid_syntax}. Skipping. + Could not get object for ${non_existing_oid}. Skipping. + Could not get commit for ${tree}. Skipping. + Could not get commit for ${tag_to_tree}. Skipping. + EOF + git -C repo-format format-rev --stdin-mode=revs \ + --format=%s \ + 2>actual >out <<-EOF && + ${invalid_syntax} + ${non_existing_oid} + ${tree} + ${tag_to_tree} + EOF + test_line_count = 0 out && + test_cmp expect actual +' + + +test_expect_success 'format-rev -z --stdin-mode=text with object name lookup failures' ' + printf "%s\0" "$(git -C repo-format rev-parse HEAD)" >input && + printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>input && + printf "%s\0" "$EMPTY_BLOB" >>input && + printf "%s\0" "$(git -C repo-format log --format=%s -1)" >expect && + printf "%s\0" "$(git -C repo-format rev-parse HEAD^{tree})" >>expect && + printf "%s\0" "$EMPTY_BLOB" >>expect && + git -C repo-format format-rev --stdin-mode=text \ + --format=%s -z actual && + test_cmp expect actual +' + +test_expect_success 'setup: format-rev input and output separators' ' + git -C repo-format rev-list HEAD >input-lf && + git -C repo-format rev-list -z HEAD >input-nul && + git -C repo-format log --format=%s >output-lf && + git -C repo-format log -z --format=%s >output-nul && + echo revs >stdin-modes && + echo text >>stdin-modes +' + +while read mode +do + test_expect_success "format-rev -z --stdin-mode=$mode" ' + cat output-nul >expect && + git -C repo-format format-rev --stdin-mode="$mode" \ + --format=%s -z actual && + test_cmp expect actual + ' + + test_expect_success "format-rev -z --no-null-input --no-null-output --stdin-mode=$mode" ' + cat output-lf >expect && + git -C repo-format format-rev --stdin-mode="$mode" \ + --format=%s -z --no-null-input --no-null-output \ + actual && + test_cmp expect actual + ' + + test_expect_success "format-rev ---null-input --stdin-mode=$mode" ' + cat output-lf >expect && + git -C repo-format format-rev --stdin-mode="$mode" \ + --format=%s --null-input \ + actual && + test_cmp expect actual + ' + + test_expect_success "format-rev --null-output --stdin-mode=$mode" ' + cat output-nul >expect && + git -C repo-format format-rev --stdin-mode="$mode" \ + --format=%s --null-output \ + actual && + test_cmp expect actual + ' + + test_expect_success "format-rev -z --stdin-mode=$mode with multi-line output" ' + format="%s%n%aI" && + git -C repo-format log -z --format="$format" \ + >expect && + git -C repo-format format-rev --stdin-mode="$mode" \ + --format="$format" -z actual && + test_cmp expect actual + ' +done xdf2.nreff + 1; kvdb += xe->xdf2.nreff + 1; - xenv.mxcost = xdl_bogosqrt(ndiags); + xenv.mxcost = (long)xdl_bogosqrt((uint64_t)ndiags); if (xenv.mxcost < XDL_MAX_COST_MIN) xenv.mxcost = XDL_MAX_COST_MIN; xenv.snake_cnt = XDL_SNAKE_CNT; diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c index cd4fc405eb18fe..beef711067b612 100644 --- a/xdiff/xprepare.c +++ b/xdiff/xprepare.c @@ -197,8 +197,8 @@ void xdl_free_env(xdfenv_t *xe) { } -static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) { - long r, rdis0, rpdis0, rdis1, rpdis1; +static bool xdl_clean_mmatch(uint8_t const *action, ptrdiff_t i, ptrdiff_t s, ptrdiff_t e) { + ptrdiff_t r, rdis0, rpdis0, rdis1, rpdis1; /* * Limits the window that is examined during the similar-lines @@ -268,8 +268,7 @@ static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) { * might be potentially discarded if they appear in a run of discardable. */ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { - long i, nm, mlim; - xrecord_t *recs; + ptrdiff_t i, nm, mlim1, mlim2; xdlclass_t *rcrec; uint8_t *action1 = NULL, *action2 = NULL; bool need_min = !!(cf->flags & XDF_NEED_MINIMAL); @@ -291,20 +290,44 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd /* * Initialize temporary arrays with DISCARD, KEEP, or INVESTIGATE. */ - if ((mlim = xdl_bogosqrt((long)xdf1->nrec)) > XDL_MAX_EQLIMIT) - mlim = XDL_MAX_EQLIMIT; - for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) { - rcrec = cf->rcrecs[recs->minimal_perfect_hash]; + if (need_min) { + /* i.e. infinity */ + mlim1 = PTRDIFF_MAX; + } else { + mlim1 = xdl_bogosqrt((uint64_t)xdf1->nrec); + if (mlim1 > XDL_MAX_EQLIMIT) + mlim1 = XDL_MAX_EQLIMIT; + } + for (i = xdf1->dstart; i <= xdf1->dend; i++) { + size_t mph1 = xdf1->recs[i].minimal_perfect_hash; + rcrec = cf->rcrecs[mph1]; nm = rcrec ? rcrec->len2 : 0; - action1[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP; + if (nm == 0) + action1[i] = DISCARD; + else if (nm < mlim1) + action1[i] = KEEP; + else /* nm >= mlim1 */ + action1[i] = INVESTIGATE; } - if ((mlim = xdl_bogosqrt((long)xdf2->nrec)) > XDL_MAX_EQLIMIT) - mlim = XDL_MAX_EQLIMIT; - for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) { - rcrec = cf->rcrecs[recs->minimal_perfect_hash]; + if (need_min) { + /* i.e. infinity */ + mlim2 = PTRDIFF_MAX; + } else { + mlim2 = xdl_bogosqrt((uint64_t)xdf2->nrec); + if (mlim2 > XDL_MAX_EQLIMIT) + mlim2 = XDL_MAX_EQLIMIT; + } + for (i = xdf2->dstart; i <= xdf2->dend; i++) { + size_t mph2 = xdf2->recs[i].minimal_perfect_hash; + rcrec = cf->rcrecs[mph2]; nm = rcrec ? rcrec->len1 : 0; - action2[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP; + if (nm == 0) + action2[i] = DISCARD; + else if (nm < mlim2) + action2[i] = KEEP; + else /* nm >= mlim2 */ + action2[i] = INVESTIGATE; } /* @@ -312,27 +335,45 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd * false, or become true. */ xdf1->nreff = 0; - for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; - i <= xdf1->dend; i++, recs++) { - if (action1[i] == KEEP || - (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) { + for (i = xdf1->dstart; i <= xdf1->dend; i++) { + uint8_t action = action1[i]; + + if (action == INVESTIGATE) { + if (!xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend)) + action = KEEP; + else + action = DISCARD; + } + + if (action == KEEP) { xdf1->reference_index[xdf1->nreff++] = i; - /* changed[i] remains false, i.e. keep */ - } else + /* changed[i] remains false */ + } else if (action == DISCARD) { xdf1->changed[i] = true; - /* i.e. discard */ + } else { + BUG("Illegal state for action"); + } } xdf2->nreff = 0; - for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; - i <= xdf2->dend; i++, recs++) { - if (action2[i] == KEEP || - (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) { + for (i = xdf2->dstart; i <= xdf2->dend; i++) { + uint8_t action = action2[i]; + + if (action == INVESTIGATE) { + if (!xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend)) + action = KEEP; + else + action = DISCARD; + } + + if (action == KEEP) { xdf2->reference_index[xdf2->nreff++] = i; - /* changed[i] remains false, i.e. keep */ - } else + /* changed[i] remains false */ + } else if (action == DISCARD) { xdf2->changed[i] = true; - /* i.e. discard */ + } else { + BUG("Illegal state for action"); + } } cleanup: diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 77ee1ad9c86875..9a999acdc079d2 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -23,8 +23,8 @@ #include "xinclude.h" -long xdl_bogosqrt(long n) { - long i; +uint64_t xdl_bogosqrt(uint64_t n) { + uint64_t i; /* * Classical integer square root approximation using shifts. diff --git a/xdiff/xutils.h b/xdiff/xutils.h index 615b4a9d355433..58f9d74cda37a3 100644 --- a/xdiff/xutils.h +++ b/xdiff/xutils.h @@ -25,7 +25,7 @@ -long xdl_bogosqrt(long n); +uint64_t xdl_bogosqrt(uint64_t n); int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, xdemitcb_t *ecb); int xdl_cha_init(chastore_t *cha, long isize, long icount);