diff options
142 files changed, 1229 insertions, 370 deletions
diff --git a/.clang-format b/.clang-format index 6408251577..0b82f3c776 100644 --- a/.clang-format +++ b/.clang-format @@ -72,6 +72,10 @@ AlwaysBreakAfterReturnType: None BinPackArguments: true BinPackParameters: true +# Add no space around the bit field +# unsigned bf:2; +BitFieldColonSpacing: None + # Attach braces to surrounding context except break before braces on function # definitions. # void foo() @@ -96,6 +100,14 @@ BreakStringLiterals: false # Switch statement body is always indented one level more than case labels. IndentCaseLabels: false +# Indents directives before the hash. Each level uses a single space for +# indentation. +# #if FOO +# # include <foo> +# #endif +IndentPPDirectives: AfterHash +PPIndentWidth: 1 + # Don't indent a function definition or declaration if it is wrapped after the # type IndentWrappedFunctionNames: false @@ -108,11 +120,18 @@ PointerAlignment: Right # x = (int32)y; not x = (int32) y; SpaceAfterCStyleCast: false +# No space is inserted after the logical not operator +SpaceAfterLogicalNot: false + # Insert spaces before and after assignment operators # int a = 5; not int a=5; # a += 42; a+=42; SpaceBeforeAssignmentOperators: true +# Spaces will be removed before case colon. +# case 1: break; not case 1 : break; +SpaceBeforeCaseColon: false + # Put a space before opening parentheses only after control statement keywords. # void f() { # if (true) { @@ -124,6 +143,14 @@ SpaceBeforeParens: ControlStatements # Don't insert spaces inside empty '()' SpaceInEmptyParentheses: false +# No space before first '[' in arrays +# int a[5][5]; not int a [5][5]; +SpaceBeforeSquareBrackets: false + +# No space will be inserted into {} +# while (true) {} not while (true) { } +SpaceInEmptyBlock: false + # The number of spaces before trailing line comments (// - comments). # This does not affect trailing block comments (/* - comments). SpacesBeforeTrailingComments: 1 diff --git a/.github/workflows/check-style.yml b/.github/workflows/check-style.yml new file mode 100644 index 0000000000..c052a5df23 --- /dev/null +++ b/.github/workflows/check-style.yml @@ -0,0 +1,34 @@ +name: check-style + +# Get the repository with all commits to ensure that we can analyze +# all of the commits contributed via the Pull Request. + +on: + pull_request: + types: [opened, synchronize] + +# Avoid unnecessary builds. Unlike the main CI jobs, these are not +# ci-configurable (but could be). +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-style: + env: + CC: clang + jobname: ClangFormat + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - run: ci/install-dependencies.sh + + - name: git clang-format + continue-on-error: true + id: check_out + run: | + ./ci/run-style-check.sh \ + "${{github.event.pull_request.base.sha}}" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37b991e080..2589098eff 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,8 +118,31 @@ check-whitespace: image: ubuntu:latest before_script: - ./ci/install-dependencies.sh + # Since $CI_MERGE_REQUEST_TARGET_BRANCH_SHA is only defined for merged + # pipelines, we fallback to $CI_MERGE_REQUEST_DIFF_BASE_SHA, which should + # be defined in all pipelines. script: - - ./ci/check-whitespace.sh "$CI_MERGE_REQUEST_TARGET_BRANCH_SHA" + - | + R=${CI_MERGE_REQUEST_TARGET_BRANCH_SHA-${CI_MERGE_REQUEST_DIFF_BASE_SHA:?}} || exit + ./ci/check-whitespace.sh "$R" + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + +check-style: + image: ubuntu:latest + allow_failure: true + variables: + CC: clang + jobname: ClangFormat + before_script: + - ./ci/install-dependencies.sh + # Since $CI_MERGE_REQUEST_TARGET_BRANCH_SHA is only defined for merged + # pipelines, we fallback to $CI_MERGE_REQUEST_DIFF_BASE_SHA, which should + # be defined in all pipelines. + script: + - | + R=${CI_MERGE_REQUEST_TARGET_BRANCH_SHA-${CI_MERGE_REQUEST_DIFF_BASE_SHA:?}} || exit + ./ci/run-style-check.sh "$R" rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 1d92b2da03..2f35b6691f 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -204,6 +204,33 @@ For shell scripts specifically (not exhaustive): local variable="$value" local variable="$(command args)" + - The common construct + + VAR=VAL command args + + to temporarily set and export environment variable VAR only while + "command args" is running is handy, but this triggers an + unspecified behaviour according to POSIX when used for a command + that is not an external command (like shell functions). Indeed, + dash 0.5.10.2-6 on Ubuntu 20.04, /bin/sh on FreeBSD 13, and AT&T + ksh all make a temporary assignment without exporting the variable, + in such a case. As it does not work portably across shells, do not + use this syntax for shell functions. A common workaround is to do + an explicit export in a subshell, like so: + + (incorrect) + VAR=VAL func args + + (correct) + ( + VAR=VAL && + export VAR && + func args + ) + + but be careful that the effect "func" makes to the variables in the + current shell will be lost across the subshell boundary. + - Use octal escape sequences (e.g. "\302\242"), not hexadecimal (e.g. "\xc2\xa2") in printf format strings, since hexadecimal escape sequences are not portable. @@ -214,6 +241,16 @@ For C programs: - We use tabs to indent, and interpret tabs as taking up to 8 spaces. + - Nested C preprocessor directives are indented after the hash by one + space per nesting level. + + #if FOO + # include <foo.h> + # if BAR + # include <bar.h> + # endif + #endif + - We try to keep to at most 80 characters per line. - As a Git developer we assume you have a reasonably modern compiler @@ -234,7 +271,7 @@ For C programs: . since around 2007 with 2b6854c863a, we have been using initializer elements which are not computable at load time. E.g.: - const char *args[] = {"constant", variable, NULL}; + const char *args[] = { "constant", variable, NULL }; . since early 2012 with e1327023ea, we have been using an enum definition whose last element is followed by a comma. This, like @@ -531,6 +568,42 @@ For C programs: use your own debugger and arguments. Example: `GIT_DEBUGGER="ddd --gdb" ./bin-wrappers/git log` (See `wrap-for-bin.sh`.) + - The primary data structure that a subsystem 'S' deals with is called + `struct S`. Functions that operate on `struct S` are named + `S_<verb>()` and should generally receive a pointer to `struct S` as + first parameter. E.g. + + struct strbuf; + + void strbuf_add(struct strbuf *buf, ...); + + void strbuf_reset(struct strbuf *buf); + + is preferred over: + + struct strbuf; + + void add_string(struct strbuf *buf, ...); + + void reset_strbuf(struct strbuf *buf); + + - There are several common idiomatic names for functions performing + specific tasks on a structure `S`: + + - `S_init()` initializes a structure without allocating the + structure itself. + + - `S_release()` releases a structure's contents without freeing the + structure. + + - `S_clear()` is equivalent to `S_release()` followed by `S_init()` + such that the structure is directly usable after clearing it. When + `S_clear()` is provided, `S_init()` shall not allocate resources + that need to be released again. + + - `S_free()` releases a structure's contents and frees the + structure. + For Perl programs: - Most of the C guidelines above apply. diff --git a/Documentation/RelNotes/2.47.0.txt b/Documentation/RelNotes/2.47.0.txt new file mode 100644 index 0000000000..bdac4c306a --- /dev/null +++ b/Documentation/RelNotes/2.47.0.txt @@ -0,0 +1,77 @@ +Git v2.47 Release Notes +======================= + +UI, Workflows & Features +------------------------ + + * Many Porcelain commands that internally use the merge machinery + were taught to consistently honor the diff.algorithm configuration. + + * A few descriptions in "git show-ref -h" have been clarified. + + * A 'P' command to "git add -p" that passes the patch hunk to the + pager has been added. + + * "git grep -W" omits blank lines that follow the found function at + the end of the file, just like it omits blank lines before the next + function. + + +Performance, Internal Implementation, Development Support etc. +-------------------------------------------------------------- + + * A build tweak knob has been simplified by not setting the value + that is already the default; another unused one has been removed. + + * A CI job that use clang-format to check coding style issues in new + code has been added. + + * The reviewing guidelines document now explicitly encourages people + to give positive reviews and how. + + * Test script linter has been updated to catch an attempt to use + one-shot export construct "VAR=VAL func" for shell functions (which + does not work for some shells) better. + + * Some project conventions have been added to CodingGuidelines. + + +Fixes since v2.46 +----------------- + + * "git add -p" by users with diff.suppressBlankEmpty set to true + failed to parse the patch that represents an unmodified empty line + with an empty line (not a line with a single space on it), which + has been corrected. + (merge 60cf761ed1 pw/add-patch-with-suppress-blank-empty later to maint). + + * "git checkout --ours" (no other arguments) complained that the + option is incompatible with branch switching, which is technically + correct, but found confusing by some users. It now says that the + user needs to give pathspec to specify what paths to checkout. + (merge d1e6c61272 jc/checkout-no-op-switch-errors later to maint). + + * It has been documented that we avoid "VAR=VAL shell_func" and why. + (merge 728a1962cd jc/doc-one-shot-export-with-shell-func later to maint). + + * "git rebase --help" referred to "offset" (the difference between + the location a change was taken from and the change gets replaced) + incorrectly and called it "fuzz", which has been corrected. + (merge 70058db385 jc/doc-rebase-fuzz-vs-offset-fix later to maint). + + * "git notes add -m '' --allow-empty" and friends that take prepared + data to create notes should not invoke an editor, but it started + doing so since Git 2.42, which has been corrected. + (merge 8b426c84f3 dd/notes-empty-no-edit-by-default later to maint). + + * An expensive operation to prepare tracing was done in re-encoding + code path even when the tracing was not requested, which has been + corrected. + (merge 63ad8dbf16 dh/encoding-trace-optim later to maint). + + * Other code cleanup, docfix, build fix, etc. + (merge 8db8786fc2 jt/doc-post-receive-hook-update later to maint). + (merge 1c473dd6af tn/doc-commit-fix later to maint). + (merge bb0498b1bb jc/how-to-maintain-updates later to maint). + (merge 6e71d6ac7c ks/unit-test-comment-typofix later to maint). + (merge 63ee933383 ps/p4-tests-updates later to maint). diff --git a/Documentation/ReviewingGuidelines.txt b/Documentation/ReviewingGuidelines.txt index 515d470d23..6534643cff 100644 --- a/Documentation/ReviewingGuidelines.txt +++ b/Documentation/ReviewingGuidelines.txt @@ -72,12 +72,29 @@ guidance, and concrete tips for interacting with patches on the mailing list. could fix it. This not only helps the author to understand and fix the issue, it also deepens and improves your understanding of the topic. -- Reviews do not need to exclusively point out problems. Feel free to "think out +- Reviews do not need to exclusively point out problems. Positive + reviews indicate that it is not only the original author of the + patches who care about the issue the patches address, and are + highly encouraged. + +- Do not hesitate to give positive reviews on a series from your + work colleague. If your positive review is written well, it will + not make you look as if you two are representing corporate + interest on a series that is otherwise uninteresting to other + community members and shoving it down their throat. + +- Write a positive review in such a way that others can understand + why you support the goal, the approach, and the implementation the + patches took. Make sure to demonstrate that you did thoroughly read + the series and understood problem area well enough to be able to + say that the patches are written well. Feel free to "think out loud" in your review: describe how you read & understood a complex section of a patch, ask a question about something that confused you, point out something - you found exceptionally well-written, etc. In particular, uplifting feedback - goes a long way towards encouraging contributors to participate more actively - in the Git community. + you found exceptionally well-written, etc. + +- In particular, uplifting feedback goes a long way towards + encouraging contributors to participate more actively in the Git + community. ==== Performing your review - Provide your review comments per-patch in a plaintext "Reply-All" email to the diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 89ecfc63a8..c822113c11 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git commit' [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend] - [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)] + [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>] [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 74df345f9e..b18cdbc023 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -737,7 +737,7 @@ The 'apply' backend works by creating a sequence of patches (by calling `format-patch` internally), and then applying the patches in sequence (calling `am` internally). Patches are composed of multiple hunks, each with line numbers, a context region, and the actual changes. The -line numbers have to be taken with some fuzz, since the other side +line numbers have to be taken with some offset, since the other side will likely have inserted or deleted lines earlier in the file. The context region is meant to help find how to adjust the line numbers in order to apply the changes to the right lines. However, if multiple diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 06e997131b..0397dec64d 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -415,13 +415,13 @@ post-receive This hook is invoked by linkgit:git-receive-pack[1] when it reacts to `git push` and updates reference(s) in its repository. -It executes on the remote repository once after all the refs have -been updated. +The hook executes on the remote repository once after all the proposed +ref updates are processed and if at least one ref is updated as the +result. -This hook executes once for the receive operation. It takes no -arguments, but gets the same information as the -<<pre-receive,'pre-receive'>> -hook does on its standard input. +The hook takes no arguments. It receives one line on standard input for +each ref that is successfully updated following the same format as the +<<pre-receive,'pre-receive'>> hook. This hook does not affect the outcome of `git receive-pack`, as it is called after the real work is done. @@ -448,6 +448,9 @@ environment variables will not be set. If the client selects to use push options, but doesn't transmit any, the count variable will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. +See the "post-receive" section in linkgit:git-receive-pack[1] for +additional details. + [[post-update]] post-update ~~~~~~~~~~~ diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt index 013014bbef..41f54050f8 100644 --- a/Documentation/howto/maintain-git.txt +++ b/Documentation/howto/maintain-git.txt @@ -37,22 +37,20 @@ The Policy The policy on Integration is informally mentioned in "A Note from the maintainer" message, which is periodically posted to -this mailing list after each feature release is made. +the mailing list after each feature release is made: - Feature releases are numbered as vX.Y.0 and are meant to contain bugfixes and enhancements in any area, including functionality, performance and usability, without regression. - - One release cycle for a feature release is expected to last for - eight to ten weeks. - - - Maintenance releases are numbered as vX.Y.Z and are meant + - Maintenance releases are numbered as vX.Y.Z (0 < Z) and are meant to contain only bugfixes for the corresponding vX.Y.0 feature release and earlier maintenance releases vX.Y.W (W < Z). - - 'master' branch is used to prepare for the next feature + - The 'master' branch is used to prepare for the next feature release. In other words, at some point, the tip of 'master' - branch is tagged with vX.Y.0. + branch is tagged as vX.(Y+1).0, when vX.Y.0 is the latest + feature release. - 'maint' branch is used to prepare for the next maintenance release. After the feature release vX.Y.0 is made, the tip @@ -63,11 +61,13 @@ this mailing list after each feature release is made. - 'next' branch is used to publish changes (both enhancements and fixes) that (1) have worthwhile goal, (2) are in a fairly good shape suitable for everyday use, (3) but have not yet - demonstrated to be regression free. New changes are tested - in 'next' before merged to 'master'. + demonstrated to be regression free. Reviews from contributors on + the mailing list help to make the determination. After a topic + is merged to 'next', it is tested for at least 7 calendar days + before getting merged to 'master'. - 'seen' branch is used to publish other proposed changes that do - not yet pass the criteria set for 'next'. + not yet pass the criteria set for 'next' (see above). - The tips of 'master' and 'maint' branches will not be rewound to allow people to build their own customization on top of them. @@ -86,6 +86,38 @@ this mailing list after each feature release is made. users are encouraged to test it so that regressions and bugs are found before new topics are merged to 'master'. + - When a problem is found in a topic in 'next', the topic is marked + not to be merged to 'master'. Follow-up patches are discussed on + the mailing list and applied to the topic after being reviewed and + then the topic is merged (again) to 'next'. After going through + the usual testing in 'next', the entire (fixed) topic is merged + to 'master'. + + - One release cycle for a feature release is expected to last for + eight to ten weeks. A few "release candidate" releases are + expected to be tagged about a week apart before the final + release, and a "preview" release is tagged about a week before + the first release candidate gets tagged. + + - After the preview release is tagged, topics that were well + reviewed may be merged to 'master' before spending the usual 7 + calendar days in 'next', with the expectation that any bugs in + them can be caught and fixed in the release candidates before + the final release. + + - After the first release candidate is tagged, the contributors are + strongly encouraged to focus on finding and fixing new regressions + introduced during the cycle, over addressing old bugs and any new + features. Topics stop getting merged down from 'next' to 'master', + and new topics stop getting merged to 'next'. Unless they are fixes + to new regressions in the cycle, that is. + + - Soon after a feature release is made, the tip of 'maint' gets + fast-forwarded to point at the release. Topics that have been + kept in 'next' are merged down to 'master' and a new development + cycle starts. + + Note that before v1.9.0 release, the version numbers used to be structured slightly differently. vX.Y.Z were feature releases while vX.Y.Z.W were maintenance releases for vX.Y.Z. @@ -179,12 +211,12 @@ by doing the following: The initial round is done with: $ git checkout ai/topic ;# or "git checkout -b ai/topic master" - $ git am -sc3 mailbox + $ git am -sc3 --whitespace=warn mailbox and replacing an existing topic with subsequent round is done with: $ git checkout master...ai/topic ;# try to reapply to the same base - $ git am -sc3 mailbox + $ git am -sc3 --whitespace=warn mailbox to prepare the new round on a detached HEAD, and then @@ -209,39 +241,59 @@ by doing the following: (trivial typofixes etc. are often squashed directly into the patches that need fixing, without being applied as a separate "SQUASH???" commit), so that they can be removed easily as needed. + The expectation is that the original author will make corrections + in a reroll. + - By now, new topic branches are created and existing topic + branches are updated. The integration branches 'next', 'jch', + and 'seen' need to be updated to contain them. - - Merge maint to master as needed: + - If there are topics that have been merged to 'master' and should + be merged to 'maint', merge them to 'maint', and update the + release notes to the next maintenance release. - $ git checkout master - $ git merge maint - $ make test + - Review the latest issue of "What's cooking" again. Are topics + that have been sufficiently long in 'next' ready to be merged to + 'master'? Are topics we saw earlier and are in 'seen' now got + positive reviews and are ready to be merged to 'next'? - - Merge master to next as needed: + - If there are topics that have been cooking in 'next' long enough + and should be merged to 'master', merge them to 'master', and + update the release notes to the next feature release. - $ git checkout next - $ git merge master - $ make test + - If there were patches directly made on 'maint', merge 'maint' to + 'master'; make sure that the result is what you want. - - Review the last issue of "What's cooking" again and see if topics - that are ready to be merged to 'next' are still in good shape - (e.g. has there any new issue identified on the list with the - series?) + $ git checkout master + $ git merge -m "Sync with 'maint'" --no-log maint + $ git log -p --first-parent ORIG_HEAD.. + $ make test - - Prepare 'jch' branch, which is used to represent somewhere - between 'master' and 'seen' and often is slightly ahead of 'next'. + - Prepare to update the 'jch' branch, which is used to represent + somewhere between 'master' and 'seen' and often is slightly ahead + of 'next', and the 'seen' branch, which is used to hold the rest. $ Meta/Reintegrate master..jch >Meta/redo-jch.sh The result is a script that lists topics to be merged in order to - rebuild 'seen' as the input to Meta/Reintegrate script. Remove - later topics that should not be in 'jch' yet. Add a line that - consists of '### match next' before the name of the first topic - in the output that should be in 'jch' but not in 'next' yet. + rebuild the current 'jch'. Do the same for 'seen'. + + - Review the Meta/redo-jch.sh and Meta/redo-seen.sh scripts. The + former should have a line '### match next'---the idea is that + merging the topics listed before the line on top of 'master' + should result in a tree identical to that of 'next'. - - Now we are ready to start merging topics to 'next'. For each - branch whose tip is not merged to 'next', one of three things can - happen: + - As newly created topics are usually merged near the tip of + 'seen', add them to the end of the Meta/redo-seen.sh script. + Among the topics that were in 'seen', there may be ones that + are not quite ready for 'next' but are getting there. Move + them from Meta/redo-seen.sh to the end of Meta/redo-jch.sh. + The expectation is that you'd use 'jch' as your daily driver + as the first guinea pig, so you should choose carefully. + + - Now we are ready to start rebuilding 'jch' and merging topics to + 'next'. For each branch whose tip is not merged to 'next', one + of three things can happen: - The commits are all next-worthy; merge the topic to next; - The new parts are of mixed quality, but earlier ones are @@ -252,10 +304,12 @@ by doing the following: If a topic that was already in 'next' gained a patch, the script would list it as "ai/topic~1". To include the new patch to the updated 'next', drop the "~1" part; to keep it excluded, do not - touch the line. If a topic that was not in 'next' should be - merged to 'next', add it at the end of the list. Then: + touch the line. + + If a topic that was not in 'next' should be merged to 'next', add + it before the '### match next' line. Then: - $ git checkout -B jch master + $ git checkout --detach master $ sh Meta/redo-jch.sh -c1 to rebuild the 'jch' branch from scratch. "-c1" tells the script @@ -267,26 +321,29 @@ by doing the following: reference to the variable under its old name), in which case prepare an appropriate merge-fix first (see appendix), and rebuild the 'jch' branch from scratch, starting at the tip of - 'master'. + 'master', this time without using "-c1" to merge all topics. - Then do the same to 'next' + Then do the same to 'next'. $ git checkout next $ sh Meta/redo-jch.sh -c1 -e The "-e" option allows the merge message that comes from the history of the topic and the comments in the "What's cooking" to - be edited. The resulting tree should match 'jch' as the same set - of topics are merged on 'master'; otherwise there is a mismerge. - Investigate why and do not proceed until the mismerge is found - and rectified. + be edited. The resulting tree should match 'jch^{/^### match next'}' + as the same set of topics are merged on 'master'; otherwise there + is a mismerge. Investigate why and do not proceed until the mismerge + is found and rectified. + + If 'master' was updated before you started redoing 'next', then - $ git diff jch next + $ git diff 'jch^{/^### match next}' next - Then build the rest of 'jch': + would show differences that went into 'master' (which 'jch' has, + but 'next' does not yet---often it is updates to the release + notes). Merge 'master' back to 'next' if that is the case. - $ git checkout jch - $ sh Meta/redo-jch.sh + $ git merge -m "Sync with 'master'" --no-log master When all is well, clean up the redo-jch.sh script with @@ -296,12 +353,7 @@ by doing the following: merged to 'master'. This may lose '### match next' marker; add it again to the appropriate place when it happens. - - Rebuild 'seen'. - - $ Meta/Reintegrate jch..seen >Meta/redo-seen.sh - - Edit the result by adding new topics that are not still in 'seen' - in the script. Then + - Rebuild 'seen' on top of 'jch'. $ git checkout -B seen jch $ sh Meta/redo-seen.sh @@ -312,7 +364,7 @@ by doing the following: Double check by running - $ git branch --no-merged seen + $ git branch --no-merged seen '??/*' to see there is no unexpected leftover topics. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 7b81915e52..ee71e9d8aa 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.46.0 +DEF_VER=v2.46.GIT LF=' ' @@ -1340,6 +1340,7 @@ UNIT_TEST_PROGRAMS += t-oidmap UNIT_TEST_PROGRAMS += t-oidtree UNIT_TEST_PROGRAMS += t-prio-queue UNIT_TEST_PROGRAMS += t-reftable-basics +UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-pq UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf @@ -1377,7 +1378,7 @@ PTHREAD_CFLAGS = # For the 'sparse' target SPARSE_FLAGS ?= -std=gnu99 -SP_EXTRA_FLAGS = -Wno-universal-initializer +SP_EXTRA_FLAGS = # For informing GIT-BUILD-OPTIONS of the SANITIZE=leak,address targets SANITIZE_LEAK = @@ -2681,7 +2682,6 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/dump.o -REFTABLE_TEST_OBJS += reftable/merged_test.o REFTABLE_TEST_OBJS += reftable/readwrite_test.o REFTABLE_TEST_OBJS += reftable/stack_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o @@ -1 +1 @@ -Documentation/RelNotes/2.46.0.txt
\ No newline at end of file +Documentation/RelNotes/2.47.0.txt
\ No newline at end of file diff --git a/add-patch.c b/add-patch.c index 6e176cd21a..1cec4ab67b 100644 --- a/add-patch.c +++ b/add-patch.c @@ -7,9 +7,11 @@ #include "environment.h" #include "gettext.h" #include "object-name.h" +#include "pager.h" #include "read-cache-ll.h" #include "repository.h" #include "strbuf.h" +#include "sigchain.h" #include "run-command.h" #include "strvec.h" #include "pathspec.h" @@ -402,6 +404,12 @@ static void complete_file(char marker, struct hunk *hunk) hunk->splittable_into++; } +/* Empty context lines may omit the leading ' ' */ +static int normalize_marker(const char *p) +{ + return p[0] == '\n' || (p[0] == '\r' && p[1] == '\n') ? ' ' : p[0]; +} + static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; @@ -487,6 +495,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) while (p != pend) { char *eol = memchr(p, '\n', pend - p); const char *deleted = NULL, *mode_change = NULL; + char ch = normalize_marker(p); if (!eol) eol = pend; @@ -534,7 +543,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) * Start counting into how many hunks this one can be * split */ - marker = *p; + marker = ch; } else if (hunk == &file_diff->head && starts_with(p, "new file")) { file_diff->added = 1; @@ -588,10 +597,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) (int)(eol - (plain->buf + file_diff->head.start)), plain->buf + file_diff->head.start); - if ((marker == '-' || marker == '+') && *p == ' ') + if ((marker == '-' || marker == '+') && ch == ' ') hunk->splittable_into++; - if (marker && *p != '\\') - marker = *p; + if (marker && ch != '\\') + marker = ch; p = eol == pend ? pend : eol + 1; hunk->end = p - plain->buf; @@ -815,7 +824,7 @@ static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff, (int)(hunk->end - hunk->start), plain + hunk->start); - if (plain[overlap_end] != ' ') + if (normalize_marker(&plain[overlap_end]) != ' ') return error(_("expected context line " "#%d in\n%.*s"), (int)(j + 1), @@ -955,7 +964,7 @@ static int split_hunk(struct add_p_state *s, struct file_diff *file_diff, context_line_count = 0; while (splittable_into > 1) { - ch = s->plain.buf[current]; + ch = normalize_marker(&s->plain.buf[current]); if (!ch) BUG("buffer overrun while splitting hunks"); @@ -1173,14 +1182,14 @@ static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk, header->old_count = header->new_count = 0; for (i = hunk->start; i < hunk->end; ) { - switch (s->plain.buf[i]) { + switch(normalize_marker(&s->plain.buf[i])) { case '-': header->old_count++; break; case '+': header->new_count++; break; - case ' ': case '\r': case '\n': + case ' ': header->old_count++; header->new_count++; break; @@ -1391,7 +1400,7 @@ N_("j - leave this hunk undecided, see next undecided hunk\n" "/ - search for a hunk matching the given regex\n" "s - split the current hunk into smaller hunks\n" "e - manually edit the current hunk\n" - "p - print the current hunk\n" + "p - print the current hunk, 'P' to use the pager\n" "? - print help\n"); static int patch_update_file(struct add_p_state *s, @@ -1402,7 +1411,7 @@ static int patch_update_file(struct add_p_state *s, struct hunk *hunk; char ch; struct child_process cp = CHILD_PROCESS_INIT; - int colored = !!s->colored.len, quit = 0; + int colored = !!s->colored.len, quit = 0, use_pager = 0; enum prompt_mode_type prompt_mode_type; enum { ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0, @@ -1452,9 +1461,18 @@ static int patch_update_file(struct add_p_state *s, strbuf_reset(&s->buf); if (file_diff->hunk_nr) { if (rendered_hunk_index != hunk_index) { + if (use_pager) { + setup_pager(); + sigchain_push(SIGPIPE, SIG_IGN); + } render_hunk(s, hunk, 0, colored, &s->buf); fputs(s->buf.buf, stdout); rendered_hunk_index = hunk_index; + if (use_pager) { + sigchain_pop(SIGPIPE); + wait_for_pager(); + use_pager = 0; + } } strbuf_reset(&s->buf); @@ -1675,8 +1693,9 @@ soft_increment: hunk->use = USE_HUNK; goto soft_increment; } - } else if (s->answer.buf[0] == 'p') { + } else if (ch == 'p') { rendered_hunk_index = -1; + use_pager = (s->answer.buf[0] == 'P') ? 1 : 0; } else if (s->answer.buf[0] == '?') { const char *p = _(help_patch_remainder), *eol = p; diff --git a/builtin/am.c b/builtin/am.c index 370f5593f2..a12be088f7 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1630,7 +1630,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa * changes. */ - init_merge_options(&o, the_repository); + init_ui_merge_options(&o, the_repository); o.branch1 = "HEAD"; their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg); diff --git a/builtin/checkout.c b/builtin/checkout.c index 3cf44b4683..0f21ddd2c6 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -884,7 +884,7 @@ static int merge_working_tree(const struct checkout_opts *opts, add_files_to_cache(the_repository, NULL, NULL, NULL, 0, 0); - init_merge_options(&o, the_repository); + init_ui_merge_options(&o, the_repository); o.verbosity = 0; work = write_in_core_index_as_tree(the_repository); @@ -1572,6 +1572,10 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, static int checkout_branch(struct checkout_opts *opts, struct branch_info *new_branch_info) { + int noop_switch = (!new_branch_info->name && + !opts->new_branch && + !opts->force_detach); + if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); @@ -1583,9 +1587,14 @@ static int checkout_branch(struct checkout_opts *opts, die(_("'%s' cannot be used with switching branches"), "--[no]-overlay"); - if (opts->writeout_stage) - die(_("'%s' cannot be used with switching branches"), - "--ours/--theirs"); + if (opts->writeout_stage) { + const char *msg; + if (noop_switch) + msg = _("'%s' needs the paths to check out"); + else + msg = _("'%s' cannot be used with switching branches"); + die(msg, "--ours/--theirs"); + } if (opts->force && opts->merge) die(_("'%s' cannot be used with '%s'"), "-f", "-m"); @@ -1612,10 +1621,8 @@ static int checkout_branch(struct checkout_opts *opts, die(_("Cannot switch branch to a non-commit '%s'"), new_branch_info->name); - if (!opts->switch_branch_doing_nothing_is_ok && - !new_branch_info->name && - !opts->new_branch && - !opts->force_detach) + if (noop_switch && + !opts->switch_branch_doing_nothing_is_ok) die(_("missing branch or commit argument")); if (!opts->implicit_detach && diff --git a/builtin/commit.c b/builtin/commit.c index dec78dfb86..66427ba82d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -41,7 +41,7 @@ static const char * const builtin_commit_usage[] = { N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n" - " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)]\n" + " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>]\n" " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n" " [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n" " [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n" diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 3db8df70a9..aaf2f8438b 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -88,6 +88,8 @@ static void spawn_daemon(const char *socket) die_errno("unable to read result code from cache daemon"); if (r != 3 || memcmp(buf, "ok\n", 3)) die("cache daemon did not start: %.*s", r, buf); + + child_process_clear(&daemon); close(daemon.out); } @@ -137,7 +139,8 @@ static void announce_capabilities(void) int cmd_credential_cache(int argc, const char **argv, const char *prefix) { - char *socket_path = NULL; + const char *socket_path_arg = NULL; + char *socket_path; int timeout = 900; const char *op; const char * const usage[] = { @@ -147,7 +150,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_INTEGER(0, "timeout", &timeout, "number of seconds to cache credentials"), - OPT_STRING(0, "socket", &socket_path, "path", + OPT_STRING(0, "socket", &socket_path_arg, "path", "path of cache-daemon socket"), OPT_END() }; @@ -160,6 +163,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) if (!have_unix_sockets()) die(_("credential-cache unavailable; no unix socket support")); + socket_path = xstrdup_or_null(socket_path_arg); if (!socket_path) socket_path = get_socket_path(); if (!socket_path) @@ -176,6 +180,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) else ; /* ignore unknown operation */ + free(socket_path); return 0; } diff --git a/builtin/credential-store.c b/builtin/credential-store.c index 494c809332..97968bfa1c 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -218,5 +218,6 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix) ; /* Ignore unknown operation. */ string_list_clear(&fns, 0); + credential_clear(&c); return 0; } diff --git a/builtin/describe.c b/builtin/describe.c index cf8edc4222..954929c514 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -529,6 +529,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst) traverse_commit_list(&revs, process_commit, process_object, &pcd); reset_revision_walk(); release_revisions(&revs); + strvec_clear(&args); } static void describe(const char *arg, int last_one) @@ -619,6 +620,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (contains) { struct string_list_item *item; struct strvec args; + const char **argv_copy; + int ret; strvec_init(&args); strvec_pushl(&args, "name-rev", @@ -637,7 +640,21 @@ int cmd_describe(int argc, const char **argv, const char *prefix) strvec_pushv(&args, argv); else strvec_push(&args, "HEAD"); - return cmd_name_rev(args.nr, args.v, prefix); + + /* + * `cmd_name_rev()` modifies the array, so we'd leak its + * contained strings if we didn't do a copy here. + */ + ALLOC_ARRAY(argv_copy, args.nr + 1); + for (size_t i = 0; i < args.nr; i++) + argv_copy[i] = args.v[i]; + argv_copy[args.nr] = NULL; + + ret = cmd_name_rev(args.nr, argv_copy, prefix); + + strvec_clear(&args); + free(argv_copy); + return ret; } hashmap_init(&names, commit_name_neq, NULL, 0); @@ -679,7 +696,6 @@ int cmd_describe(int argc, const char **argv, const char *prefix) } else if (dirty) { struct lock_file index_lock = LOCK_INIT; struct rev_info revs; - struct strvec args = STRVEC_INIT; int fd; setup_work_tree(); @@ -694,8 +710,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) repo_update_index_if_able(the_repository, &index_lock); repo_init_revisions(the_repository, &revs, prefix); - strvec_pushv(&args, diff_index_args); - if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) + + if (setup_revisions(ARRAY_SIZE(diff_index_args) - 1, + diff_index_args, &revs, NULL) != 1) BUG("malformed internal diff-index command line"); run_diff_index(&revs, 0); diff --git a/builtin/log.c b/builtin/log.c index 4d4b60caa7..a73a767606 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1434,6 +1434,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, int need_8bit_cte = 0; struct pretty_print_context pp = {0}; struct commit *head = list[0]; + char *to_free = NULL; if (!cmit_fmt_is_mail(rev->commit_format)) die(_("cover letter needs email format")); @@ -1455,7 +1456,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, } if (!branch_name) - branch_name = find_branch_name(rev); + branch_name = to_free = find_branch_name(rev); pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; @@ -1466,6 +1467,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, encoding, need_8bit_cte, cfg); fprintf(rev->diffopt.file, "%s\n", sb.buf); + free(to_free); free(pp.after_subject); strbuf_release(&sb); diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index debf2d4f88..5b61af5d78 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -19,17 +19,16 @@ static const char * const ls_remote_usage[] = { * Is there one among the list of patterns that match the tail part * of the path? */ -static int tail_match(const char **pattern, const char *path) +static int tail_match(const struct strvec *pattern, const char *path) { - const char *p; char *pathbuf; - if (!pattern) + if (!pattern->nr) return 1; /* no restriction */ pathbuf = xstrfmt("/%s", path); - while ((p = *(pattern++)) != NULL) { - if (!wildmatch(p, pathbuf, 0)) { + for (size_t i = 0; i < pattern->nr; i++) { + if (!wildmatch(pattern->v[i], pathbuf, 0)) { free(pathbuf); return 1; } @@ -47,7 +46,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) int status = 0; int show_symref_target = 0; const char *uploadpack = NULL; - const char **pattern = NULL; + struct strvec pattern = STRVEC_INIT; struct transport_ls_refs_options transport_options = TRANSPORT_LS_REFS_OPTIONS_INIT; int i; @@ -93,13 +92,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) packet_trace_identity("ls-remote"); - if (argc > 1) { - int i; - CALLOC_ARRAY(pattern, argc); - for (i = 1; i < argc; i++) { - pattern[i - 1] = xstrfmt("*/%s", argv[i]); - } - } + for (int i = 1; i < argc; i++) + strvec_pushf(&pattern, "*/%s", argv[i]); if (flags & REF_TAGS) strvec_push(&transport_options.ref_prefixes, "refs/tags/"); @@ -136,7 +130,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) struct ref_array_item *item; if (!check_ref_type(ref, flags)) continue; - if (!tail_match(pattern, ref->name)) + if (!tail_match(&pattern, ref->name)) continue; item = ref_array_push(&ref_array, ref->name, &ref->old_oid); item->symref = xstrdup_or_null(ref->symref); @@ -158,5 +152,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (transport_disconnect(transport)) status = 1; transport_ls_refs_options_release(&transport_options); + + strvec_clear(&pattern); return status; } diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 82bebea15b..e951b09805 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -31,7 +31,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED) char *better1, *better2; struct commit *result; - init_merge_options(&o, the_repository); + init_basic_merge_options(&o, the_repository); if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index dab2fdc2a6..9bca9b5f33 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -571,7 +571,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) }; /* Init merge options */ - init_merge_options(&o.merge_options, the_repository); + init_ui_merge_options(&o.merge_options, the_repository); /* Parse arguments */ original_argc = argc - 1; /* ignoring argv[0] */ diff --git a/builtin/merge.c b/builtin/merge.c index 9fba27d85d..c896b18d1a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -724,7 +724,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, return 2; } - init_merge_options(&o, the_repository); + init_ui_merge_options(&o, the_repository); if (!strcmp(strategy, "subtree")) o.subtree_shift = ""; diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 70e9ec4e47..f62c0a36cb 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -677,7 +677,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) always, allow_undefined, data.name_only); } - UNLEAK(string_pool); - UNLEAK(revs); + string_list_clear(&data.ref_filters, 0); + string_list_clear(&data.exclude_filters, 0); + mem_pool_discard(&string_pool, 0); + object_array_clear(&revs); return 0; } diff --git a/builtin/notes.c b/builtin/notes.c index d9c356e354..4cc5bfedc3 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -114,7 +114,6 @@ struct note_msg { }; struct note_data { - int given; int use_editor; int stripspace; char *edit_path; @@ -193,7 +192,7 @@ static void write_commented_object(int fd, const struct object_id *object) static void prepare_note_data(const struct object_id *object, struct note_data *d, const struct object_id *old_note) { - if (d->use_editor || !d->given) { + if (d->use_editor || !d->msg_nr) { int fd; struct strbuf buf = STRBUF_INIT; @@ -201,7 +200,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * d->edit_path = git_pathdup("NOTES_EDITMSG"); fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (d->given) + if (d->msg_nr) write_or_die(fd, d->buf.buf, d->buf.len); else if (old_note) copy_obj_to_fd(fd, old_note); @@ -515,7 +514,6 @@ static int add(int argc, const char **argv, const char *prefix) if (d.msg_nr) concat_messages(&d); - d.given = !!d.buf.len; object_ref = argc > 1 ? argv[1] : "HEAD"; @@ -528,7 +526,7 @@ static int add(int argc, const char **argv, const char *prefix) if (note) { if (!force) { free_notes(t); - if (d.given) { + if (d.msg_nr) { free_note_data(&d); return error(_("Cannot add notes. " "Found existing notes for object %s. " @@ -690,14 +688,14 @@ static int append_edit(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); } - if (d.msg_nr) + if (d.msg_nr) { concat_messages(&d); - d.given = !!d.buf.len; - - if (d.given && edit) - fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " - "for the 'edit' subcommand.\n" - "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); + if (edit) + fprintf(stderr, _("The -m/-F/-c/-C options have been " + "deprecated for the 'edit' subcommand.\n" + "Please use 'git notes add -f -m/-F/-c/-C' " + "instead.\n")); + } object_ref = 1 < argc ? argv[1] : "HEAD"; diff --git a/builtin/remote.c b/builtin/remote.c index 08292498bd..9d54fddf8c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -258,7 +258,7 @@ struct branch_info { char *push_remote_name; }; -static struct string_list branch_list = STRING_LIST_INIT_NODUP; +static struct string_list branch_list = STRING_LIST_INIT_DUP; static const char *abbrev_ref(const char *name, const char *prefix) { @@ -292,8 +292,8 @@ static int config_read_branches(const char *key, const char *value, type = PUSH_REMOTE; else return 0; - name = xmemdupz(key, key_len); + name = xmemdupz(key, key_len); item = string_list_insert(&branch_list, name); if (!item->util) @@ -337,6 +337,7 @@ static int config_read_branches(const char *key, const char *value, BUG("unexpected type=%d", type); } + free(name); return 0; } @@ -554,13 +555,16 @@ static int add_branch_for_removal(const char *refname, refspec.dst = (char *)refname; if (remote_find_tracking(branches->remote, &refspec)) return 0; + free(refspec.src); /* don't delete a branch if another remote also uses it */ for (kr = branches->keep->list; kr; kr = kr->next) { memset(&refspec, 0, sizeof(refspec)); refspec.dst = (char *)refname; - if (!remote_find_tracking(kr->remote, &refspec)) + if (!remote_find_tracking(kr->remote, &refspec)) { + free(refspec.src); return 0; + } } /* don't delete non-remote-tracking refs */ @@ -667,7 +671,11 @@ static int config_read_push_default(const char *key, const char *value, static void handle_push_default(const char* old_name, const char* new_name) { struct push_default_info push_default = { - old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 }; + .old_name = old_name, + .scope = CONFIG_SCOPE_UNKNOWN, + .origin = STRBUF_INIT, + .linenr = -1, + }; git_config(config_read_push_default, &push_default); if (push_default.scope >= CONFIG_SCOPE_COMMAND) ; /* pass */ @@ -687,6 +695,8 @@ static void handle_push_default(const char* old_name, const char* new_name) push_default.origin.buf, push_default.linenr, old_name); } + + strbuf_release(&push_default.origin); } @@ -784,7 +794,7 @@ static int mv(int argc, const char **argv, const char *prefix) } if (!refspec_updated) - return 0; + goto out; /* * First remove symrefs, then rename the rest, finally create @@ -850,10 +860,15 @@ static int mv(int argc, const char **argv, const char *prefix) display_progress(progress, ++refs_renamed_nr); } stop_progress(&progress); - string_list_clear(&remote_branches, 1); handle_push_default(rename.old_name, rename.new_name); +out: + string_list_clear(&remote_branches, 1); + strbuf_release(&old_remote_context); + strbuf_release(&buf); + strbuf_release(&buf2); + strbuf_release(&buf3); return 0; } @@ -944,12 +959,21 @@ static int rm(int argc, const char **argv, const char *prefix) if (!result) { strbuf_addf(&buf, "remote.%s", remote->name); - if (git_config_rename_section(buf.buf, NULL) < 1) - return error(_("Could not remove config section '%s'"), buf.buf); + if (git_config_rename_section(buf.buf, NULL) < 1) { + result = error(_("Could not remove config section '%s'"), buf.buf); + goto out; + } handle_push_default(remote->name, NULL); } +out: + for (struct known_remote *r = known_remotes.list; r;) { + struct known_remote *next = r->next; + free(r); + r = next; + } + strbuf_release(&buf); return result; } @@ -982,8 +1006,10 @@ static int append_ref_to_tracked_list(const char *refname, memset(&refspec, 0, sizeof(refspec)); refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) + if (!remote_find_tracking(states->remote, &refspec)) { string_list_append(&states->tracked, abbrev_branch(refspec.src)); + free(refspec.src); + } return 0; } diff --git a/builtin/replay.c b/builtin/replay.c index 0448326636..138198ce9c 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -151,7 +151,7 @@ static void get_ref_information(struct rev_cmdline_info *cmd_info, static void determine_replay_mode(struct rev_cmdline_info *cmd_info, const char *onto_name, - const char **advance_name, + char **advance_name, struct commit **onto, struct strset **update_refs) { @@ -174,6 +174,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, *onto = peel_committish(*advance_name); if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name), &oid, &fullname, 0) == 1) { + free(*advance_name); *advance_name = fullname; } else { die(_("argument to --advance must be a reference")); @@ -197,6 +198,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, if (negative_refs_complete) { struct hashmap_iter iter; struct strmap_entry *entry; + const char *last_key = NULL; if (rinfo.negative_refexprs == 0) die(_("all positive revisions given must be references")); @@ -208,8 +210,11 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, /* Only one entry, but we have to loop to get it */ strset_for_each_entry(&rinfo.negative_refs, &iter, entry) { - *advance_name = entry->key; + last_key = entry->key; } + + free(*advance_name); + *advance_name = xstrdup_or_null(last_key); } else { /* positive_refs_complete */ if (rinfo.negative_refexprs > 1) die(_("cannot implicitly determine correct base for --onto")); @@ -271,7 +276,8 @@ static struct commit *pick_regular_commit(struct commit *pickme, int cmd_replay(int argc, const char **argv, const char *prefix) { - const char *advance_name = NULL; + const char *advance_name_opt = NULL; + char *advance_name = NULL; struct commit *onto = NULL; const char *onto_name = NULL; int contained = 0; @@ -292,7 +298,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) NULL }; struct option replay_options[] = { - OPT_STRING(0, "advance", &advance_name, + OPT_STRING(0, "advance", &advance_name_opt, N_("branch"), N_("make replay advance given branch")), OPT_STRING(0, "onto", &onto_name, @@ -306,14 +312,15 @@ int cmd_replay(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, replay_options, replay_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); - if (!onto_name && !advance_name) { + if (!onto_name && !advance_name_opt) { error(_("option --onto or --advance is mandatory")); usage_with_options(replay_usage, replay_options); } - if (advance_name && contained) + if (advance_name_opt && contained) die(_("options '%s' and '%s' cannot be used together"), "--advance", "--contained"); + advance_name = xstrdup_or_null(advance_name_opt); repo_init_revisions(the_repository, &revs, prefix); @@ -377,7 +384,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) goto cleanup; } - init_merge_options(&merge_opt, the_repository); + init_basic_merge_options(&merge_opt, the_repository); memset(&result, 0, sizeof(result)); merge_opt.show_rename_progress = 0; last_commit = onto; @@ -441,6 +448,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) cleanup: release_revisions(&revs); + free(advance_name); /* Return */ if (ret < 0) diff --git a/builtin/rerere.c b/builtin/rerere.c index b2efc6f640..81b65ffa39 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -73,11 +73,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) if (!strcmp(argv[0], "forget")) { struct pathspec pathspec; + int ret; + if (argc < 2) warning(_("'git rerere forget' without paths is deprecated")); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv + 1); - return rerere_forget(the_repository, &pathspec); + + ret = rerere_forget(the_repository, &pathspec); + + clear_pathspec(&pathspec); + return ret; } if (!strcmp(argv[0], "clear")) { diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 2e64f5bda7..5845d3f59b 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -553,7 +553,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) strbuf_release(&sb); strvec_clear(&longnames); strvec_clear(&usage); - free((char *) opts->help); + for (size_t i = 0; i < opts_nr; i++) { + free((char *) opts[i].help); + free((char *) opts[i].argh); + } free(opts); return 0; } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 5bde7c68c2..b529608c92 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -514,4 +514,5 @@ void shortlog_output(struct shortlog *log) string_list_clear(&log->list, 1); clear_mailmap(&log->mailmap); string_list_clear(&log->format, 0); + string_list_clear(&log->trailers, 0); } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index d72f4cb98d..7d797a880c 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -502,14 +502,14 @@ static int rev_is_head(const char *head, const char *name) return !strcmp(head, name); } -static int show_merge_base(struct commit_list *seen, int num_rev) +static int show_merge_base(const struct commit_list *seen, int num_rev) { int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); int exit_status = 1; - while (seen) { - struct commit *commit = pop_commit(&seen); + for (const struct commit_list *s = seen; s; s = s->next) { + struct commit *commit = s->item; int flags = commit->object.flags & all_mask; if (!(flags & UNINTERESTING) && ((flags & all_revs) == all_revs)) { @@ -635,7 +635,7 @@ static int parse_reflog_param(const struct option *opt, const char *arg, int cmd_show_branch(int ac, const char **av, const char *prefix) { struct commit *rev[MAX_REVS], *commit; - char *reflog_msg[MAX_REVS]; + char *reflog_msg[MAX_REVS] = {0}; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; @@ -692,6 +692,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) parse_reflog_param), OPT_END() }; + const char **args_copy = NULL; + int ret; init_commit_name_slab(&name_slab); @@ -699,8 +701,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) /* If nothing is specified, try the default first */ if (ac == 1 && default_args.nr) { + DUP_ARRAY(args_copy, default_args.v, default_args.nr); ac = default_args.nr; - av = default_args.v; + av = args_copy; } ac = parse_options(ac, av, prefix, builtin_show_branch_options, @@ -780,7 +783,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) } for (i = 0; i < reflog; i++) { - char *logmsg; + char *logmsg = NULL; char *nth_desc; const char *msg; char *end; @@ -790,6 +793,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (read_ref_at(get_main_ref_store(the_repository), ref, flags, 0, base + i, &oid, &logmsg, ×tamp, &tz, NULL)) { + free(logmsg); reflog = i; break; } @@ -842,7 +846,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); - exit(0); + ret = 0; + goto out; } for (num_rev = 0; ref_name[num_rev]; num_rev++) { @@ -879,11 +884,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) commit_list_sort_by_date(&seen); - if (merge_base) - return show_merge_base(seen, num_rev); + if (merge_base) { + ret = show_merge_base(seen, num_rev); + goto out; + } - if (independent) - return show_independent(rev, num_rev, rev_mask); + if (independent) { + ret = show_independent(rev, num_rev, rev_mask); + goto out; + } /* Show list; --more=-1 means list-only */ if (1 < num_rev || extra < 0) { @@ -919,8 +928,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) putchar('\n'); } } - if (extra < 0) - exit(0); + if (extra < 0) { + ret = 0; + goto out; + } /* Sort topologically */ sort_in_topological_order(&seen, sort_order); @@ -932,8 +943,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); all_revs = all_mask & ~((1u << REV_SHIFT) - 1); - while (seen) { - struct commit *commit = pop_commit(&seen); + for (struct commit_list *l = seen; l; l = l->next) { + struct commit *commit = l->item; int this_flag = commit->object.flags; int is_merge_point = ((this_flag & all_revs) == all_revs); @@ -973,6 +984,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (shown_merge_point && --extra < 0) break; } + + ret = 0; + +out: + for (size_t i = 0; i < ARRAY_SIZE(reflog_msg); i++) + free(reflog_msg[i]); + free_commit_list(seen); + free_commit_list(list); + free(args_copy); free(head); - return 0; + return ret; } diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 839a5c29f3..85700caae9 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -293,8 +293,8 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) struct show_one_options show_one_opts = {0}; int verify = 0, exists = 0; const struct option show_ref_options[] = { - OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with branches)")), - OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with tags)")), + OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with --branches)")), + OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with --tags)")), OPT_HIDDEN_BOOL(0, "heads", &patterns_opts.branches_only, N_("deprecated synonym for --branches")), OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")), diff --git a/builtin/stash.c b/builtin/stash.c index 46b981c4dd..d90e072ddc 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -574,7 +574,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, } } - init_merge_options(&o, the_repository); + init_ui_merge_options(&o, the_repository); o.branch1 = "Updated upstream"; o.branch2 = "Stashed changes"; @@ -1521,6 +1521,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q struct strbuf patch = STRBUF_INIT; struct strbuf stash_msg_buf = STRBUF_INIT; struct strbuf untracked_files = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; if (patch_mode && keep_index == -1) keep_index = 1; @@ -1626,7 +1627,6 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q struct child_process cp_add = CHILD_PROCESS_INIT; struct child_process cp_diff = CHILD_PROCESS_INIT; struct child_process cp_apply = CHILD_PROCESS_INIT; - struct strbuf out = STRBUF_INIT; cp_add.git_cmd = 1; strvec_push(&cp_add.args, "add"); @@ -1718,6 +1718,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q done: strbuf_release(&patch); + strbuf_release(&out); free_stash_info(&info); strbuf_release(&stash_msg_buf); strbuf_release(&untracked_files); @@ -1869,6 +1870,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix) OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; + const char **args_copy; + int ret; git_config(git_stash_config, NULL); @@ -1892,5 +1895,16 @@ int cmd_stash(int argc, const char **argv, const char *prefix) /* Assume 'stash push' */ strvec_push(&args, "push"); strvec_pushv(&args, argv); - return !!push_stash(args.nr, args.v, prefix, 1); + + /* + * `push_stash()` ends up modifying the array, which causes memory + * leaks if we didn't copy the array here. + */ + DUP_ARRAY(args_copy, args.v, args.nr); + + ret = !!push_stash(args.nr, args_copy, prefix, 1); + + strvec_clear(&args); + free(args_copy); + return ret; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f1218a1995..673810d2c0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1530,7 +1530,7 @@ struct module_clone_data { const char *path; const char *name; const char *url; - const char *depth; + int depth; struct list_objects_filter_options *filter_options; unsigned int quiet: 1; unsigned int progress: 1; @@ -1729,8 +1729,8 @@ static int clone_submodule(const struct module_clone_data *clone_data, strvec_push(&cp.args, "--quiet"); if (clone_data->progress) strvec_push(&cp.args, "--progress"); - if (clone_data->depth && *(clone_data->depth)) - strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL); + if (clone_data->depth > 0) + strvec_pushf(&cp.args, "--depth=%d", clone_data->depth); if (reference->nr) { struct string_list_item *item; @@ -1851,8 +1851,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) N_("reference repository")), OPT_BOOL(0, "dissociate", &dissociate, N_("use --reference only while cloning")), - OPT_STRING(0, "depth", &clone_data.depth, - N_("string"), + OPT_INTEGER(0, "depth", &clone_data.depth, N_("depth for shallow clones")), OPT__QUIET(&quiet, "suppress output for cloning a submodule"), OPT_BOOL(0, "progress", &progress, @@ -2269,6 +2268,7 @@ static int is_tip_reachable(const char *path, const struct object_id *oid) struct child_process cp = CHILD_PROCESS_INIT; struct strbuf rev = STRBUF_INIT; char *hex = oid_to_hex(oid); + int reachable; cp.git_cmd = 1; cp.dir = path; @@ -2278,9 +2278,12 @@ static int is_tip_reachable(const char *path, const struct object_id *oid) prepare_submodule_repo_env(&cp.env); if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len) - return 0; + reachable = 0; + else + reachable = 1; - return 1; + strbuf_release(&rev); + return reachable; } static int fetch_in_submodule(const char *module_path, int depth, int quiet, @@ -3200,7 +3203,7 @@ static int add_submodule(const struct add_data *add_data) } clone_data.dissociate = add_data->dissociate; if (add_data->depth >= 0) - clone_data.depth = xstrfmt("%d", add_data->depth); + clone_data.depth = add_data->depth; if (clone_submodule(&clone_data, &reference)) goto cleanup; @@ -3223,6 +3226,7 @@ static int add_submodule(const struct add_data *add_data) die(_("unable to checkout submodule '%s'"), add_data->sm_path); } ret = 0; + cleanup: string_list_clear(&reference, 1); return ret; diff --git a/builtin/worktree.c b/builtin/worktree.c index 1d51e54fcd..cec3ada6b0 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -769,7 +769,7 @@ static int add(int ac, const char **av, const char *prefix) char *branch_to_free = NULL; char *new_branch_to_free = NULL; const char *new_branch = NULL; - const char *opt_track = NULL; + char *opt_track = NULL; const char *lock_reason = NULL; int keep_locked = 0; int used_new_branch_options; @@ -846,7 +846,7 @@ static int add(int ac, const char **av, const char *prefix) if (opts.orphan && !new_branch) { int n; const char *s = worktree_basename(path, &n); - new_branch = xstrndup(s, n); + new_branch = new_branch_to_free = xstrndup(s, n); } else if (opts.orphan) { ; /* no-op */ } else if (opts.detach) { @@ -875,7 +875,7 @@ static int add(int ac, const char **av, const char *prefix) remote = unique_tracking_name(branch, &oid, NULL); if (remote) { new_branch = branch; - branch = remote; + branch = new_branch_to_free = remote; } } @@ -923,6 +923,7 @@ static int add(int ac, const char **av, const char *prefix) ret = add_worktree(path, branch, &opts); free(path); + free(opt_track); free(branch_to_free); free(new_branch_to_free); return ret; diff --git a/ci/check-whitespace.sh b/ci/check-whitespace.sh index db399097a5..c40804394c 100755 --- a/ci/check-whitespace.sh +++ b/ci/check-whitespace.sh @@ -9,7 +9,7 @@ baseCommit=$1 outputFile=$2 url=$3 -if test "$#" -ne 1 && test "$#" -ne 3 +if test "$#" -ne 1 && test "$#" -ne 3 || test -z "$1" then echo "USAGE: $0 <BASE_COMMIT> [<OUTPUT_FILE> <URL>]" exit 1 @@ -21,6 +21,12 @@ commitText= commitTextmd= goodParent= +if ! git rev-parse --quiet --verify "${baseCommit}" +then + echo "Invalid <BASE_COMMIT> '${baseCommit}'" + exit 1 +fi + while read dash sha etc do case "${dash}" in @@ -67,7 +73,7 @@ then goodParent=${baseCommit: 0:7} fi - echo "A whitespace issue was found in onen of more of the commits." + echo "A whitespace issue was found in one or more of the commits." echo "Run the following command to resolve whitespace issues:" echo "git rebase --whitespace=fix ${goodParent}" diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index 6ec0f85972..4781cd20bb 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -7,7 +7,7 @@ begin_group "Install dependencies" -P4WHENCE=https://cdist2.perforce.com/perforce/r21.2 +P4WHENCE=https://cdist2.perforce.com/perforce/r23.2 LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION JGITWHENCE=https://repo.eclipse.org/content/groups/releases//org/eclipse/jgit/org.eclipse.jgit.pgm/6.8.0.202311291450-r/org.eclipse.jgit.pgm-6.8.0.202311291450-r.sh @@ -87,6 +87,10 @@ macos-*) esac case "$jobname" in +ClangFormat) + sudo apt-get -q update + sudo apt-get -q -y install clang-format + ;; StaticAnalysis) sudo apt-get -q update sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \ diff --git a/ci/run-style-check.sh b/ci/run-style-check.sh new file mode 100755 index 0000000000..6cd4b1d934 --- /dev/null +++ b/ci/run-style-check.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Perform style check +# + +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 \ + --diff --extensions c,h "$baseCommit" diff --git a/commit-reach.c b/commit-reach.c index dabc2972e4..02f8218b8e 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -1227,4 +1227,5 @@ void tips_reachable_from_bases(struct repository *r, done: free(commits); repo_clear_commit_marks(r, SEEN); + free_commit_list(stack); } diff --git a/config.mak.uname b/config.mak.uname index 85d63821ec..aa0fd26bd5 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -8,7 +8,6 @@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') -uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') ifneq ($(findstring MINGW,$(uname_S)),) diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index 6ce22a28ed..1c8310d7fe 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -141,7 +141,7 @@ static void find_username_in_item(CFDictionaryRef item) username_buf, buffer_len, ENCODING)) { - write_item("username", username_buf, buffer_len - 1); + write_item("username", username_buf, strlen(username_buf)); } free(username_buf); } @@ -324,6 +324,9 @@ static void trace_encoding(const char *context, const char *path, struct strbuf trace = STRBUF_INIT; int i; + if (!trace_want(&coe)) + return; + strbuf_addf(&trace, "%s (%s, considered %s):\n", context, path, encoding); for (i = 0; i < len && buf; ++i) { strbuf_addf( @@ -960,7 +963,7 @@ int async_query_available_blobs(const char *cmd, struct string_list *available_p while ((line = packet_read_line(process->out, NULL))) { const char *path; if (skip_prefix(line, "pathname=", &path)) - string_list_insert(available_paths, xstrdup(path)); + string_list_insert(available_paths, path); else ; /* ignore unknown keys */ } @@ -1050,14 +1053,20 @@ static int read_convert_config(const char *var, const char *value, * The command-line will not be interpolated in any way. */ - if (!strcmp("smudge", key)) + if (!strcmp("smudge", key)) { + FREE_AND_NULL(drv->smudge); return git_config_string(&drv->smudge, var, value); + } - if (!strcmp("clean", key)) + if (!strcmp("clean", key)) { + FREE_AND_NULL(drv->clean); return git_config_string(&drv->clean, var, value); + } - if (!strcmp("process", key)) + if (!strcmp("process", key)) { + FREE_AND_NULL(drv->process); return git_config_string(&drv->process, var, value); + } if (!strcmp("required", key)) { drv->required = git_config_bool(var, value); @@ -191,7 +191,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress) progress = start_delayed_progress(_("Filtering content"), dco->paths.nr); while (dco->filters.nr > 0) { for_each_string_list_item(filter, &dco->filters) { - struct string_list available_paths = STRING_LIST_INIT_NODUP; + struct string_list available_paths = STRING_LIST_INIT_DUP; if (!async_query_available_blobs(filter->string, &available_paths)) { /* Filter reported an error */ @@ -245,6 +245,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress) } else errs = 1; } + + string_list_clear(&available_paths, 0); } filter_string_list(&dco->filters, 0, string_is_not_null, NULL); @@ -1735,7 +1735,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle peek_eol = end_of_line(peek_bol, &peek_left); } - if (match_funcname(opt, gs, peek_bol, peek_eol)) + if (peek_bol >= gs->buf + gs->size || + match_funcname(opt, gs, peek_bol, peek_eol)) show_function = 0; } if (show_function || diff --git a/log-tree.c b/log-tree.c index 52feec4356..576ef30d90 100644 --- a/log-tree.c +++ b/log-tree.c @@ -1025,7 +1025,7 @@ static int do_remerge_diff(struct rev_info *opt, struct strbuf parent2_desc = STRBUF_INIT; /* Setup merge options */ - init_merge_options(&o, the_repository); + init_ui_merge_options(&o, the_repository); o.show_rename_progress = 0; o.record_conflict_msgs_as_headers = 1; o.msg_header_prefix = "remerge"; diff --git a/merge-recursive.c b/merge-recursive.c index 5cc638066a..ed64a4c537 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3921,7 +3921,7 @@ int merge_recursive_generic(struct merge_options *opt, return clean ? 0 : 1; } -static void merge_recursive_config(struct merge_options *opt) +static void merge_recursive_config(struct merge_options *opt, int ui) { char *value = NULL; int renormalize = 0; @@ -3950,11 +3950,20 @@ static void merge_recursive_config(struct merge_options *opt) } /* avoid erroring on values from future versions of git */ free(value); } + if (ui) { + if (!git_config_get_string("diff.algorithm", &value)) { + long diff_algorithm = parse_algorithm_value(value); + if (diff_algorithm < 0) + die(_("unknown value for config '%s': %s"), "diff.algorithm", value); + opt->xdl_opts = (opt->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm; + free(value); + } + } git_config(git_xmerge_config, NULL); } -void init_merge_options(struct merge_options *opt, - struct repository *repo) +static void init_merge_options(struct merge_options *opt, + struct repository *repo, int ui) { const char *merge_verbosity; memset(opt, 0, sizeof(struct merge_options)); @@ -3973,7 +3982,7 @@ void init_merge_options(struct merge_options *opt, opt->conflict_style = -1; - merge_recursive_config(opt); + merge_recursive_config(opt, ui); merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); if (merge_verbosity) opt->verbosity = strtol(merge_verbosity, NULL, 10); @@ -3981,6 +3990,18 @@ void init_merge_options(struct merge_options *opt, opt->buffer_output = 0; } +void init_ui_merge_options(struct merge_options *opt, + struct repository *repo) +{ + init_merge_options(opt, repo, 1); +} + +void init_basic_merge_options(struct merge_options *opt, + struct repository *repo) +{ + init_merge_options(opt, repo, 0); +} + /* * For now, members of merge_options do not need deep copying, but * it may change in the future, in which case we would need to update diff --git a/merge-recursive.h b/merge-recursive.h index 3136c7cc2d..0b91f28f90 100644 --- a/merge-recursive.h +++ b/merge-recursive.h @@ -54,7 +54,10 @@ struct merge_options { struct merge_options_internal *priv; }; -void init_merge_options(struct merge_options *opt, struct repository *repo); +/* for use by porcelain commands */ +void init_ui_merge_options(struct merge_options *opt, struct repository *repo); +/* for use by plumbing commands */ +void init_basic_merge_options(struct merge_options *opt, struct repository *repo); void copy_merge_options(struct merge_options *dst, struct merge_options *src); void clear_merge_options(struct merge_options *opt); diff --git a/object-name.c b/object-name.c index 527b853ac4..240a93e7ce 100644 --- a/object-name.c +++ b/object-name.c @@ -27,7 +27,8 @@ #include "date.h" #include "object-file-convert.h" -static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *); +static int get_oid_oneline(struct repository *r, const char *, struct object_id *, + const struct commit_list *); typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *); @@ -1254,6 +1255,8 @@ static int peel_onion(struct repository *r, const char *name, int len, prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); commit_list_insert((struct commit *)o, &list); ret = get_oid_oneline(r, prefix, oid, list); + + free_commit_list(list); free(prefix); return ret; } @@ -1388,9 +1391,10 @@ static int handle_one_ref(const char *path, const struct object_id *oid, static int get_oid_oneline(struct repository *r, const char *prefix, struct object_id *oid, - struct commit_list *list) + const struct commit_list *list) { - struct commit_list *backup = NULL, *l; + struct commit_list *copy = NULL; + const struct commit_list *l; int found = 0; int negative = 0; regex_t regex; @@ -1411,14 +1415,14 @@ static int get_oid_oneline(struct repository *r, for (l = list; l; l = l->next) { l->item->object.flags |= ONELINE_SEEN; - commit_list_insert(l->item, &backup); + commit_list_insert(l->item, ©); } - while (list) { + while (copy) { const char *p, *buf; struct commit *commit; int matches; - commit = pop_most_recent_commit(&list, ONELINE_SEEN); + commit = pop_most_recent_commit(©, ONELINE_SEEN); if (!parse_object(r, &commit->object.oid)) continue; buf = repo_get_commit_buffer(r, commit, NULL); @@ -1433,10 +1437,9 @@ static int get_oid_oneline(struct repository *r, } } regfree(®ex); - free_commit_list(list); - for (l = backup; l; l = l->next) + for (l = list; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); - free_commit_list(backup); + free_commit_list(copy); return found ? 0 : -1; } @@ -2024,7 +2027,10 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, refs_for_each_ref(get_main_ref_store(repo), handle_one_ref, &cb); refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb); commit_list_sort_by_date(&list); - return get_oid_oneline(repo, name + 2, oid, list); + ret = get_oid_oneline(repo, name + 2, oid, list); + + free_commit_list(list); + return ret; } if (namelen < 3 || name[2] != ':' || @@ -14,6 +14,7 @@ int pager_use_color = 1; static struct child_process pager_process; static char *pager_program; +static int old_fd1 = -1, old_fd2 = -1; /* Is the value coming back from term_columns() just a guess? */ static int term_columns_guessed; @@ -23,10 +24,11 @@ static void close_pager_fds(void) { /* signal EOF to pager */ close(1); - close(2); + if (old_fd2 != -1) + close(2); } -static void wait_for_pager_atexit(void) +static void finish_pager(void) { fflush(stdout); fflush(stderr); @@ -34,8 +36,37 @@ static void wait_for_pager_atexit(void) finish_command(&pager_process); } +static void wait_for_pager_atexit(void) +{ + if (old_fd1 == -1) + return; + + finish_pager(); +} + +void wait_for_pager(void) +{ + if (old_fd1 == -1) + return; + + finish_pager(); + sigchain_pop_common(); + unsetenv("GIT_PAGER_IN_USE"); + dup2(old_fd1, 1); + close(old_fd1); + old_fd1 = -1; + if (old_fd2 != -1) { + dup2(old_fd2, 2); + close(old_fd2); + old_fd2 = -1; + } +} + static void wait_for_pager_signal(int signo) { + if (old_fd1 == -1) + return; + close_pager_fds(); finish_command_in_signal(&pager_process); sigchain_pop(signo); @@ -111,6 +142,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager) void setup_pager(void) { + static int once = 0; const char *pager = git_pager(isatty(1)); if (!pager) @@ -140,14 +172,20 @@ void setup_pager(void) die("unable to execute pager '%s'", pager); /* original process continues, but writes to the pipe */ + old_fd1 = dup(1); dup2(pager_process.in, 1); - if (isatty(2)) + if (isatty(2)) { + old_fd2 = dup(2); dup2(pager_process.in, 2); + } close(pager_process.in); - /* this makes sure that the parent terminates after the pager */ sigchain_push_common(wait_for_pager_signal); - atexit(wait_for_pager_atexit); + + if (!once) { + once++; + atexit(wait_for_pager_atexit); + } } int pager_in_use(void) @@ -5,6 +5,7 @@ struct child_process; const char *git_pager(int stdout_is_tty); void setup_pager(void); +void wait_for_pager(void); int pager_in_use(void); int term_columns(void); void term_clear_line(void); diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h index 67283faf06..3bc1d88d9b 100644 --- a/reftable/reftable-tests.h +++ b/reftable/reftable-tests.h @@ -11,7 +11,6 @@ https://developers.google.com/open-source/licenses/bsd int basics_test_main(int argc, const char **argv); int block_test_main(int argc, const char **argv); -int merged_test_main(int argc, const char **argv); int record_test_main(int argc, const char **argv); int readwrite_test_main(int argc, const char **argv); int stack_test_main(int argc, const char **argv); @@ -1107,7 +1107,7 @@ fail_exit: int rerere_forget(struct repository *r, struct pathspec *pathspec) { - int i, fd; + int i, fd, ret; struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list merge_rr = STRING_LIST_INIT_DUP; @@ -1132,7 +1132,12 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec) continue; rerere_forget_one_path(r->index, it->string, &merge_rr); } - return write_rr(&merge_rr, fd); + + ret = write_rr(&merge_rr, fd); + + string_list_clear(&conflict, 0); + string_list_clear(&merge_rr, 1); + return ret; } /* diff --git a/sequencer.c b/sequencer.c index a2284ac9e9..0291920f0b 100644 --- a/sequencer.c +++ b/sequencer.c @@ -762,7 +762,7 @@ static int do_recursive_merge(struct repository *r, repo_read_index(r); - init_merge_options(&o, r); + init_ui_merge_options(&o, r); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; o.branch2 = next ? next_label : "(empty tree)"; @@ -4309,7 +4309,7 @@ static int do_merge(struct repository *r, bases = reverse_commit_list(bases); repo_read_index(r); - init_merge_options(&o, r); + init_ui_merge_options(&o, r); o.branch1 = "HEAD"; o.branch2 = ref_name.buf; o.buffer_output = 2; diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl index b2b28c2ced..6ee7700eb4 100755 --- a/t/check-non-portable-shell.pl +++ b/t/check-non-portable-shell.pl @@ -49,8 +49,8 @@ while (<>) { /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)'; /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and err q(quote "$val" in 'local var=$val'); - /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and - err '"FOO=bar shell_func" assignment extends beyond "shell_func"'; + /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and + err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)'; $line = ''; # this resets our $. for each file close ARGV if eof; diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index b808ad3e12..672eaedae0 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c @@ -8,7 +8,6 @@ int cmd__reftable(int argc, const char **argv) block_test_main(argc, argv); tree_test_main(argc, argv); readwrite_test_main(argc, argv); - merged_test_main(argc, argv); stack_test_main(argc, argv); return 0; } diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index c6a074df3d..63c37de33d 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -19,7 +19,7 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); @@ -49,7 +49,7 @@ static void test_get_commit_tree_in_graph(const char *gitdir, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 0b4997022b..eeb2714d9d 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -5,6 +5,7 @@ test_description='blob conversion via gitattributes' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index c10e35905e..5d5b64205f 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-cache tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh index 716bf1af9f..f83db659e2 100755 --- a/t/t0302-credential-store.sh +++ b/t/t0302-credential-store.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-store tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh index 72ae405c3e..8aadbe86c4 100755 --- a/t/t0303-credential-external.sh +++ b/t/t0303-credential-external.sh @@ -29,6 +29,7 @@ you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell commands. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index b754b9fd74..5eaa6428c4 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test git rev-parse --parseopt' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_invalid_long_option () { diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh index 6ecfed86bc..e7e78a4028 100755 --- a/t/t1511-rev-parse-caret.sh +++ b/t/t1511-rev-parse-caret.sh @@ -5,6 +5,7 @@ test_description='tests for ref^{stuff}' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index be3fcdde07..b3f6bc97b5 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -5,6 +5,7 @@ test_description='undoing resolution' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_resolve_undo () { diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh index 5ffe1a41e2..59e5570cb2 100755 --- a/t/t2080-parallel-checkout-basics.sh +++ b/t/t2080-parallel-checkout-basics.sh @@ -8,6 +8,7 @@ working tree. ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh index f3511cd43a..aec55496eb 100755 --- a/t/t2082-parallel-checkout-attributes.sh +++ b/t/t2082-parallel-checkout-attributes.sh @@ -10,6 +10,7 @@ properly (without access to the index or attribute stack). ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" . "$TEST_DIRECTORY/lib-encoding.sh" diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index ba320dc417..cfc4aeb179 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh index f6d8d7d03d..8af4e8cfe3 100755 --- a/t/t2501-cwd-empty.sh +++ b/t/t2501-cwd-empty.sh @@ -2,6 +2,7 @@ test_description='Test handling of the current working directory becoming empty' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 800fc33165..6e587d27d7 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -2,6 +2,7 @@ test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index a1139f79e2..3b6dad0c46 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh @@ -2,6 +2,7 @@ test_description='test show-branch' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'error descriptions on empty repository' ' diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index a767c3520e..973e20254b 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -5,6 +5,7 @@ test_description='range-diff tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Note that because of the range-diff's heuristics, test_commit does more diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index 536bd11ff4..99137fb235 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -1557,4 +1557,14 @@ test_expect_success 'empty notes are displayed by git log' ' test_cmp expect actual ' +test_expect_success 'empty notes do not invoke the editor' ' + test_commit 18th && + GIT_EDITOR="false" git notes add -C "$empty_blob" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -m "" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -F /dev/null --allow-empty && + git notes remove HEAD +' + test_done diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 36ca126bcd..2aa8593f77 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -392,8 +392,7 @@ test_expect_success 'refuse to merge ancestors of HEAD' ' test_expect_success 'root commits' ' git checkout --orphan unrelated && - (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \ - test_commit second-root) && + test_commit --author "Parsnip <root@example.com>" second-root && test_commit third-root && cat >script-from-scratch <<-\EOF && pick third-root diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 389670262e..12bd3db4cb 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -5,6 +5,7 @@ test_description='basic git replay tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh GIT_AUTHOR_NAME=author@name diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5d78868ac1..718438ffc7 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -575,6 +575,54 @@ test_expect_success 'navigate to hunk via regex / pattern' ' test_cmp expect actual.trimmed ' +test_expect_success 'print again the hunk' ' + test_when_finished "git reset" && + tr _ " " >expect <<-EOF && + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@ + 10 + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_ + EOF + test_write_lines s y g 1 p | git add -p >actual && + tail -n 7 <actual >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'print again the hunk (PAGER)' ' + test_when_finished "git reset" && + cat >expect <<-EOF && + <GREEN>+<RESET><GREEN>15<RESET> + 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET> + PAGER 10<RESET> + PAGER <GREEN>+<RESET><GREEN>15<RESET> + PAGER 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET> + EOF + test_write_lines s y g 1 P | + ( + GIT_PAGER="sed s/^/PAGER\ /" && + export GIT_PAGER && + test_terminal git add -p >actual + ) && + tail -n 7 <actual | test_decode_color >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'P handles SIGPIPE when writing to pager' ' + test_when_finished "rm -f huge_file; git reset" && + printf "\n%2500000s" Y >huge_file && + git add -N huge_file && + test_write_lines P q | ( + GIT_PAGER="head -n 1" && + export GIT_PAGER && + test_terminal git add -p + ) +' + test_expect_success 'split hunk "add -p (edit)"' ' # Split, say Edit and do nothing. Then: # @@ -1164,4 +1212,23 @@ test_expect_success 'reset -p with unmerged files' ' test_must_be_empty staged ' +test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' + test_config diff.suppressBlankEmpty true && + write_script fake-editor.sh <<-\EOF && + tr F G <"$1" >"$1.tmp" && + mv "$1.tmp" "$1" + EOF + + test_write_lines a b "" c d "" e f "" >file && + git add file && + test_write_lines A b "" c D "" e F "" >file && + ( + test_set_editor "$(pwd)/fake-editor.sh" && + test_write_lines s n y e q | git add -p file + ) && + git cat-file blob :file >actual && + test_write_lines a b "" c D "" e G "" >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index a7f71f8126..e4c0937f61 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -8,6 +8,7 @@ test_description='Test git stash' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-unique-files.sh diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index 368fc2a6cc..aa5019fd6c 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='stash -p' + +TEST_PASSES_SANITIZE_LEAK=true . ./lib-patch-mode.sh test_expect_success 'setup' ' diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index 1289ae3e07..a1733f45c3 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -5,6 +5,7 @@ test_description='Test git stash --include-untracked' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'stash save --include-untracked some dirty working directory' ' diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 74586f3813..4dcd7e9925 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -70,7 +70,7 @@ test_language_driver () { word_diff --color-words ' test_expect_success "diff driver '$lang' in Islandic" ' - LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ + test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ word_diff --color-words ' } diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index b0a3e84984..213b36fb96 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -25,6 +25,7 @@ test_description='git rerere GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index f698d0c9ad..c20c885724 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -9,6 +9,7 @@ test_description='git shortlog GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index a2b4442660..2916c07e3c 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='commit graph' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-chunk.sh diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 42e77eb5a9..d687d824d1 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -5,6 +5,7 @@ test_description='git ls-remote' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh generate_references () { diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 25772c85c5..579872c258 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh @@ -5,6 +5,7 @@ test_description='fetch --all works correctly' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh setup_repository () { diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 47534f1062..1098cbd0a1 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -5,6 +5,7 @@ test_description='pulling into void' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh modify () { diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh index 14f7eced9a..bc2bada34c 100755 --- a/t/t5528-push-default.sh +++ b/t/t5528-push-default.sh @@ -4,6 +4,7 @@ test_description='check various push.default settings' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup bare remotes' ' diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh index e8f6d233ff..7122af7fdb 100755 --- a/t/t5535-fetch-push-symref.sh +++ b/t/t5535-fetch-push-symref.sh @@ -2,6 +2,7 @@ test_description='avoiding conflicting update through symref aliasing' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh index 04b47ad84a..479d103469 100755 --- a/t/t5543-atomic-push.sh +++ b/t/t5543-atomic-push.sh @@ -5,6 +5,7 @@ test_description='pushing to a repository using the atomic push option' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh mk_repo_pair () { diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index f9a9bf9503..c5f08b6799 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -4,6 +4,7 @@ test_description='test fetching over git protocol' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-git-daemon.sh diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index 6f3e543977..2d337d7287 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -5,6 +5,7 @@ test_description='test git rev-list --cherry-pick -- file' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # A---B---D---F diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 44c726ea39..f96ea82e78 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -6,6 +6,7 @@ test_description='Merge base and parent list computation. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh M=1130000000 diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 79e0f19deb..05ed2510d9 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -14,6 +14,7 @@ test_description='test describe' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_describe () { diff --git a/t/t6133-pathspec-rev-dwim.sh b/t/t6133-pathspec-rev-dwim.sh index a290ffca0d..6dd4bbbf9f 100755 --- a/t/t6133-pathspec-rev-dwim.sh +++ b/t/t6133-pathspec-rev-dwim.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test dwim of revs versus pathspecs in revision parser' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index 11884d2fc3..06c1301222 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -4,6 +4,7 @@ test_description='git status --porcelain=v2 This test exercises porcelain V2 output for git status.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 189d8e341b..2d984eb4c6 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -498,6 +498,19 @@ test_expect_success 'checkout unmerged stage' ' test ztheirside = "z$(cat file)" ' +test_expect_success 'checkout --ours is incompatible with switching' ' + test_must_fail git checkout --ours 2>error && + test_grep "needs the paths to check out" error && + + test_must_fail git checkout --ours HEAD 2>error && + test_grep "cannot be used with switching" error && + + test_must_fail git checkout --ours main 2>error && + test_grep "cannot be used with switching" error && + + git checkout --ours file +' + test_expect_success 'checkout path with --merge from tree-ish is a no-no' ' setup_conflicting_index && test_must_fail git checkout -m HEAD -- file diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 981488885f..098d8833b6 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -12,6 +12,7 @@ subcommands of git submodule. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup - enable local submodules' ' diff --git a/t/t7615-diff-algo-with-mergy-operations.sh b/t/t7615-diff-algo-with-mergy-operations.sh new file mode 100755 index 0000000000..9a83be518c --- /dev/null +++ b/t/t7615-diff-algo-with-mergy-operations.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +test_description='git merge and other operations that rely on merge + +Testing the influence of the diff algorithm on the merge output.' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'setup' ' + cp "$TEST_DIRECTORY"/t7615/base.c file.c && + git add file.c && + git commit -m c0 && + git tag c0 && + cp "$TEST_DIRECTORY"/t7615/ours.c file.c && + git add file.c && + git commit -m c1 && + git tag c1 && + git reset --hard c0 && + cp "$TEST_DIRECTORY"/t7615/theirs.c file.c && + git add file.c && + git commit -m c2 && + git tag c2 +' + +GIT_TEST_MERGE_ALGORITHM=recursive + +test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' + git reset --hard c1 && + test_must_fail git merge -s recursive c2 +' + +test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' + git reset --hard c1 && + git merge --strategy recursive -Xdiff-algorithm=histogram c2 +' + +test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' + git reset --hard c1 && + git config diff.algorithm histogram && + git merge --strategy recursive c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' + git reset --hard c1 && + test_must_fail git cherry-pick -s recursive c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' + git reset --hard c1 && + git cherry-pick --strategy recursive -Xdiff-algorithm=histogram c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' + git reset --hard c1 && + git config diff.algorithm histogram && + git cherry-pick --strategy recursive c2 +' + +test_done diff --git a/t/t7615/base.c b/t/t7615/base.c new file mode 100644 index 0000000000..c64abc5936 --- /dev/null +++ b/t/t7615/base.c @@ -0,0 +1,17 @@ +int f(int x, int y) +{ + if (x == 0) + { + return y; + } + return x; +} + +int g(size_t u) +{ + while (u < 30) + { + u++; + } + return u; +} diff --git a/t/t7615/ours.c b/t/t7615/ours.c new file mode 100644 index 0000000000..44d8251397 --- /dev/null +++ b/t/t7615/ours.c @@ -0,0 +1,17 @@ +int g(size_t u) +{ + while (u < 30) + { + u++; + } + return u; +} + +int h(int x, int y, int z) +{ + if (z == 0) + { + return x; + } + return y; +} diff --git a/t/t7615/theirs.c b/t/t7615/theirs.c new file mode 100644 index 0000000000..85f02146fe --- /dev/null +++ b/t/t7615/theirs.c @@ -0,0 +1,17 @@ +int f(int x, int y) +{ + if (x == 0) + { + return y; + } + return x; +} + +int g(size_t u) +{ + while (u > 34) + { + u--; + } + return u; +} diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 875dcfd98f..af2cf2f78a 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -31,6 +31,7 @@ int main(int argc, const char **argv) return 0; /* char ?? */ } + EOF test_expect_success setup ' diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 53af8e34ac..3e6dfce248 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -5,6 +5,7 @@ test_description='git p4 tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -297,8 +298,20 @@ test_expect_success 'exit when p4 fails to produce marshaled output' ' # p4 changes, files, or describe; just in p4 print. If P4CLIENT is unset, the # message will include "Librarian checkout". test_expect_success 'exit gracefully for p4 server errors' ' - test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && - mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden && + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo "$db"/depot/file1*)" in + *,v) + test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && + mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden;; + *,d) + path="$(echo "$db"/depot/file1,d/*.gz)" && + test_when_finished "mv \"$path\",hidden \"$path\"" && + mv "$path" "$path",hidden;; + *) + BUG "unhandled p4d layout";; + esac && test_when_finished cleanup_git && test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err && test_grep "Error from p4 print" err diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index c598011635..cdbfacc727 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -5,6 +5,7 @@ test_description='git p4 tests for p4 branches' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index bb236cd2b5..1bc48305b0 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 filetype tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -300,10 +301,22 @@ test_expect_success SYMLINKS 'empty symlink target' ' # text # @@ # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. cd "$db/depot" && - sed "/@target1/{; s/target1/@/; n; d; }" \ - empty-symlink,v >empty-symlink,v.tmp && - mv empty-symlink,v.tmp empty-symlink,v + case "$(echo empty-symlink*)" in + empty-symlink,v) + sed "/@target1/{; s/target1/@/; n; d; }" \ + empty-symlink,v >empty-symlink,v.tmp && + mv empty-symlink,v.tmp empty-symlink,v;; + empty-symlink,d) + path="empty-symlink,d/$(ls empty-symlink,d/ | tail -n1)" && + rm "$path" && + gzip </dev/null >"$path";; + *) + BUG "unhandled p4d layout";; + esac ) && ( # Make sure symlink really is empty. Asking diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh index 2913277013..ab7fe16266 100755 --- a/t/t9803-git-p4-shell-metachars.sh +++ b/t/t9803-git-p4-shell-metachars.sh @@ -2,6 +2,7 @@ test_description='git p4 transparency to shell metachars in filenames' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh index 3236457106..c8963fd398 100755 --- a/t/t9804-git-p4-label.sh +++ b/t/t9804-git-p4-label.sh @@ -2,6 +2,7 @@ test_description='git p4 label tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index 90ef647db7..72dce3d2b4 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -2,6 +2,7 @@ test_description='git p4 skipSubmitEdit config variables' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index c26d297433..e4ce44ebf3 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -5,6 +5,7 @@ test_description='git p4 options' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh index 58a9b3b71e..342f7f3d4a 100755 --- a/t/t9808-git-p4-chdir.sh +++ b/t/t9808-git-p4-chdir.sh @@ -2,6 +2,7 @@ test_description='git p4 relative chdir' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh index 9c9710d8c7..f33fdea889 100755 --- a/t/t9809-git-p4-client-view.sh +++ b/t/t9809-git-p4-client-view.sh @@ -2,6 +2,7 @@ test_description='git p4 client view' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index 5fe83315ec..15e32c9f35 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -2,6 +2,7 @@ test_description='git p4 rcs keywords' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh CP1252="\223\224" diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh index 5ac5383fb7..52a4b0af81 100755 --- a/t/t9811-git-p4-label-import.sh +++ b/t/t9811-git-p4-label-import.sh @@ -5,6 +5,7 @@ test_description='git p4 label tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh index 254a7c2446..46aa5fd56c 100755 --- a/t/t9812-git-p4-wildcards.sh +++ b/t/t9812-git-p4-wildcards.sh @@ -2,6 +2,7 @@ test_description='git p4 wildcards' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index fd018c87a8..0efea28da2 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh @@ -2,6 +2,7 @@ test_description='git p4 preserve users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index 2a9838f37f..00df6ebd3b 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh @@ -2,6 +2,7 @@ test_description='git p4 rename' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index c766fd159f..92ef9d8c24 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -2,6 +2,7 @@ test_description='git p4 submit failure handling' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh index 5e904ac80d..e687fbc25f 100755 --- a/t/t9816-git-p4-locked.sh +++ b/t/t9816-git-p4-locked.sh @@ -2,6 +2,7 @@ test_description='git p4 locked file behavior' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh index ec3d937c6a..3deb334fed 100755 --- a/t/t9817-git-p4-exclude.sh +++ b/t/t9817-git-p4-exclude.sh @@ -2,6 +2,7 @@ test_description='git p4 tests for excluded paths during clone and sync' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh index de591d875c..091bb72bdb 100755 --- a/t/t9818-git-p4-block.sh +++ b/t/t9818-git-p4-block.sh @@ -2,6 +2,7 @@ test_description='git p4 fetching changes in multiple blocks' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh index b4d93f0c17..985be20357 100755 --- a/t/t9819-git-p4-case-folding.sh +++ b/t/t9819-git-p4-case-folding.sh @@ -2,6 +2,7 @@ test_description='interaction with P4 case-folding' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh if test_have_prereq CASE_INSENSITIVE_FS diff --git a/t/t9820-git-p4-editor-handling.sh b/t/t9820-git-p4-editor-handling.sh index fa1bba1dd9..48e4dfb95c 100755 --- a/t/t9820-git-p4-editor-handling.sh +++ b/t/t9820-git-p4-editor-handling.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of EDITOR' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9821-git-p4-path-variations.sh b/t/t9821-git-p4-path-variations.sh index ef80f1690b..49691c53da 100755 --- a/t/t9821-git-p4-path-variations.sh +++ b/t/t9821-git-p4-path-variations.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with path case variations' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d with case folding enabled' ' diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh index 572d395498..e62ed49f51 100755 --- a/t/t9822-git-p4-path-encoding.sh +++ b/t/t9822-git-p4-path-encoding.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with non ASCII paths' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt" diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh index 88b76dc4d6..98a40d8af3 100755 --- a/t/t9823-git-p4-mock-lfs.sh +++ b/t/t9823-git-p4-mock-lfs.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and store files in Mock LFS' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_file_is_not_in_mock_lfs () { diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh index f049ff8229..d0b86537dd 100755 --- a/t/t9825-git-p4-handle-utf16-without-bom.sh +++ b/t/t9825-git-p4-handle-utf16-without-bom.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of UTF-16 files without BOM' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF16="\227\000\227\000" @@ -22,9 +23,25 @@ test_expect_success 'init depot with UTF-16 encoded file and artificially remove cd db && p4d -jc && # P4D automatically adds a BOM. Remove it here to make the file invalid. - sed -e "\$d" depot/file1,v >depot/file1,v.new && - mv depot/file1,v.new depot/file1,v && - printf "@$UTF16@" >>depot/file1,v && + # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo depot/file1*)" in + depot/file1,v) + sed -e "\$d" depot/file1,v >depot/file1,v.new && + mv depot/file1,v.new depot/file1,v && + printf "@$UTF16@" >>depot/file1,v;; + depot/file1,d) + path="$(echo depot/file1,d/*.gz)" && + gunzip -c "$path" >"$path.unzipped" && + sed -e "\$d" "$path.unzipped" >"$path.new" && + printf "$UTF16" >>"$path.new" && + gzip -c "$path.new" >"$path" && + rm "$path.unzipped" "$path.new";; + *) + BUG "unhandled p4d layout";; + esac && p4d -jrF checkpoint.1 ) ' diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh index fd64afe064..54083f842e 100755 --- a/t/t9826-git-p4-keep-empty-commits.sh +++ b/t/t9826-git-p4-keep-empty-commits.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and keep empty commits' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh index d3670bd7a2..3476ea2fd3 100755 --- a/t/t9827-git-p4-change-filetype.sh +++ b/t/t9827-git-p4-change-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 support for file type change' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh index ca6c2942bd..7c8f9e3930 100755 --- a/t/t9828-git-p4-map-user.sh +++ b/t/t9828-git-p4-map-user.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and map users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh index 88cfb1fcd3..3fc0948d9c 100755 --- a/t/t9829-git-p4-jobs.sh +++ b/t/t9829-git-p4-jobs.sh @@ -2,6 +2,7 @@ test_description='git p4 retrieve job info' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh index 3fb6960c18..02561a7f0e 100755 --- a/t/t9830-git-p4-symlink-dir.sh +++ b/t/t9830-git-p4-symlink-dir.sh @@ -2,6 +2,7 @@ test_description='git p4 symlinked directories' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh index ff6c0352e6..f287f41e37 100755 --- a/t/t9831-git-p4-triggers.sh +++ b/t/t9831-git-p4-triggers.sh @@ -2,6 +2,7 @@ test_description='git p4 with server triggers' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh index 6b3cb0414a..a266775408 100755 --- a/t/t9832-unshelve.sh +++ b/t/t9832-unshelve.sh @@ -6,6 +6,7 @@ last_shelved_change () { test_description='git p4 unshelve' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh index e22369ccdf..da1d30c142 100755 --- a/t/t9833-errors.sh +++ b/t/t9833-errors.sh @@ -2,6 +2,7 @@ test_description='git p4 errors' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh index dac67e89d7..565870fc74 100755 --- a/t/t9834-git-p4-file-dir-bug.sh +++ b/t/t9834-git-p4-file-dir-bug.sh @@ -6,6 +6,7 @@ This test creates files and directories with the same name in perforce and checks that git-p4 recovers from the error at the same time as the perforce repository.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh index 036bf79c66..ad20ffdede 100755 --- a/t/t9835-git-p4-metadata-encoding-python2.sh +++ b/t/t9835-git-p4-metadata-encoding-python2.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='2' diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh index 63350dc4b5..71ae763399 100755 --- a/t/t9836-git-p4-metadata-encoding-python3.sh +++ b/t/t9836-git-p4-metadata-encoding-python3.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='3' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 932d5ad759..cc6aa9f0cd 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -16,6 +16,7 @@ test_untraceable=UnfortunatelyYes GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh complete () diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index d667dda654..95e9955bca 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -8,6 +8,7 @@ test_description='test git-specific bash prompt functions' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" diff --git a/reftable/merged_test.c b/t/unit-tests/t-reftable-merged.c index a9d6661c13..b6263ee8b5 100644 --- a/reftable/merged_test.c +++ b/t/unit-tests/t-reftable-merged.c @@ -6,27 +6,33 @@ license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd */ -#include "merged.h" - -#include "system.h" +#include "test-lib.h" +#include "reftable/blocksource.h" +#include "reftable/constants.h" +#include "reftable/merged.h" +#include "reftable/reader.h" +#include "reftable/reftable-error.h" +#include "reftable/reftable-generic.h" +#include "reftable/reftable-merged.h" +#include "reftable/reftable-writer.h" + +static ssize_t strbuf_add_void(void *b, const void *data, const size_t sz) +{ + strbuf_add(b, data, sz); + return sz; +} -#include "basics.h" -#include "blocksource.h" -#include "constants.h" -#include "reader.h" -#include "record.h" -#include "test_framework.h" -#include "reftable-merged.h" -#include "reftable-tests.h" -#include "reftable-generic.h" -#include "reftable-writer.h" +static int noop_flush(void *arg) +{ + return 0; +} static void write_test_table(struct strbuf *buf, - struct reftable_ref_record refs[], int n) + struct reftable_ref_record refs[], const size_t n) { uint64_t min = 0xffffffff; uint64_t max = 0; - int i = 0; + size_t i; int err; struct reftable_write_options opts = { @@ -35,12 +41,10 @@ static void write_test_table(struct strbuf *buf, struct reftable_writer *w = NULL; for (i = 0; i < n; i++) { uint64_t ui = refs[i].update_index; - if (ui > max) { + if (ui > max) max = ui; - } - if (ui < min) { + if (ui < min) min = ui; - } } w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); @@ -49,21 +53,19 @@ static void write_test_table(struct strbuf *buf, for (i = 0; i < n; i++) { uint64_t before = refs[i].update_index; int n = reftable_writer_add_ref(w, &refs[i]); - EXPECT(n == 0); - EXPECT(before == refs[i].update_index); + check_int(n, ==, 0); + check_int(before, ==, refs[i].update_index); } err = reftable_writer_close(w); - EXPECT_ERR(err); + check(!err); reftable_writer_free(w); } -static void write_test_log_table(struct strbuf *buf, - struct reftable_log_record logs[], int n, - uint64_t update_index) +static void write_test_log_table(struct strbuf *buf, struct reftable_log_record logs[], + const size_t n, const uint64_t update_index) { - int i = 0; int err; struct reftable_write_options opts = { @@ -74,13 +76,13 @@ static void write_test_log_table(struct strbuf *buf, w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); reftable_writer_set_limits(w, update_index, update_index); - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { int err = reftable_writer_add_log(w, &logs[i]); - EXPECT_ERR(err); + check(!err); } err = reftable_writer_close(w); - EXPECT_ERR(err); + check(!err); reftable_writer_free(w); } @@ -88,8 +90,8 @@ static void write_test_log_table(struct strbuf *buf, static struct reftable_merged_table * merged_table_from_records(struct reftable_ref_record **refs, struct reftable_block_source **source, - struct reftable_reader ***readers, int *sizes, - struct strbuf *buf, size_t n) + struct reftable_reader ***readers, const size_t *sizes, + struct strbuf *buf, const size_t n) { struct reftable_merged_table *mt = NULL; struct reftable_table *tabs; @@ -105,24 +107,23 @@ merged_table_from_records(struct reftable_ref_record **refs, err = reftable_new_reader(&(*readers)[i], &(*source)[i], "name"); - EXPECT_ERR(err); + check(!err); reftable_table_from_reader(&tabs[i], (*readers)[i]); } err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); - EXPECT_ERR(err); + check(!err); return mt; } -static void readers_destroy(struct reftable_reader **readers, size_t n) +static void readers_destroy(struct reftable_reader **readers, const size_t n) { - int i = 0; - for (; i < n; i++) + for (size_t i = 0; i < n; i++) reftable_reader_free(readers[i]); reftable_free(readers); } -static void test_merged_between(void) +static void t_merged_single_record(void) { struct reftable_ref_record r1[] = { { .refname = (char *) "b", @@ -135,37 +136,40 @@ static void test_merged_between(void) .update_index = 2, .value_type = REFTABLE_REF_DELETION, } }; + struct reftable_ref_record r3[] = { { + .refname = (char *) "c", + .update_index = 3, + .value_type = REFTABLE_REF_DELETION, + } }; - struct reftable_ref_record *refs[] = { r1, r2 }; - int sizes[] = { 1, 1 }; - struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT }; + struct reftable_ref_record *refs[] = { r1, r2, r3 }; + size_t sizes[] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; + struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; struct reftable_block_source *bs = NULL; struct reftable_reader **readers = NULL; struct reftable_merged_table *mt = - merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2); - int i; - struct reftable_ref_record ref = { NULL }; - struct reftable_iterator it = { NULL }; + merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3); + struct reftable_ref_record ref = { 0 }; + struct reftable_iterator it = { 0 }; int err; merged_table_init_iter(mt, &it, BLOCK_TYPE_REF); err = reftable_iterator_seek_ref(&it, "a"); - EXPECT_ERR(err); + check(!err); err = reftable_iterator_next_ref(&it, &ref); - EXPECT_ERR(err); - EXPECT(ref.update_index == 2); + check(!err); + check(reftable_ref_record_equal(&r2[0], &ref, GIT_SHA1_RAWSZ)); reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); - readers_destroy(readers, 2); + readers_destroy(readers, 3); reftable_merged_table_free(mt); - for (i = 0; i < ARRAY_SIZE(bufs); i++) { + for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) strbuf_release(&bufs[i]); - } reftable_free(bs); } -static void test_merged(void) +static void t_merged_refs(void) { struct reftable_ref_record r1[] = { { @@ -215,27 +219,28 @@ static void test_merged(void) }; struct reftable_ref_record *refs[] = { r1, r2, r3 }; - int sizes[3] = { 3, 1, 2 }; + size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; struct reftable_block_source *bs = NULL; struct reftable_reader **readers = NULL; struct reftable_merged_table *mt = merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3); - struct reftable_iterator it = { NULL }; + struct reftable_iterator it = { 0 }; int err; struct reftable_ref_record *out = NULL; size_t len = 0; size_t cap = 0; - int i = 0; + size_t i; merged_table_init_iter(mt, &it, BLOCK_TYPE_REF); err = reftable_iterator_seek_ref(&it, "a"); - EXPECT_ERR(err); - EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID); - EXPECT(reftable_merged_table_min_update_index(mt) == 1); + check(!err); + check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID); + check_int(reftable_merged_table_min_update_index(mt), ==, 1); + check_int(reftable_merged_table_max_update_index(mt), ==, 3); while (len < 100) { /* cap loops/recursion. */ - struct reftable_ref_record ref = { NULL }; + struct reftable_ref_record ref = { 0 }; int err = reftable_iterator_next_ref(&it, &ref); if (err > 0) break; @@ -245,19 +250,16 @@ static void test_merged(void) } reftable_iterator_destroy(&it); - EXPECT(ARRAY_SIZE(want) == len); - for (i = 0; i < len; i++) { - EXPECT(reftable_ref_record_equal(want[i], &out[i], + check_int(ARRAY_SIZE(want), ==, len); + for (i = 0; i < len; i++) + check(reftable_ref_record_equal(want[i], &out[i], GIT_SHA1_RAWSZ)); - } - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) reftable_ref_record_release(&out[i]); - } reftable_free(out); - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) strbuf_release(&bufs[i]); - } readers_destroy(readers, 3); reftable_merged_table_free(mt); reftable_free(bs); @@ -266,8 +268,8 @@ static void test_merged(void) static struct reftable_merged_table * merged_table_from_log_records(struct reftable_log_record **logs, struct reftable_block_source **source, - struct reftable_reader ***readers, int *sizes, - struct strbuf *buf, size_t n) + struct reftable_reader ***readers, const size_t *sizes, + struct strbuf *buf, const size_t n) { struct reftable_merged_table *mt = NULL; struct reftable_table *tabs; @@ -283,16 +285,16 @@ merged_table_from_log_records(struct reftable_log_record **logs, err = reftable_new_reader(&(*readers)[i], &(*source)[i], "name"); - EXPECT_ERR(err); + check(!err); reftable_table_from_reader(&tabs[i], (*readers)[i]); } err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); - EXPECT_ERR(err); + check(!err); return mt; } -static void test_merged_logs(void) +static void t_merged_logs(void) { struct reftable_log_record r1[] = { { @@ -347,27 +349,28 @@ static void test_merged_logs(void) }; struct reftable_log_record *logs[] = { r1, r2, r3 }; - int sizes[3] = { 2, 1, 1 }; + size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; struct reftable_block_source *bs = NULL; struct reftable_reader **readers = NULL; struct reftable_merged_table *mt = merged_table_from_log_records( logs, &bs, &readers, sizes, bufs, 3); - struct reftable_iterator it = { NULL }; + struct reftable_iterator it = { 0 }; int err; struct reftable_log_record *out = NULL; size_t len = 0; size_t cap = 0; - int i = 0; + size_t i; merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG); err = reftable_iterator_seek_log(&it, "a"); - EXPECT_ERR(err); - EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID); - EXPECT(reftable_merged_table_min_update_index(mt) == 1); + check(!err); + check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID); + check_int(reftable_merged_table_min_update_index(mt), ==, 1); + check_int(reftable_merged_table_max_update_index(mt), ==, 3); while (len < 100) { /* cap loops/recursion. */ - struct reftable_log_record log = { NULL }; + struct reftable_log_record log = { 0 }; int err = reftable_iterator_next_log(&it, &log); if (err > 0) break; @@ -377,35 +380,32 @@ static void test_merged_logs(void) } reftable_iterator_destroy(&it); - EXPECT(ARRAY_SIZE(want) == len); - for (i = 0; i < len; i++) { - EXPECT(reftable_log_record_equal(want[i], &out[i], + check_int(ARRAY_SIZE(want), ==, len); + for (i = 0; i < len; i++) + check(reftable_log_record_equal(want[i], &out[i], GIT_SHA1_RAWSZ)); - } merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG); err = reftable_iterator_seek_log_at(&it, "a", 2); - EXPECT_ERR(err); + check(!err); reftable_log_record_release(&out[0]); err = reftable_iterator_next_log(&it, &out[0]); - EXPECT_ERR(err); - EXPECT(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ)); + check(!err); + check(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ)); reftable_iterator_destroy(&it); - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) reftable_log_record_release(&out[i]); - } reftable_free(out); - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) strbuf_release(&bufs[i]); - } readers_destroy(readers, 3); reftable_merged_table_free(mt); reftable_free(bs); } -static void test_default_write_opts(void) +static void t_default_write_opts(void) { struct reftable_write_options opts = { 0 }; struct strbuf buf = STRBUF_INIT; @@ -417,7 +417,7 @@ static void test_default_write_opts(void) .update_index = 1, }; int err; - struct reftable_block_source source = { NULL }; + struct reftable_block_source source = { 0 }; struct reftable_table *tab = reftable_calloc(1, sizeof(*tab)); uint32_t hash_id; struct reftable_reader *rd = NULL; @@ -426,36 +426,38 @@ static void test_default_write_opts(void) reftable_writer_set_limits(w, 1, 1); err = reftable_writer_add_ref(w, &rec); - EXPECT_ERR(err); + check(!err); err = reftable_writer_close(w); - EXPECT_ERR(err); + check(!err); reftable_writer_free(w); block_source_from_strbuf(&source, &buf); err = reftable_new_reader(&rd, &source, "filename"); - EXPECT_ERR(err); + check(!err); hash_id = reftable_reader_hash_id(rd); - EXPECT(hash_id == GIT_SHA1_FORMAT_ID); + check_int(hash_id, ==, GIT_SHA1_FORMAT_ID); reftable_table_from_reader(&tab[0], rd); + err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA256_FORMAT_ID); + check_int(err, ==, REFTABLE_FORMAT_ERROR); err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID); - EXPECT_ERR(err); + check(!err); reftable_reader_free(rd); reftable_merged_table_free(merged); strbuf_release(&buf); } -/* XXX test refs_for(oid) */ -int merged_test_main(int argc, const char *argv[]) +int cmd_main(int argc, const char *argv[]) { - RUN_TEST(test_merged_logs); - RUN_TEST(test_merged_between); - RUN_TEST(test_merged); - RUN_TEST(test_default_write_opts); - return 0; + TEST(t_default_write_opts(), "merged table with default write opts"); + TEST(t_merged_logs(), "merged table with multiple log updates for same ref"); + TEST(t_merged_refs(), "merged table with multiple updates to same ref"); + TEST(t_merged_single_record(), "ref ocurring in only one record can be fetched"); + + return test_done(); } diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c index d4615ab06d..fa1a041469 100644 --- a/t/unit-tests/t-strvec.c +++ b/t/unit-tests/t-strvec.c @@ -3,38 +3,21 @@ #include "strvec.h" #define check_strvec(vec, ...) \ - check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__) -LAST_ARG_MUST_BE_NULL -static void check_strvec_loc(const char *loc, struct strvec *vec, ...) -{ - va_list ap; - size_t nr = 0; - - va_start(ap, vec); - while (1) { - const char *str = va_arg(ap, const char *); - if (!str) - break; - - if (!check_uint(vec->nr, >, nr) || - !check_uint(vec->alloc, >, nr) || - !check_str(vec->v[nr], str)) { - struct strbuf msg = STRBUF_INIT; - strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr); - test_assert(loc, msg.buf, 0); - strbuf_release(&msg); - va_end(ap); - return; - } - - nr++; - } - va_end(ap); - - check_uint(vec->nr, ==, nr); - check_uint(vec->alloc, >=, nr); - check_pointer_eq(vec->v[nr], NULL); -} + do { \ + const char *expect[] = { __VA_ARGS__ }; \ + if (check_uint(ARRAY_SIZE(expect), >, 0) && \ + check_pointer_eq(expect[ARRAY_SIZE(expect) - 1], NULL) && \ + check_uint((vec)->nr, ==, ARRAY_SIZE(expect) - 1) && \ + check_uint((vec)->nr, <=, (vec)->alloc)) { \ + for (size_t i = 0; i < ARRAY_SIZE(expect); i++) { \ + if (!check_str((vec)->v[i], expect[i])) { \ + test_msg(" i: %"PRIuMAX, \ + (uintmax_t)i); \ + break; \ + } \ + } \ + } \ + } while (0) static void t_static_init(void) { diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h index 2de6d715d5..c59f646fd9 100644 --- a/t/unit-tests/test-lib.h +++ b/t/unit-tests/test-lib.h @@ -76,8 +76,9 @@ int test_assert(const char *location, const char *check, int ok); int check_bool_loc(const char *loc, const char *check, int ok); /* - * Compare two integers. Prints a message with the two values if the - * comparison fails. NB this is not thread safe. + * Compare the equality of two pointers of same type. Prints a message + * with the two values if the equality fails. NB this is not thread + * safe. */ #define check_pointer_eq(a, b) \ (test__tmp[0].p = (a), test__tmp[1].p = (b), \ |
