diff --git a/.clang-format b/.clang-format index 9547fe1b77cac0..dcfd0aad60c31e 100644 --- a/.clang-format +++ b/.clang-format @@ -12,7 +12,15 @@ UseTab: Always TabWidth: 8 IndentWidth: 8 ContinuationIndentWidth: 8 -ColumnLimit: 80 + +# While we do want to enforce a character limit of 80 characters, we often +# allow lines to overflow that limit to prioritize readability. Setting a +# character limit here with penalties has been finicky and creates too many +# false positives. +# +# NEEDSWORK: It would be nice if we can find optimal settings to ensure we +# can re-enable the limit here. +ColumnLimit: 0 # C Language specifics Language: Cpp @@ -210,16 +218,11 @@ MaxEmptyLinesToKeep: 1 # No empty line at the start of a block. KeepEmptyLinesAtTheStartOfBlocks: false -# Penalties -# This decides what order things should be done if a line is too long -PenaltyBreakAssignment: 5 -PenaltyBreakBeforeFirstCallParameter: 5 -PenaltyBreakComment: 5 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 300 -PenaltyBreakString: 5 -PenaltyExcessCharacter: 10 -PenaltyReturnTypeOnItsOwnLine: 300 - # Don't sort #include's SortIncludes: false + +# Remove optional braces of control statements (if, else, for, and while) +# according to the LLVM coding style. This avoids braces on simple +# single-statement bodies of statements but keeps braces if one side of +# if/else if/.../else cascade has multi-statement body. +RemoveBracesLLVM: true diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc index c6bd94986c5fcc..f8d2eba061c82a 100644 --- a/Documentation/BreakingChanges.adoc +++ b/Documentation/BreakingChanges.adoc @@ -118,6 +118,53 @@ Cf. <2f5de416-04ba-c23d-1e0b-83bb655829a7@zombino.com>, <20170223155046.e7nxivfwqqoprsqj@LykOS.localdomain>, . +* The default storage format for references in newly created repositories will + be changed from "files" to "reftable". The "reftable" format provides + multiple advantages over the "files" format: ++ + ** It is impossible to store two references that only differ in casing on + case-insensitive filesystems with the "files" format. This issue is common + on Windows and macOS platforms. As the "reftable" backend does not use + filesystem paths to encode reference names this problem goes away. + ** Similarly, macOS normalizes path names that contain unicode characters, + which has the consequence that you cannot store two names with unicode + characters that are encoded differently with the "files" backend. Again, + this is not an issue with the "reftable" backend. + ** Deleting references with the "files" backend requires Git to rewrite the + complete "packed-refs" file. In large repositories with many references + this file can easily be dozens of megabytes in size, in extreme cases it + may be gigabytes. The "reftable" backend uses tombstone markers for + deleted references and thus does not have to rewrite all of its data. + ** Repository housekeeping with the "files" backend typically performs + all-into-one repacks of references. This can be quite expensive, and + consequently housekeeping is a tradeoff between the number of loose + references that accumulate and slow down operations that read references, + and compressing those loose references into the "packed-refs" file. The + "reftable" backend uses geometric compaction after every write, which + amortizes costs and ensures that the backend is always in a + well-maintained state. + ** Operations that write multiple references at once are not atomic with the + "files" backend. Consequently, Git may see in-between states when it reads + references while a reference transaction is in the process of being + committed to disk. + ** Writing many references at once is slow with the "files" backend because + every reference is created as a separate file. The "reftable" backend + significantly outperforms the "files" backend by multiple orders of + magnitude. + ** The reftable backend uses a binary format with prefix compression for + reference names. As a result, the format uses less space compared to the + "packed-refs" file. ++ +Users that get immediate benefit from the "reftable" backend could continue to +opt-in to the "reftable" format manually by setting the "init.defaultRefFormat" +config. But defaults matter, and we think that overall users will have a better +experience with less platform-specific quirks when they use the new backend by +default. ++ +A prerequisite for this change is that the ecosystem is ready to support the +"reftable" format. Most importantly, alternative implementations of Git like +JGit, libgit2 and Gitoxide need to support it. + === Removals * Support for grafting commits has long been superseded by git-replace(1). diff --git a/Documentation/RelNotes/2.51.0.adoc b/Documentation/RelNotes/2.51.0.adoc index 40920e649525ff..4eb30d26a11201 100644 --- a/Documentation/RelNotes/2.51.0.adoc +++ b/Documentation/RelNotes/2.51.0.adoc @@ -40,6 +40,13 @@ UI, Workflows & Features client, instead of retrying, it skipped it by mistake, which has been corrected. + * The reftable ref backend has matured enough; Git 3.0 will make it + the default format in a newly created repositories by default. + + * "netrc" credential helper has been improved to understand textual + service names (like smtp) in addition to the numeric port numbers + (like 25). + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -65,6 +72,16 @@ Performance, Internal Implementation, Development Support etc. * "git push" and "git fetch" are taught to update refs in batches to gain performance. + * Some code paths in the "git prune" used to ignore passed in + repository object and used the_repository singleton instance + instead, which has been corrected. + + * Update ".clang-format" and ".editorconfig" to match our style guide + a bit better. + + * "make coccicheck" succeeds even when spatch made suggestions, which + has been updated to fail in such a case. + Fixes since v2.50 ----------------- @@ -117,8 +134,32 @@ including security updates, are included in this release. * Remove unnecessary check from "git daemon" code. (merge 0c856224d2 cb/daemon-fd-check-fix later to maint). - * Leakfix. - (merge b0e9d25865 jk/fix-leak-send-pack later to maint). + * Use of sysctl() system call to learn the total RAM size used on + BSDs has been corrected. + (merge 781c1cf571 cb/total-ram-bsd-fix later to maint). + + * Drop FreeBSD 4 support and declare that we support only FreeBSD 12 + or later, which has memmem() supported. + (merge 0392f976a7 bs/config-mak-freebsd later to maint). + + * A diff-filter with negative-only specification like "git log + --diff-filter=d" did not trigger correctly, which has been fixed. + (merge 375ac087c5 jk/all-negative-diff-filter-fix later to maint). + + * A failure to open the index file for writing due to conflicting + access did not state what went wrong, which has been corrected. + (merge 9455397a5c hy/read-cache-lock-error-fix later to maint). + + * Tempfile removal fix in the codepath to sign commits with SSH keys. + (merge 4498127b04 re/ssh-sign-buffer-fix later to maint). + + * Code and test clean-up around string-list API. + (merge 6e5b26c3ff sj/string-list later to maint). + + * "git apply -N" should start from the current index and register + only new files, but it instead started from an empty index, which + has been corrected. + (merge 2b49d97fcb rp/apply-intent-to-add-fix later to maint). * Other code cleanup, docfix, build fix, etc. (merge b257adb571 lo/my-first-ow-doc-update later to maint). @@ -136,3 +177,9 @@ including security updates, are included in this release. (merge ff73f375bb jg/mailinfo-leakfix later to maint). (merge 996f14c02b jj/doc-branch-markup-fix later to maint). (merge 1e77de1864 cb/ci-freebsd-update-to-14.3 later to maint). + (merge b0e9d25865 jk/fix-leak-send-pack later to maint). + (merge f3a9558c8c bs/remote-helpers-doc-markup-fix later to maint). + (merge c4e9775c60 kh/doc-config-subcommands later to maint). + (merge de404249ab ps/perlless-test-fixes later to maint). + (merge 953049eed8 ts/merge-orig-head-doc-fix later to maint). + (merge 0c83bbc704 rj/freebsd-sysinfo-build-fix later to maint). diff --git a/Documentation/config/feature.adoc b/Documentation/config/feature.adoc index cb49ff2604a632..924f5ff4e3caaa 100644 --- a/Documentation/config/feature.adoc +++ b/Documentation/config/feature.adoc @@ -24,6 +24,12 @@ reusing objects from multiple packs instead of just one. * `pack.usePathWalk` may speed up packfile creation and make the packfiles be significantly smaller in the presence of certain filename collisions with Git's default name-hash. ++ +* `init.defaultRefFormat=reftable` causes newly initialized repositories to use +the reftable format for storing references. This new format solves issues with +case-insensitive filesystems, compresses better and performs significantly +better with many use cases. Refer to Documentation/technical/reftable.adoc for +more information on this new storage format. feature.manyFiles:: Enable config options that optimize for repos with many files in the diff --git a/Documentation/git-apply.adoc b/Documentation/git-apply.adoc index 952518b8af6fc0..6c71ee69da977d 100644 --- a/Documentation/git-apply.adoc +++ b/Documentation/git-apply.adoc @@ -75,13 +75,14 @@ OPTIONS tree. If `--check` is in effect, merely check that it would apply cleanly to the index entry. +-N:: --intent-to-add:: When applying the patch only to the working tree, mark new files to be added to the index later (see `--intent-to-add` - option in linkgit:git-add[1]). This option is ignored unless - running in a Git repository and `--index` is not specified. - Note that `--index` could be implied by other options such - as `--cached` or `--3way`. + option in linkgit:git-add[1]). This option is ignored if + `--index` or `--cached` are used, and has no effect outside a Git + repository. Note that `--index` could be implied by other options + such as `--3way`. -3:: --3way:: diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc index 936e0c5130fe7d..511b2e26bfb00f 100644 --- a/Documentation/git-config.adoc +++ b/Documentation/git-config.adoc @@ -10,9 +10,9 @@ SYNOPSIS -------- [verse] 'git config list' [] [] [--includes] -'git config get' [] [] [--includes] [--all] [--regexp] [--value=] [--fixed-value] [--default=] -'git config set' [] [--type=] [--all] [--value=] [--fixed-value] -'git config unset' [] [--all] [--value=] [--fixed-value] +'git config get' [] [] [--includes] [--all] [--regexp] [--value=] [--fixed-value] [--default=] [--url=] +'git config set' [] [--type=] [--all] [--value=] [--fixed-value] +'git config unset' [] [--all] [--value=] [--fixed-value] 'git config rename-section' [] 'git config remove-section' [] 'git config edit' [] @@ -26,7 +26,7 @@ escaped. Multiple lines can be added to an option by using the `--append` option. If you want to update or unset an option which can occur on multiple -lines, a `value-pattern` (which is an extended regular expression, +lines, `--value=` (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 @@ -109,7 +109,7 @@ OPTIONS --replace-all:: Default behavior is to replace at most one line. This replaces - all lines matching the key (and optionally the `value-pattern`). + all lines matching the key (and optionally `--value=`). --append:: Adds a new line to the option without altering any existing @@ -200,11 +200,19 @@ See also <>. section in linkgit:gitrevisions[7] for a more complete list of ways to spell blob names. +`--value=`:: +`--no-value`:: + With `get`, `set`, and `unset`, match only against + __. The pattern is an extended regular expression unless + `--fixed-value` is given. ++ +Use `--no-value` to unset __. + --fixed-value:: - When used with the `value-pattern` argument, treat `value-pattern` as + When used with `--value=`, treat __ 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`. + is exactly equal to __. --type :: 'git config' will ensure that any input or output is valid under the given @@ -259,6 +267,12 @@ Valid ``'s include: Output only the names of config variables for `list` or `get`. +`--show-names`:: +`--no-show-names`:: + With `get`, show config keys in addition to their values. The + default is `--no-show-names` unless `--url` is given and there + are no subsections in __. + --show-origin:: Augment the output of all queried config options with the origin type (file, standard input, blob, command line) and diff --git a/Documentation/git-merge.adoc b/Documentation/git-merge.adoc index d53923c3b73cfc..a055384ad6956c 100644 --- a/Documentation/git-merge.adoc +++ b/Documentation/git-merge.adoc @@ -28,8 +28,8 @@ Assume the following history exists and the current branch is `master`: ------------ - A---B---C topic - / + A---B---C topic + / D---E---F---G master ------------ @@ -38,11 +38,11 @@ Then `git merge topic` will replay the changes made on the its current commit (`C`) on top of `master`, and record the result in a new commit along with the names of the two parent commits and a log message from the user describing the changes. Before the operation, -`ORIG_HEAD` is set to the tip of the current branch (`C`). +`ORIG_HEAD` is set to the tip of the current branch (`G`). ------------ - A---B---C topic - / \ + A---B---C topic + / \ D---E---F---G---H master ------------ diff --git a/Documentation/gitremote-helpers.adoc b/Documentation/gitremote-helpers.adoc index d0be008e5e3576..39cdece16e93a4 100644 --- a/Documentation/gitremote-helpers.adoc +++ b/Documentation/gitremote-helpers.adoc @@ -498,7 +498,7 @@ set by Git if the remote helper has the 'option' capability. ask for the tag specifically. Some helpers may be able to use this option to avoid a second network connection. -'option dry-run' {'true'|'false'}: +'option dry-run' {'true'|'false'}:: If true, pretend the operation completed successfully, but don't actually change any repository data. For most helpers this only applies to the 'push', if supported. diff --git a/Makefile b/Makefile index 70d1543b6b8688..014d6fcaed5237 100644 --- a/Makefile +++ b/Makefile @@ -114,8 +114,6 @@ include shared.mak # # Define NO_INTPTR_T if you don't have intptr_t or uintptr_t. # -# Define NO_UINTMAX_T if you don't have uintmax_t. -# # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). # @@ -1367,6 +1365,7 @@ CLAR_TEST_SUITES += u-prio-queue CLAR_TEST_SUITES += u-reftable-tree CLAR_TEST_SUITES += u-strbuf CLAR_TEST_SUITES += u-strcmp-offset +CLAR_TEST_SUITES += u-string-list CLAR_TEST_SUITES += u-strvec CLAR_TEST_SUITES += u-trailer CLAR_TEST_SUITES += u-urlmatch-normalization @@ -1918,9 +1917,6 @@ endif ifdef NO_INTPTR_T COMPAT_CFLAGS += -DNO_INTPTR_T endif -ifdef NO_UINTMAX_T - BASIC_CFLAGS += -Duintmax_t=uint32_t -endif ifdef NO_SOCKADDR_STORAGE ifdef NO_IPV6 BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in @@ -3473,11 +3469,14 @@ endif coccicheck-test: $(COCCI_TEST_RES_GEN) coccicheck: coccicheck-test + ifdef SPATCH_CONCAT_COCCI -coccicheck: contrib/coccinelle/ALL.cocci.patch +COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = contrib/coccinelle/ALL.cocci.patch else -coccicheck: $(COCCICHECK_PATCHES_INTREE) +COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = $(COCCICHECK_PATCHES_INTREE) endif +coccicheck: $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) + ! grep -q ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null # See contrib/coccinelle/README coccicheck-pending: coccicheck-test diff --git a/apply.c b/apply.c index 8bbe6ed224032e..8637ad4c9fc9f0 100644 --- a/apply.c +++ b/apply.c @@ -4565,7 +4565,7 @@ static int create_file(struct apply_state *state, struct patch *patch) if (patch->conflicted_threeway) return add_conflicted_stages_file(state, patch); - else if (state->update_index) + else if (state->check_index || (state->ita_only && patch->is_new > 0)) return add_index_file(state, path, mode, buf, size); return 0; } @@ -4833,7 +4833,7 @@ static int apply_patch(struct apply_state *state, LOCK_DIE_ON_ERROR); } - if (state->check_index && read_apply_cache(state) < 0) { + if ((state->check_index || state->update_index) && read_apply_cache(state) < 0) { error(_("unable to read index file")); res = -128; goto end; diff --git a/builtin/config.c b/builtin/config.c index f70d6354772259..5efe2730106c99 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -17,9 +17,9 @@ static const char *const builtin_config_usage[] = { N_("git config list [] [] [--includes]"), - N_("git config get [] [] [--includes] [--all] [--regexp] [--value=] [--fixed-value] [--default=] "), - N_("git config set [] [--type=] [--all] [--value=] [--fixed-value] "), - N_("git config unset [] [--all] [--value=] [--fixed-value] "), + N_("git config get [] [] [--includes] [--all] [--regexp] [--value=] [--fixed-value] [--default=] [--url=] "), + N_("git config set [] [--type=] [--all] [--value=] [--fixed-value] "), + N_("git config unset [] [--all] [--value=] [--fixed-value] "), N_("git config rename-section [] "), N_("git config remove-section [] "), N_("git config edit []"), @@ -33,17 +33,17 @@ static const char *const builtin_config_list_usage[] = { }; static const char *const builtin_config_get_usage[] = { - N_("git config get [] [] [--includes] [--all] [--regexp=] [--value=] [--fixed-value] [--default=] "), + N_("git config get [] [] [--includes] [--all] [--regexp=] [--value=] [--fixed-value] [--default=] "), NULL }; static const char *const builtin_config_set_usage[] = { - N_("git config set [] [--type=] [--comment=] [--all] [--value=] [--fixed-value] "), + N_("git config set [] [--type=] [--comment=] [--all] [--value=] [--fixed-value] "), NULL }; static const char *const builtin_config_unset_usage[] = { - N_("git config unset [] [--all] [--value=] [--fixed-value] "), + N_("git config unset [] [--all] [--value=] [--fixed-value] "), NULL }; diff --git a/builtin/gc.c b/builtin/gc.c index 845876ff02869b..0a583912e34fb8 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -539,7 +539,7 @@ static uint64_t total_ram(void) return total; } #elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM) || defined(HW_PHYSMEM64)) - int64_t physical_memory; + uint64_t physical_memory; int mib[2]; size_t length; @@ -551,9 +551,16 @@ static uint64_t total_ram(void) # else mib[1] = HW_PHYSMEM; # endif - length = sizeof(int64_t); - if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) + length = sizeof(physical_memory); + if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) { + if (length == 4) { + uint32_t mem; + + if (!sysctl(mib, 2, &mem, &length, NULL, 0)) + physical_memory = mem; + } return physical_memory; + } #elif defined(GIT_WINDOWS_NATIVE) MEMORYSTATUSEX memInfo; @@ -998,7 +1005,7 @@ int cmd_gc(int argc, if (opts.detach <= 0 && !skip_foreground_tasks) gc_foreground_tasks(&opts, &cfg); - if (!repository_format_precious_objects) { + if (!the_repository->repository_format_precious_objects) { struct child_process repack_cmd = CHILD_PROCESS_INIT; repack_cmd.git_cmd = 1; diff --git a/builtin/prune.c b/builtin/prune.c index e930caa0c0af0e..320e9c234161a9 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" @@ -64,7 +63,7 @@ static void perform_reachability_traversal(struct rev_info *revs) return; if (show_progress) - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(revs->repo, _("Checking connectivity"), 0); mark_reachable_objects(revs, 1, expire, progress); stop_progress(&progress); @@ -78,7 +77,7 @@ static int is_object_reachable(const struct object_id *oid, perform_reachability_traversal(revs); - obj = lookup_object(the_repository, oid); + obj = lookup_object(revs->repo, oid); return obj && (obj->flags & SEEN); } @@ -99,8 +98,7 @@ static int prune_object(const struct object_id *oid, const char *fullpath, if (st.st_mtime > expire) return 0; if (show_only || verbose) { - enum object_type type = oid_object_info(the_repository, oid, - NULL); + enum object_type type = oid_object_info(revs->repo, oid, NULL); printf("%s %s\n", oid_to_hex(oid), (type > 0) ? type_name(type) : "unknown"); } @@ -154,7 +152,7 @@ static void remove_temporary_files(const char *path) int cmd_prune(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct rev_info revs; int exclude_promisor_objects = 0; @@ -173,20 +171,19 @@ int cmd_prune(int argc, expire = TIME_MAX; save_commit_buffer = 0; disable_replace_refs(); - repo_init_revisions(the_repository, &revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); - if (repository_format_precious_objects) + repo_init_revisions(repo, &revs, prefix); + if (repo->repository_format_precious_objects) die(_("cannot prune in a precious-objects repo")); while (argc--) { struct object_id oid; const char *name = *argv++; - if (!repo_get_oid(the_repository, name, &oid)) { - struct object *object = parse_object_or_die(the_repository, &oid, - name); + if (!repo_get_oid(repo, name, &oid)) { + struct object *object = parse_object_or_die(repo, &oid, name); add_pending_object(&revs, object, ""); } else @@ -200,16 +197,16 @@ int cmd_prune(int argc, revs.exclude_promisor_objects = 1; } - for_each_loose_file_in_objdir(repo_get_object_directory(the_repository), + for_each_loose_file_in_objdir(repo_get_object_directory(repo), prune_object, prune_cruft, prune_subdir, &revs); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); - remove_temporary_files(repo_get_object_directory(the_repository)); - s = mkpathdup("%s/pack", repo_get_object_directory(the_repository)); + remove_temporary_files(repo_get_object_directory(repo)); + s = mkpathdup("%s/pack", repo_get_object_directory(repo)); remove_temporary_files(s); free(s); - if (is_repository_shallow(the_repository)) { + if (is_repository_shallow(repo)) { perform_reachability_traversal(&revs); prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0); } diff --git a/builtin/repack.c b/builtin/repack.c index 5ddc6e7f9573d4..d0e4fa6bed16e3 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -1240,7 +1240,7 @@ int cmd_repack(int argc, po_args.depth = xstrdup_or_null(opt_depth); po_args.threads = xstrdup_or_null(opt_threads); - if (delete_redundant && repository_format_precious_objects) + if (delete_redundant && the_repository->repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A", diff --git a/ci/run-style-check.sh b/ci/run-style-check.sh index 6cd4b1d934a76a..0832c19df076f4 100755 --- a/ci/run-style-check.sh +++ b/ci/run-style-check.sh @@ -5,21 +5,5 @@ baseCommit=$1 -# Remove optional braces of control statements (if, else, for, and while) -# according to the LLVM coding style. This avoids braces on simple -# single-statement bodies of statements but keeps braces if one side of -# if/else if/.../else cascade has multi-statement body. -# -# As this rule comes with a warning [1], we want to experiment with it -# before adding it in-tree. since the CI job for the style check is allowed -# to fail, appending the rule here allows us to validate its efficacy. -# While also ensuring that end-users are not affected directly. -# -# [1]: https://clang.llvm.org/docs/ClangFormatStyleOptions.html#removebracesllvm -{ - cat .clang-format - echo "RemoveBracesLLVM: true" -} >/tmp/clang-format-rules - -git clang-format --style=file:/tmp/clang-format-rules \ +git clang-format --style=file:.clang-format \ --diff --extensions c,h "$baseCommit" diff --git a/config.mak.uname b/config.mak.uname index b1c5c4d5e8edd1..1691c6ae6e01e3 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -302,16 +302,13 @@ ifeq ($(uname_S),FreeBSD) ifeq ($(firstword $(subst -, ,$(uname_R))),10.1) OLD_ICONV = YesPlease endif - NO_MEMMEM = YesPlease + ifeq ($(shell v=$(uname_R) && test $${v%%.*} -lt 12 && echo 1),1) + NO_MEMMEM = UnfortunatelyYes + endif BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease USE_ST_TIMESPEC = YesPlease - ifeq ($(shell expr "$(uname_R)" : '4\.'),2) - PTHREAD_LIBS = -pthread - NO_UINTMAX_T = YesPlease - NO_STRTOUMAX = YesPlease - endif PYTHON_PATH = /usr/local/bin/python PERL_PATH = /usr/local/bin/perl HAVE_PATHS_H = YesPlease diff --git a/configure.ac b/configure.ac index f6caab919a3e0e..cfb50112bf81ff 100644 --- a/configure.ac +++ b/configure.ac @@ -1067,32 +1067,6 @@ AC_CHECK_LIB([iconv], [locale_charset], [CHARSET_LIB=-lcharset])]) GIT_CONF_SUBST([CHARSET_LIB]) -# -# Define HAVE_SYSINFO=YesPlease if sysinfo is available. -# -AC_DEFUN([HAVE_SYSINFO_SRC], [ -AC_LANG_PROGRAM([[ -#include -#include -]], [[ -struct sysinfo si; -uint64_t t = 0; -if (!sysinfo(&si)) { - t = si.totalram; - if (si.mem_unit > 1) - t *= (uint64_t)si.mem_unit; -} -return t; -]])]) - -AC_MSG_CHECKING([for sysinfo]) -AC_COMPILE_IFELSE([HAVE_SYSINFO_SRC], - [AC_MSG_RESULT([yes]) - HAVE_SYSINFO=YesPlease], - [AC_MSG_RESULT([no]) - HAVE_SYSINFO=]) -GIT_CONF_SUBST([HAVE_SYSINFO]) - # # Define HAVE_CLOCK_GETTIME=YesPlease if clock_gettime is available. GIT_CHECK_FUNC(clock_gettime, @@ -1148,14 +1122,6 @@ GIT_CHECK_FUNC(strlcpy, [NO_STRLCPY=YesPlease]) GIT_CONF_SUBST([NO_STRLCPY]) # -# Define NO_UINTMAX_T if your platform does not have uintmax_t -AC_CHECK_TYPE(uintmax_t, -[NO_UINTMAX_T=], -[NO_UINTMAX_T=YesPlease],[ -#include -]) -GIT_CONF_SUBST([NO_UINTMAX_T]) -# # Define NO_STRTOUMAX if you don't have strtoumax in the C library. GIT_CHECK_FUNC(strtoumax, [NO_STRTOUMAX=], @@ -1221,6 +1187,41 @@ AC_COMPILE_IFELSE([BSD_SYSCTL_SRC], HAVE_BSD_SYSCTL=]) GIT_CONF_SUBST([HAVE_BSD_SYSCTL]) +# +# Define HAVE_SYSINFO=YesPlease if sysinfo is available. +# + +HAVE_SYSINFO= +# on a *BSD system, sysctl() takes precedence over the +# sysinfo() compatibility library (if installed). + +if test -z "$HAVE_BSD_SYSCTL"; then + + AC_DEFUN([HAVE_SYSINFO_SRC], [ + AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + struct sysinfo si; + uint64_t t = 0; + if (!sysinfo(&si)) { + t = si.totalram; + if (si.mem_unit > 1) + t *= (uint64_t)si.mem_unit; + } + return t; + ]])]) + + AC_MSG_CHECKING([for sysinfo]) + AC_COMPILE_IFELSE([HAVE_SYSINFO_SRC], + [AC_MSG_RESULT([yes]) + HAVE_SYSINFO=YesPlease], + [AC_MSG_RESULT([no]) + HAVE_SYSINFO=]) + GIT_CONF_SUBST([HAVE_SYSINFO]) + +fi + ## Other checks. # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. # Enable it on Windows. By default, symrefs are still used. diff --git a/contrib/credential/netrc/git-credential-netrc.perl b/contrib/credential/netrc/git-credential-netrc.perl index 9fb998ae090325..3c0a532d0e2409 100755 --- a/contrib/credential/netrc/git-credential-netrc.perl +++ b/contrib/credential/netrc/git-credential-netrc.perl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl use strict; use warnings; @@ -267,8 +267,16 @@ sub load_netrc { if (!defined $nentry->{machine}) { next; } - if (defined $nentry->{port} && $nentry->{port} =~ m/^\d+$/) { - $num_port = $nentry->{port}; + if (defined $nentry->{port}) { + $num_port = Git::port_num($nentry->{port}); + unless ($num_port) { + printf(STDERR "ignoring invalid port `%s' " . + "from netrc file\n", $nentry->{port}); + } + # Since we've already validated and converted + # the port to its numerical value, do not + # capture it as the `protocol' value, as used + # to be the case for symbolic port names. delete $nentry->{port}; } diff --git a/contrib/credential/netrc/test.pl b/contrib/credential/netrc/test.pl index 67a0ede5644dd2..8a7fc2588a37f3 100755 --- a/contrib/credential/netrc/test.pl +++ b/contrib/credential/netrc/test.pl @@ -45,7 +45,7 @@ BEGIN diag "Testing with invalid data\n"; $cred = run_credential(['-f', $netrc, 'get'], "bad data"); -ok(scalar keys %$cred == 4, "Got first found keys with bad data"); +ok(scalar keys %$cred == 3, "Got first found keys with bad data"); diag "Testing netrc file for a missing corovamilkbar entry\n"; $cred = run_credential(['-f', $netrc, 'get'], @@ -64,12 +64,12 @@ BEGIN diag "Testing netrc file for a username-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], - { host => 'imap', username => 'bob' }); + { host => 'imap:993', username => 'bob' }); -ok(scalar keys %$cred == 2, "Got 2 username-specific keys"); +# Only the password field gets returned. +ok(scalar keys %$cred == 1, "Got 1 username-specific keys"); is($cred->{password}, 'bobwillknow', "Got correct user-specific password"); -is($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); diag "Testing netrc file for a host:port-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], diff --git a/environment.c b/environment.c index 7bf0390a3355d3..7c2480b22e5991 100644 --- a/environment.c +++ b/environment.c @@ -37,7 +37,6 @@ int ignore_case; int assume_unchanged; int is_bare_repository_cfg = -1; /* unspecified */ int warn_on_object_refname_ambiguity = 1; -int repository_format_precious_objects; char *git_commit_encoding; char *git_log_output_encoding; char *apply_default_whitespace; diff --git a/environment.h b/environment.h index 9a3d05d414a7ad..3d806ced6e4552 100644 --- a/environment.h +++ b/environment.h @@ -189,8 +189,6 @@ extern enum object_creation_mode object_creation_mode; extern int grafts_keep_true_parents; -extern int repository_format_precious_objects; - const char *get_log_output_encoding(void); const char *get_commit_output_encoding(void); diff --git a/git-send-email.perl b/git-send-email.perl index cb6dca2500c93f..437f8ac46a85dd 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -2112,6 +2112,17 @@ sub initialize_modified_loop_vars { } } + # Validate the SMTP server port, if provided. + if (defined $smtp_server_port) { + my $port = Git::port_num($smtp_server_port); + if ($port) { + $smtp_server_port = $port; + } else { + die sprintf(__("error: invalid SMTP port '%s'\n"), + $smtp_server_port); + } + } + # Run the loop once again to avoid gaps in the counter due to FIFO # arguments provided by the user. my $num = 1; diff --git a/gpg-interface.c b/gpg-interface.c index 0896458de5a988..bdcc8c2a2ed683 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -1048,7 +1048,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, key_file->filename.buf); goto out; } - ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL); + ssh_signing_key_file = xstrdup(key_file->filename.buf); } else { /* We assume a file */ ssh_signing_key_file = interpolate_path(signing_key, 1); diff --git a/help.c b/help.c index 21b778707a6a65..89cd47e3b867f7 100644 --- a/help.c +++ b/help.c @@ -810,6 +810,8 @@ void get_version_info(struct strbuf *buf, int show_build_options) SHA1_UNSAFE_BACKEND); #endif strbuf_addf(buf, "SHA-256: %s\n", SHA256_BACKEND); + strbuf_addf(buf, "default-ref-format: %s\n", + ref_storage_format_to_name(REF_STORAGE_FORMAT_DEFAULT)); } } diff --git a/meson.build b/meson.build index 7fea4a34d684c5..d50c340e89c6cc 100644 --- a/meson.build +++ b/meson.build @@ -1331,10 +1331,6 @@ if host_machine.system() != 'windows' endif endif -if compiler.has_member('struct sysinfo', 'totalram', prefix: '#include ') - libgit_c_args += '-DHAVE_SYSINFO' -endif - if compiler.has_member('struct stat', 'st_mtimespec.tv_nsec', prefix: '#include ') libgit_c_args += '-DUSE_ST_TIMESPEC' elif not compiler.has_member('struct stat', 'st_mtim.tv_nsec', prefix: '#include ') @@ -1420,17 +1416,6 @@ if compiler.compiles(''' libgit_c_args += '-DHAVE_CLOCK_MONOTONIC' endif -if not compiler.compiles(''' - #include - - void func(void) - { - uintmax_t x = 0; - } -''', name: 'uintmax_t') - libgit_c_args += '-DNO_UINTMAX_T' -endif - has_bsd_sysctl = false if compiler.has_header('sys/sysctl.h') if compiler.compiles(''' @@ -1449,6 +1434,12 @@ if compiler.has_header('sys/sysctl.h') endif endif +if not has_bsd_sysctl + if compiler.has_member('struct sysinfo', 'totalram', prefix: '#include ') + libgit_c_args += '-DHAVE_SYSINFO' + endif +endif + if not meson.is_cross_build() and compiler.run(''' #include @@ -2144,6 +2135,18 @@ if headers_to_check.length() != 0 and compiler.get_argument_syntax() == 'gcc' alias_target('check-headers', hdr_check) endif +git_clang_format = find_program('git-clang-format', required: false, native: true) +if git_clang_format.found() + run_target('style', + command: [ + git_clang_format, + '--style', 'file', + '--diff', + '--extensions', 'c,h' + ] + ) +endif + foreach key, value : { 'DIFF': diff.full_path(), 'GIT_SOURCE_DIR': meson.project_source_root(), diff --git a/perl/Git.pm b/perl/Git.pm index 6f47d653abe522..090cf77daba323 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -1061,6 +1061,19 @@ sub _close_cat_blob { delete @$self{@vars}; } +# Given PORT, a port number or service name, return its numerical +# value else undef. +sub port_num { + my ($port) = @_; + + # Port can be either a positive integer within the 16-bit range... + if ($port =~ /^\d+$/ && $port > 0 && $port <= (2**16 - 1)) { + return $port; + } + + # ... or a symbolic port (service name). + return scalar getservbyname($port, ''); +} =item credential_read( FILEHANDLE ) diff --git a/read-cache.c b/read-cache.c index c0bb760ad473ef..c44efc89324aa7 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1456,7 +1456,8 @@ int repo_refresh_and_write_index(struct repository *repo, struct lock_file lock_file = LOCK_INIT; int fd, ret = 0; - fd = repo_hold_locked_index(repo, &lock_file, 0); + fd = repo_hold_locked_index(repo, &lock_file, + gentle ? 0 : LOCK_REPORT_ON_ERROR); if (!gentle && fd < 0) return -1; if (refresh_index(repo->index, refresh_flags, pathspec, seen, header_msg)) diff --git a/repository.c b/repository.c index 9b3d6665fc6d4f..62709d1c916e1b 100644 --- a/repository.c +++ b/repository.c @@ -284,6 +284,7 @@ int repo_init(struct repository *repo, repo_set_ref_storage_format(repo, format.ref_storage_format); repo->repository_format_worktree_config = format.worktree_config; repo->repository_format_relative_worktrees = format.relative_worktrees; + repo->repository_format_precious_objects = format.precious_objects; /* take ownership of format.partial_clone */ repo->repository_format_partial_clone = format.partial_clone; diff --git a/repository.h b/repository.h index c4c92b2ab9c9e3..76133b37850789 100644 --- a/repository.h +++ b/repository.h @@ -20,6 +20,12 @@ enum ref_storage_format { REF_STORAGE_FORMAT_REFTABLE, }; +#ifdef WITH_BREAKING_CHANGES /* Git 3.0 */ +# define REF_STORAGE_FORMAT_DEFAULT REF_STORAGE_FORMAT_REFTABLE +#else +# define REF_STORAGE_FORMAT_DEFAULT REF_STORAGE_FORMAT_FILES +#endif + struct repo_path_cache { char *squash_msg; char *merge_msg; @@ -151,6 +157,7 @@ struct repository { /* Configurations */ int repository_format_worktree_config; int repository_format_relative_worktrees; + int repository_format_precious_objects; /* Indicate if a repository has a different 'commondir' from 'gitdir' */ unsigned different_commondir:1; diff --git a/revision.c b/revision.c index afee11119615c4..9892d08748636f 100644 --- a/revision.c +++ b/revision.c @@ -3112,7 +3112,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s /* Pickaxe, diff-filter and rename following need diffs */ if ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) || - revs->diffopt.filter || + revs->diffopt.filter || revs->diffopt.filter_not || revs->diffopt.flags.follow_renames) revs->diff = 1; diff --git a/setup.c b/setup.c index f93bd6a24a5d9c..18661fc048a6e3 100644 --- a/setup.c +++ b/setup.c @@ -753,7 +753,8 @@ static int check_repository_format_gently(const char *gitdir, struct repository_ die("%s", err.buf); } - repository_format_precious_objects = candidate->precious_objects; + the_repository->repository_format_precious_objects = candidate->precious_objects; + string_list_clear(&candidate->unknown_extensions, 0); string_list_clear(&candidate->v1_only_extensions, 0); @@ -1864,6 +1865,8 @@ const char *setup_git_directory_gently(int *nongit_ok) the_repository->repository_format_partial_clone = repo_fmt.partial_clone; repo_fmt.partial_clone = NULL; + the_repository->repository_format_precious_objects = + repo_fmt.precious_objects; } } /* @@ -2481,6 +2484,18 @@ static int read_default_format_config(const char *key, const char *value, goto out; } + /* + * Enable the reftable format when "features.experimental" is enabled. + * "init.defaultRefFormat" takes precedence over this setting. + */ + if (!strcmp(key, "feature.experimental") && + cfg->ref_format == REF_STORAGE_FORMAT_UNKNOWN && + git_config_bool(key, value)) { + cfg->ref_format = REF_STORAGE_FORMAT_REFTABLE; + ret = 0; + goto out; + } + ret = 0; out: free(str); @@ -2541,6 +2556,8 @@ static void repository_format_configure(struct repository_format *repo_fmt, repo_fmt->ref_storage_format = ref_format; } else if (cfg.ref_format != REF_STORAGE_FORMAT_UNKNOWN) { repo_fmt->ref_storage_format = cfg.ref_format; + } else { + repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT; } repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format); } diff --git a/string-list.c b/string-list.c index bf061fec563d74..53faaa84207bf9 100644 --- a/string-list.c +++ b/string-list.c @@ -1,5 +1,3 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "git-compat-util.h" #include "string-list.h" @@ -17,19 +15,19 @@ void string_list_init_dup(struct string_list *list) /* if there is no exact match, point to the index where the entry could be * inserted */ -static int get_entry_index(const struct string_list *list, const char *string, - int *exact_match) +static size_t get_entry_index(const struct string_list *list, const char *string, + int *exact_match) { - int left = -1, right = list->nr; + size_t left = 0, right = list->nr; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - while (left + 1 < right) { - int middle = left + (right - left) / 2; + while (left < right) { + size_t middle = left + (right - left) / 2; int compare = cmp(string, list->items[middle].string); if (compare < 0) right = middle; else if (compare > 0) - left = middle; + left = middle + 1; else { *exact_match = 1; return middle; @@ -40,14 +38,13 @@ static int get_entry_index(const struct string_list *list, const char *string, return right; } -/* returns -1-index if already exists */ -static int add_entry(int insert_at, struct string_list *list, const char *string) +static size_t add_entry(struct string_list *list, const char *string) { int exact_match = 0; - int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match); + size_t index = get_entry_index(list, string, &exact_match); if (exact_match) - return -1 - index; + return index; ALLOC_GROW(list->items, list->nr+1, list->alloc); if (index < list->nr) @@ -63,10 +60,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string struct string_list_item *string_list_insert(struct string_list *list, const char *string) { - int index = add_entry(-1, list, string); - - if (index < 0) - index = -1 - index; + size_t index = add_entry(list, string); return list->items + index; } @@ -116,9 +110,9 @@ struct string_list_item *string_list_lookup(struct string_list *list, const char void string_list_remove_duplicates(struct string_list *list, int free_util) { if (list->nr > 1) { - int src, dst; + size_t dst = 1; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - for (src = dst = 1; src < list->nr; src++) { + for (size_t src = 1; src < list->nr; src++) { if (!cmp(list->items[dst - 1].string, list->items[src].string)) { if (list->strdup_strings) free(list->items[src].string); @@ -134,8 +128,8 @@ void string_list_remove_duplicates(struct string_list *list, int free_util) int for_each_string_list(struct string_list *list, string_list_each_func_t fn, void *cb_data) { - int i, ret = 0; - for (i = 0; i < list->nr; i++) + int ret = 0; + for (size_t i = 0; i < list->nr; i++) if ((ret = fn(&list->items[i], cb_data))) break; return ret; @@ -144,8 +138,8 @@ int for_each_string_list(struct string_list *list, void filter_string_list(struct string_list *list, int free_util, string_list_each_func_t want, void *cb_data) { - int src, dst = 0; - for (src = 0; src < list->nr; src++) { + size_t dst = 0; + for (size_t src = 0; src < list->nr; src++) { if (want(&list->items[src], cb_data)) { list->items[dst++] = list->items[src]; } else { @@ -171,13 +165,12 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) void string_list_clear(struct string_list *list, int free_util) { if (list->items) { - int i; if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } if (free_util) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].util); } free(list->items); @@ -189,13 +182,12 @@ void string_list_clear(struct string_list *list, int free_util) void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc) { if (list->items) { - int i; if (clearfunc) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) clearfunc(list->items[i].util, list->items[i].string); } if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } free(list->items); diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c index 6f10c5a43540b8..6be0cdb8e2006a 100644 --- a/t/helper/test-string-list.c +++ b/t/helper/test-string-list.c @@ -1,105 +1,9 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "test-tool.h" #include "strbuf.h" #include "string-list.h" -/* - * Parse an argument into a string list. arg should either be a - * ':'-separated list of strings, or "-" to indicate an empty string - * list (as opposed to "", which indicates a string list containing a - * single empty string). list->strdup_strings must be set. - */ -static void parse_string_list(struct string_list *list, const char *arg) -{ - if (!strcmp(arg, "-")) - return; - - (void)string_list_split(list, arg, ':', -1); -} - -static void write_list(const struct string_list *list) -{ - int i; - for (i = 0; i < list->nr; i++) - printf("[%d]: \"%s\"\n", i, list->items[i].string); -} - -static void write_list_compact(const struct string_list *list) -{ - int i; - if (!list->nr) - printf("-\n"); - else { - printf("%s", list->items[0].string); - for (i = 1; i < list->nr; i++) - printf(":%s", list->items[i].string); - printf("\n"); - } -} - -static int prefix_cb(struct string_list_item *item, void *cb_data) -{ - const char *prefix = (const char *)cb_data; - return starts_with(item->string, prefix); -} - int cmd__string_list(int argc, const char **argv) { - if (argc == 5 && !strcmp(argv[1], "split")) { - struct string_list list = STRING_LIST_INIT_DUP; - int i; - const char *s = argv[2]; - int delim = *argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 5 && !strcmp(argv[1], "split_in_place")) { - struct string_list list = STRING_LIST_INIT_NODUP; - int i; - char *s = xstrdup(argv[2]); - const char *delim = argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split_in_place(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - free(s); - return 0; - } - - if (argc == 4 && !strcmp(argv[1], "filter")) { - /* - * Retain only the items that have the specified prefix. - * Arguments: list|- prefix - */ - struct string_list list = STRING_LIST_INIT_DUP; - const char *prefix = argv[3]; - - parse_string_list(&list, argv[2]); - filter_string_list(&list, 0, prefix_cb, (void *)prefix); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) { - struct string_list list = STRING_LIST_INIT_DUP; - - parse_string_list(&list, argv[2]); - string_list_remove_duplicates(&list, 0); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - if (argc == 2 && !strcmp(argv[1], "sort")) { struct string_list list = STRING_LIST_INIT_NODUP; struct strbuf sb = STRBUF_INIT; diff --git a/t/meson.build b/t/meson.build index 6d7fe6b117ea4a..1af289425d4370 100644 --- a/t/meson.build +++ b/t/meson.build @@ -11,6 +11,7 @@ clar_test_suites = [ 'unit-tests/u-reftable-tree.c', 'unit-tests/u-strbuf.c', 'unit-tests/u-strcmp-offset.c', + 'unit-tests/u-string-list.c', 'unit-tests/u-strvec.c', 'unit-tests/u-trailer.c', 'unit-tests/u-urlmatch-normalization.c', @@ -123,7 +124,6 @@ integration_tests = [ 't0060-path-utils.sh', 't0061-run-command.sh', 't0062-revision-walking.sh', - 't0063-string-list.sh', 't0066-dir-iterator.sh', 't0067-parse_pathspec_file.sh', 't0068-for-each-repo.sh', diff --git a/t/t0001-init.sh b/t/t0001-init.sh index f11a40811f243a..f593c5368746fa 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -658,6 +658,17 @@ test_expect_success 'init warns about invalid init.defaultRefFormat' ' test_cmp expected actual ' +test_expect_success 'default ref format' ' + test_when_finished "rm -rf refformat" && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + git version --build-options | sed -ne "s/^default-ref-format: //p" >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + backends="files reftable" for format in $backends do @@ -738,6 +749,40 @@ test_expect_success "GIT_DEFAULT_REF_FORMAT= overrides init.defaultRefFormat" ' test_cmp expect actual ' +test_expect_success "init with feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + echo reftable >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success "init.defaultRefFormat overrides feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + test_config_global init.defaultRefFormat files && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success "GIT_DEFAULT_REF_FORMAT= overrides feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + GIT_DEFAULT_REF_FORMAT=files git init refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + for from_format in $backends do test_expect_success "re-init with same format ($from_format)" ' diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh deleted file mode 100755 index aac63ba5064b78..00000000000000 --- a/t/t0063-string-list.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2012 Michael Haggerty -# - -test_description='Test string list functionality' - -. ./test-lib.sh - -test_split () { - cat >expected && - test_expect_success "split $1 at $2, max $3" " - test-tool string-list split '$1' '$2' '$3' >actual && - test_cmp expected actual && - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split_in_place() { - cat >expected && - test_expect_success "split (in place) $1 at $2, max $3" " - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split "foo:bar:baz" ":" "-1" <usage && + test_grep "[Uu]sage: git prune " usage && + test_expect_code 129 nongit git prune -h >usage && + test_grep "[Uu]sage: git prune " usage +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index c58ccb136c7b1a..0bb4648e3639b2 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1672,11 +1672,9 @@ test_expect_success 'stash create reports a locked index' ' echo change >A.file && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash create 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err ) ' @@ -1689,11 +1687,9 @@ test_expect_success 'stash push reports a locked index' ' echo change >A.file && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash push 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err ) ' @@ -1707,11 +1703,9 @@ test_expect_success 'stash apply reports a locked index' ' git stash push && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash apply 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err ) ' diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh index c614eaf04cca93..0b11a8aef4fa12 100755 --- a/t/t4140-apply-ita.sh +++ b/t/t4140-apply-ita.sh @@ -7,6 +7,10 @@ test_description='git apply of i-t-a file' test_expect_success setup ' test_write_lines 1 2 3 4 5 >blueprint && + cat blueprint >committed-file && + git add committed-file && + git commit -m "commit" && + cat blueprint >test-file && git add -N test-file && git diff >creation-patch && @@ -14,7 +18,14 @@ test_expect_success setup ' rm -f test-file && git diff >deletion-patch && - grep "deleted file mode 100644" deletion-patch + grep "deleted file mode 100644" deletion-patch && + + git rm -f test-file && + test_write_lines 6 >>committed-file && + cat blueprint >test-file && + git add -N test-file && + git diff >complex-patch && + git restore committed-file ' test_expect_success 'apply creation patch to ita path (--cached)' ' @@ -53,4 +64,22 @@ test_expect_success 'apply deletion patch to ita path (--index)' ' git ls-files --stage --error-unmatch test-file ' +test_expect_success 'apply creation patch to existing index with -N' ' + git rm -f test-file && + cat blueprint >index-file && + git add index-file && + git apply -N creation-patch && + + git ls-files --stage --error-unmatch index-file && + git ls-files --stage --error-unmatch test-file +' + +test_expect_success 'apply complex patch with -N' ' + git rm -f test-file index-file && + git apply -N complex-patch && + + git ls-files --stage --error-unmatch test-file && + git diff | grep "a/committed-file" +' + test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 2ae93d3c967aad..699a81ab5cc6d2 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -1086,7 +1086,7 @@ test_expect_success 'am works with multi-line in-body headers' ' # bump from, date, and subject down to in-body header awk " /^From:/{ - print \"From: x \"; + print \"From: x \"; print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\"; print \"Subject: x\n\"; }; 1 diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 4a6c4dfbf4d62d..05cee9e41bb48d 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -134,6 +134,12 @@ test_expect_success 'diff-filter=D' ' ' +test_expect_success 'all-negative filter' ' + git log --no-renames --format=%s --diff-filter=d HEAD >actual && + printf "%s\n" fifth fourth third second initial >expect && + test_cmp expect actual +' + test_expect_success 'diff-filter=R' ' git log -M --pretty="format:%s" --diff-filter=R HEAD >actual && diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh index ba5ae6a00c9847..1f7a5d82ee4d44 100755 --- a/t/t5333-pseudo-merge-bitmaps.sh +++ b/t/t5333-pseudo-merge-bitmaps.sh @@ -234,8 +234,8 @@ test_expect_success 'pseudo-merge pattern with capture groups' ' test_commit_bulk 16 && git rev-list HEAD~16.. >in && - sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1" in | - git update-ref --stdin || return 1 + sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1|" in >refs && + git update-ref --stdin file && git add file && - git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && - echo 2 >file && - test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && - git commit -a -m rsa-config -S && - ssh-add "${GPGSSH_KEY_ECDSA}" && - echo 3 >file && - git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && - echo 4 >file && - test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && - git commit -a -m ecdsa-config -S + test_when_finished "test_unconfig user.signingkey" && + mkdir tmpdir && + TMPDIR="$(pwd)/tmpdir" && + ( + export TMPDIR && + ssh-add "${GPGSSH_KEY_PRIMARY}" && + echo 1 >file && git add file && + git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && + echo 2 >file && + git config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && + git commit -a -m rsa-config -S && + ssh-add "${GPGSSH_KEY_ECDSA}" && + echo 3 >file && + git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && + echo 4 >file && + git config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && + git commit -a -m ecdsa-config -S + ) && + find tmpdir -type f >tmpfiles && + test_must_be_empty tmpfiles ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 0c1af43f6fbb41..e56e0c8d7770b0 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -201,6 +201,13 @@ test_expect_success $PREREQ 'cc trailer with get_maintainer.pl output' ' test_cmp expected-cc commandline1 ' +test_expect_failure $PREREQ 'invalid smtp server port value' ' + clean_fake_sendmail && + git send-email -1 --to=recipient@example.com \ + --smtp-server-port=bogus-symbolic-name \ + --smtp-server="$(pwd)/fake.sendmail" +' + test_expect_success $PREREQ 'setup expect' " cat >expected-show-all-headers <<\EOF 0001-Second.patch diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c new file mode 100644 index 00000000000000..d4ba5f9fa52aa2 --- /dev/null +++ b/t/unit-tests/u-string-list.c @@ -0,0 +1,227 @@ +#include "unit-test.h" +#include "string-list.h" + +static void t_vcreate_string_list_dup(struct string_list *list, + int free_util, va_list ap) +{ + const char *arg; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + while ((arg = va_arg(ap, const char *))) + string_list_append(list, arg); +} + +static void t_create_string_list_dup(struct string_list *list, int free_util, ...) +{ + va_list ap; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + va_start(ap, free_util); + t_vcreate_string_list_dup(list, free_util, ap); + va_end(ap); +} + +static void t_string_list_clear(struct string_list *list, int free_util) +{ + string_list_clear(list, free_util); + cl_assert_equal_p(list->items, NULL); + cl_assert_equal_i(list->nr, 0); + cl_assert_equal_i(list->alloc, 0); +} + +static void t_string_list_equal(struct string_list *list, + struct string_list *expected_strings) +{ + cl_assert_equal_i(list->nr, expected_strings->nr); + cl_assert(list->nr <= list->alloc); + for (size_t i = 0; i < expected_strings->nr; i++) + cl_assert_equal_s(list->items[i].string, + expected_strings->items[i].string); +} + +static void t_string_list_split(const char *data, int delim, int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_DUP; + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split(&list, data, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split(void) +{ + t_string_list_split("foo:bar:baz", ':', -1, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:baz", ':', 0, "foo:bar:baz", NULL); + t_string_list_split("foo:bar:baz", ':', 1, "foo", "bar:baz", NULL); + t_string_list_split("foo:bar:baz", ':', 2, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:", ':', -1, "foo", "bar", "", NULL); + t_string_list_split("", ':', -1, "", NULL); + t_string_list_split(":", ':', -1, "", "", NULL); +} + +static void t_string_list_split_in_place(const char *data, const char *delim, + int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_NODUP; + char *string = xstrdup(data); + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_in_place(&list, string, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + free(string); + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_in_place(void) +{ + t_string_list_split_in_place("foo:;:bar:;:baz:;:", ":;", -1, + "foo", "", "", "bar", "", "", "baz", "", "", "", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 0, + "foo:;:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 1, + "foo", ";:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 2, + "foo", "", ":bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:", ":;", -1, + "foo", "", "", "bar", "", "", "", NULL); +} + +static int prefix_cb(struct string_list_item *item, void *cb_data) +{ + const char *prefix = (const char *)cb_data; + return starts_with(item->string, prefix); +} + +static void t_string_list_filter(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + const char *prefix = "y"; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + filter_string_list(list, 0, prefix_cb, (void *)prefix); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__filter(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "no", NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "no", "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "yes", "no", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "y1", "y2", NULL); + t_string_list_filter(&list, "y1", "y2", NULL); + + t_create_string_list_dup(&list, 0, "y2", "y1", NULL); + t_string_list_filter(&list, "y2", "y1", NULL); + + t_create_string_list_dup(&list, 0, "x1", "x2", NULL); + t_string_list_filter(&list, NULL); + + t_string_list_clear(&list, 0); +} + +static void t_string_list_remove_duplicates(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_remove_duplicates(list, 0); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__remove_duplicates(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_remove_duplicates(&list, NULL); + + t_create_string_list_dup(&list, 0, "", NULL); + t_string_list_remove_duplicates(&list, "", NULL); + + t_create_string_list_dup(&list, 0, "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", "b", "b", "b", + "c", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_string_list_clear(&list, 0); +}